| 
									
										
										
										
											2023-08-04 19:27:29 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace BookStack\Activity\Tools; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | use BookStack\Activity\Models\Watch; | 
					
						
							|  |  |  | use BookStack\Entities\Models\BookChild; | 
					
						
							|  |  |  | use BookStack\Entities\Models\Entity; | 
					
						
							|  |  |  | use BookStack\Entities\Models\Page; | 
					
						
							|  |  |  | use Illuminate\Database\Eloquent\Builder; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class EntityWatchers | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-08-04 23:51:29 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * @var int[] | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2023-08-04 19:27:29 +08:00
										 |  |  |     protected array $watchers = []; | 
					
						
							| 
									
										
										
										
											2023-08-04 23:51:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * @var int[] | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2023-08-04 19:27:29 +08:00
										 |  |  |     protected array $ignorers = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function __construct( | 
					
						
							|  |  |  |         protected Entity $entity, | 
					
						
							|  |  |  |         protected int $watchLevel, | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |         $this->build(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-04 23:51:29 +08:00
										 |  |  |     public function getWatcherUserIds(): array | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return $this->watchers; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-05 21:19:23 +08:00
										 |  |  |     public function isUserIgnoring(int $userId): bool | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return in_array($userId, $this->ignorers); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-04 19:27:29 +08:00
										 |  |  |     protected function build(): void | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $watches = $this->getRelevantWatches(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-04 23:51:29 +08:00
										 |  |  |         // Sort before de-duping, so that the order looped below follows book -> chapter -> page ordering
 | 
					
						
							|  |  |  |         usort($watches, function (Watch $watchA, Watch $watchB) { | 
					
						
							|  |  |  |             $entityTypeDiff = $watchA->watchable_type <=> $watchB->watchable_type; | 
					
						
							|  |  |  |             return $entityTypeDiff === 0 ? ($watchA->user_id <=> $watchB->user_id) : $entityTypeDiff; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // De-dupe by user id to get their most relevant level
 | 
					
						
							|  |  |  |         $levelByUserId = []; | 
					
						
							|  |  |  |         foreach ($watches as $watch) { | 
					
						
							|  |  |  |             $levelByUserId[$watch->user_id] = $watch->level; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Populate the class arrays
 | 
					
						
							|  |  |  |         $this->watchers = array_keys(array_filter($levelByUserId, fn(int $level) => $level >= $this->watchLevel)); | 
					
						
							|  |  |  |         $this->ignorers = array_keys(array_filter($levelByUserId, fn(int $level) => $level === 0)); | 
					
						
							| 
									
										
										
										
											2023-08-04 19:27:29 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-04 23:51:29 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * @return Watch[] | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2023-08-04 19:27:29 +08:00
										 |  |  |     protected function getRelevantWatches(): array | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         /** @var Entity[] $entitiesInvolved */ | 
					
						
							|  |  |  |         $entitiesInvolved = array_filter([ | 
					
						
							|  |  |  |             $this->entity, | 
					
						
							|  |  |  |             $this->entity instanceof BookChild ? $this->entity->book : null, | 
					
						
							|  |  |  |             $this->entity instanceof Page ? $this->entity->chapter : null, | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $query = Watch::query()->where(function (Builder $query) use ($entitiesInvolved) { | 
					
						
							|  |  |  |             foreach ($entitiesInvolved as $entity) { | 
					
						
							|  |  |  |                 $query->orWhere(function (Builder $query) use ($entity) { | 
					
						
							|  |  |  |                     $query->where('watchable_type', '=', $entity->getMorphClass()) | 
					
						
							|  |  |  |                         ->where('watchable_id', '=', $entity->id); | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $query->get([ | 
					
						
							| 
									
										
										
										
											2023-08-04 23:51:29 +08:00
										 |  |  |             'level', 'watchable_id', 'watchable_type', 'user_id' | 
					
						
							| 
									
										
										
										
											2023-08-04 19:27:29 +08:00
										 |  |  |         ])->all(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |