diff --git a/Admin/Install/Navigation.php b/Admin/Install/Navigation.php index ed0b197..6b50e31 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 b44ad9b..6bc8550 100755 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -117,6 +117,16 @@ "type": "TEXT", "null": false }, + "wiki_article_versioned": { + "name": "wiki_article_versioned", + "type": "TINYINT", + "null": false + }, + "wiki_article_version": { + "name": "wiki_article_version", + "type": "VARCHAR(50)", + "null": false + }, "wiki_article_category": { "name": "wiki_article_category", "type": "INT", @@ -132,6 +142,18 @@ "null": true, "foreignTable": "wiki_app", "foreignKey": "wiki_app_id" + }, + "wiki_article_created_by": { + "name": "wiki_article_created_by", + "type": "INT", + "null": false, + "foreignTable": "account", + "foreignKey": "account_id" + }, + "wiki_article_created_at": { + "name": "wiki_article_created_at", + "type": "DATETIME", + "null": false } } }, @@ -186,5 +208,61 @@ "foreignKey": "tag_id" } } + }, + "wiki_article_versioned": { + "name": "wiki_article_versioned", + "fields": { + "wiki_article_versioned_id": { + "name": "wiki_article_versioned_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "wiki_article_versioned_title": { + "name": "wiki_article_versioned_title", + "type": "VARCHAR(255)", + "null": false + }, + "wiki_article_versioned_version": { + "name": "wiki_article_versioned_version", + "type": "VARCHAR(50)", + "null": false + }, + "wiki_article_versioned_language": { + "name": "wiki_article_versioned_language", + "type": "VARCHAR(3)", + "null": false + }, + "wiki_article_versioned_doc": { + "name": "wiki_article_versioned_doc", + "type": "TEXT", + "null": false + }, + "wiki_article_versioned_docraw": { + "name": "wiki_article_versioned_docraw", + "type": "TEXT", + "null": false + }, + "wiki_article_versioned_article": { + "name": "wiki_article_versioned_article", + "type": "INT", + "null": false, + "foreignTable": "wiki_article", + "foreignKey": "wiki_article_id" + }, + "wiki_article_versioned_at": { + "name": "wiki_article_versioned_at", + "type": "DATETIME", + "null": false + }, + "wiki_article_versioned_by": { + "name": "wiki_article_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 6b3eac4..c90578d 100755 --- a/Admin/Installer.php +++ b/Admin/Installer.php @@ -19,6 +19,7 @@ use Modules\Knowledgebase\Models\WikiApp; use Modules\Knowledgebase\Models\WikiAppMapper; use Modules\Knowledgebase\Models\WikiCategory; use Modules\Knowledgebase\Models\WikiCategoryMapper; +use phpOMS\Application\ApplicationAbstract; use phpOMS\Config\SettingsInterface; use phpOMS\DataStorage\Database\DatabasePool; use phpOMS\Module\InstallerAbstract; @@ -45,9 +46,9 @@ 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); $app = new WikiApp(); $app->name = 'Default'; diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 468f350..871f592 100755 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -16,6 +16,7 @@ namespace Modules\Knowledgebase\Controller; use Modules\Admin\Models\AccountMapper; use Modules\Admin\Models\NullAccount; +use Modules\Editor\Models\EditorDocHistoryMapper; use Modules\Knowledgebase\Models\NullWikiApp; use Modules\Knowledgebase\Models\NullWikiCategory; use Modules\Knowledgebase\Models\WikiApp; @@ -25,6 +26,7 @@ use Modules\Knowledgebase\Models\WikiCategoryL11n; use Modules\Knowledgebase\Models\WikiCategoryL11nMapper; use Modules\Knowledgebase\Models\WikiCategoryMapper; use Modules\Knowledgebase\Models\WikiDoc; +use Modules\Knowledgebase\Models\WikiDocHistory; use Modules\Knowledgebase\Models\WikiDocMapper; use Modules\Knowledgebase\Models\WikiStatus; use Modules\Media\Models\CollectionMapper; @@ -83,6 +85,11 @@ final class ApiController extends Controller $this->createWikiMedia($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, 'Wiki', 'Wiki successfully created.', $doc); } @@ -178,10 +185,12 @@ final class ApiController extends Controller $doc->name = (string) $request->getData('title'); $doc->doc = Markdown::parse((string) ($request->getData('plain') ?? '')); $doc->docRaw = (string) ($request->getData('plain') ?? ''); + $doc->isVersioned = (bool) ($request->getData('versioned') ?? false); $doc->category = new NullWikiCategory((int) ($request->getData('category') ?? 1)); $doc->setLanguage((string) ($request->getData('language') ?? $request->getLanguage())); $doc->setStatus((int) ($request->getData('status') ?? WikiStatus::INACTIVE)); $doc->app = new NullWikiApp((int) ($request->getData('app') ?? 1)); + $doc->version = (string) ($request->getData('version') ?? ''); if (!empty($tags = $request->getDataJson('tags'))) { foreach ($tags as $tag) { @@ -203,6 +212,13 @@ final class ApiController extends Controller return $doc; } + private function createHistory(WikiDoc $doc) : WikiDocHistory + { + $history = WikiDocHistory::createFromDoc($doc); + + return $history; + } + /** * Method to validate wiki entry creation from request * @@ -335,6 +351,16 @@ final class ApiController extends Controller $old = clone WikiDocMapper::get()->where('id', (int) $request->getData('id'))->execute(); $new = $this->updateDocFromRequest($request); $this->updateModel($request->header->account, $old, $new, WikiDocMapper::class, 'doc', $request->getOrigin()); + + if ($new->isVersioned + && ($old->docRaw !== $new->docRaw + || $old->name !== $new->name + ) + ) { + $history = $this->createHistory($new); + $this->createModel($request->header->account, $history, EditorDocHistoryMapper::class, 'doc_history', $request->getOrigin()); + } + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Doc', 'Doc successfully updated', $new); } @@ -349,8 +375,13 @@ final class ApiController extends Controller */ private function updateDocFromRequest(RequestAbstract $request) : WikiDoc { + /** @var WikiDoc $doc */ $doc = WikiDocMapper::get()->where('id', (int) $request->getData('id'))->execute(); + $doc->isVersioned = (bool) ($request->getData('versioned') ?? $doc->isVersioned); $doc->name = (string) ($request->getData('title') ?? $doc->name); + $doc->docRaw = (string) ($request->getData('plain') ?? $doc->docRaw); + $doc->doc = Markdown::parse((string) ($request->getData('plain') ?? $doc->docRaw)); + $doc->version = (string) ($request->getData('version') ?? $doc->version); return $doc; } diff --git a/Models/NullWikiDocHistory.php b/Models/NullWikiDocHistory.php new file mode 100644 index 0000000..532c02a --- /dev/null +++ b/Models/NullWikiDocHistory.php @@ -0,0 +1,38 @@ +id = $id; + } +} diff --git a/Models/WikiDoc.php b/Models/WikiDoc.php index 18f9e5a..fcac152 100755 --- a/Models/WikiDoc.php +++ b/Models/WikiDoc.php @@ -14,6 +14,8 @@ declare(strict_types=1); namespace Modules\Knowledgebase\Models; +use Modules\Admin\Models\Account; +use Modules\Admin\Models\NullAccount; use Modules\Media\Models\Media; use Modules\Tag\Models\Tag; use phpOMS\Localization\ISO639x1Enum; @@ -36,6 +38,14 @@ class WikiDoc implements \JsonSerializable */ protected int $id = 0; + /** + * Version. + * + * @var string + * @since 1.0.0 + */ + public string $version = ''; + /** * App id. * @@ -110,6 +120,41 @@ class WikiDoc implements \JsonSerializable */ protected array $media = []; + /** + * Is versioned + * + * @var bool + * @since 1.0.0 + */ + public bool $isVersioned = false; + + /** + * Created. + * + * @var \DateTimeImmutable + * @since 1.0.0 + */ + public \DateTimeImmutable $createdAt; + + /** + * Creator. + * + * @var Account + * @since 1.0.0 + */ + public Account $createdBy; + + /** + * Constructor. + * + * @since 1.0.0 + */ + public function __construct() + { + $this->createdBy = new NullAccount(); + $this->createdAt = new \DateTimeImmutable('now'); + } + /** * Get id. * @@ -241,6 +286,8 @@ class WikiDoc implements \JsonSerializable 'language' => $this->language, 'tags' => $this->tags, 'media' => $this->media, + 'createdAt' => $this->createdAt, + 'createdBy' => $this->createdBy, ]; } diff --git a/Models/WikiDocHistory.php b/Models/WikiDocHistory.php new file mode 100644 index 0000000..f9e6059 --- /dev/null +++ b/Models/WikiDocHistory.php @@ -0,0 +1,156 @@ +article = $doc->getId(); + $hist->createdBy = $doc->createdBy; + $hist->name = $doc->name; + $hist->doc = $doc->doc; + $hist->docRaw = $doc->docRaw; + $hist->version = $doc->version; + + return $hist; + } + + /** + * Get id. + * + * @return int Model id + * + * @since 1.0.0 + */ + public function getId() : int + { + return $this->id; + } + + /** + * Get language + * + * @return string + * + * @since 1.0.0 + */ + public function getLanguage() : string + { + return $this->language; + } + + /** + * Set language + * + * @param string $language Language + * + * @return void + * + * @since 1.0.0 + */ + public function setLanguage(string $language) : void + { + $this->language = $language; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'doc' => $this->doc, + 'docRaw' => $this->docRaw, + ]; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/Models/WikiDocHistoryMapper.php b/Models/WikiDocHistoryMapper.php new file mode 100644 index 0000000..b406f12 --- /dev/null +++ b/Models/WikiDocHistoryMapper.php @@ -0,0 +1,76 @@ + + * @since 1.0.0 + */ + public const COLUMNS = [ + 'wiki_article_versioned_id' => ['name' => 'wiki_article_versioned_id', 'type' => 'int', 'internal' => 'id'], + 'wiki_article_versioned_version' => ['name' => 'wiki_article_versioned_version', 'type' => 'string', 'internal' => 'version'], + 'wiki_article_versioned_title' => ['name' => 'wiki_article_versioned_title', 'type' => 'string', 'internal' => 'name'], + 'wiki_article_versioned_language' => ['name' => 'wiki_article_versioned_language', 'type' => 'string', 'internal' => 'language'], + 'wiki_article_versioned_doc' => ['name' => 'wiki_article_versioned_doc', 'type' => 'string', 'internal' => 'doc'], + 'wiki_article_versioned_docraw' => ['name' => 'wiki_article_versioned_docraw', 'type' => 'string', 'internal' => 'docRaw'], + 'wiki_article_versioned_article' => ['name' => 'wiki_article_versioned_article', 'type' => 'DateTimeImmutable', 'internal' => 'article'], + 'wiki_article_versioned_at' => ['name' => 'wiki_article_versioned_at', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt'], + 'wiki_article_versioned_by' => ['name' => 'wiki_article_versioned_by', 'type' => 'int', 'internal' => 'createdBy'], + ]; + + /** + * Belongs to. + * + * @var array + * @since 1.0.0 + */ + public const BELONGS_TO = [ + 'createdBy' => [ + 'mapper' => AccountMapper::class, + 'external' => 'wiki_article_versioned_by', + ], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + public const TABLE = 'wiki_article_versioned'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + public const PRIMARYFIELD ='wiki_article_versioned_id'; +} diff --git a/Models/WikiDocMapper.php b/Models/WikiDocMapper.php index 8b3d1f8..1754c89 100755 --- a/Models/WikiDocMapper.php +++ b/Models/WikiDocMapper.php @@ -14,6 +14,7 @@ declare(strict_types=1); namespace Modules\Knowledgebase\Models; +use Modules\Admin\Models\AccountMapper; use Modules\Media\Models\MediaMapper; use Modules\Tag\Models\TagMapper; use phpOMS\DataStorage\Database\Mapper\DataMapperFactory; @@ -36,13 +37,17 @@ final class WikiDocMapper extends DataMapperFactory */ public const COLUMNS = [ 'wiki_article_id' => ['name' => 'wiki_article_id', 'type' => 'int', 'internal' => 'id'], + 'wiki_article_version' => ['name' => 'wiki_article_version', 'type' => 'string', 'internal' => 'version'], 'wiki_article_app' => ['name' => 'wiki_article_app', 'type' => 'int', 'internal' => 'app'], 'wiki_article_title' => ['name' => 'wiki_article_title', 'type' => 'string', 'internal' => 'name'], 'wiki_article_language' => ['name' => 'wiki_article_language', 'type' => 'string', 'internal' => 'language'], 'wiki_article_doc' => ['name' => 'wiki_article_doc', 'type' => 'string', 'internal' => 'doc'], 'wiki_article_docraw' => ['name' => 'wiki_article_docraw', 'type' => 'string', 'internal' => 'docRaw'], + 'wiki_article_versioned' => ['name' => 'wiki_article_versioned', 'type' => 'bool', 'internal' => 'isVersioned'], 'wiki_article_status' => ['name' => 'wiki_article_status', 'type' => 'int', 'internal' => 'status'], 'wiki_article_category' => ['name' => 'wiki_article_category', 'type' => 'int', 'internal' => 'category'], + 'wiki_article_created_at' => ['name' => 'wiki_article_created_at', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt'], + 'wiki_article_created_by' => ['name' => 'wiki_article_created_by', 'type' => 'int', 'internal' => 'createdBy'], ]; /** @@ -81,6 +86,10 @@ final class WikiDocMapper extends DataMapperFactory 'mapper' => WikiAppMapper::class, 'external' => 'wiki_article_app', ], + 'createdBy' => [ + 'mapper' => AccountMapper::class, + 'external' => 'wiki_article_created_by', + ], ]; /**