* @link https://getkirby.com * @copyright Bastian Allgeier * @license https://getkirby.com/license */ class PageRules { /** * Validates if the sorting number of the page can be changed * * @param \Kirby\Cms\Page $page * @param int|null $num * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the given number is invalid */ public static function changeNum(Page $page, int $num = null): bool { if ($num !== null && $num < 0) { throw new InvalidArgumentException(['key' => 'page.num.invalid']); } return true; } /** * Validates if the slug for the page can be changed * * @param \Kirby\Cms\Page $page * @param string $slug * @return bool * @throws \Kirby\Exception\DuplicateException If a page with this slug already exists * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the slug */ public static function changeSlug(Page $page, string $slug): bool { if ($page->permissions()->changeSlug() !== true) { throw new PermissionException([ 'key' => 'page.changeSlug.permission', 'data' => [ 'slug' => $page->slug() ] ]); } self::validateSlugLength($slug); $siblings = $page->parentModel()->children(); $drafts = $page->parentModel()->drafts(); if ($duplicate = $siblings->find($slug)) { if ($duplicate->is($page) === false) { throw new DuplicateException([ 'key' => 'page.duplicate', 'data' => [ 'slug' => $slug ] ]); } } if ($duplicate = $drafts->find($slug)) { if ($duplicate->is($page) === false) { throw new DuplicateException([ 'key' => 'page.draft.duplicate', 'data' => [ 'slug' => $slug ] ]); } } return true; } /** * Validates if the status for the page can be changed * * @param \Kirby\Cms\Page $page * @param string $status * @param int|null $position * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the given status is invalid */ public static function changeStatus(Page $page, string $status, int $position = null): bool { if (isset($page->blueprint()->status()[$status]) === false) { throw new InvalidArgumentException(['key' => 'page.status.invalid']); } switch ($status) { case 'draft': return static::changeStatusToDraft($page); case 'listed': return static::changeStatusToListed($page, $position); case 'unlisted': return static::changeStatusToUnlisted($page); default: throw new InvalidArgumentException(['key' => 'page.status.invalid']); } } /** * Validates if a page can be converted to a draft * * @param \Kirby\Cms\Page $page * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status or the page cannot be converted to a draft */ public static function changeStatusToDraft(Page $page) { if ($page->permissions()->changeStatus() !== true) { throw new PermissionException([ 'key' => 'page.changeStatus.permission', 'data' => [ 'slug' => $page->slug() ] ]); } if ($page->isHomeOrErrorPage() === true) { throw new PermissionException([ 'key' => 'page.changeStatus.toDraft.invalid', 'data' => [ 'slug' => $page->slug() ] ]); } return true; } /** * Validates if the status of a page can be changed to listed * * @param \Kirby\Cms\Page $page * @param int $position * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the given position is invalid * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status or the status for the page cannot be changed by any user */ public static function changeStatusToListed(Page $page, int $position) { // no need to check for status changing permissions, // instead we need to check for sorting permissions if ($page->isListed() === true) { if ($page->isSortable() !== true) { throw new PermissionException([ 'key' => 'page.sort.permission', 'data' => [ 'slug' => $page->slug() ] ]); } return true; } static::publish($page); if ($position !== null && $position < 0) { throw new InvalidArgumentException(['key' => 'page.num.invalid']); } return true; } /** * Validates if the status of a page can be changed to unlisted * * @param \Kirby\Cms\Page $page * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status */ public static function changeStatusToUnlisted(Page $page) { static::publish($page); return true; } /** * Validates if the template of the page can be changed * * @param \Kirby\Cms\Page $page * @param string $template * @return bool * @throws \Kirby\Exception\LogicException If the template of the page cannot be changed at all * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the template */ public static function changeTemplate(Page $page, string $template): bool { if ($page->permissions()->changeTemplate() !== true) { throw new PermissionException([ 'key' => 'page.changeTemplate.permission', 'data' => [ 'slug' => $page->slug() ] ]); } if (count($page->blueprints()) <= 1) { throw new LogicException([ 'key' => 'page.changeTemplate.invalid', 'data' => ['slug' => $page->slug()] ]); } return true; } /** * Validates if the title of the page can be changed * * @param \Kirby\Cms\Page $page * @param string $title * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the new title is empty * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the title */ public static function changeTitle(Page $page, string $title): bool { if ($page->permissions()->changeTitle() !== true) { throw new PermissionException([ 'key' => 'page.changeTitle.permission', 'data' => [ 'slug' => $page->slug() ] ]); } if (Str::length($title) === 0) { throw new InvalidArgumentException([ 'key' => 'page.changeTitle.empty', ]); } return true; } /** * Validates if the page can be created * * @param \Kirby\Cms\Page $page * @return bool * @throws \Kirby\Exception\DuplicateException If the same page or a draft already exists * @throws \Kirby\Exception\InvalidArgumentException If the slug is invalid * @throws \Kirby\Exception\PermissionException If the user is not allowed to create this page */ public static function create(Page $page): bool { if ($page->permissions()->create() !== true) { throw new PermissionException([ 'key' => 'page.create.permission', 'data' => [ 'slug' => $page->slug() ] ]); } self::validateSlugLength($page->slug()); if ($page->exists() === true) { throw new DuplicateException([ 'key' => 'page.draft.duplicate', 'data' => [ 'slug' => $page->slug() ] ]); } $siblings = $page->parentModel()->children(); $drafts = $page->parentModel()->drafts(); $slug = $page->slug(); if ($siblings->find($slug)) { throw new DuplicateException([ 'key' => 'page.duplicate', 'data' => ['slug' => $slug] ]); } if ($drafts->find($slug)) { throw new DuplicateException([ 'key' => 'page.draft.duplicate', 'data' => ['slug' => $slug] ]); } return true; } /** * Validates if the page can be deleted * * @param \Kirby\Cms\Page $page * @param bool $force * @return bool * @throws \Kirby\Exception\LogicException If the page has children and should not be force-deleted * @throws \Kirby\Exception\PermissionException If the user is not allowed to delete the page */ public static function delete(Page $page, bool $force = false): bool { if ($page->permissions()->delete() !== true) { throw new PermissionException([ 'key' => 'page.delete.permission', 'data' => [ 'slug' => $page->slug() ] ]); } if (($page->hasChildren() === true || $page->hasDrafts() === true) && $force === false) { throw new LogicException(['key' => 'page.delete.hasChildren']); } return true; } /** * Validates if the page can be duplicated * * @param \Kirby\Cms\Page $page * @param string $slug * @param array $options * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to duplicate the page */ public static function duplicate(Page $page, string $slug, array $options = []): bool { if ($page->permissions()->duplicate() !== true) { throw new PermissionException([ 'key' => 'page.duplicate.permission', 'data' => [ 'slug' => $page->slug() ] ]); } self::validateSlugLength($slug); return true; } /** * Check if the page can be published * (status change from draft to listed or unlisted) * * @param Page $page * @return bool */ public static function publish(Page $page): bool { if ($page->permissions()->changeStatus() !== true) { throw new PermissionException([ 'key' => 'page.changeStatus.permission', 'data' => [ 'slug' => $page->slug() ] ]); } if ($page->isDraft() === true && empty($page->errors()) === false) { throw new PermissionException([ 'key' => 'page.changeStatus.incomplete', 'details' => $page->errors() ]); } return true; } /** * Validates if the page can be updated * * @param \Kirby\Cms\Page $page * @param array $content * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to update the page */ public static function update(Page $page, array $content = []): bool { if ($page->permissions()->update() !== true) { throw new PermissionException([ 'key' => 'page.update.permission', 'data' => [ 'slug' => $page->slug() ] ]); } return true; } /** * Ensures that the slug is not empty and doesn't exceed the maximum length * to make sure that the directory name will be accepted by the filesystem * * @param string $slug New slug to check * @return void * @throws \Kirby\Exception\InvalidArgumentException If the slug is empty or too long */ protected static function validateSlugLength(string $slug): void { $slugLength = Str::length($slug); if ($slugLength === 0) { throw new InvalidArgumentException([ 'key' => 'page.slug.invalid', ]); } if ($slugsMaxlength = App::instance()->option('slugs.maxlength', 255)) { $maxlength = (int)$slugsMaxlength; if ($slugLength > $maxlength) { throw new InvalidArgumentException([ 'key' => 'page.slug.maxlength', 'data' => [ 'length' => $maxlength ] ]); } } } }