diff --git a/Admin/Install/Navigation.php b/Admin/Install/Navigation.php index f4bbdcc..433304f 100755 --- a/Admin/Install/Navigation.php +++ b/Admin/Install/Navigation.php @@ -29,14 +29,14 @@ class Navigation /** * Install navigation providing * - * @param string $path Module path * @param ApplicationAbstract $app Application + * @param string $path Module path * * @return void * * @since 1.0.0 */ - public static function install(string $path, ApplicationAbstract $app) : void + public static function install(ApplicationAbstract $app, string $path) : void { \Modules\Navigation\Admin\Installer::installExternal($app, ['path' => __DIR__ . '/Navigation.install.json']); } diff --git a/Admin/Install/db.json b/Admin/Install/db.json index 8004a7c..fa2f9f6 100755 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -80,6 +80,16 @@ "type": "TEXT", "null": false }, + "editor_doc_versioned": { + "name": "editor_doc_versioned", + "type": "TINYINT", + "null": false + }, + "editor_doc_version": { + "name": "editor_doc_version", + "type": "VARCHAR(50)", + "null": false + }, "editor_doc_virtual": { "name": "editor_doc_virtual", "type": "VARCHAR(255)", @@ -155,5 +165,56 @@ "foreignKey": "media_id" } } + }, + "editor_doc_versioned": { + "name": "editor_doc_versioned", + "fields": { + "editor_doc_versioned_id": { + "name": "editor_doc_versioned_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "editor_doc_versioned_title": { + "name": "editor_doc_versioned_title", + "type": "VARCHAR(255)", + "null": false + }, + "editor_doc_versioned_version": { + "name": "editor_doc_versioned_version", + "type": "VARCHAR(50)", + "null": false + }, + "editor_doc_versioned_content": { + "name": "editor_doc_versioned_content", + "type": "TEXT", + "null": false + }, + "editor_doc_versioned_plain": { + "name": "editor_doc_versioned_plain", + "type": "TEXT", + "null": false + }, + "editor_doc_versioned_doc": { + "name": "editor_doc_versioned_doc", + "type": "INT", + "null": false, + "foreignTable": "editor_doc", + "foreignKey": "editor_doc_id" + }, + "editor_doc_versioned_at": { + "name": "editor_doc_versioned_at", + "type": "DATETIME", + "null": false + }, + "editor_doc_versioned_by": { + "name": "editor_doc_versioned_by", + "type": "INT", + "null": false, + "foreignTable": "account", + "foreignKey": "account_id" + } + } } } \ No newline at end of file diff --git a/Admin/Installer.php b/Admin/Installer.php index 1c4dd19..cd2cebe 100755 --- a/Admin/Installer.php +++ b/Admin/Installer.php @@ -46,13 +46,13 @@ final class Installer extends InstallerAbstract /** * {@inheritdoc} */ - public static function install(DatabasePool $dbPool, ModuleInfo $info, SettingsInterface $cfgHandler) : void + public static function install(ApplicationAbstract $app, ModuleInfo $info, SettingsInterface $cfgHandler) : void { - parent::install($dbPool, $info, $cfgHandler); + parent::install($app, $info, $cfgHandler); $types = include __DIR__ . '/Install/Types/types.php'; foreach ($types as $type) { - self::createType($dbPool, $type); + self::createType($app->dbPool, $type); } } diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 945b08b..3f2794f 100755 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -17,6 +17,8 @@ namespace Modules\Editor\Controller; use Modules\Admin\Models\AccountMapper; use Modules\Admin\Models\NullAccount; use Modules\Editor\Models\EditorDoc; +use Modules\Editor\Models\EditorDocHistory; +use Modules\Editor\Models\EditorDocHistoryMapper; use Modules\Editor\Models\EditorDocMapper; use Modules\Media\Models\CollectionMapper; use Modules\Media\Models\MediaMapper; @@ -90,11 +92,16 @@ final class ApiController extends Controller $this->createModel($request->header->account, $doc, EditorDocMapper::class, 'doc', $request->getOrigin()); if (!empty($request->getFiles() ?? []) - || !empty($request->getDataJson('media') ?? []) + || !empty($request->getDataJson('media') ?? []) ) { $this->createDocMedia($doc, $request); } + if ($doc->isVersioned) { + $history = $this->createHistory($doc); + $this->createModel($request->header->account, $history, EditorDocHistoryMapper::class, 'doc_history', $request->getOrigin()); + } + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Document', 'Document successfully created', $doc); } @@ -190,8 +197,10 @@ final class ApiController extends Controller $doc->title = (string) ($request->getData('title') ?? ''); $doc->plain = (string) ($request->getData('plain') ?? ''); $doc->content = Markdown::parse((string) ($request->getData('plain') ?? '')); + $doc->isVersioned = (bool) ($request->getData('versioned') ?? false); $doc->setVirtualPath((string) ($request->getData('virtualpath') ?? '/')); $doc->createdBy = new NullAccount($request->header->account); + $doc->version = (string) ($request->getData('version') ?? ''); if (!empty($tags = $request->getDataJson('tags'))) { foreach ($tags as $tag) { @@ -213,6 +222,13 @@ final class ApiController extends Controller return $doc; } + private function createHistory(EditorDoc $doc) : EditorDocHistory + { + $history = EditorDocHistory::createFromDoc($doc); + + return $history; + } + /** * Api method to create document * @@ -232,6 +248,16 @@ final class ApiController extends Controller $old = clone EditorDocMapper::get()->where('id', (int) $request->getData('id'))->execute(); $new = $this->updateEditorFromRequest($request); $this->updateModel($request->header->account, $old, $new, EditorDocMapper::class, 'doc', $request->getOrigin()); + + if ($new->isVersioned + && ($old->plain !== $new->plain + || $old->title !== $new->title + ) + ) { + $history = $this->createHistory($new); + $this->createModel($request->header->account, $history, EditorDocHistoryMapper::class, 'doc_history', $request->getOrigin()); + } + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Document', 'Document successfully updated', $new); } @@ -248,9 +274,11 @@ final class ApiController extends Controller { /** @var \Modules\Editor\Models\EditorDoc $doc */ $doc = EditorDocMapper::get()->where('id', (int) $request->getData('id'))->execute(); + $doc->isVersioned = (bool) ($request->getData('versioned') ?? $doc->isVersioned); $doc->title = (string) ($request->getData('title') ?? $doc->title); $doc->plain = (string) ($request->getData('plain') ?? $doc->plain); $doc->content = Markdown::parse((string) ($request->getData('plain') ?? $doc->plain)); + $doc->version = (string) ($request->getData('version') ?? $doc->version); return $doc; } diff --git a/Models/EditorDoc.php b/Models/EditorDoc.php index 7b0637a..4232a9f 100755 --- a/Models/EditorDoc.php +++ b/Models/EditorDoc.php @@ -38,6 +38,14 @@ class EditorDoc implements \JsonSerializable, ArrayableInterface */ protected int $id = 0; + /** + * Version. + * + * @var string + * @since 1.0.0 + */ + public string $version = ''; + /** * Title. * @@ -118,6 +126,14 @@ class EditorDoc implements \JsonSerializable, ArrayableInterface */ protected array $media = []; + /** + * Is versioned + * + * @var bool + * @since 1.0.0 + */ + public bool $isVersioned = false; + /** * Constructor. * diff --git a/Models/EditorDocHistory.php b/Models/EditorDocHistory.php new file mode 100644 index 0000000..bd7a5fb --- /dev/null +++ b/Models/EditorDocHistory.php @@ -0,0 +1,163 @@ +createdBy = new NullAccount(); + $this->createdAt = new \DateTimeImmutable('now'); + } + + public static function createFromDoc(EditorDoc $doc) : self + { + $hist = new self(); + $hist->doc = $doc->getId(); + $hist->createdBy = $doc->createdBy; + $hist->title = $doc->title; + $hist->plain = $doc->plain; + $hist->content = $doc->content; + $hist->version = $doc->version; + + return $hist; + } + + /** + * Get the id + * + * @return int + * + * @since 1.0.0 + */ + public function getId() : int + { + return $this->id; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return [ + 'id' => $this->id, + 'title' => $this->title, + 'plain' => $this->plain, + 'content' => $this->content, + 'createdAt' => $this->createdAt, + 'createdBy' => $this->createdBy, + ]; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return (string) \json_encode($this->toArray()); + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/Models/EditorDocHistoryMapper.php b/Models/EditorDocHistoryMapper.php new file mode 100644 index 0000000..4ef27c7 --- /dev/null +++ b/Models/EditorDocHistoryMapper.php @@ -0,0 +1,82 @@ + + * @since 1.0.0 + */ + public const COLUMNS = [ + 'editor_doc_versioned_id' => ['name' => 'editor_doc_versioned_id', 'type' => 'int', 'internal' => 'id'], + 'editor_doc_versioned_version' => ['name' => 'editor_doc_versioned_version', 'type' => 'string', 'internal' => 'version'], + 'editor_doc_versioned_title' => ['name' => 'editor_doc_versioned_title', 'type' => 'string', 'internal' => 'title'], + 'editor_doc_versioned_plain' => ['name' => 'editor_doc_versioned_plain', 'type' => 'string', 'internal' => 'plain'], + 'editor_doc_versioned_content' => ['name' => 'editor_doc_versioned_content', 'type' => 'string', 'internal' => 'content'], + 'editor_doc_versioned_at' => ['name' => 'editor_doc_versioned_at', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt', 'readonly' => true], + 'editor_doc_versioned_by' => ['name' => 'editor_doc_versioned_by', 'type' => 'int', 'internal' => 'createdBy', 'readonly' => true], + ]; + + /** + * Belongs to. + * + * @var array + * @since 1.0.0 + */ + public const BELONGS_TO = [ + 'createdBy' => [ + 'mapper' => AccountMapper::class, + 'external' => 'editor_doc_versioned_by', + ], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + public const TABLE = 'editor_doc_versioned'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + public const PRIMARYFIELD ='editor_doc_versioned_id'; + + /** + * Created at. + * + * @var string + * @since 1.0.0 + */ + public const AT = 'editor_doc_versioned_at'; +} diff --git a/Models/EditorDocMapper.php b/Models/EditorDocMapper.php index 4b1a438..11c7fc1 100755 --- a/Models/EditorDocMapper.php +++ b/Models/EditorDocMapper.php @@ -38,13 +38,15 @@ final class EditorDocMapper extends DataMapperFactory */ public const COLUMNS = [ 'editor_doc_id' => ['name' => 'editor_doc_id', 'type' => 'int', 'internal' => 'id'], - 'editor_doc_created_by' => ['name' => 'editor_doc_created_by', 'type' => 'int', 'internal' => 'createdBy', 'readonly' => true], + 'editor_doc_version' => ['name' => 'editor_doc_version', 'type' => 'string', 'internal' => 'version'], 'editor_doc_title' => ['name' => 'editor_doc_title', 'type' => 'string', 'internal' => 'title'], 'editor_doc_plain' => ['name' => 'editor_doc_plain', 'type' => 'string', 'internal' => 'plain'], 'editor_doc_content' => ['name' => 'editor_doc_content', 'type' => 'string', 'internal' => 'content'], 'editor_doc_type' => ['name' => 'editor_doc_type', 'type' => 'int', 'internal' => 'type'], 'editor_doc_virtual' => ['name' => 'editor_doc_virtual', 'type' => 'string', 'internal' => 'virtualPath'], - 'editor_doc_created_at' => ['name' => 'editor_doc_created_at', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt', 'readonly' => true], + 'editor_doc_versioned' => ['name' => 'editor_doc_versioned', 'type' => 'bool', 'internal' => 'isVersioned'], + 'editor_doc_created_at' => ['name' => 'editor_doc_created_at', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt'], + 'editor_doc_created_by' => ['name' => 'editor_doc_created_by', 'type' => 'int', 'internal' => 'createdBy'], 'editor_doc_visible' => ['name' => 'editor_doc_visible', 'type' => 'bool', 'internal' => 'isVisible'], ]; diff --git a/Models/NullEditorDocHistory.php b/Models/NullEditorDocHistory.php new file mode 100644 index 0000000..d8d089d --- /dev/null +++ b/Models/NullEditorDocHistory.php @@ -0,0 +1,39 @@ +id = $id; + parent::__construct(); + } +}