From 08cdb94daebd5318c5835e86d06fab791f2d9e1d Mon Sep 17 00:00:00 2001 From: crispycat Date: Sun, 6 Oct 2024 23:07:49 -0400 Subject: [PATCH] slug checks, asset editor improvements --- .../Actions/Backend/AssetEditorAction.php | 172 +++++++++--------- core/app/assets/AssetManager.php | 17 ++ 2 files changed, 104 insertions(+), 85 deletions(-) diff --git a/core/actions/Crispage/Actions/Backend/AssetEditorAction.php b/core/actions/Crispage/Actions/Backend/AssetEditorAction.php index dddf350..883c56d 100644 --- a/core/actions/Crispage/Actions/Backend/AssetEditorAction.php +++ b/core/actions/Crispage/Actions/Backend/AssetEditorAction.php @@ -3,7 +3,7 @@ Crispage CMS crispycat https://crispy.cat/software/crispage - + Crispage is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -14,7 +14,6 @@ defined("ROOT") or die(); - use \Crispage\Exceptions\AssetException; use \Crispage\Utils\URIUtils; use \Crispage\Framework\Asset; @@ -26,81 +25,69 @@ ]; } - public function __construct(\Crispage $app, array $data) { - parent::__construct($app, $data); - } - public function run(): void { - // Get class name - $this->data["classname"] = strval( - $this->app->request->params["classname"] ?? "\\Crispage\\Framework\\Asset" - ); + // get asset + $this->data["asset_id"] = intval($this->app->request->params["asset_id"] ?? 0); + $this->data["asset"] = $this->app->assets->get($this->data["asset_id"]); - // Check class is valid - if (!class_exists($this->data["classname"])) - throw new AssetException("Class {$this->data["classname"]} does not exist"); - if (!is_a($this->data["classname"], "\\Crispage\\Framework\\Asset", true)) - throw new AssetException("Class {$this->data["classname"]} is not an Asset"); + $edit = false; + + if ($this->data["asset"]) { + $edit = true; + $this->data["classname"] = "\\" . $this->data["asset"]::class; + } + + else { + // get and validate classname string + $this->data["classname"] = strval($this->app->request->params["classname"] ?? ""); + + if ( + !class_exists($this->data["classname"]) || + !is_a($this->data["classname"], "\\Crispage\\Framework\\Asset", true) + ) { + $this->app->page->setPersistentMessage( + "invalid_asset_class", + "Invalid asset class {$this->data["classname"]}", + "danger" + ); + $this->app->page->redirect(URIUtils::iuri("/backend", ["route" => "dashboard"])); + } + } $this->data["list_uri"] = URIUtils::iuri("/backend", [ "route" => "assets/list", "classname" => $this->data["classname"] ]); - // Get asset id - $this->data["asset_id"] = intval($this->app->request->params["asset_id"] ?? 0); - - // If id is not 0, edit mode - if ($this->data["asset_id"]) { - // Get asset - $this->data["asset"] = $this->app->assets->get($this->data["asset_id"]); - - // Check that asset exists - if (!$this->data["asset"]) { - $this->app->page->setPersistentMessage( - "asset_not_exist", - $this->app->i18n->translate( - "{%ASSET_WITH_ID_X_DOES_NOT_EXIST}", - $this->data["asset_id"] - ), - "danger" + if (!$this->data["asset"] && $this->data["asset_id"] > 0) { + $this->app->page->setPersistentMessage( + "asset_not_exist", + $this->app->i18n->translate( + "{%ASSET_WITH_ID_X_DOES_NOT_EXIST}", + $this->data["asset_id"] + ), + "danger" ); - $this->app->page->redirect($this->data["list_uri"]); - } + $this->app->page->redirect($this->data["list_uri"]); + } - $this->data["classname"] = "\\" . $this->data["asset"]::class; - - // Check permissions + // permission check + if ($edit) { if ($this->data["asset"]->getOwnerId() == $this->app->auth->currentUser->id) $perm = $this->data["asset"]::getPermissions()["modify_own"]; else $perm = $this->data["asset"]::getPermissions()["modify"]; - - if (!$this->app->auth->userHasPermission($perm)) { - $this->app->page->setPersistentMessage( - "no_permission", - $this->app->i18n->translate( - "{%YOU_DO_NOT_HAVE_PERMISSION__THIS_ACTION}", - ), - "danger" - ); - $this->app->page->redirect($this->data["list_uri"]); - } } - // Otherwise, create mode - else { - $this->data["asset"] = null; + else $perm = $this->data["classname"]::getPermissions()["create"]; - $perm = $this->data["classname"]::getPermissions()["create"]; - if (!$this->app->auth->userHasPermission($perm)) { - $this->app->page->setPersistentMessage( - "no_permission", - $this->app->i18n->translate( - "{%YOU_DO_NOT_HAVE_PERMISSION__THIS_ACTION}", - ), - "danger" - ); - $this->app->page->redirect($this->data["list_uri"]); - } + if (!$this->app->auth->userHasPermission($perm)) { + $this->app->page->setPersistentMessage( + "no_permission", + $this->app->i18n->translate( + "{%YOU_DO_NOT_HAVE_PERMISSION__THIS_ACTION}", + ), + "danger" + ); + $this->app->page->redirect($this->data["list_uri"]); } $this->data["fields"] = $this->data["classname"]::getEditorFields(); @@ -109,6 +96,7 @@ // If fields given, create or edit the asset $data = $this->app->request->params["fields"] ?? null; if (!empty($data) && is_array($data)) { + // create temporary asset if (!$this->data["asset"]) { $this->data["asset"] = $this->app->assets->create( $this->data["classname"], [], true @@ -116,6 +104,7 @@ } foreach ($this->data["fields"] as $field) { + if ($field->name == "slug") continue; $this->data["asset"]->{$field->name} = $field->filter( $this->app, $this->app->request->params["fields"][$field->name] ?? null, $this->data["asset"]->{$field->name} @@ -132,42 +121,55 @@ ); } - // Automatic slug - if (empty($this->data["asset"]->slug)) - $this->data["asset"]->slug = Asset::slug($this->data["asset"]->getName()); - else $this->data["asset"]->slug = Asset::slug($this->data["asset"]->slug); + $slug = $this->app->request->params["fields"]["slug"] ?? ""; + if (empty($slug)) $slug = $this->data["asset"]->getName(); + $slug = Asset::slug($slug); - // Check slug for duplicates - $asset = $this->app->assets->getBySlug( - $this->data["classname"], $this->data["asset"]->slug - ); - if ($asset && $asset->id != $this->data["asset_id"]) - $this->data["asset"]->slug .= "_" . time(); + if (!$edit && $slug != $this->data["asset"]->slug) { + $existing = $this->app->assets->getBySlug($this->data["classname"], $slug, null); + if ($existing && $existing->id != $this->data["asset"]->id) + $slug = uniqid("{$slug}_"); + } - // Save - if ($this->data["asset"]->isTemporary()) - $this->data["asset"] = $this->app->assets->makePermanent($this->data["asset"]); - else $this->app->assets->set($this->data["asset"]); + $this->data["asset"]->slug = $slug; - // Set message - $this->app->page->data["messages"]["changes_saved"] = [ - "color" => "success", - "content" => $this->app->i18n->translate("{%CHANGES_SAVED}") - ]; + if ($edit) { + $this->app->assets->set($this->data["asset"]); + $this->app->page->data["messages"]["changes_saved"] = [ + "color" => "success", + "content" => $this->app->i18n->translate("{%CHANGES_SAVED}") + ]; + } + + else { + $asset = $this->app->assets->makePermanent($this->data["asset"]); + $this->app->page->setPersistentMessage( + "changes_saved", + $this->app->i18n->translate("{%CHANGES_SAVED}"), + "success" + ); + $this->app->page->redirect( + URIUtils::iuri("/backend", [ + "route" => "assets/editor", + "asset_id" => $asset->id + ]) + ); + } } - // Set title - if ($this->data["asset"]) { - $this->app->page->data["title"] = $this->app->i18n->translate( + // title + if ($edit) { + $title = $this->app->i18n->translate( "{%EDITOR_TITLE_EDIT}", $this->data["asset"]->getName() ); } else { - $this->app->page->data["title"] = $this->app->i18n->translate( + $title = $this->app->i18n->translate( "{%EDITOR_TITLE_CREATE}", $this->app->i18n->translate($this->data["classname"]::getNames()[0]) ); } + $this->app->page->data["title"] = $title; // Set main component $com = $this->app->page->createComponent( diff --git a/core/app/assets/AssetManager.php b/core/app/assets/AssetManager.php index 1319476..cdbe2a4 100644 --- a/core/app/assets/AssetManager.php +++ b/core/app/assets/AssetManager.php @@ -341,6 +341,15 @@ if ($asset->isTemporary()) return; $asset->mtime = time(); + // check for conflicting slug + $res = $this->app->database->select("assets", null, [ + "classname" => "\\" . $asset::class, + "slug" => $asset->slug, + "id" => [$asset->id, $this->app->database::OP_NOT_EQUAL] + ]); + if ($res->fetch() !== false) + throw new AssetException("Asset of type " . $asset::class . " with slug {$asset->slug} already exists"); + // Update tables $this->app->database->update( $asset->_table, $asset->toArray(), ["id" => $asset->id] @@ -402,6 +411,14 @@ public function makePermanent(Asset $asset): Asset { if (!$asset->isTemporary()) return $asset; + // check for conflicting slug + $res = $this->app->database->select("assets", null, [ + "classname" => "\\" . $asset::class, + "slug" => $asset->slug + ]); + if ($res->fetch() !== false) + throw new AssetException("Asset of type " . $asset::class . " with slug {$asset->slug} already exists"); + $data = $asset->toArray(); $data["id"] = $this->getNewID(); $data["_table"] = $this->getTable("\\" . $asset::class);