]> BookStack Code Mirror - bookstack/blobdiff - app/Permissions/PermissionApplicator.php
ZIP Imports: Added API examples, finished testing
[bookstack] / app / Permissions / PermissionApplicator.php
index b4fafaa9ee1756f6604d5cc20f46e2091ed5f787..ce4a543fd8374e5882fe032cb82152f7dfdb70e2 100644 (file)
@@ -3,23 +3,29 @@
 namespace BookStack\Permissions;
 
 use BookStack\App\Model;
+use BookStack\Entities\EntityProvider;
 use BookStack\Entities\Models\Entity;
 use BookStack\Entities\Models\Page;
 use BookStack\Permissions\Models\EntityPermission;
 use BookStack\Users\Models\HasCreatorAndUpdater;
 use BookStack\Users\Models\HasOwner;
-use BookStack\Users\Models\Role;
 use BookStack\Users\Models\User;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Query\Builder as QueryBuilder;
+use Illuminate\Database\Query\JoinClause;
 use InvalidArgumentException;
 
 class PermissionApplicator
 {
+    public function __construct(
+        protected ?User $user = null
+    ) {
+    }
+
     /**
      * Checks if an entity has a restriction set upon it.
      *
-     * @param HasCreatorAndUpdater|HasOwner $ownable
+     * @param Model&(HasCreatorAndUpdater|HasOwner) $ownable
      */
     public function checkOwnableUserAccess(Model $ownable, string $permission): bool
     {
@@ -143,6 +149,41 @@ class PermissionApplicator
             });
     }
 
+    /**
+     * Filter out items that have related entity relations where
+     * the entity is marked as deleted.
+     */
+    public function filterDeletedFromEntityRelationQuery(Builder $query, string $tableName, string $entityIdColumn, string $entityTypeColumn): Builder
+    {
+        $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn];
+        $entityProvider = new EntityProvider();
+
+        $joinQuery = function ($query) use ($entityProvider) {
+            $first = true;
+            foreach ($entityProvider->all() as $entity) {
+                /** @var Builder $query */
+                $entityQuery = function ($query) use ($entity) {
+                    $query->select(['id', 'deleted_at'])
+                        ->selectRaw("'{$entity->getMorphClass()}' as type")
+                        ->from($entity->getTable())
+                        ->whereNotNull('deleted_at');
+                };
+
+                if ($first) {
+                    $entityQuery($query);
+                    $first = false;
+                } else {
+                    $query->union($entityQuery);
+                }
+            }
+        };
+
+        return $query->leftJoinSub($joinQuery, 'deletions', function (JoinClause $join) use ($tableDetails) {
+            $join->on($tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'], '=', 'deletions.id')
+                ->on($tableDetails['tableName'] . '.' . $tableDetails['entityTypeColumn'], '=', 'deletions.type');
+        })->whereNull('deletions.deleted_at');
+    }
+
     /**
      * Add conditions to a query for a model that's a relation of a page, so only the model results
      * on visible pages are returned by the query.
@@ -173,7 +214,7 @@ class PermissionApplicator
      */
     protected function currentUser(): User
     {
-        return user();
+        return $this->user ?? user();
     }
 
     /**