diff --git a/Admin/Install/Navigation.install.json b/Admin/Install/Navigation.install.json new file mode 100644 index 0000000..8456344 --- /dev/null +++ b/Admin/Install/Navigation.install.json @@ -0,0 +1,48 @@ +[ + { + "id": 1007501001, + "pid": "/", + "type": 2, + "subtype": 1, + "name": "Tag", + "uri": "{/prefix}tag/list?{?}", + "target": "self", + "icon": null, + "order": 75, + "from": "Tag", + "permission": { "permission": 2, "type": null, "element": null }, + "parent": 1003301001, + "children": [ + { + "id": 1007502001, + "pid": "/tag", + "type": 3, + "subtype": 1, + "name": "List", + "uri": "{/prefix}tag/list?{?}", + "target": "self", + "icon": null, + "order": 1, + "from": "Tag", + "permission": { "permission": 2, "type": null, "element": null }, + "parent": 1007501001, + "children": [] + }, + { + "id": 1007502101, + "pid": "/tag", + "type": 3, + "subtype": 5, + "name": "Create", + "uri": "{/prefix}tag/create?{?}", + "target": "self", + "icon": null, + "order": 15, + "from": "Tag", + "permission": { "permission": 4, "type": null, "element": null }, + "parent": 1007501001, + "children": [] + } + ] + } +] diff --git a/Admin/Install/Navigation.php b/Admin/Install/Navigation.php new file mode 100644 index 0000000..a357c62 --- /dev/null +++ b/Admin/Install/Navigation.php @@ -0,0 +1,43 @@ + __DIR__ . '/Navigation.install.json']); + } +} diff --git a/Admin/Install/db.json b/Admin/Install/db.json index 09c918c..27587c5 100644 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -16,7 +16,7 @@ }, "tag_color": { "name": "tag_color", - "type": "VARCHAR(8)", + "type": "VARCHAR(9)", "null": false }, "tag_type": { diff --git a/Admin/Routes/Web/Api.php b/Admin/Routes/Web/Api.php index 18f1e74..11b2975 100644 --- a/Admin/Routes/Web/Api.php +++ b/Admin/Routes/Web/Api.php @@ -6,7 +6,7 @@ use phpOMS\Account\PermissionType; use phpOMS\Router\RouteVerb; return [ - '^.*/tag.*$' => [ + '^.*/tag$' => [ [ 'dest' => '\Modules\Tag\Controller\ApiController:apiTagCreate', 'verb' => RouteVerb::PUT, @@ -35,4 +35,15 @@ return [ ], ], ], + '^.*/tag/find.*$' => [ + [ + 'dest' => '\Modules\Tag\Controller\ApiController:apiTagFind', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => ApiController::MODULE_NAME, + 'type' => PermissionType::READ, + 'state' => PermissionState::TAG, + ], + ], + ], ]; diff --git a/Admin/Routes/Web/Backend.php b/Admin/Routes/Web/Backend.php new file mode 100644 index 0000000..4f32bed --- /dev/null +++ b/Admin/Routes/Web/Backend.php @@ -0,0 +1,42 @@ + [ + [ + 'dest' => '\Modules\Tag\Controller\BackendController:viewTagCreate', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::MODULE_NAME, + 'type' => PermissionType::CREATE, + 'state' => PermissionState::TAG, + ], + ], + ], + '^.*/tag/list.*$' => [ + [ + 'dest' => '\Modules\Tag\Controller\BackendController:viewTagList', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::MODULE_NAME, + 'type' => PermissionType::READ, + 'state' => PermissionState::TAG, + ], + ], + ], + '^.*/tag/single.*$' => [ + [ + 'dest' => '\Modules\Tag\Controller\BackendController:viewTagSingle', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::MODULE_NAME, + 'type' => PermissionType::READ, + 'state' => PermissionState::TAG, + ], + ], + ], +]; diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 80cac03..95cb4aa 100644 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -21,6 +21,7 @@ use phpOMS\Message\NotificationLevel; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Model\Message\FormValidation; +use phpOMS\System\MimeType; /** * Tag controller class. @@ -49,7 +50,7 @@ final class ApiController extends Controller { $val = []; if (($val['title'] = empty($request->getData('title'))) - || ($val['color'] = (!empty($request->getData('color')) && !\ctype_xdigit($request->getData('color')))) + || ($val['color'] = (!empty($request->getData('color')) && !\ctype_xdigit(\ltrim($request->getData('color'), '#')))) ) { return $val; } @@ -135,7 +136,7 @@ final class ApiController extends Controller { $tag = new Tag(); $tag->setTitle((string) ($request->getData('title') ?? '')); - $tag->setColor($request->getData('color') ?? '00000000'); + $tag->setColor($request->getData('color') ?? '#00000000'); return $tag; } @@ -178,4 +179,29 @@ final class ApiController extends Controller $this->deleteModel($request->getHeader()->getAccount(), $tag, TagMapper::class, 'tag'); $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Tag', 'Tag successfully deleted', $tag); } + + /** + * Api method to find tags + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiTagFind(RequestAbstract $request, ResponseAbstract $response, $data = null) : void + { + $response->getHeader()->set('Content-Type', MimeType::M_JSON, true); + $response->set( + $request->getUri()->__toString(), + \array_values( + TagMapper::find((string) ($request->getData('search') ?? '')) + ) + ); + } + } diff --git a/Controller/BackendController.php b/Controller/BackendController.php new file mode 100644 index 0000000..81c666c --- /dev/null +++ b/Controller/BackendController.php @@ -0,0 +1,107 @@ +app->l11nManager, $request, $response); + + $view->setTemplate('/Modules/Tag/Theme/Backend/tag-create'); + $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1007501001, $request, $response)); + + return $view; + } + + /** + * Routing end-point for application behaviour. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return RenderableInterface + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewTagList(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface + { + $view = new View($this->app->l11nManager, $request, $response); + + $view->setTemplate('/Modules/Tag/Theme/Backend/tag-list'); + $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1007501001, $request, $response)); + + $tags = TagMapper::getNewest(50); + $view->addData('tags', $tags); + + return $view; + } + + /** + * Routing end-point for application behaviour. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return RenderableInterface + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewTagSingle(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface + { + $view = new View($this->app->l11nManager, $request, $response); + $tag = TagMapper::get((int) $request->getData('id')); + + $view->setTemplate('/Modules/Tag/Theme/Backend/tag-single'); + $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1007501001, $request, $response)); + $view->addData('tag', $tag); + + return $view; + } +} diff --git a/Models/Tag.php b/Models/Tag.php index 3a82158..5b904b7 100644 --- a/Models/Tag.php +++ b/Models/Tag.php @@ -17,7 +17,7 @@ namespace Modules\Tag\Models; use phpOMS\Contract\ArrayableInterface; /** - * Tag article class. + * Tag class. * * @package Modules\Tag\Models * @license OMS License 1.0 @@ -53,10 +53,10 @@ class Tag implements ArrayableInterface, \JsonSerializable /** * Creator. * - * @var int + * @var null|int|\Modules\Admin\Models\Account * @since 1.0.0 */ - protected $owner = 0; + protected $owner = null; /** * Tag type. @@ -69,7 +69,7 @@ class Tag implements ArrayableInterface, \JsonSerializable /** * Get created by * - * @return null|int|\phpOMS\Account\Account + * @return null|int|\Modules\Admin\Models\Account * * @since 1.0.0 */ @@ -192,14 +192,6 @@ class Tag implements ArrayableInterface, \JsonSerializable ]; } - /** - * {@inheritdoc} - */ - public function __toString() - { - return (string) \json_encode($this->toArray()); - } - /** * {@inheritdoc} */ diff --git a/Models/TagMapper.php b/Models/TagMapper.php index 65a5f22..0a238fa 100644 --- a/Models/TagMapper.php +++ b/Models/TagMapper.php @@ -35,7 +35,7 @@ final class TagMapper extends DataMapperAbstract */ protected static array $columns = [ 'tag_id' => ['name' => 'tag_id', 'type' => 'int', 'internal' => 'id'], - 'tag_title' => ['name' => 'tag_title', 'type' => 'string', 'internal' => 'title'], + 'tag_title' => ['name' => 'tag_title', 'type' => 'string', 'internal' => 'title', 'autocomplete' => true], 'tag_color' => ['name' => 'tag_color', 'type' => 'string', 'internal' => 'color'], 'tag_type' => ['name' => 'tag_type', 'type' => 'int', 'internal' => 'type'], 'tag_owner' => ['name' => 'tag_owner', 'type' => 'int', 'internal' => 'owner'], diff --git a/Theme/Backend/Components/TagSelector/BaseView.php b/Theme/Backend/Components/TagSelector/BaseView.php new file mode 100644 index 0000000..5df92a0 --- /dev/null +++ b/Theme/Backend/Components/TagSelector/BaseView.php @@ -0,0 +1,92 @@ +setTemplate('/Modules/Tag/Theme/Backend/Components/TagSelector/base'); + } + + /** + * Get selector id + * + * @return string + * + * @since 1.0.0 + */ + public function getId() : string + { + return $this->id; + } + + /** + * Get name + * + * @return string + * + * @since 1.0.0 + */ + public function getName() : string + { + return $this->name; + } + + /** + * Is required? + * + * @return bool + * + * @since 1.0.0 + */ + public function isRequired() : bool + { + return $this->isRequired; + } + + /** + * {@inheritdoc} + */ + public function render(...$data) : string + { + $this->id = $data[0]; + $this->name = $data[1]; + $this->isRequired = $data[2] ?? false; + return parent::render(); + } +} diff --git a/Theme/Backend/Components/TagSelector/base.tpl.php b/Theme/Backend/Components/TagSelector/base.tpl.php new file mode 100644 index 0000000..ba6c972 --- /dev/null +++ b/Theme/Backend/Components/TagSelector/base.tpl.php @@ -0,0 +1,49 @@ +
+
+ + +
+ + +
+
+
+
+
+
+ +
\ No newline at end of file diff --git a/Theme/Backend/Lang/Navigation.en.lang.php b/Theme/Backend/Lang/Navigation.en.lang.php new file mode 100644 index 0000000..0000326 --- /dev/null +++ b/Theme/Backend/Lang/Navigation.en.lang.php @@ -0,0 +1,19 @@ + [ + 'Create' => 'Create', + 'List' => 'List', + 'Tag' => 'Tag', +]]; diff --git a/Theme/Backend/Lang/en.lang.php b/Theme/Backend/Lang/en.lang.php index c240819..83a3bdc 100644 --- a/Theme/Backend/Lang/en.lang.php +++ b/Theme/Backend/Lang/en.lang.php @@ -13,6 +13,10 @@ declare(strict_types=1); return ['Tag' => [ - 'Tag' => 'Tag', - 'Tags' => 'Tags', + 'Color' => 'Color', + 'Create' => 'Create', + 'List' => 'List', + 'Tag' => 'Tag', + 'Tags' => 'Tags', + 'Title' => 'Title', ]]; diff --git a/Theme/Backend/tag-create.tpl.php b/Theme/Backend/tag-create.tpl.php new file mode 100644 index 0000000..4603ce3 --- /dev/null +++ b/Theme/Backend/tag-create.tpl.php @@ -0,0 +1,40 @@ +getData('nav')->render(); ?> +
+
+
+
+
getHtml('Tag') ?>
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/Theme/Backend/tag-list.tpl.php b/Theme/Backend/tag-list.tpl.php new file mode 100644 index 0000000..2cd18ea --- /dev/null +++ b/Theme/Backend/tag-list.tpl.php @@ -0,0 +1,44 @@ +getData('tags'); + +echo $this->getData('nav')->render(); ?> +
+
+
+
getHtml('Tags') ?>
+ + + + + $value) : ++$count; + $url = \phpOMS\Uri\UriFactory::build('{/prefix}tag/single?{?}&id=' . $value->getId()); ?> + +
getHtml('Title') ?> + getHtml('Color') ?> +
printHtml($value->getTitle()); ?> +            + + +
getHtml('Empty', '0', '0'); ?> + +
+
+
+
diff --git a/Theme/Backend/tag-single.tpl.php b/Theme/Backend/tag-single.tpl.php new file mode 100644 index 0000000..09bc338 --- /dev/null +++ b/Theme/Backend/tag-single.tpl.php @@ -0,0 +1,42 @@ +getData('tag'); + +/** + * @var \phpOMS\Views\View $this + */ +echo $this->getData('nav')->render(); ?> +
+
+
+
+
getHtml('Tag') ?>
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/info.json b/info.json index ca8d7d6..fa2a790 100644 --- a/info.json +++ b/info.json @@ -24,5 +24,23 @@ "Navigation": "*" }, "load": [ + { + "pid": [ + "/tag" + ], + "type": 4, + "for": "Tag", + "file": "Tag", + "from": "Tag" + }, + { + "pid": [ + "/" + ], + "type": 5, + "from": "Tag", + "for": "Navigation", + "file": "Navigation" + } ] }