87 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
			
		
		
	
	
			87 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
<?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
 | 
						|
{
 | 
						|
    /**
 | 
						|
     * @var int[]
 | 
						|
     */
 | 
						|
    protected array $watchers = [];
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var int[]
 | 
						|
     */
 | 
						|
    protected array $ignorers = [];
 | 
						|
 | 
						|
    public function __construct(
 | 
						|
        protected Entity $entity,
 | 
						|
        protected int $watchLevel,
 | 
						|
    ) {
 | 
						|
        $this->build();
 | 
						|
    }
 | 
						|
 | 
						|
    public function getWatcherUserIds(): array
 | 
						|
    {
 | 
						|
        return $this->watchers;
 | 
						|
    }
 | 
						|
 | 
						|
    public function isUserIgnoring(int $userId): bool
 | 
						|
    {
 | 
						|
        return in_array($userId, $this->ignorers);
 | 
						|
    }
 | 
						|
 | 
						|
    protected function build(): void
 | 
						|
    {
 | 
						|
        $watches = $this->getRelevantWatches();
 | 
						|
 | 
						|
        // 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));
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @return Watch[]
 | 
						|
     */
 | 
						|
    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([
 | 
						|
            'level', 'watchable_id', 'watchable_type', 'user_id'
 | 
						|
        ])->all();
 | 
						|
    }
 | 
						|
}
 |