diff --git a/week-06/project/administrator/favicon.ico b/week-06/project/administrator/favicon.ico new file mode 100644 index 0000000..63cd6a1 Binary files /dev/null and b/week-06/project/administrator/favicon.ico differ diff --git a/week-06/project/favicon.ico b/week-06/project/favicon.ico new file mode 100644 index 0000000..63cd6a1 Binary files /dev/null and b/week-06/project/favicon.ico differ diff --git a/week-06/project/libraries/src/Controller/DashboardController.php b/week-06/project/libraries/src/Controller/DashboardController.php index 2f36c39..d7f5f0b 100644 --- a/week-06/project/libraries/src/Controller/DashboardController.php +++ b/week-06/project/libraries/src/Controller/DashboardController.php @@ -14,11 +14,15 @@ use Joomla\Application\AbstractApplication; use Joomla\Controller\AbstractController; use Joomla\Input\Input; use Joomla\Uri\Uri; +use Octoleo\CMS\Controller\Util\AccessInterface; +use Octoleo\CMS\Controller\Util\AccessTrait; +use Octoleo\CMS\Controller\Util\CheckTokenInterface; +use Octoleo\CMS\Controller\Util\CheckTokenTrait; use Octoleo\CMS\View\Admin\DashboardHtmlView; use Laminas\Diactoros\Response\HtmlResponse; /** - * Controller handling the site's dashboard + * Controller handling the requests * * @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object. * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object @@ -37,10 +41,9 @@ class DashboardController extends AbstractController implements AccessInterface, /** * Constructor. * - * @param DashboardHtmlView $view The view object. - * @param Input $user The user object. - * @param Input $input The input object. - * @param AbstractApplication $app The application object. + * @param DashboardHtmlView $view The view object. + * @param Input|null $input The input object. + * @param AbstractApplication|null $app The application object. */ public function __construct(DashboardHtmlView $view, Input $input = null, AbstractApplication $app = null) { diff --git a/week-06/project/libraries/src/Controller/HomepageController.php b/week-06/project/libraries/src/Controller/HomepageController.php deleted file mode 100644 index 7fd0296..0000000 --- a/week-06/project/libraries/src/Controller/HomepageController.php +++ /dev/null @@ -1,63 +0,0 @@ - - * @git WEBD-325-45 - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -namespace Octoleo\CMS\Controller; - -use Joomla\Application\AbstractApplication; -use Joomla\Controller\AbstractController; -use Joomla\Input\Input; -use Laminas\Diactoros\Response\HtmlResponse; -use Octoleo\CMS\View\Page\HomepageHtmlView; - -/** - * Controller handling the site's homepage - * - * @method \Octoleo\CMS\Application\SiteApplication getApplication() Get the application object. - * @property-read \Octoleo\CMS\Application\SiteApplication $app Application object - */ -class HomepageController extends AbstractController -{ - /** - * The view object. - * - * @var HomepageHtmlView - */ - private $view; - - /** - * Constructor. - * - * @param HomepageHtmlView $view The view object. - * @param Input $input The input object. - * @param AbstractApplication $app The application object. - */ - public function __construct(HomepageHtmlView $view, Input $input = null, AbstractApplication $app = null) - { - parent::__construct($input, $app); - - $this->view = $view; - } - - /** - * Execute the controller. - * - * @return boolean - */ - public function execute(): bool - { - // Disable all cache for now - $this->getApplication()->allowCache(false); - - // check if there is a home page - $this->getApplication()->setResponse(new HtmlResponse($this->view->render())); - - return true; - } -} diff --git a/week-06/project/libraries/src/Controller/ItemController.php b/week-06/project/libraries/src/Controller/ItemController.php index 089de29..e29b190 100644 --- a/week-06/project/libraries/src/Controller/ItemController.php +++ b/week-06/project/libraries/src/Controller/ItemController.php @@ -14,19 +14,24 @@ use Joomla\Application\AbstractApplication; use Joomla\Controller\AbstractController; use Joomla\Filter\InputFilter as InputFilterAlias; use Joomla\Input\Input; +use Octoleo\CMS\Controller\Util\AccessInterface; +use Octoleo\CMS\Controller\Util\AccessTrait; +use Octoleo\CMS\Controller\Util\CheckTokenInterface; +use Octoleo\CMS\Controller\Util\CheckTokenTrait; use Octoleo\CMS\Date\Date; use Octoleo\CMS\Factory; use Octoleo\CMS\Filter\InputFilter; use Octoleo\CMS\Model\ItemModel; +use Octoleo\CMS\User\User; use Octoleo\CMS\User\UserFactoryInterface; use Octoleo\CMS\View\Admin\ItemHtmlView; use Laminas\Diactoros\Response\HtmlResponse; /** - * Controller handling the site's dashboard + * Controller handling the requests * * @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object. - * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object + * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object */ class ItemController extends AbstractController implements AccessInterface, CheckTokenInterface { @@ -51,20 +56,32 @@ class ItemController extends AbstractController implements AccessInterface, Chec */ private $inputFilter; + /** + * @var User + */ + private $user; + /** * Constructor. * - * @param ItemModel $model The model object. - * @param ItemHtmlView $view The view object. - * @param Input $input The input object. - * @param AbstractApplication $app The application object. + * @param ItemModel $model The model object. + * @param ItemHtmlView $view The view object. + * @param Input|null $input The input object. + * @param AbstractApplication|null $app The application object. + * @param User|null $user */ - public function __construct(ItemModel $model, $view, Input $input = null, AbstractApplication $app = null) + public function __construct( + ItemModel $model, + $view, + Input $input = null, + AbstractApplication $app = null, + User $user = null) { parent::__construct($input, $app); - $this->model = $model; - $this->view = $view; + $this->model = $model; + $this->view = $view; + $this->user = ($user) ?: Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); $this->inputFilter = InputFilter::getInstance( [], [], @@ -85,23 +102,31 @@ class ItemController extends AbstractController implements AccessInterface, Chec $this->getApplication()->allowCache(false); $method = $this->getInput()->getMethod(); - $task = $this->getInput()->getString('task', ''); - $id = $this->getInput()->getInt('id', 0); + $task = $this->getInput()->getString('task', ''); + $id = $this->getInput()->getInt('id', 0); // if task is delete if ('delete' === $task) { - if ($id > 0 && $this->model->linked($id)) + // check that the user does not delete him/her self + if ($this->allow('item') && $this->user->get('access.item.delete', false)) { - $this->getApplication()->enqueueMessage('This item is still linked to a menu, first remove it from the menu.', 'error'); - } - elseif ($id > 0 && $this->model->delete($id)) - { - $this->getApplication()->enqueueMessage('Item was deleted!', 'success'); + if ($id > 0 && $this->model->linked($id)) + { + $this->getApplication()->enqueueMessage('This item is still linked to a menu, first remove it from the menu.', 'error'); + } + elseif ($id > 0 && $this->model->delete($id)) + { + $this->getApplication()->enqueueMessage('Item was deleted!', 'success'); + } + else + { + $this->getApplication()->enqueueMessage('Item could not be deleted!', 'error'); + } } else { - $this->getApplication()->enqueueMessage('Item could not be deleted!', 'error'); + $this->getApplication()->enqueueMessage('You do not have permission to delete this item!', 'error'); } // go to set page $this->_redirect('items'); @@ -111,21 +136,57 @@ class ItemController extends AbstractController implements AccessInterface, Chec if ('POST' === $method) { - $id = $this->setItem(); + // check permissions + $update = ($id > 0 && $this->user->get('access.item.update', false)); + $create = ($id == 0 && $this->user->get('access.item.create', false)); + + if ( $create || $update ) + { + $id = $this->setItem(); + } + else + { + // not allowed creating item + if ($id == 0) + { + $this->getApplication()->enqueueMessage('You do not have permission to create items!', 'error'); + } + // not allowed updating item + if ($id > 0) + { + $this->getApplication()->enqueueMessage('You do not have permission to update the item details!', 'error'); + } + } } - $this->view->setActiveId($id); - $this->view->setActiveView('item'); + // check permissions + $read = ($id > 0 && $this->user->get('access.item.read', false)); + $create = ($id == 0 && $this->user->get('access.item.create', false)); // check if user is allowed to access - if ($this->allow('item')) + if ($this->allow('item') && ( $read || $create )) { + // set values for view + $this->view->setActiveId($id); + $this->view->setActiveView('item'); + $this->getApplication()->setResponse(new HtmlResponse($this->view->render())); } else { + // not allowed creating item + if ($id == 0 && !$create) + { + $this->getApplication()->enqueueMessage('You do not have permission to create items!', 'error'); + } + // not allowed read item + if ($id > 0 && !$read) + { + $this->getApplication()->enqueueMessage('You do not have permission to read the item details!', 'error'); + } + // go to set page - $this->_redirect(); + $this->_redirect('items'); } return true; @@ -146,18 +207,18 @@ class ItemController extends AbstractController implements AccessInterface, Chec $post = $this->getInput()->getInputForRequestMethod(); // we get all the needed items - $tempItem = []; - $tempItem['id'] = $post->getInt('item_id', 0); - $tempItem['title'] = $post->getString('title', ''); - $tempItem['fulltext'] = $this->inputFilter->clean($post->getRaw('fulltext', ''), 'html'); - $tempItem['created_by_alias'] = $post->getString('created_by_alias', ''); - $tempItem['state'] = $post->getInt('state', 1); - $tempItem['metakey'] = $post->getString('metakey', ''); - $tempItem['metadesc'] = $post->getString('metadesc', ''); - $tempItem['metadata'] = $post->getString('metadata', ''); - $tempItem['publish_up'] = $post->getString('publish_up', ''); - $tempItem['publish_down'] = $post->getString('publish_down', ''); - $tempItem['featured'] = $post->getInt('featured', 0); + $tempItem = []; + $tempItem['id'] = $post->getInt('item_id', 0); + $tempItem['title'] = $post->getString('title', ''); + $tempItem['fulltext'] = $this->inputFilter->clean($post->getRaw('fulltext', ''), 'html'); + $tempItem['created_by_alias'] = $post->getString('created_by_alias', ''); + $tempItem['state'] = $post->getInt('state', 1); + $tempItem['metakey'] = $post->getString('metakey', ''); + $tempItem['metadesc'] = $post->getString('metadesc', ''); + $tempItem['metadata'] = $post->getString('metadata', ''); + $tempItem['publish_up'] = $post->getString('publish_up', ''); + $tempItem['publish_down'] = $post->getString('publish_down', ''); + $tempItem['featured'] = $post->getInt('featured', 0); // check that we have a Title $can_save = true; @@ -183,7 +244,7 @@ class ItemController extends AbstractController implements AccessInterface, Chec $user = Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); $user_id = (int) $user->get('id', 0); - $today = (new Date())->toSql(); + $today = (new Date())->toSql(); return $this->model->setItem( $tempItem['id'], diff --git a/week-06/project/libraries/src/Controller/ItemsController.php b/week-06/project/libraries/src/Controller/ItemsController.php index 7205934..97f49ef 100644 --- a/week-06/project/libraries/src/Controller/ItemsController.php +++ b/week-06/project/libraries/src/Controller/ItemsController.php @@ -13,14 +13,21 @@ namespace Octoleo\CMS\Controller; use Joomla\Application\AbstractApplication; use Joomla\Controller\AbstractController; use Joomla\Input\Input; -use Octoleo\CMS\View\Admin\ItemsHtmlView; use Laminas\Diactoros\Response\HtmlResponse; +use Octoleo\CMS\Controller\Util\AccessInterface; +use Octoleo\CMS\Controller\Util\AccessTrait; +use Octoleo\CMS\Controller\Util\CheckTokenInterface; +use Octoleo\CMS\Controller\Util\CheckTokenTrait; +use Octoleo\CMS\Factory; +use Octoleo\CMS\User\User; +use Octoleo\CMS\User\UserFactoryInterface; +use Octoleo\CMS\View\Admin\ItemsHtmlView; /** - * Controller handling the site's dashboard + * Controller handling the requests * * @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object. - * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object + * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object */ class ItemsController extends AbstractController implements AccessInterface, CheckTokenInterface { @@ -33,18 +40,29 @@ class ItemsController extends AbstractController implements AccessInterface, Che */ private $view; + /** + * @var User + */ + private $user; + /** * Constructor. * - * @param ItemsHtmlView $view The view object. - * @param Input $input The input object. - * @param AbstractApplication $app The application object. + * @param ItemsHtmlView $view The view object. + * @param Input|null $input The input object. + * @param AbstractApplication|null $app The application object. + * @param User|null $user */ - public function __construct(ItemsHtmlView $view, Input $input = null, AbstractApplication $app = null) + public function __construct( + ItemsHtmlView $view, + Input $input = null, + AbstractApplication $app = null, + User $user = null) { parent::__construct($input, $app); $this->view = $view; + $this->user = ($user) ?: Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); } /** @@ -61,7 +79,7 @@ class ItemsController extends AbstractController implements AccessInterface, Che $this->view->setActiveView('items'); // check if user is allowed to access - if ($this->allow('items')) + if ($this->allow('items') && $this->user->get('access.item.read', false)) { $this->getApplication()->setResponse(new HtmlResponse($this->view->render())); } diff --git a/week-06/project/libraries/src/Controller/LoginController.php b/week-06/project/libraries/src/Controller/LoginController.php index 2d94c07..6d75652 100644 --- a/week-06/project/libraries/src/Controller/LoginController.php +++ b/week-06/project/libraries/src/Controller/LoginController.php @@ -18,7 +18,7 @@ use Laminas\Diactoros\Response\HtmlResponse; use Octoleo\CMS\View\Admin\DashboardHtmlView; /** - * Controller handling the site's homepage + * Controller handling the requests * * @method \Octoleo\CMS\Application\SiteApplication getApplication() Get the application object. * @property-read \Octoleo\CMS\Application\SiteApplication $app Application object @@ -75,7 +75,7 @@ class LoginController extends AbstractController $userFactory = $app->getUserFactory(); // if the user is logged in we go to dashboard - if ($userFactory->active(false)) + if ($userFactory->active()) { $this->view->setActiveDashboard('dashboard'); $this->view->setActiveId(0); diff --git a/week-06/project/libraries/src/Controller/MenuController.php b/week-06/project/libraries/src/Controller/MenuController.php index 2134604..5634593 100644 --- a/week-06/project/libraries/src/Controller/MenuController.php +++ b/week-06/project/libraries/src/Controller/MenuController.php @@ -12,18 +12,24 @@ namespace Octoleo\CMS\Controller; use Joomla\Application\AbstractApplication; use Joomla\Controller\AbstractController; -use Joomla\Filter\InputFilter as InputFilterAlias; use Joomla\Input\Input; +use Laminas\Diactoros\Response\HtmlResponse; +use Octoleo\CMS\Controller\Util\AccessInterface; +use Octoleo\CMS\Controller\Util\AccessTrait; +use Octoleo\CMS\Controller\Util\CheckTokenInterface; +use Octoleo\CMS\Controller\Util\CheckTokenTrait; +use Octoleo\CMS\Factory; use Octoleo\CMS\Filter\InputFilter; use Octoleo\CMS\Model\MenuModel; -use Laminas\Diactoros\Response\HtmlResponse; +use Octoleo\CMS\User\User; +use Octoleo\CMS\User\UserFactoryInterface; use Octoleo\CMS\View\Admin\MenuHtmlView; /** - * Controller handling the site's dashboard + * Controller handling the requests * * @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object. - * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object + * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object */ class MenuController extends AbstractController implements AccessInterface, CheckTokenInterface { @@ -48,26 +54,31 @@ class MenuController extends AbstractController implements AccessInterface, Chec */ private $inputFilter; + /** + * @var User + */ + private $user; + /** * Constructor. * - * @param MenuModel $model The model object. - * @param MenuHtmlView $view The view object. - * @param Input $input The input object. - * @param AbstractApplication $app The application object. + * @param MenuModel $model The model object. + * @param MenuHtmlView $view The view object. + * @param Input|null $input The input object. + * @param AbstractApplication|null $app The application object. */ - public function __construct(MenuModel $model, MenuHtmlView $view, Input $input = null, AbstractApplication $app = null) + public function __construct( + MenuModel $model, + MenuHtmlView $view, + Input $input = null, + AbstractApplication $app = null, + User $user = null) { parent::__construct($input, $app); $this->model = $model; - $this->view = $view; - $this->inputFilter = InputFilter::getInstance( - [], - [], - InputFilterAlias::ONLY_BLOCK_DEFINED_TAGS, - InputFilterAlias::ONLY_BLOCK_DEFINED_ATTRIBUTES - ); + $this->view = $view; + $this->user = ($user) ?: Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); } /** @@ -82,19 +93,26 @@ class MenuController extends AbstractController implements AccessInterface, Chec $this->getApplication()->allowCache(false); $method = $this->getInput()->getMethod(); - $task = $this->getInput()->getString('task', ''); - $id = $this->getInput()->getInt('id', 0); + $task = $this->getInput()->getString('task', ''); + $id = $this->getInput()->getInt('id', 0); // if task is delete if ('delete' === $task) { - if ($this->model->delete($id)) + if ($this->allow('menu') && $this->user->get('access.menu.delete', false)) { - $this->getApplication()->enqueueMessage('Menu was deleted!', 'success'); + if ($this->model->delete($id)) + { + $this->getApplication()->enqueueMessage('Menu was deleted!', 'success'); + } + else + { + $this->getApplication()->enqueueMessage('Menu could not be deleted!', 'error'); + } } else { - $this->getApplication()->enqueueMessage('Menu could not be deleted!', 'error'); + $this->getApplication()->enqueueMessage('You do not have permission to delete this menu!', 'error'); } // go to set page $this->_redirect('menus'); @@ -104,21 +122,57 @@ class MenuController extends AbstractController implements AccessInterface, Chec if ('POST' === $method) { - $id = $this->setItem(); + // check permissions + $update = ($id > 0 && $this->user->get('access.menu.update', false)); + $create = ($id == 0 && $this->user->get('access.menu.create', false)); + + if ( $create || $update ) + { + $id = $this->setItem(); + } + else + { + // not allowed creating menu + if ($id == 0) + { + $this->getApplication()->enqueueMessage('You do not have permission to create menus!', 'error'); + } + // not allowed updating menu + if ($id > 0) + { + $this->getApplication()->enqueueMessage('You do not have permission to update the menu details!', 'error'); + } + } } - $this->view->setActiveId($id); - $this->view->setActiveView('menu'); + // check permissions + $read = ($id > 0 && $this->user->get('access.menu.read', false)); + $create = ($id == 0 && $this->user->get('access.menu.create', false)); // check if user is allowed to access - if ($this->allow('menu')) + if ($this->allow('menu') && ( $read || $create )) { + // set values for view + $this->view->setActiveId($id); + $this->view->setActiveView('menu'); + $this->getApplication()->setResponse(new HtmlResponse($this->view->render())); } else { + // not allowed creating menu + if ($id == 0 && !$create) + { + $this->getApplication()->enqueueMessage('You do not have permission to create menus!', 'error'); + } + // not allowed read menu + if ($id > 0 && !$read) + { + $this->getApplication()->enqueueMessage('You do not have permission to read the menu details!', 'error'); + } + // go to set page - $this->_redirect(); + $this->_redirect('menus'); } return true; @@ -139,17 +193,18 @@ class MenuController extends AbstractController implements AccessInterface, Chec $post = $this->getInput()->getInputForRequestMethod(); // we get all the needed items - $tempItem = []; - $tempItem['id'] = $post->getInt('menu_id', 0); - $tempItem['title'] = $post->getString('title', ''); - $tempItem['alias'] = $post->getString('alias', ''); - $tempItem['path'] = $post->getString('path', ''); - $tempItem['item_id'] = $post->getInt('item_id', 0); - $tempItem['published'] = $post->getInt('published', 1); - $tempItem['publish_up'] = $post->getString('publish_up', ''); + $tempItem = []; + $tempItem['id'] = $post->getInt('menu_id', 0); + $tempItem['title'] = $post->getString('title', ''); + $tempItem['alias'] = $post->getString('alias', ''); + $tempItem['path'] = $post->getString('path', ''); + $tempItem['item_id'] = $post->getInt('item_id', 0); + $tempItem['published'] = $post->getInt('published', 1); + $tempItem['publish_up'] = $post->getString('publish_up', ''); $tempItem['publish_down'] = $post->getString('publish_down', ''); - $tempItem['position'] = $post->getString('position', 'center'); - $tempItem['home'] = $post->getInt('home', 0); + $tempItem['position'] = $post->getString('position', 'center'); + $tempItem['home'] = $post->getInt('home', 0); + $tempItem['parent_id'] = $post->getInt('parent_id', 0); // check that we have a Title $can_save = true; @@ -182,7 +237,8 @@ class MenuController extends AbstractController implements AccessInterface, Chec $tempItem['publish_up'], $tempItem['publish_down'], $tempItem['position'], - $tempItem['home']); + $tempItem['home'], + $tempItem['parent_id']); } // add to model the post values diff --git a/week-06/project/libraries/src/Controller/MenusController.php b/week-06/project/libraries/src/Controller/MenusController.php index d468af8..c6a655d 100644 --- a/week-06/project/libraries/src/Controller/MenusController.php +++ b/week-06/project/libraries/src/Controller/MenusController.php @@ -13,14 +13,21 @@ namespace Octoleo\CMS\Controller; use Joomla\Application\AbstractApplication; use Joomla\Controller\AbstractController; use Joomla\Input\Input; -use Octoleo\CMS\View\Admin\MenusHtmlView; use Laminas\Diactoros\Response\HtmlResponse; +use Octoleo\CMS\Controller\Util\AccessInterface; +use Octoleo\CMS\Controller\Util\AccessTrait; +use Octoleo\CMS\Controller\Util\CheckTokenInterface; +use Octoleo\CMS\Controller\Util\CheckTokenTrait; +use Octoleo\CMS\Factory; +use Octoleo\CMS\User\User; +use Octoleo\CMS\User\UserFactoryInterface; +use Octoleo\CMS\View\Admin\MenusHtmlView; /** - * Controller handling the site's dashboard + * Controller handling the requests * * @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object. - * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object + * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object */ class MenusController extends AbstractController implements AccessInterface, CheckTokenInterface { @@ -33,19 +40,29 @@ class MenusController extends AbstractController implements AccessInterface, Che */ private $view; + /** + * @var User + */ + private $user; + /** * Constructor. * - * @param MenusHtmlView $view The view object. - * @param Input $user The user object. - * @param Input $input The input object. - * @param AbstractApplication $app The application object. + * @param MenusHtmlView $view The view object. + * @param Input|null $input The input object. + * @param AbstractApplication|null $app The application object. + * @param User|null $user The user object. */ - public function __construct(MenusHtmlView $view, Input $input = null, AbstractApplication $app = null) + public function __construct( + MenusHtmlView $view, + Input $input = null, + AbstractApplication $app = null, + User $user = null) { parent::__construct($input, $app); $this->view = $view; + $this->user = ($user) ?: Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); } /** @@ -62,7 +79,7 @@ class MenusController extends AbstractController implements AccessInterface, Che $this->view->setActiveView('menus'); // check if user is allowed to access - if ($this->allow('menus')) + if ($this->allow('menus') && $this->user->get('access.menu.read', false)) { $this->getApplication()->setResponse(new HtmlResponse($this->view->render())); } diff --git a/week-06/project/libraries/src/Controller/PageController.php b/week-06/project/libraries/src/Controller/PageController.php index 70e1ef7..0c65cdb 100644 --- a/week-06/project/libraries/src/Controller/PageController.php +++ b/week-06/project/libraries/src/Controller/PageController.php @@ -14,12 +14,13 @@ use Joomla\Application\AbstractApplication; use Joomla\Controller\AbstractController; use Joomla\Input\Input; use Joomla\Uri\Uri; -use Octoleo\CMS\View\Page\PageHtmlView; +use Octoleo\CMS\Utilities\StringHelper; +use Octoleo\CMS\View\Site\PageHtmlView; use Laminas\Diactoros\Response\HtmlResponse; use Laminas\Diactoros\Response\RedirectResponse; /** - * Controller handling the site's simple text pages + * Controller handling the requests * * @method \Octoleo\CMS\Application\SiteApplication getApplication() Get the application object. * @property-read \Octoleo\CMS\Application\SiteApplication $app Application object @@ -36,9 +37,9 @@ class PageController extends AbstractController /** * Constructor. * - * @param PageHtmlView $view The view object. - * @param Input $input The input object. - * @param AbstractApplication $app The application object. + * @param PageHtmlView $view The view object. + * @param Input|null $input The input object. + * @param AbstractApplication|null $app The application object. */ public function __construct(PageHtmlView $view, Input $input = null, AbstractApplication $app = null) { @@ -57,11 +58,34 @@ class PageController extends AbstractController // Disable all cache for now $this->getApplication()->allowCache(false); - $page = $this->getInput()->getString('view', ''); - $details = $this->getInput()->getString('details', ''); + // get the root name + $root = $this->getInput()->getString('root', ''); + // start building the full path + $path = []; + $path[] = $root; + // set a mad depth TODO: we should limit the menu depth to 6 or something + $depth = range(1,20); + // load the whole path + foreach ($depth as $page) + { + $page = StringHelper::numbers($page); + // check if there is a value + $result = $this->getInput()->getString($page, false); + if ($result) + { + $path[] = $result; + } + else + { + // first false means we are at the end of the line + break; + } + } + // set the final path + $path = implode('/', $path); // if for some reason the view value is administrator - if ('administrator' === $page) + if ('administrator' === $root) { // get uri request to get host $uri = new Uri($this->getApplication()->get('uri.request')); @@ -71,7 +95,7 @@ class PageController extends AbstractController } else { - $this->view->setPage($page); + $this->view->setPage($path); $this->getApplication()->setResponse(new HtmlResponse($this->view->render())); } diff --git a/week-06/project/libraries/src/Controller/UserController.php b/week-06/project/libraries/src/Controller/UserController.php index 9ad6c22..e7d1de8 100644 --- a/week-06/project/libraries/src/Controller/UserController.php +++ b/week-06/project/libraries/src/Controller/UserController.php @@ -10,21 +10,25 @@ namespace Octoleo\CMS\Controller; +use Exception; use Joomla\Application\AbstractApplication; use Joomla\Controller\AbstractController; -use Joomla\Filter\InputFilter as InputFilterAlias; use Joomla\Input\Input; -use Octoleo\CMS\Date\Date; use Joomla\Authentication\Password\BCryptHandler; +use Laminas\Diactoros\Response\HtmlResponse; +use Octoleo\CMS\Controller\Util\AccessInterface; +use Octoleo\CMS\Controller\Util\AccessTrait; +use Octoleo\CMS\Controller\Util\CheckTokenInterface; +use Octoleo\CMS\Controller\Util\CheckTokenTrait; +use Octoleo\CMS\Date\Date; use Octoleo\CMS\Factory; -use Octoleo\CMS\Filter\InputFilter; use Octoleo\CMS\Model\UserModel; +use Octoleo\CMS\User\User; use Octoleo\CMS\User\UserFactoryInterface; use Octoleo\CMS\View\Admin\UserHtmlView; -use Laminas\Diactoros\Response\HtmlResponse; /** - * Controller handling the site's dashboard + * Controller handling the requests * * @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object. * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object @@ -47,44 +51,46 @@ class UserController extends AbstractController implements AccessInterface, Chec */ private $model; - /** - * @var InputFilter - */ - private $inputFilter; - /** * @var BCryptHandler */ private $secure; + /** + * @var User + */ + private $user; + /** * Constructor. * * @param UserModel $model The model object. * @param UserHtmlView $view The view object. * @param Input|null $input The input object. + * @param User|null $user The current user. * @param AbstractApplication|null $app The application object. */ - public function __construct(UserModel $model, UserHtmlView $view, Input $input = null, AbstractApplication $app = null, BCryptHandler $secure = null) + public function __construct( + UserModel $model, + UserHtmlView $view, + Input $input = null, + AbstractApplication $app = null, + User $user = null, + BCryptHandler $secure = null) { parent::__construct($input, $app); - $this->model = $model; - $this->view = $view; - $this->inputFilter = InputFilter::getInstance( - [], - [], - InputFilterAlias::ONLY_BLOCK_DEFINED_TAGS, - InputFilterAlias::ONLY_BLOCK_DEFINED_ATTRIBUTES - ); - $this->secure = ($secure) ?: new BCryptHandler(); + $this->model = $model; + $this->view = $view; + $this->user = ($user) ?: Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); + $this->secure = ($secure) ?: new BCryptHandler(); } /** * Execute the controller. * * @return boolean - * @throws \Exception + * @throws Exception */ public function execute(): bool { @@ -98,21 +104,35 @@ class UserController extends AbstractController implements AccessInterface, Chec // if task is delete if ('delete' === $task) { - /** @var \Octoleo\CMS\User\User $user */ - $user = Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); // check that the user does not delete him/her self - $user_id = $user->get('id', -1); - if ($user_id == $id) + if ($this->allow('user') && $this->user->get('access.user.delete', false)) { - $this->getApplication()->enqueueMessage('You can not delete your own account!', 'warning'); - } - elseif ($this->model->delete($id)) - { - $this->getApplication()->enqueueMessage('User was deleted!', 'success'); + // get the current user being deleted + /** @var \Octoleo\CMS\User\User $userBeingDeleted */ + $userBeingDeleted = Factory::getContainer()->get(UserFactoryInterface::class)->getUser($id); + // get the current active user ID + $user_id = $this->user->get('id', -1); + // is this the same user account as the active user + if ($user_id == $id) + { + $this->getApplication()->enqueueMessage('You can not delete your own account!', 'warning'); + } + elseif ($userBeingDeleted->get('is_admin', false) && !$this->user->get('is_admin', false)) + { + $this->getApplication()->enqueueMessage('You dont have the permission to delete an administrator account!', 'error'); + } + elseif ($this->model->delete($id)) + { + $this->getApplication()->enqueueMessage('User was deleted!', 'success'); + } + else + { + $this->getApplication()->enqueueMessage('User could not be deleted!', 'error'); + } } else { - $this->getApplication()->enqueueMessage('User could not be deleted!', 'error'); + $this->getApplication()->enqueueMessage('You do not have permission to delete this user!', 'error'); } // go to set page $this->_redirect('users'); @@ -120,23 +140,81 @@ class UserController extends AbstractController implements AccessInterface, Chec return true; } + // set the current user ID + $user_id = $this->user->get('id', -1); + if ('POST' === $method) { - $id = $this->setItem(); + // always check the post token + $this->checkToken(); + // get the post + $post = $this->getInput()->getInputForRequestMethod(); + + // we get all the needed items + $tempItem = $post->getArray(['groups' => 'INT']); + $tempItem['id'] = $post->getInt('user_id', 0); + $tempItem['name'] = $post->getString('name', ''); + $tempItem['username'] = $post->getUsername('username', ''); + $tempItem['password'] = $post->getString('password', ''); + $tempItem['password2'] = $post->getString('password2', ''); + $tempItem['email'] = $post->getString('email', ''); + $tempItem['block'] = $post->getInt('block', 1); + $tempItem['sendEmail'] = $post->getInt('sendEmail', 1); + $tempItem['activation'] = $post->getInt('activation', 0); + + // check permissions + $update = ($tempItem['id'] > 0 && $this->user->get('access.user.update', false)); + $create = ($tempItem['id'] == 0 && $this->user->get('access.user.create', false)); + $selfUpdate = ($tempItem['id'] > 0 && $tempItem['id'] == $user_id); + + if ($create || $update || $selfUpdate) + { + $id = $this->setItem($tempItem); + } + else + { + // not allowed creating user + if ($id == 0) + { + $this->getApplication()->enqueueMessage('You do not have permission to create users!', 'error'); + } + // not allowed updating user + if ($id > 0) + { + $this->getApplication()->enqueueMessage('You do not have permission to update the user details!', 'error'); + } + } } - $this->view->setActiveId($id); - $this->view->setActiveView('user'); + // check permissions + $read = ($id > 0 && $this->user->get('access.user.read', false)); + $create = ($id == 0 && $this->user->get('access.user.create', false)); + $selfUpdate = ($id > 0 && $id == $user_id); // check if user is allowed to access - if ($this->allow('user')) + if ($this->allow('user') && ($read || $create || $selfUpdate)) { + // set values for view + $this->view->setActiveId($id); + $this->view->setActiveView('user'); + $this->getApplication()->setResponse(new HtmlResponse($this->view->render())); } else { + // not allowed creating user + if ($id == 0 && !$create) + { + $this->getApplication()->enqueueMessage('You do not have permission to create users!', 'error'); + } + // not allowed updating user + if ($id > 0 && !$read) + { + $this->getApplication()->enqueueMessage('You do not have permission to read the user details!', 'error'); + } + // go to set page - $this->_redirect(); + $this->_redirect('users'); } return true; @@ -145,33 +223,18 @@ class UserController extends AbstractController implements AccessInterface, Chec /** * Set an item * + * @param array $tempItem + * * @return int - * @throws \Exception + * @throws Exception */ - protected function setItem(): int + protected function setItem(array $tempItem): int { - // always check the post token - $this->checkToken(); - // get the post - $post = $this->getInput()->getInputForRequestMethod(); - - // we get all the needed items - $tempItem = []; - $tempItem['id'] = $post->getInt('user_id', 0); - $tempItem['name'] = $post->getString('name', ''); - $tempItem['username'] = $post->getUsername('username', ''); - $tempItem['password'] = $post->getString('password', ''); - $tempItem['password2'] = $post->getString('password2', ''); - $tempItem['email'] = $post->getString('email', ''); - $tempItem['block'] = $post->getInt('block', 1); - $tempItem['sendEmail'] = $post->getInt('sendEmail', 1); - $tempItem['activation'] = $post->getInt('activation', 0); - $can_save = true; // check that we have a name if (empty($tempItem['name'])) { - // we show a warning message + // we show an error message $tempItem['name'] = ''; $this->getApplication()->enqueueMessage('Name field is required.', 'error'); $can_save = false; @@ -179,7 +242,7 @@ class UserController extends AbstractController implements AccessInterface, Chec // check that we have a username if (empty($tempItem['username'])) { - // we show a warning message + // we show an error message $tempItem['username'] = ''; $this->getApplication()->enqueueMessage('Username field is required.', 'error'); $can_save = false; @@ -187,7 +250,7 @@ class UserController extends AbstractController implements AccessInterface, Chec // check that we have an email TODO: check that we have a valid email if (empty($tempItem['email'])) { - // we show a warning message + // we show an error message $tempItem['email'] = ''; $this->getApplication()->enqueueMessage('Email field is required.', 'error'); $can_save = false; @@ -195,8 +258,8 @@ class UserController extends AbstractController implements AccessInterface, Chec // check passwords if (isset($tempItem['password2']) && $tempItem['password'] != $tempItem['password2']) { - // we show a warning message - $tempItem['password'] = 'xxxxxxxxxx'; + // we show an error message + $tempItem['password'] = 'xxxxxxxxxx'; $tempItem['password2'] = 'xxxxxxxxxx'; $this->getApplication()->enqueueMessage('Passwords do not match.', 'error'); $can_save = false; @@ -207,8 +270,8 @@ class UserController extends AbstractController implements AccessInterface, Chec { if ($tempItem['id'] == 0) { - // we show a warning message - $tempItem['password'] = 'xxxxxxxxxx'; + // we show an error message + $tempItem['password'] = 'xxxxxxxxxx'; $tempItem['password2'] = 'xxxxxxxxxx'; $this->getApplication()->enqueueMessage('Passwords not set.', 'error'); $can_save = false; @@ -220,8 +283,8 @@ class UserController extends AbstractController implements AccessInterface, Chec } elseif (strlen($tempItem['password']) < 7) { - // we show a warning message - $tempItem['password'] = 'xxxxxxxxxx'; + // we show an error message + $tempItem['password'] = 'xxxxxxxxxx'; $tempItem['password2'] = 'xxxxxxxxxx'; $this->getApplication()->enqueueMessage('Passwords must be longer than 6 characters.', 'error'); $can_save = false; @@ -235,29 +298,150 @@ class UserController extends AbstractController implements AccessInterface, Chec // can we save the item if ($can_save) { - /** @var \Octoleo\CMS\User\User $user */ - $user = Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); - $today = (new Date())->toSql(); - // check that the user does not block him/her self - $user_id = $user->get('id', -1); - if ($user_id == $tempItem['id'] && $tempItem['block'] == 1) + $user_id = $this->user->get('id', -1); + $block_status = $tempItem['block']; + // this user is the current user + if ($user_id == $tempItem['id']) { // don't allow user to block self - $this->getApplication()->enqueueMessage('You can not block yourself!', 'warning'); - $tempItem['block'] = 0; + if ($tempItem['block'] != 0) + { + // we show a warning message + $this->getApplication()->enqueueMessage('You can not block yourself!', 'warning'); + $tempItem['block'] = 0; + } + // don't allow user remove self from admin groups + if ($this->user->get('is_admin', false)) + { + $admin_groups = $this->user->get('is_admin_groups', []); + if (is_array($admin_groups) && count($admin_groups) > 0) + { + $notice_set_groups = true; + foreach ($admin_groups as $admin_group) + { + if (!is_array($tempItem['groups']) || !in_array($admin_group, $tempItem['groups'])) + { + if ($notice_set_groups) + { + // we show a warning message + $this->getApplication()->enqueueMessage('You can not remove yourself from the administrator group!', 'warning'); + $notice_set_groups = false; + } + $tempItem['groups'][] = $admin_group; + } + } + } + else + { + // we show an error message + $this->getApplication()->enqueueMessage('There is a problem with the admin user groups, we can not save the user details.', 'error'); + $can_save = false; + } + } } - return $this->model->setItem( - $tempItem['id'], - $tempItem['name'], - $tempItem['username'], - $tempItem['email'], - $tempItem['password'], - $tempItem['block'], - $tempItem['sendEmail'], - $today, - $tempItem['activation']); + // can we save the item + if ($can_save) + { + // check that the user will have some groups left + if (!is_array($tempItem['groups']) || count($tempItem['groups']) == 0) + { + // we show a warning message + $this->getApplication()->enqueueMessage('You must select at least one group.', 'warning'); + // this user is the current user + if ($user_id == $tempItem['id']) + { + $tempItem['groups'] = $this->user->get('groups_ids', []); + // check if we still have no groups + if (count($tempItem['groups']) == 0) + { + $can_save = false; + } + } + else + { + $can_save = false; + } + } + } + + // can we save the item + if ($can_save) + { + // none admin restrictions TODO would like to move this to the database and not hard code it + if (!$this->user->get('is_admin', false)) + { + // with existing users + if ($tempItem['id'] > 0) + { + // get the current user being saved + /** @var \Octoleo\CMS\User\User $userBeingSaved */ + $userBeingSaved = Factory::getContainer()->get(UserFactoryInterface::class)->getUser($tempItem['id']); + // don't allow block status change by none admin users + $block = $userBeingSaved->get('block', 1); + $current_posted_block = $tempItem['block']; + // if the status changed we revert and give message + // we allow block but not un-block + if ($block != $current_posted_block && $current_posted_block == 0) + { + // we show a warning message + $this->getApplication()->enqueueMessage('Only the administrator can update user access to system.', 'warning'); + $tempItem['block'] = 1; + } + // get current group to see if we must give a notice + $groups = $userBeingSaved->get('groups_ids', []); + $current_posted_groups = $tempItem['groups']; + sort($groups); + sort($current_posted_groups); + // if the groups changes we give a message + if ($groups !== $current_posted_groups) + { + // we show a warning message + $this->getApplication()->enqueueMessage('Only the administrator can update user group selection.', 'warning'); + } + // if the current user being saved is an admin account + // we don't allow the following changes + if ($userBeingSaved->get('is_admin', false)) + { + // we do not allow password changes of admin accounts + $tempItem['password'] = ''; + // we don't allow username changes + $tempItem['username'] = $userBeingSaved->get('username', $tempItem['username']); + // we don't allow change of status + if ($block != $current_posted_block) + { + // we show an error message + $this->getApplication()->enqueueMessage('Only the administrator can update another administrator account access to system.', 'error'); + $tempItem['block'] = $block; + } + } + } + else + { + // new users created by none admin must be blocked by default + // since only admin can unblock any users + $tempItem['block'] = 1; + } + // only admin can change groups + // empty groups will not get updated + $tempItem['groups'] = []; + } + + $today = (new Date())->toSql(); + + return $this->model->setItem( + $tempItem['id'], + $tempItem['name'], + $tempItem['username'], + $tempItem['groups'], + $tempItem['email'], + $tempItem['password'], + $tempItem['block'], + $tempItem['sendEmail'], + $today, + $tempItem['activation']); + } } // add to model the post values diff --git a/week-06/project/libraries/src/Controller/UserGroupController.php b/week-06/project/libraries/src/Controller/UserGroupController.php new file mode 100644 index 0000000..d2be47b --- /dev/null +++ b/week-06/project/libraries/src/Controller/UserGroupController.php @@ -0,0 +1,262 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Controller; + +use Joomla\Application\AbstractApplication; +use Joomla\Controller\AbstractController; +use Joomla\Input\Input; +use Octoleo\CMS\Controller\Util\AccessInterface; +use Octoleo\CMS\Controller\Util\AccessTrait; +use Octoleo\CMS\Controller\Util\CheckTokenInterface; +use Octoleo\CMS\Controller\Util\CheckTokenTrait; +use Octoleo\CMS\Factory; +use Octoleo\CMS\Model\UsergroupModel; +use Octoleo\CMS\User\User; +use Octoleo\CMS\User\UserFactoryInterface; +use Octoleo\CMS\View\Admin\UsergroupHtmlView; +use Laminas\Diactoros\Response\HtmlResponse; + +/** + * Controller handling the requests + * + * @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object. + * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object + */ +class UserGroupController extends AbstractController implements AccessInterface, CheckTokenInterface +{ + use AccessTrait, CheckTokenTrait; + + /** + * The view object. + * + * @var UsergroupHtmlView + */ + private $view; + + /** + * The model object. + * + * @var UsergroupModel + */ + private $model; + + /** + * @var User + */ + private $user; + + /** + * Constructor. + * + * @param UsergroupModel $model The model object. + * @param UsergroupHtmlView $view The view object. + * @param Input|null $input The input object. + * @param AbstractApplication|null $app The application object. + */ + public function __construct( + UsergroupModel $model, + UsergroupHtmlView $view, + Input $input = null, + AbstractApplication $app = null, + User $user = null) + { + parent::__construct($input, $app); + + $this->model = $model; + $this->view = $view; + $this->user = ($user) ?: Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); + } + + /** + * Execute the controller. + * + * @return boolean + * @throws \Exception + */ + public function execute(): bool + { + // Do not Enable browser caching + $this->getApplication()->allowCache(false); + + $method = $this->getInput()->getMethod(); + $task = $this->getInput()->getString('task', ''); + $id = $this->getInput()->getInt('id', 0); + + // if task is delete + if ('delete' === $task) + { + if ($this->allow('usergroup') && $this->user->get('access.usergroup.delete', false)) + { + // TODO not ideal to hard code any ID + if ($id == 1) + { + $this->getApplication()->enqueueMessage('This is the administrator user group that can not be deleted.', 'error'); + } + elseif ($id > 0 && $this->model->linked($id)) + { + $this->getApplication()->enqueueMessage('This user group is still in use and can therefore no be deleted.', 'error'); + } + elseif ($this->model->delete($id)) + { + $this->getApplication()->enqueueMessage('User group was deleted!', 'success'); + } + else + { + $this->getApplication()->enqueueMessage('User group could not be deleted!', 'error'); + } + } + else + { + $this->getApplication()->enqueueMessage('You do not have permission to delete this user group!', 'error'); + } + // go to set page + $this->_redirect('usergroups'); + + return true; + } + + if ('POST' === $method) + { + // check permissions + $update = ($id > 0 && $this->user->get('access.usergroup.update', false)); + $create = ($id == 0 && $this->user->get('access.usergroup.create', false)); + + // TODO not ideal to hard code any ID + if ($id == 1 && $update) + { + $this->getApplication()->enqueueMessage('This is the administrator user group that can not change.', 'error'); + } + elseif ( $create || $update ) + { + $id = $this->setItem(); + } + else + { + // not allowed creating user group + if ($id == 0) + { + $this->getApplication()->enqueueMessage('You do not have permission to create user groups!', 'error'); + } + // not allowed updating user group + if ($id > 0) + { + $this->getApplication()->enqueueMessage('You do not have permission to update the user group details!', 'error'); + } + } + } + + // check permissions + $read = ($id > 0 && $this->user->get('access.usergroup.read', false)); + $create = ($id == 0 && $this->user->get('access.usergroup.create', false)); + + // check if user is allowed to access + if ($this->allow('usergroup') && ( $read || $create )) + { + // set values for view + $this->view->setActiveId($id); + $this->view->setActiveView('usergroup'); + + $this->getApplication()->setResponse(new HtmlResponse($this->view->render())); + } + else + { + // not allowed creating user group + if ($id == 0 && !$create) + { + $this->getApplication()->enqueueMessage('You do not have permission to create user groups!', 'error'); + } + // not allowed read user group + if ($id > 0 && !$read) + { + $this->getApplication()->enqueueMessage('You do not have permission to read the user group details!', 'error'); + } + + // go to set page + $this->_redirect('items'); + } + + return true; + } + + /** + * Set an item + * + * @return int + * @throws \Exception + */ + protected function setItem(): int + { + // always check the post token + $this->checkToken(); + // get the post + $post = $this->getInput()->getInputForRequestMethod(); + + // we get all the needed items + $tempItem = $post->getArray(['params' => 'STRING']);; + $tempItem['id'] = $post->getInt('usergroup_id', 0); + $tempItem['title'] = $post->getString('title', ''); + + $can_save = true; + // check that we have a name + if (empty($tempItem['title'])) + { + // we show a warning message + $tempItem['name'] = ''; + $this->getApplication()->enqueueMessage('User group name field is required.', 'error'); + $can_save = false; + } + // set the params + $build_params = $this->model->getGroupDefaultsAccess(); + if (isset($tempItem['params']) && is_array($tempItem['params']) && count($tempItem['params'])) + { + $only = 'CRUD'; + foreach ($build_params as $n => &$item) + { + if (isset($tempItem['params'][$item->area]) && strlen($tempItem['params'][$item->area])) + { + $array_of_access = str_split(strtoupper($tempItem['params'][$item->area])); + $access_keeper = []; + if ($array_of_access) + { + foreach ($array_of_access as $char) + { + if (strpos($only, $char) === false) + { + $this->getApplication()->enqueueMessage("User group access in ({$item->area} area) had a wrong key ({$char}) so we removed it. Please only use the keys prescribed.", 'warning'); + } + else + { + $access_keeper[] = $char; + } + } + } + $item->access = implode($access_keeper); + } + } + } + // update the params + $tempItem['params'] = $build_params; + + // can we save the item + if ($can_save) + { + return $this->model->setItem( + $tempItem['id'], + $tempItem['title'], + $tempItem['params']); + } + + // add to model the post values + $this->model->tempItem = $tempItem; + + return $tempItem['id']; + } +} diff --git a/week-06/project/libraries/src/Controller/UsergroupsController.php b/week-06/project/libraries/src/Controller/UsergroupsController.php new file mode 100644 index 0000000..46c6823 --- /dev/null +++ b/week-06/project/libraries/src/Controller/UsergroupsController.php @@ -0,0 +1,94 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Controller; + +use Joomla\Application\AbstractApplication; +use Joomla\Controller\AbstractController; +use Joomla\Input\Input; +use Octoleo\CMS\Controller\Util\AccessInterface; +use Octoleo\CMS\Controller\Util\AccessTrait; +use Octoleo\CMS\Controller\Util\CheckTokenInterface; +use Octoleo\CMS\Controller\Util\CheckTokenTrait; +use Octoleo\CMS\Factory; +use Octoleo\CMS\User\User; +use Octoleo\CMS\User\UserFactoryInterface; +use Octoleo\CMS\View\Admin\UsergroupsHtmlView; +use Laminas\Diactoros\Response\HtmlResponse; + +/** + * Controller handling the requests + * + * @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object. + * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object + */ +class UsergroupsController extends AbstractController implements AccessInterface, CheckTokenInterface +{ + use AccessTrait, CheckTokenTrait; + + /** + * The view object. + * + * @var UsergroupsHtmlView + */ + private $view; + + /** + * @var User + */ + private $user; + + /** + * Constructor. + * + * @param UsergroupsHtmlView $view The view object. + * @param Input|null $input The input object. + * @param AbstractApplication|null $app The application object. + * @param User|null $user + */ + public function __construct( + UsergroupsHtmlView $view, + Input $input = null, + AbstractApplication $app = null, + User $user = null) + { + parent::__construct($input, $app); + + $this->view = $view; + $this->user = ($user) ?: Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); + } + + /** + * Execute the controller. + * + * @return boolean + * @throws \Exception + */ + public function execute(): bool + { + // Do not Enable browser caching + $this->getApplication()->allowCache(false); + + $this->view->setActiveView('usergroups'); + + // check if user is allowed to access + if ($this->allow('usergroups') && $this->user->get('access.usergroup.read', false)) + { + $this->getApplication()->setResponse(new HtmlResponse($this->view->render())); + } + else + { + // go to set page + $this->_redirect(); + } + + return true; + } +} diff --git a/week-06/project/libraries/src/Controller/UsersController.php b/week-06/project/libraries/src/Controller/UsersController.php index 628d58e..e25d7fc 100644 --- a/week-06/project/libraries/src/Controller/UsersController.php +++ b/week-06/project/libraries/src/Controller/UsersController.php @@ -13,14 +13,21 @@ namespace Octoleo\CMS\Controller; use Joomla\Application\AbstractApplication; use Joomla\Controller\AbstractController; use Joomla\Input\Input; +use Octoleo\CMS\Controller\Util\AccessInterface; +use Octoleo\CMS\Controller\Util\AccessTrait; +use Octoleo\CMS\Controller\Util\CheckTokenInterface; +use Octoleo\CMS\Controller\Util\CheckTokenTrait; +use Octoleo\CMS\Factory; +use Octoleo\CMS\User\User; +use Octoleo\CMS\User\UserFactoryInterface; use Octoleo\CMS\View\Admin\UsersHtmlView; use Laminas\Diactoros\Response\HtmlResponse; /** - * Controller handling the site's dashboard + * Controller handling the requests * * @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object. - * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object + * @property-read \Octoleo\CMS\Application\AdminApplication $app Application object */ class UsersController extends AbstractController implements AccessInterface, CheckTokenInterface { @@ -33,6 +40,11 @@ class UsersController extends AbstractController implements AccessInterface, Che */ private $view; + /** + * @var User + */ + private $user; + /** * Constructor. * @@ -40,11 +52,16 @@ class UsersController extends AbstractController implements AccessInterface, Che * @param Input $input The input object. * @param AbstractApplication $app The application object. */ - public function __construct(UsersHtmlView $view, Input $input = null, AbstractApplication $app = null) + public function __construct( + UsersHtmlView $view, + Input $input = null, + AbstractApplication $app = null, + User $user = null) { parent::__construct($input, $app); $this->view = $view; + $this->user = ($user) ?: Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); } /** @@ -61,7 +78,7 @@ class UsersController extends AbstractController implements AccessInterface, Che $this->view->setActiveView('users'); // check if user is allowed to access - if ($this->allow('users')) + if ($this->allow('users') && $this->user->get('access.user.read', false)) { $this->getApplication()->setResponse(new HtmlResponse($this->view->render())); } diff --git a/week-06/project/libraries/src/Controller/AccessInterface.php b/week-06/project/libraries/src/Controller/Util/AccessInterface.php similarity index 93% rename from week-06/project/libraries/src/Controller/AccessInterface.php rename to week-06/project/libraries/src/Controller/Util/AccessInterface.php index de6dcdc..60b29c9 100644 --- a/week-06/project/libraries/src/Controller/AccessInterface.php +++ b/week-06/project/libraries/src/Controller/Util/AccessInterface.php @@ -8,7 +8,7 @@ * @license GNU General Public License version 2 or later; see LICENSE.txt */ -namespace Octoleo\CMS\Controller; +namespace Octoleo\CMS\Controller\Util; /** * Class for checking the user access diff --git a/week-06/project/libraries/src/Controller/AccessTrait.php b/week-06/project/libraries/src/Controller/Util/AccessTrait.php similarity index 98% rename from week-06/project/libraries/src/Controller/AccessTrait.php rename to week-06/project/libraries/src/Controller/Util/AccessTrait.php index 347dfbb..0069b1f 100644 --- a/week-06/project/libraries/src/Controller/AccessTrait.php +++ b/week-06/project/libraries/src/Controller/Util/AccessTrait.php @@ -8,7 +8,7 @@ * @license GNU General Public License version 2 or later; see LICENSE.txt */ -namespace Octoleo\CMS\Controller; +namespace Octoleo\CMS\Controller\Util; use Joomla\Uri\Uri; diff --git a/week-06/project/libraries/src/Controller/CheckTokenInterface.php b/week-06/project/libraries/src/Controller/Util/CheckTokenInterface.php similarity index 92% rename from week-06/project/libraries/src/Controller/CheckTokenInterface.php rename to week-06/project/libraries/src/Controller/Util/CheckTokenInterface.php index ba83510..bfbb659 100644 --- a/week-06/project/libraries/src/Controller/CheckTokenInterface.php +++ b/week-06/project/libraries/src/Controller/Util/CheckTokenInterface.php @@ -8,7 +8,7 @@ * @license GNU General Public License version 2 or later; see LICENSE.txt */ -namespace Octoleo\CMS\Controller; +namespace Octoleo\CMS\Controller\Util; /** * Class for checking the form had a token diff --git a/week-06/project/libraries/src/Controller/CheckTokenTrait.php b/week-06/project/libraries/src/Controller/Util/CheckTokenTrait.php similarity index 94% rename from week-06/project/libraries/src/Controller/CheckTokenTrait.php rename to week-06/project/libraries/src/Controller/Util/CheckTokenTrait.php index 5fd0df5..b3ade22 100644 --- a/week-06/project/libraries/src/Controller/CheckTokenTrait.php +++ b/week-06/project/libraries/src/Controller/Util/CheckTokenTrait.php @@ -8,7 +8,7 @@ * @license GNU General Public License version 2 or later; see LICENSE.txt */ -namespace Octoleo\CMS\Controller; +namespace Octoleo\CMS\Controller\Util; /** * Class for checking the form had a token diff --git a/week-06/project/libraries/src/Model/DashboardModel.php b/week-06/project/libraries/src/Model/DashboardModel.php index d942bdd..f5cb6ac 100644 --- a/week-06/project/libraries/src/Model/DashboardModel.php +++ b/week-06/project/libraries/src/Model/DashboardModel.php @@ -11,32 +11,17 @@ namespace Octoleo\CMS\Model; use Joomla\Database\DatabaseDriver; -use Joomla\Database\ParameterType; use Joomla\Model\DatabaseModelInterface; use Joomla\Model\DatabaseModelTrait; /** - * Model class for pages + * Model class * source: https://github.com/joomla/framework.joomla.org/blob/master/src/Model/PackageModel.php */ class DashboardModel implements DatabaseModelInterface { use DatabaseModelTrait; - /** - * Array of legal dashboards - * - * @var array - */ - private $legalDashboards = [ - 'items' => 'items.twig', - 'item' => 'edit.twig', - 'menus' => 'menus.twig', - 'menu' => 'edit.twig', - 'users' => 'users.twig', - 'user' => 'edit.twig' - ]; - /** * Instantiate the model. * @@ -48,7 +33,7 @@ class DashboardModel implements DatabaseModelInterface } /** - * Get a active dashboard template name + * Get an active dashboard template name * * @param string $dashboardName The dashboard to lookup * @@ -57,11 +42,6 @@ class DashboardModel implements DatabaseModelInterface */ public function getDashboard(string $dashboardName): string { - if (!isset($this->legalDashboards[$dashboardName])) - { - return 'dashboard.twig'; - } - - return $this->legalDashboards[$dashboardName]; + return 'dashboard.twig'; // only one at this time } } diff --git a/week-06/project/libraries/src/Model/ItemModel.php b/week-06/project/libraries/src/Model/ItemModel.php index a7c5622..e94f6c7 100644 --- a/week-06/project/libraries/src/Model/ItemModel.php +++ b/week-06/project/libraries/src/Model/ItemModel.php @@ -17,7 +17,7 @@ use Joomla\Model\DatabaseModelTrait; use Octoleo\CMS\Date\Date; /** - * Model class for items + * Model class */ class ItemModel implements DatabaseModelInterface { @@ -134,6 +134,8 @@ class ItemModel implements DatabaseModelInterface // remove what can not now be set $data['modified'] = '0000-00-00 00:00:00'; $data['modified_by'] = 0; + // we don't have any params for now + $data['params'] = ''; // change to object $data = (object) $data; diff --git a/week-06/project/libraries/src/Model/ItemsModel.php b/week-06/project/libraries/src/Model/ItemsModel.php index 30cc63e..8d85c58 100644 --- a/week-06/project/libraries/src/Model/ItemsModel.php +++ b/week-06/project/libraries/src/Model/ItemsModel.php @@ -11,12 +11,11 @@ namespace Octoleo\CMS\Model; use Joomla\Database\DatabaseDriver; -use Joomla\Database\ParameterType; use Joomla\Model\DatabaseModelInterface; use Joomla\Model\DatabaseModelTrait; /** - * Model class for items + * Model class */ class ItemsModel implements DatabaseModelInterface { diff --git a/week-06/project/libraries/src/Model/MenuModel.php b/week-06/project/libraries/src/Model/MenuModel.php index 0bc23b3..a1344db 100644 --- a/week-06/project/libraries/src/Model/MenuModel.php +++ b/week-06/project/libraries/src/Model/MenuModel.php @@ -14,15 +14,25 @@ use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; use Joomla\Model\DatabaseModelInterface; use Joomla\Model\DatabaseModelTrait; -use Joomla\String\StringHelper; use Octoleo\CMS\Date\Date; +use Octoleo\CMS\Model\Util\MenuInterface; +use Octoleo\CMS\Model\Util\SelectMenuTrait; +use Octoleo\CMS\Model\Util\UniqueInterface; +use Octoleo\CMS\Model\Util\UniqueMenuAliasTrait; /** - * Model class for menu item + * Model class */ -class MenuModel implements DatabaseModelInterface +class MenuModel implements DatabaseModelInterface, UniqueInterface, MenuInterface { - use DatabaseModelTrait; + use DatabaseModelTrait, UniqueMenuAliasTrait, SelectMenuTrait; + + /** + * Active id + * + * @var int + */ + public $id = 0; /** * @var array @@ -52,6 +62,7 @@ class MenuModel implements DatabaseModelInterface * @param string $publishDown * @param string $position * @param int $home + * @param int $parent * * @return int * @throws \Exception @@ -66,12 +77,16 @@ class MenuModel implements DatabaseModelInterface string $publishUp, string $publishDown, string $position, - int $home): int + int $home, + int $parent): int { $db = $this->getDb(); - // set the path if not set - $this->setPathAlias($id, $title, $path, $alias); + // set the alias if not set + $alias = (empty($alias)) ? $title : $alias; + $alias = $this->unique($id, $alias, $parent); + // set the path + $path = $this->getPath($alias, $parent); $data = [ 'title' => (string) $title, @@ -82,7 +97,7 @@ class MenuModel implements DatabaseModelInterface 'publish_up' => (string) (empty($publishUp)) ? '0000-00-00 00:00:00' : (new Date($publishUp))->toSql(), 'publish_down' => (string) (empty($publishDown)) ? '0000-00-00 00:00:00' : (new Date($publishDown))->toSql(), 'home' => (int) $home, - 'parent_id' => 0 // only root items for now + 'parent_id' => (int) $parent ]; // we set position in params @@ -91,7 +106,9 @@ class MenuModel implements DatabaseModelInterface // if we have ID update if ($id > 0) { + // set active ID $data['id'] = (int) $id; + $this->id = (int) $id; // change to object $data = (object) $data; @@ -272,67 +289,65 @@ class MenuModel implements DatabaseModelInterface } /** - * @param int $id - * @param string $title - * @param string $path + * get path + * * @param string $alias + * @param int $parent + * + * @return string */ - private function setPathAlias(int $id, string $title, string &$path, string &$alias) + private function getPath(string $alias, int $parent): string { - $alias = (empty($alias)) ? $title : $alias; - $alias = preg_replace('/\s+/', '-', strtolower(preg_replace("/[^A-Za-z0-9\- ]/", '', $alias))); - // TODO: we will only have root menus for now, no sub-menus - $seeker = $alias; - $pointer = 2; - while ($this->exist($id, $seeker)) + // alias bucket + $bucket = []; + $bucket[] = $alias; + $parent = $this->getParent($parent); + // make sure to get all path aliases TODO: we should limit the menu depth to 6 or something + while (isset($parent->alias)) { - $seeker = $alias . '-' . $pointer; - $pointer++; + // load the alias + $bucket[] = $parent->alias; + // get the next parent + $parent = $this->getParent($parent->parent_id); } - // update the path - $alias = $seeker; - $path = $seeker; + // now return the path + return implode('/', array_reverse($bucket)); } /** - * Check if an alias exist + * get parent * * @param int $id - * @param string $alias * - * @return bool + * @return \stdClass */ - private function exist(int $id, string $alias): bool + private function getParent(int $id): \stdClass { - $db = $this->getDb(); - $query = $db->getQuery(true) - ->select('id') - ->from($db->quoteName('#__menu')) - ->where($db->quoteName('alias') . ' = :alias') - ->bind(':alias', $alias) - ->setLimit(1); - - // only add the id item exist if ($id > 0) { - $query - ->where($db->quoteName('id') . ' != :id') - ->bind(':id', $id, ParameterType::INTEGER); - } + $db = $this->getDb(); + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__menu')) + ->where($db->quoteName('id') . ' = :id') + ->bind(':id', $id) + ->setLimit(1); - try - { - $id = $db->setQuery($query)->loadResult(); - } - catch (\RuntimeException $e) - { - // we ignore this and just return an empty object - } + try + { + $parent = $db->setQuery($query)->loadObject(); + } + catch (\RuntimeException $e) + { + // we ignore this and just return an empty object + } - if (isset($id) && $id > 0) - { - return true; + // return only if found + if (isset($parent) && $parent instanceof \stdClass) + { + return $parent; + } } - return false; + return new \stdClass(); } } diff --git a/week-06/project/libraries/src/Model/MenusModel.php b/week-06/project/libraries/src/Model/MenusModel.php index 41bd3dc..3a9d529 100644 --- a/week-06/project/libraries/src/Model/MenusModel.php +++ b/week-06/project/libraries/src/Model/MenusModel.php @@ -16,7 +16,7 @@ use Joomla\Model\DatabaseModelInterface; use Joomla\Model\DatabaseModelTrait; /** - * Model class for menus + * Model class */ class MenusModel implements DatabaseModelInterface { diff --git a/week-06/project/libraries/src/Model/PageModel.php b/week-06/project/libraries/src/Model/PageModel.php index cfb6ac5..65fd2c8 100644 --- a/week-06/project/libraries/src/Model/PageModel.php +++ b/week-06/project/libraries/src/Model/PageModel.php @@ -13,13 +13,19 @@ namespace Octoleo\CMS\Model; use Joomla\Database\DatabaseDriver; use Joomla\Model\DatabaseModelInterface; use Joomla\Model\DatabaseModelTrait; +use Octoleo\CMS\Model\Util\MenuInterface; +use Octoleo\CMS\Model\Util\PageInterface; +use Octoleo\CMS\Model\Util\HomeMenuInterface; +use Octoleo\CMS\Model\Util\HomeMenuTrait; +use Octoleo\CMS\Model\Util\SiteMenuTrait; +use Octoleo\CMS\Model\Util\SitePageTrait; /** - * Model class for pages + * Model class */ -class PageModel implements DatabaseModelInterface, MenuInterface, PageInterface +class PageModel implements DatabaseModelInterface, MenuInterface, PageInterface, HomeMenuInterface { - use DatabaseModelTrait, SiteMenuTrait, SitePageTrait; + use DatabaseModelTrait, HomeMenuTrait, SiteMenuTrait, SitePageTrait; /** * Instantiate the model. diff --git a/week-06/project/libraries/src/Model/UserModel.php b/week-06/project/libraries/src/Model/UserModel.php index cc1cf65..29d5bb7 100644 --- a/week-06/project/libraries/src/Model/UserModel.php +++ b/week-06/project/libraries/src/Model/UserModel.php @@ -15,13 +15,18 @@ use Joomla\Database\ParameterType; use Joomla\Model\DatabaseModelInterface; use Joomla\Model\DatabaseModelTrait; use Octoleo\CMS\Date\Date; +use Octoleo\CMS\Model\Util\GetUsergroupsInterface; +use Octoleo\CMS\Model\Util\GetUsergroupsTrait; +use Exception; +use RuntimeException; +use stdClass; /** - * Model class for items + * Model class */ -class UserModel implements DatabaseModelInterface +class UserModel implements DatabaseModelInterface, GetUsergroupsInterface { - use DatabaseModelTrait; + use DatabaseModelTrait, GetUsergroupsTrait; /** * @var array @@ -31,9 +36,9 @@ class UserModel implements DatabaseModelInterface /** * Instantiate the model. * - * @param DatabaseDriver $db The database adapter. + * @param DatabaseDriver|null $db The database adapter. */ - public function __construct(DatabaseDriver $db) + public function __construct(DatabaseDriver $db = null) { $this->setDb($db); } @@ -44,6 +49,7 @@ class UserModel implements DatabaseModelInterface * @param int $id * @param string $name * @param string $username + * @param array $groups * @param string $email * @param string $password * @param int $block @@ -52,12 +58,13 @@ class UserModel implements DatabaseModelInterface * @param int $activation * * @return int - * @throws \Exception + * @throws Exception */ public function setItem( int $id, string $name, string $username, + array $groups, string $email, string $password, int $block, @@ -96,16 +103,15 @@ class UserModel implements DatabaseModelInterface { $db->updateObject('#__users', $data, 'id'); } - catch (\RuntimeException $exception) + catch (RuntimeException $exception) { - throw new \RuntimeException($exception->getMessage(), 404); + throw new RuntimeException($exception->getMessage(), 404); } - - return $id; - } else { + // we don't have any params for now + $data['params'] = ''; // change to object $data = (object) $data; @@ -113,13 +119,68 @@ class UserModel implements DatabaseModelInterface { $db->insertObject('#__users', $data); } - catch (\RuntimeException $exception) + catch (RuntimeException $exception) { - throw new \RuntimeException($exception->getMessage(), 404); + throw new RuntimeException($exception->getMessage(), 404); } - return $db->insertid(); + $id = $db->insertid(); } + + // update the group linked to this user + // only if there are groups + if (count($groups) > 0) + { + try + { + $this->setGroups($id, $groups); + } + catch (RuntimeException $exception) + { + throw new RuntimeException($exception->getMessage(), 404); + } + } + + return $id; + } + + /** + * Add groups for this user + * + * @param int $id + * @param array $groups + * + * @return bool + * @throws Exception + */ + private function setGroups(int $id, array $groups): bool + { + $db = $this->getDb(); + // add the new groups + $query = $db->getQuery(true) + ->insert($db->quoteName('#__user_usergroup_map')) + ->columns($db->quoteName(['user_id', 'group_id'])); + // Insert values. + foreach ($groups as $group) + { + $query->values(implode(',', [(int) $id, (int) $group])); + } + // execute the update/change + try + { + // delete link to groups + if ($this->deleteGroups($id)) + { + // add the new groups + $db->setQuery($query)->execute(); + } + } + catch (RuntimeException $e) + { + throw new RuntimeException($e->getMessage(), 404); + } + + return true; } /** @@ -127,10 +188,10 @@ class UserModel implements DatabaseModelInterface * * @param int|null $id * - * @return \stdClass - * @throws \Exception + * @return stdClass + * @throws Exception */ - public function getItem(?int $id): \stdClass + public function getItem(?int $id): stdClass { $db = $this->getDb(); // default object (use posted values if set) @@ -140,7 +201,7 @@ class UserModel implements DatabaseModelInterface } else { - $default = new \stdClass(); + $default = new stdClass(); } // to be sure ;) $default->today_date = (new Date())->toSql(); @@ -169,12 +230,12 @@ class UserModel implements DatabaseModelInterface { $result = $db->setQuery($query)->loadObject(); } - catch (\RuntimeException $e) + catch (RuntimeException $e) { // we ignore this and just return an empty object } - if (isset($result) && $result instanceof \stdClass) + if (isset($result) && $result instanceof stdClass && isset($result->id)) { $result->post_key = "?id=$id&task=edit"; $result->today_date = $default->today_date; @@ -182,6 +243,23 @@ class UserModel implements DatabaseModelInterface $result->password = $default->password; $result->password2 = $default->password2; + // Initialise some variables + $query = $db->getQuery(true) + ->select('m.group_id') + ->from($db->quoteName('#__user_usergroup_map', 'm')) + ->where($db->quoteName('m.user_id') . ' = :user_id') + ->bind(':user_id', $result->id, ParameterType::INTEGER); + + try + { + // we just load the ID's + $result->groups = $db->setQuery($query)->loadColumn(); + } + catch (RuntimeException $e) + { + // we ignore this and just return result + } + return $result; } @@ -202,23 +280,56 @@ class UserModel implements DatabaseModelInterface * @param int $id * * @return bool + * @throws Exception */ public function delete(int $id): bool { $db = $this->getDb(); - // Purge the session + // Delete the user from the database $query = $db->getQuery(true) ->delete($db->quoteName('#__users')) ->where($db->quoteName('id') . ' = :id') ->bind(':id', $id, ParameterType::INTEGER); try + { + // delete link to groups + if ($this->deleteGroups($id)) + { + // delete user + $db->setQuery($query)->execute(); + } + } + catch (RuntimeException $e) + { + throw new RuntimeException($e->getMessage(), 404); + } + + return true; + } + + /** + * delete all groups form this user + * + * @param int $id + * + * @return bool + * @throws Exception + */ + private function deleteGroups(int $id): bool + { + $db = $this->getDb(); + // Delete the user from the database + $query = $db->getQuery(true) + ->delete($db->quoteName('#__user_usergroup_map')) + ->where($db->quoteName('user_id') . ' = :user_id') + ->bind(':user_id', $id, ParameterType::INTEGER); + try { $db->setQuery($query)->execute(); } - catch (\RuntimeException $e) + catch (RuntimeException $e) { - // delete failed - return false; + throw new RuntimeException($e->getMessage(), 404); } return true; diff --git a/week-06/project/libraries/src/Model/UsergroupModel.php b/week-06/project/libraries/src/Model/UsergroupModel.php new file mode 100644 index 0000000..0be2fb5 --- /dev/null +++ b/week-06/project/libraries/src/Model/UsergroupModel.php @@ -0,0 +1,239 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Model; + +use Joomla\Database\DatabaseDriver; +use Joomla\Database\ParameterType; +use Joomla\Model\DatabaseModelInterface; +use Joomla\Model\DatabaseModelTrait; +use Octoleo\CMS\Model\Util\GetUsergroupsInterface; +use Octoleo\CMS\Model\Util\GetUsergroupsTrait; + +/** + * Model class + */ +class UsergroupModel implements DatabaseModelInterface, GetUsergroupsInterface +{ + use DatabaseModelTrait, GetUsergroupsTrait; + + /** + * @var array + */ + public $tempItem; + + /** + * Instantiate the model. + * + * @param DatabaseDriver $db The database adapter. + */ + public function __construct(DatabaseDriver $db) + { + $this->setDb($db); + } + + /** + * Add an item + * + * @param int $id + * @param string $title + * @param array $params + * + * @return int + */ + public function setItem( + int $id, + string $title, + array $params): int + { + $db = $this->getDb(); + + if (count($params) > 0) + { + $params = json_encode($params); + } + else + { + $params = ''; + } + + $data = [ + 'title' => (string) $title, + 'params' => (string) $params + ]; + + // if we have ID update + if ($id > 0) + { + $data['id'] = (int) $id; + // change to object + $data = (object) $data; + + try + { + $db->updateObject('#__usergroups', $data, 'id'); + } + catch (\RuntimeException $exception) + { + throw new \RuntimeException($exception->getMessage(), 404); + } + + return $id; + + } + else + { + // change to object + $data = (object) $data; + + try + { + $db->insertObject('#__usergroups', $data); + } + catch (\RuntimeException $exception) + { + throw new \RuntimeException($exception->getMessage(), 404); + } + + return $db->insertid(); + } + } + + /** + * Get an item + * + * @param int|null $id + * + * @return \stdClass + * @throws \Exception + */ + public function getItem(?int $id): \stdClass + { + $db = $this->getDb(); + // default object (use posted values if set) + if (is_array($this->tempItem)) + { + $default = (object) $this->tempItem; + } + else + { + $default = new \stdClass(); + $default->params = $this->getGroupDefaultsAccess(); + } + + // we return the default if id not correct + if (!is_numeric($id)) + { + return $default; + } + + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__usergroups')) + ->where($db->quoteName('id') . ' = :id') + ->bind(':id', $id, ParameterType::INTEGER) + ->setLimit(1); + + try + { + $result = $db->setQuery($query)->loadObject(); + } + catch (\RuntimeException $e) + { + // we ignore this and just return an empty object + } + + if (isset($result) && $result instanceof \stdClass) + { + $result->post_key = "?id=$id&task=edit"; + + // make sure to set the params + $result->params = json_decode($result->params); + // We set an empty default + if (!is_array($result->params)) + { + $result->params = $this->getGroupDefaultsAccess(); + } + + return $result; + } + + return $default; + } + + /** + * @param string $name + * + * @return string + */ + public function setLayout(string $name): string + { + return $name . '.twig'; + } + + /** + * @param int $id + * + * @return bool + */ + public function delete(int $id): bool + { + $db = $this->getDb(); + // Purge the session + $query = $db->getQuery(true) + ->delete($db->quoteName('#__usergroups')) + ->where($db->quoteName('id') . ' = :id') + ->bind(':id', $id, ParameterType::INTEGER); + try + { + $db->setQuery($query)->execute(); + } + catch (\RuntimeException $e) + { + // delete failed + return false; + } + + return true; + } + + /** + * @param int $id + * + * @return bool + */ + public function linked(int $id): bool + { + $db = $this->getDb(); + // first check if this item is linked to menu + $query = $db->getQuery(true) + ->select($db->quoteName('user_id')) + ->from($db->quoteName('#__user_usergroup_map')) + ->where($db->quoteName('group_id') . ' = :id') + ->bind(':id', $id, ParameterType::INTEGER); + + try + { + $users = $db->setQuery($query)->loadColumn(); + } + catch (\RuntimeException $e) + { + // not linked... or something + return false; + } + + if ($users) + { + return true; + } + + return false; + } +} diff --git a/week-06/project/libraries/src/Model/UsergroupsModel.php b/week-06/project/libraries/src/Model/UsergroupsModel.php new file mode 100644 index 0000000..76364fd --- /dev/null +++ b/week-06/project/libraries/src/Model/UsergroupsModel.php @@ -0,0 +1,50 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Model; + +use Joomla\Database\DatabaseDriver; +use Joomla\Model\DatabaseModelInterface; +use Joomla\Model\DatabaseModelTrait; +use Octoleo\CMS\Model\Util\GetUsergroupsInterface; +use Octoleo\CMS\Model\Util\GetUsergroupsTrait; + +/** + * Model class + */ +class UsergroupsModel implements DatabaseModelInterface, GetUsergroupsInterface +{ + use DatabaseModelTrait, GetUsergroupsTrait; + + /** + * Instantiate the model. + * + * @param DatabaseDriver $db The database adapter. + */ + public function __construct(DatabaseDriver $db) + { + $this->setDb($db); + } + + /** + * Get all items + * + * @return array + */ + public function getItems(): array + { + return $this->getUsergroups(); + } + + public function setLayout(string $name): string + { + return $name . '.twig'; + } +} diff --git a/week-06/project/libraries/src/Model/UsersModel.php b/week-06/project/libraries/src/Model/UsersModel.php index dcd16cf..91c8c4f 100644 --- a/week-06/project/libraries/src/Model/UsersModel.php +++ b/week-06/project/libraries/src/Model/UsersModel.php @@ -11,16 +11,17 @@ namespace Octoleo\CMS\Model; use Joomla\Database\DatabaseDriver; -use Joomla\Database\ParameterType; use Joomla\Model\DatabaseModelInterface; use Joomla\Model\DatabaseModelTrait; +use Octoleo\CMS\Model\Util\GetUsergroupsInterface; +use Octoleo\CMS\Model\Util\GetUsergroupsTrait; /** - * Model class for users + * Model class */ -class UsersModel implements DatabaseModelInterface +class UsersModel implements DatabaseModelInterface, GetUsergroupsInterface { - use DatabaseModelTrait; + use DatabaseModelTrait, GetUsergroupsTrait; /** * Instantiate the model. @@ -45,7 +46,18 @@ class UsersModel implements DatabaseModelInterface ->select('*') ->from($db->quoteName('#__users')); - return $db->setQuery($query)->loadObjectList('id'); + $users = $db->setQuery($query)->loadObjectList('id'); + + // add groups + if ($users) + { + foreach ($users as $id => &$user) + { + $user->groups = $this->getUsergroups($id); + } + } + + return $users; } public function setLayout(string $name): string diff --git a/week-06/project/libraries/src/Model/Util/GetUsergroupsInterface.php b/week-06/project/libraries/src/Model/Util/GetUsergroupsInterface.php new file mode 100644 index 0000000..79ff425 --- /dev/null +++ b/week-06/project/libraries/src/Model/Util/GetUsergroupsInterface.php @@ -0,0 +1,37 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Model\Util; + +/** + * Class for all user groups + * + * @since 1.0.0 + */ +interface GetUsergroupsInterface +{ + /** + * Get all user groups + * + * @param int|null $id + * + * @return array + */ + public function getUsergroups(?int $id = null): array; + + /** + * Get the group default full access values + * + * @param string $access + * + * @return array + */ + public function getGroupDefaultsAccess(string $access = 'CRUD'): array; +} diff --git a/week-06/project/libraries/src/Model/Util/GetUsergroupsTrait.php b/week-06/project/libraries/src/Model/Util/GetUsergroupsTrait.php new file mode 100644 index 0000000..3efde67 --- /dev/null +++ b/week-06/project/libraries/src/Model/Util/GetUsergroupsTrait.php @@ -0,0 +1,103 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Model\Util; + +use Joomla\Database\ParameterType; +use Octoleo\CMS\Utilities\StringHelper; +use RuntimeException; +use stdClass; + +/** + * Class for all user groups + * + * @since 1.0.0 + * @method getDb() + */ +trait GetUsergroupsTrait +{ + /** + * Get all user groups + * + * @param int|null $id user ID + * + * @return array + */ + public function getUsergroups(?int $id = null): array + { + $db = $this->getDb(); + + $query = $db->getQuery(true) + ->select('g.*') + ->from($db->quoteName('#__usergroups', 'g')); + + if ($id > 0) + { + $query + ->join('INNER', $db->quoteName('#__user_usergroup_map', 'm'), 'g.id = m.group_id') + ->where($db->quoteName('m.user_id') . ' = :user_id') + ->bind(':user_id', $id, ParameterType::INTEGER); + } + + try + { + $groups = $db->setQuery($query)->loadObjectList('id'); + } + catch (RuntimeException $e) + { + throw new RuntimeException($e->getMessage(), 404); + } + + if (is_array($groups) && count($groups) > 0) + { + foreach ($groups as $n => &$group) + { + $group->params = json_decode($group->params); + // We set an empty default + if (!is_array($group->params)) + { + $group->params = $this->getGroupDefaultsAccess(); + } + } + return $groups; + } + + return []; + } + + /** + * Get the group default full access values + * + * @param string $access + * + * @return array + */ + public function getGroupDefaultsAccess(string $access = ''): array + { + return [ + (object) [ + 'area' => 'user', + 'access' => $access + ], + (object) [ + 'area' => 'usergroup', + 'access' => $access + ], + (object) [ + 'area' => 'menu', + 'access' => $access + ], + (object) [ + 'area' => 'item', + 'access' => $access + ], + ]; + } +} diff --git a/week-06/project/libraries/src/Model/Util/HomeMenuInterface.php b/week-06/project/libraries/src/Model/Util/HomeMenuInterface.php new file mode 100644 index 0000000..5069245 --- /dev/null +++ b/week-06/project/libraries/src/Model/Util/HomeMenuInterface.php @@ -0,0 +1,24 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Model\Util; + +/** + * Class for getting the home page + * + * @since 1.0.0 + */ +interface HomeMenuInterface +{ + /** + * @return \stdClass + */ + public function getHomePage(): \stdClass; +} diff --git a/week-06/project/libraries/src/Model/HomepageModel.php b/week-06/project/libraries/src/Model/Util/HomeMenuTrait.php similarity index 56% rename from week-06/project/libraries/src/Model/HomepageModel.php rename to week-06/project/libraries/src/Model/Util/HomeMenuTrait.php index 232ef8e..9b02dcd 100644 --- a/week-06/project/libraries/src/Model/HomepageModel.php +++ b/week-06/project/libraries/src/Model/Util/HomeMenuTrait.php @@ -2,37 +2,21 @@ /** * @package Octoleo CMS * - * @created 9th April 2022 + * @created 21th April 2022 * @author Llewellyn van der Merwe * @git WEBD-325-45 * @license GNU General Public License version 2 or later; see LICENSE.txt */ -namespace Octoleo\CMS\Model; - -use Joomla\Database\DatabaseDriver; -use Joomla\Database\ParameterType; -use Joomla\Model\DatabaseModelInterface; -use Joomla\Model\DatabaseModelTrait; +namespace Octoleo\CMS\Model\Util; /** - * Model class for pages - * source: https://github.com/joomla/framework.joomla.org/blob/master/src/Model/PackageModel.php + * Trait for getting home menu + * + * @since 1.0.0 */ -class HomepageModel implements DatabaseModelInterface, MenuInterface, PageInterface +trait HomeMenuTrait { - use DatabaseModelTrait, SiteMenuTrait, SitePageTrait; - - /** - * Instantiate the model. - * - * @param DatabaseDriver $db The database adapter. - */ - public function __construct(DatabaseDriver $db) - { - $this->setDb($db); - } - /** * @return \stdClass */ diff --git a/week-06/project/libraries/src/Model/MenuInterface.php b/week-06/project/libraries/src/Model/Util/MenuInterface.php similarity index 78% rename from week-06/project/libraries/src/Model/MenuInterface.php rename to week-06/project/libraries/src/Model/Util/MenuInterface.php index 3e6c5eb..f713aa7 100644 --- a/week-06/project/libraries/src/Model/MenuInterface.php +++ b/week-06/project/libraries/src/Model/Util/MenuInterface.php @@ -8,7 +8,7 @@ * @license GNU General Public License version 2 or later; see LICENSE.txt */ -namespace Octoleo\CMS\Model; +namespace Octoleo\CMS\Model\Util; /** * Class for getting menu items @@ -20,9 +20,9 @@ interface MenuInterface /** * Get all menu items * - * @param string|null $active + * @param int $active * * @return array */ - public function getMenus(string $active = null): array; + public function getMenus(int $active = 0): array; } diff --git a/week-06/project/libraries/src/Model/PageInterface.php b/week-06/project/libraries/src/Model/Util/PageInterface.php similarity index 95% rename from week-06/project/libraries/src/Model/PageInterface.php rename to week-06/project/libraries/src/Model/Util/PageInterface.php index 91233d4..9f36d63 100644 --- a/week-06/project/libraries/src/Model/PageInterface.php +++ b/week-06/project/libraries/src/Model/Util/PageInterface.php @@ -8,7 +8,7 @@ * @license GNU General Public License version 2 or later; see LICENSE.txt */ -namespace Octoleo\CMS\Model; +namespace Octoleo\CMS\Model\Util; /** * Class for getting page data diff --git a/week-06/project/libraries/src/Model/Util/SelectMenuTrait.php b/week-06/project/libraries/src/Model/Util/SelectMenuTrait.php new file mode 100644 index 0000000..16c29e5 --- /dev/null +++ b/week-06/project/libraries/src/Model/Util/SelectMenuTrait.php @@ -0,0 +1,54 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Model\Util; + +use Joomla\Database\ParameterType; + +/** + * Trait for getting menu items + * + * @since 1.0.0 + */ +trait SelectMenuTrait +{ + /** + * Get all menu items + * + * @param int $active + * + * @return array + */ + public function getMenus(int $active = 0): array + { + $db = $this->getDb(); + + if (empty($active) && !empty($this->id)) + { + $active = $this->id; + } + + $query = $db->getQuery(true) + ->select($db->quoteName(array('id', 'title'))) + ->from($db->quoteName('#__menu')) + ->where('published = 1') + ->where('home = 0'); + + // we need to remove the active menu + if ($active > 0) + { + $query + ->where($db->quoteName('id') . ' != :id') + ->bind(':id', $active, ParameterType::INTEGER); + } + + return $db->setQuery($query)->loadObjectList('id'); + } +} diff --git a/week-06/project/libraries/src/Model/SiteMenuTrait.php b/week-06/project/libraries/src/Model/Util/SiteMenuTrait.php similarity index 87% rename from week-06/project/libraries/src/Model/SiteMenuTrait.php rename to week-06/project/libraries/src/Model/Util/SiteMenuTrait.php index 41d7af1..a62bf31 100644 --- a/week-06/project/libraries/src/Model/SiteMenuTrait.php +++ b/week-06/project/libraries/src/Model/Util/SiteMenuTrait.php @@ -8,7 +8,7 @@ * @license GNU General Public License version 2 or later; see LICENSE.txt */ -namespace Octoleo\CMS\Model; +namespace Octoleo\CMS\Model\Util; /** * Trait for getting menu items @@ -20,18 +20,17 @@ trait SiteMenuTrait /** * Get all menu items that are root and published and not home page * - * @param string|null $active + * @param int $active * * @return array */ - public function getMenus($active = null): array + public function getMenus(int $active = 0): array { $db = $this->getDb(); $query = $db->getQuery(true) ->select('a.*') ->from($db->quoteName('#__menu', 'a')) - ->where($db->quoteName('a.parent_id') . ' = 0') ->where($db->quoteName('a.published') . ' = 1') ->where($db->quoteName('a.home') . ' = 0'); @@ -51,9 +50,10 @@ trait SiteMenuTrait { $row = []; // set the details + $row['id'] = $menu->id; $row['title'] = $menu->title; $row['path'] = $menu->path; - $row['root'] = true; + $row['parent'] = $menu->parent_id; // set position $params = (isset($menu->params) && strpos($menu->params, 'position') !== false) ? json_decode($menu->params) : null; // default is center diff --git a/week-06/project/libraries/src/Model/SitePageTrait.php b/week-06/project/libraries/src/Model/Util/SitePageTrait.php similarity index 66% rename from week-06/project/libraries/src/Model/SitePageTrait.php rename to week-06/project/libraries/src/Model/Util/SitePageTrait.php index da9b0db..910a605 100644 --- a/week-06/project/libraries/src/Model/SitePageTrait.php +++ b/week-06/project/libraries/src/Model/Util/SitePageTrait.php @@ -8,7 +8,7 @@ * @license GNU General Public License version 2 or later; see LICENSE.txt */ -namespace Octoleo\CMS\Model; +namespace Octoleo\CMS\Model\Util; use Joomla\Database\ParameterType; @@ -33,10 +33,13 @@ trait SitePageTrait $db = $this->getDb(); $query = $db->getQuery(true) - ->select('t.*') - ->from($db->quoteName('#__menu', 'a')) - ->join('INNER', $db->quoteName('#__item', 't'), 'a.item_id = t.id') - ->where($db->quoteName('path') . ' = :path') + ->select('i.*') + ->select($db->quoteName(array('m.id'), array('menu_id'))) + ->from($db->quoteName('#__menu', 'm')) + ->join('INNER', $db->quoteName('#__item', 'i'), 'm.item_id = i.id') + ->where($db->quoteName('i.state') . ' >= 1') + ->where($db->quoteName('m.published') . ' = 1') + ->where($db->quoteName('m.path') . ' = :path') ->bind(':path', $path) ->setLimit(1); @@ -71,9 +74,13 @@ trait SitePageTrait $db = $this->getDb(); $query = $db->getQuery(true) - ->select('a.*') - ->from($db->quoteName('#__item', 'a')) - ->where($db->quoteName('id') . ' = :id') + ->select('i.*') + ->select($db->quoteName(array('m.id'), array('menu_id'))) + ->from($db->quoteName('#__item', 'i')) + ->join('INNER', $db->quoteName('#__menu', 'm'), 'i.id = m.item_id') + ->where($db->quoteName('m.published') . ' = 1') + ->where($db->quoteName('i.state') . ' >= 1') + ->where($db->quoteName('i.id') . ' = :id') ->bind(':id', $item, ParameterType::INTEGER) ->setLimit(1); diff --git a/week-06/project/libraries/src/Model/Util/UniqueInterface.php b/week-06/project/libraries/src/Model/Util/UniqueInterface.php new file mode 100644 index 0000000..245d3cf --- /dev/null +++ b/week-06/project/libraries/src/Model/Util/UniqueInterface.php @@ -0,0 +1,44 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Model\Util; + +/** + * Class for getting unique string + * + * @since 1.0.0 + */ +interface UniqueInterface +{ + /** + * Get a unique string + * + * @param int $id + * @param string $value + * @param int $parent + * @param string $key + * @param string $spacer + * + * @return string + */ + public function unique(int $id, string $value, int $parent = -1, string $key = 'alias', string $spacer = '-'): string; + + /** + * Check if an any key exist with same parent + * + * @param int $id + * @param string $value + * @param string $key + * @param int $parent + * + * @return bool + */ + public function exist(int $id, string $value, string $key = 'alias', int $parent = -1): bool; +} diff --git a/week-06/project/libraries/src/Model/Util/UniqueMenuAliasTrait.php b/week-06/project/libraries/src/Model/Util/UniqueMenuAliasTrait.php new file mode 100644 index 0000000..327b35e --- /dev/null +++ b/week-06/project/libraries/src/Model/Util/UniqueMenuAliasTrait.php @@ -0,0 +1,103 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Model\Util; + +use Joomla\Database\ParameterType; + +/** + * Trait for getting unique string + * + * @since 1.0.0 + */ +trait UniqueMenuAliasTrait +{ + /** + * Get a unique string + * + * @param int $id + * @param string $value + * @param int $parent + * @param string $key + * @param string $spacer + * + * @return string + */ + public function unique(int $id, string $value, int $parent = -1, string $key = 'alias', string $spacer = '-'): string + { + // start building the value + $value = str_replace($spacer, ' ', $value); + $value = preg_replace('/\s+/', $spacer, strtolower(preg_replace("/[^A-Za-z0-9\- ]/", '', $value))); + // set a counter + $counter = 2; + // set original tracker + $original = $value; + // check if we found any with the same alias + while ($this->exist($id, $value, $key, $parent)) + { + $value = $original . '-' . $counter; + $counter++; + } + // return the unique value (on this parent layer) + return $value; + } + + /** + * Check if an any key exist with same parent + * + * @param int $id + * @param string $value + * @param string $key + * @param int $parent + * + * @return bool + */ + public function exist(int $id, string $value, string $key = 'alias', int $parent = -1): bool + { + $db = $this->getDb(); + $query = $db->getQuery(true) + ->select('id') + ->from($db->quoteName('#__menu')) + ->where($db->quoteName($key) . " = :$key") + ->bind(":$key", $value) + ->setLimit(1); + + // only add the id item exist + if ($parent >= 0) + { + $query + ->where($db->quoteName('parent_id') . ' = :parent_id') + ->bind(':parent_id', $parent, ParameterType::INTEGER); + } + + // only add the id item exist + if ($id > 0) + { + $query + ->where($db->quoteName('id') . ' != :id') + ->bind(':id', $id, ParameterType::INTEGER); + } + + try + { + $result = $db->setQuery($query)->loadResult(); + } + catch (\RuntimeException $e) + { + // we ignore this and just return an empty object + } + + if (isset($result) && $result > 0) + { + return true; + } + return false; + } +} diff --git a/week-06/project/libraries/src/Renderer/FrameworkExtension.php b/week-06/project/libraries/src/Renderer/FrameworkExtension.php index f5d0e5f..62f7e25 100644 --- a/week-06/project/libraries/src/Renderer/FrameworkExtension.php +++ b/week-06/project/libraries/src/Renderer/FrameworkExtension.php @@ -46,6 +46,8 @@ class FrameworkExtension extends AbstractExtension new TwigFunction('route', [FrameworkTwigRuntime::class, 'getRouteUri']), new TwigFunction('sri', [FrameworkTwigRuntime::class, 'getSriAttributes'], ['is_safe' => ['html']]), new TwigFunction('message_queue', [FrameworkTwigRuntime::class, 'getMessageQueue']), + new TwigFunction('user_array', [FrameworkTwigRuntime::class, 'getUserArray']), + new TwigFunction('user', [FrameworkTwigRuntime::class, 'getUser']), new TwigFunction('token', [FrameworkTwigRuntime::class, 'getToken']), new TwigFunction('shorten_string', [FrameworkTwigRuntime::class, 'shortenString']), ]; diff --git a/week-06/project/libraries/src/Renderer/FrameworkTwigRuntime.php b/week-06/project/libraries/src/Renderer/FrameworkTwigRuntime.php index b9c89f2..6aa2bc8 100644 --- a/week-06/project/libraries/src/Renderer/FrameworkTwigRuntime.php +++ b/week-06/project/libraries/src/Renderer/FrameworkTwigRuntime.php @@ -11,6 +11,9 @@ namespace Octoleo\CMS\Renderer; use Joomla\Application\AbstractApplication; use Joomla\Preload\PreloadManager; use Joomla\Session\SessionInterface; +use Octoleo\CMS\Factory; +use Octoleo\CMS\User\User; +use Octoleo\CMS\User\UserFactoryInterface; /** * Twig runtime class @@ -47,10 +50,15 @@ class FrameworkTwigRuntime private $sriManifestPath; /** - * @var SessionInterface + * @var SessionInterface|null */ private $session; + /** + * @var User|null + */ + private $user; + /** * Constructor * @@ -59,12 +67,16 @@ class FrameworkTwigRuntime * @param string $sriManifestPath The path to the SRI manifest data * @param SessionInterface|null $session The session object */ - public function __construct(AbstractApplication $app, PreloadManager $preloadManager, string $sriManifestPath, $session = null) + public function __construct( + AbstractApplication $app, + PreloadManager $preloadManager, + string $sriManifestPath, + SessionInterface $session = null) { - $this->session = $session; $this->app = $app; $this->preloadManager = $preloadManager; $this->sriManifestPath = $sriManifestPath; + $this->session = $session; } /** @@ -149,6 +161,41 @@ class FrameworkTwigRuntime return $string; } + /** + * Get current user as array + * + * @return array + */ + public function getUserArray(): array + { + if (!$this->user instanceof User) + { + /** @var \Octoleo\CMS\User\User $user */ + $this->user = Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); + } + // check again + if ($this->user instanceof User && method_exists($this->user, 'toArray')) + { + return $this->user->toArray(); + } + return []; + } + + public function getUser(string $key = 'name', $default = '') + { + if (!$this->user instanceof User) + { + /** @var \Octoleo\CMS\User\User $user */ + $this->user = Factory::getContainer()->get(UserFactoryInterface::class)->getUser(); + } + // check again + if ($this->user instanceof User) + { + return $this->user->get($key, $default); + } + return ''; + } + /** * Get any messages in the queue * diff --git a/week-06/project/libraries/src/Service/AdminApplicationProvider.php b/week-06/project/libraries/src/Service/AdminApplicationProvider.php index 1daf534..d1c73c8 100644 --- a/week-06/project/libraries/src/Service/AdminApplicationProvider.php +++ b/week-06/project/libraries/src/Service/AdminApplicationProvider.php @@ -26,7 +26,7 @@ use Joomla\Router\RouterInterface; use Psr\Log\LoggerInterface; /** - * Application service provider + * Admin Application service provider * source: https://github.com/joomla/framework.joomla.org/blob/master/src/Service/ApplicationProvider.php */ class AdminApplicationProvider implements ServiceProviderInterface diff --git a/week-06/project/libraries/src/Service/AdminMVCProvider.php b/week-06/project/libraries/src/Service/AdminMVCProvider.php index d572893..07f2e1f 100644 --- a/week-06/project/libraries/src/Service/AdminMVCProvider.php +++ b/week-06/project/libraries/src/Service/AdminMVCProvider.php @@ -24,6 +24,8 @@ use Octoleo\CMS\Controller\MenuController; use Octoleo\CMS\Controller\MenusController; use Octoleo\CMS\Controller\UserController; use Octoleo\CMS\Controller\UsersController; +use Octoleo\CMS\Controller\UserGroupController; +use Octoleo\CMS\Controller\UsergroupsController; use Octoleo\CMS\Controller\WrongCmsController; use Octoleo\CMS\Model\DashboardModel; use Octoleo\CMS\Model\ItemsModel; @@ -32,6 +34,9 @@ use Octoleo\CMS\Model\MenusModel; use Octoleo\CMS\Model\MenuModel; use Octoleo\CMS\Model\UserModel; use Octoleo\CMS\Model\UsersModel; +use Octoleo\CMS\Model\UsergroupModel; +use Octoleo\CMS\Model\UsergroupsModel; +use Octoleo\CMS\User\UserFactoryInterface; use Octoleo\CMS\View\Admin\DashboardHtmlView; use Octoleo\CMS\View\Admin\ItemsHtmlView; use Octoleo\CMS\View\Admin\ItemHtmlView; @@ -39,6 +44,8 @@ use Octoleo\CMS\View\Admin\MenuHtmlView; use Octoleo\CMS\View\Admin\MenusHtmlView; use Octoleo\CMS\View\Admin\UserHtmlView; use Octoleo\CMS\View\Admin\UsersHtmlView; +use Octoleo\CMS\View\Admin\UsergroupHtmlView; +use Octoleo\CMS\View\Admin\UsergroupsHtmlView; use Octoleo\CMS\Application\AdminApplication; use Joomla\Input\Input; @@ -72,6 +79,12 @@ class AdminMVCProvider implements ServiceProviderInterface $container->alias(UserController::class, 'controller.user') ->share('controller.user', [$this, 'getControllerUserService'], true); + $container->alias(UsergroupsController::class, 'controller.usergroups') + ->share('controller.usergroups', [$this, 'getControllerUsergroupsService'], true); + + $container->alias(UsergroupController::class, 'controller.usergroup') + ->share('controller.usergroup', [$this, 'getControllerUsergroupService'], true); + $container->alias(MenusController::class, 'controller.menus') ->share('controller.menus', [$this, 'getControllerMenusService'], true); @@ -100,6 +113,12 @@ class AdminMVCProvider implements ServiceProviderInterface $container->alias(UserModel::class, 'model.user') ->share('model.user', [$this, 'getModelUserService'], true); + $container->alias(UsergroupsModel::class, 'model.usergroups') + ->share('model.usergroups', [$this, 'getModelUsergroupsService'], true); + + $container->alias(UsergroupModel::class, 'model.usergroup') + ->share('model.usergroup', [$this, 'getModelUsergroupService'], true); + $container->alias(MenusModel::class, 'model.menus') ->share('model.menus', [$this, 'getModelMenusService'], true); @@ -122,6 +141,12 @@ class AdminMVCProvider implements ServiceProviderInterface $container->alias(UserHtmlView::class, 'view.user.html') ->share('view.user.html', [$this, 'getViewUserHtmlService'], true); + $container->alias(UsergroupsHtmlView::class, 'view.usergroups.html') + ->share('view.usergroups.html', [$this, 'getViewUsergroupsHtmlService'], true); + + $container->alias(UsergroupHtmlView::class, 'view.usergroup.html') + ->share('view.usergroup.html', [$this, 'getViewUsergroupHtmlService'], true); + $container->alias(MenusHtmlView::class, 'view.menus.html') ->share('view.menus.html', [$this, 'getViewMenusHtmlService'], true); @@ -234,7 +259,8 @@ class AdminMVCProvider implements ServiceProviderInterface return new UsersController( $container->get(UsersHtmlView::class), $container->get(Input::class), - $container->get(AdminApplication::class) + $container->get(AdminApplication::class), + $container->get(UserFactoryInterface::class)->getUser() ); } @@ -278,7 +304,8 @@ class AdminMVCProvider implements ServiceProviderInterface $container->get(UserModel::class), $container->get(UserHtmlView::class), $container->get(Input::class), - $container->get(AdminApplication::class) + $container->get(AdminApplication::class), + $container->get(UserFactoryInterface::class)->getUser() ); } @@ -309,6 +336,95 @@ class AdminMVCProvider implements ServiceProviderInterface ); } + /** + * Get the `controller.usergroups` service + * + * @param Container $container The DI container. + * + * @return UsergroupsController + */ + public function getControllerUsergroupsService(Container $container): UsergroupsController + { + return new UsergroupsController( + $container->get(UsergroupsHtmlView::class), + $container->get(Input::class), + $container->get(AdminApplication::class), + $container->get(UserFactoryInterface::class)->getUser() + ); + } + + /** + * Get the `model.usergroups` service + * + * @param Container $container The DI container. + * + * @return UsergroupsModel + */ + public function getModelUsergroupsService(Container $container): UsergroupsModel + { + return new UsergroupsModel($container->get(DatabaseInterface::class)); + } + + /** + * Get the `view.usergroups.html` service + * + * @param Container $container The DI container. + * + * @return UsergroupsHtmlView + */ + public function getViewUsergroupsHtmlService(Container $container): UsergroupsHtmlView + { + return new UsergroupsHtmlView( + $container->get('model.usergroups'), + $container->get('renderer') + ); + } + + /** + * Get the `controller.usergroup` service + * + * @param Container $container The DI container. + * + * @return UsergroupController + */ + public function getControllerUsergroupService(Container $container): UsergroupController + { + return new UsergroupController( + $container->get(UsergroupModel::class), + $container->get(UsergroupHtmlView::class), + $container->get(Input::class), + $container->get(AdminApplication::class), + $container->get(UserFactoryInterface::class)->getUser() + ); + } + + /** + * Get the `model.usergroup` service + * + * @param Container $container The DI container. + * + * @return UsergroupModel + */ + public function getModelUsergroupService(Container $container): UsergroupModel + { + return new UsergroupModel($container->get(DatabaseInterface::class)); + } + + /** + * Get the `view.usergroup.html` service + * + * @param Container $container The DI container. + * + * @return UsergroupHtmlView + */ + public function getViewUsergroupHtmlService(Container $container): UsergroupHtmlView + { + return new UsergroupHtmlView( + $container->get('model.usergroup'), + $container->get('renderer') + ); + } + /** * Get the `controller.menus` service * @@ -321,7 +437,8 @@ class AdminMVCProvider implements ServiceProviderInterface return new MenusController( $container->get(MenusHtmlView::class), $container->get(Input::class), - $container->get(AdminApplication::class) + $container->get(AdminApplication::class), + $container->get(UserFactoryInterface::class)->getUser() ); } @@ -365,7 +482,8 @@ class AdminMVCProvider implements ServiceProviderInterface $container->get(MenuModel::class), $container->get(MenuHtmlView::class), $container->get(Input::class), - $container->get(AdminApplication::class) + $container->get(AdminApplication::class), + $container->get(UserFactoryInterface::class)->getUser() ); } @@ -408,7 +526,8 @@ class AdminMVCProvider implements ServiceProviderInterface return new ItemsController( $container->get(ItemsHtmlView::class), $container->get(Input::class), - $container->get(AdminApplication::class) + $container->get(AdminApplication::class), + $container->get(UserFactoryInterface::class)->getUser() ); } @@ -452,7 +571,8 @@ class AdminMVCProvider implements ServiceProviderInterface $container->get(ItemModel::class), $container->get(ItemHtmlView::class), $container->get(Input::class), - $container->get(AdminApplication::class) + $container->get(AdminApplication::class), + $container->get(UserFactoryInterface::class)->getUser() ); } diff --git a/week-06/project/libraries/src/Service/AdminRouterProvider.php b/week-06/project/libraries/src/Service/AdminRouterProvider.php index 5ec5e29..2b5074d 100644 --- a/week-06/project/libraries/src/Service/AdminRouterProvider.php +++ b/week-06/project/libraries/src/Service/AdminRouterProvider.php @@ -21,6 +21,8 @@ use Octoleo\CMS\Controller\MenuController; use Octoleo\CMS\Controller\MenusController; use Octoleo\CMS\Controller\UserController; use Octoleo\CMS\Controller\UsersController; +use Octoleo\CMS\Controller\UserGroupController; +use Octoleo\CMS\Controller\UsergroupsController; use Joomla\Router\Router; use Joomla\Router\RouterInterface; @@ -72,6 +74,14 @@ class AdminRouterProvider implements ServiceProviderInterface '/index.php/user', UserController::class ); + $router->get( + '/index.php/usergroups', + UsergroupsController::class + ); + $router->all( + '/index.php/usergroup', + UsergroupController::class + ); $router->get( '/index.php/menus', MenusController::class diff --git a/week-06/project/libraries/src/Service/ConfigurationProvider.php b/week-06/project/libraries/src/Service/ConfigurationProvider.php index 05c2394..a5c537d 100644 --- a/week-06/project/libraries/src/Service/ConfigurationProvider.php +++ b/week-06/project/libraries/src/Service/ConfigurationProvider.php @@ -14,6 +14,9 @@ use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Registry\Registry; +/** + * Configuration service provider + */ class ConfigurationProvider implements ServiceProviderInterface { /** diff --git a/week-06/project/libraries/src/Service/InputProvider.php b/week-06/project/libraries/src/Service/InputProvider.php index 03cf6c4..50c5082 100644 --- a/week-06/project/libraries/src/Service/InputProvider.php +++ b/week-06/project/libraries/src/Service/InputProvider.php @@ -16,8 +16,7 @@ use Joomla\DI\ServiceProviderInterface; use Joomla\Input\Input; /** - * Application service provider - * source: https://github.com/joomla/framework.joomla.org/blob/master/src/Service/ApplicationProvider.php + * Input service provider */ class InputProvider implements ServiceProviderInterface { diff --git a/week-06/project/libraries/src/Service/SessionProvider.php b/week-06/project/libraries/src/Service/SessionProvider.php index 9553030..4de65de 100644 --- a/week-06/project/libraries/src/Service/SessionProvider.php +++ b/week-06/project/libraries/src/Service/SessionProvider.php @@ -36,7 +36,6 @@ class SessionProvider implements ServiceProviderInterface */ public function register(Container $container): void { - $container->alias(SessionDatabaseHandler::class, HandlerInterface::class) ->share(HandlerInterface::class, [$this, 'getSessionDatabaseHandlerClassService'], true); diff --git a/week-06/project/libraries/src/Service/SiteApplicationProvider.php b/week-06/project/libraries/src/Service/SiteApplicationProvider.php index 48f7dad..4801912 100644 --- a/week-06/project/libraries/src/Service/SiteApplicationProvider.php +++ b/week-06/project/libraries/src/Service/SiteApplicationProvider.php @@ -19,24 +19,21 @@ use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; -use Octoleo\CMS\Controller\HomepageController; use Octoleo\CMS\Controller\WrongCmsController; use Octoleo\CMS\Controller\PageController; -use Octoleo\CMS\Model\HomepageModel; use Octoleo\CMS\Model\PageModel; -use Octoleo\CMS\View\Page\HomepageHtmlView; -use Octoleo\CMS\View\Page\PageHtmlView; +use Octoleo\CMS\Utilities\StringHelper; +use Octoleo\CMS\View\Site\PageHtmlView; use Octoleo\CMS\Application\SiteApplication; use Joomla\Input\Input; -use Joomla\Renderer\RendererInterface; use Joomla\Router\Route; use Joomla\Router\Router; use Joomla\Router\RouterInterface; use Psr\Log\LoggerInterface; /** - * Application service provider + * Site Application service provider * source: https://github.com/joomla/framework.joomla.org/blob/master/src/Service/ApplicationProvider.php */ class SiteApplicationProvider implements ServiceProviderInterface @@ -78,9 +75,6 @@ class SiteApplicationProvider implements ServiceProviderInterface */ // Controllers - $container->alias(HomepageController::class, 'controller.homepage') - ->share('controller.homepage', [$this, 'getControllerHomepageService'], true); - $container->alias(PageController::class, 'controller.page') ->share('controller.page', [$this, 'getControllerPageService'], true); @@ -88,16 +82,10 @@ class SiteApplicationProvider implements ServiceProviderInterface ->share('controller.wrong.cms', [$this, 'getControllerWrongCmsService'], true); // Models - $container->alias(HomepageModel::class, 'model.homepage') - ->share('model.homepage', [$this, 'getModelHomepageService'], true); - $container->alias(PageModel::class, 'model.page') ->share('model.page', [$this, 'getModelPageService'], true); // Views - $container->alias(HomepageHtmlView::class, 'view.homepage.html') - ->share('view.homepage.html', [$this, 'getViewHomepageHtmlService'], true); - $container->alias(PageHtmlView::class, 'view.page.html') ->share('view.page.html', [$this, 'getViewPageHtmlService'], true); } @@ -134,32 +122,29 @@ class SiteApplicationProvider implements ServiceProviderInterface /* * Web routes */ - $router->addRoute(new Route(['GET', 'HEAD'], '/', HomepageController::class)); + $router->addRoute(new Route(['GET', 'HEAD'], '/', PageController::class)); + // dynamic pages + $pages = '/:root'; $router->get( - '/:view', + $pages, PageController::class ); + // set a mad depth TODO: we should limit the menu depth to 6 or something + $depth = range(1,20); + foreach ($depth as $page) + { + $page = StringHelper::numbers($page); + $pages .= "/:$page"; + $router->get( + $pages, + PageController::class + ); + } return $router; } - /** - * Get the `controller.homepage` service - * - * @param Container $container The DI container. - * - * @return HomepageController - */ - public function getControllerHomepageService(Container $container): HomepageController - { - return new HomepageController( - $container->get(HomepageHtmlView::class), - $container->get(Input::class), - $container->get(SiteApplication::class) - ); - } - /** * Get the `controller.page` service * @@ -191,18 +176,6 @@ class SiteApplicationProvider implements ServiceProviderInterface ); } - /** - * Get the `model.homepage` service - * - * @param Container $container The DI container. - * - * @return HomepageModel - */ - public function getModelHomepageService(Container $container): HomepageModel - { - return new HomepageModel($container->get(DatabaseInterface::class)); - } - /** * Get the `model.page` service * @@ -253,21 +226,6 @@ class SiteApplicationProvider implements ServiceProviderInterface return new ContainerControllerResolver($container); } - /** - * Get the `view.homepage.html` service - * - * @param Container $container The DI container. - * - * @return HomepageHtmlView - */ - public function getViewHomepageHtmlService(Container $container): HomepageHtmlView - { - return new HomepageHtmlView( - $container->get('model.homepage'), - $container->get('renderer') - ); - } - /** * Get the `view.page.html` service * diff --git a/week-06/project/libraries/src/Service/UserProvider.php b/week-06/project/libraries/src/Service/UserProvider.php index c6affef..a3f4fe1 100644 --- a/week-06/project/libraries/src/Service/UserProvider.php +++ b/week-06/project/libraries/src/Service/UserProvider.php @@ -13,6 +13,7 @@ namespace Octoleo\CMS\Service; use Joomla\Authentication\AuthenticationStrategyInterface; use Joomla\Authentication\Strategies\DatabaseStrategy; use Joomla\Input\Input; +use Octoleo\CMS\Session\MetadataManager; use Octoleo\CMS\User\UserFactory; use Octoleo\CMS\User\UserFactoryInterface; use Joomla\Database\DatabaseInterface; @@ -58,7 +59,8 @@ class UserProvider implements ServiceProviderInterface { return new UserFactory( $container->get(DatabaseInterface::class), - $container->get(AuthenticationStrategyInterface::class) + $container->get(AuthenticationStrategyInterface::class), + $container->get(MetadataManager::class) ); } diff --git a/week-06/project/libraries/src/User/User.php b/week-06/project/libraries/src/User/User.php index cb91d0d..3326a03 100644 --- a/week-06/project/libraries/src/User/User.php +++ b/week-06/project/libraries/src/User/User.php @@ -12,7 +12,11 @@ namespace Octoleo\CMS\User; use Joomla\Registry\Registry; use Joomla\Database\DatabaseInterface; +use Joomla\Database\ParameterType; use Octoleo\CMS\Factory; +use Octoleo\CMS\Utilities\StringHelper; +use stdClass; +use Exception; /** * User class. Handles all application interaction with a user @@ -26,7 +30,7 @@ class User extends Registry * * @param integer $identifier The primary key of the user to load (optional). * - * @throws \Exception + * @throws Exception * @since 1.1.0 */ public function __construct($identifier = 0) @@ -41,7 +45,7 @@ class User extends Registry else { // Initialise guest - $data = (object) ['id' => 0, 'sendEmail' => 0, 'aid' => 0, 'guest' => 1]; + $data = (object) ['id' => 0, 'sendEmail' => 0, 'block' => 1, 'aid' => 0, 'guest' => 1, 'groups' => []]; } // set the data parent::__construct($data); @@ -52,25 +56,124 @@ class User extends Registry * * @param int $id The user id of the user to load * - * @return Object on success + * @return stdClass on success * - * @throws \Exception + * @throws Exception * @since 1.0.0 */ - protected function load( int $id): object + protected function load(int $id): stdClass { // Get the database $db = Factory::getContainer()->get(DatabaseInterface::class); // Initialise some variables $query = $db->getQuery(true) - ->select('*') - ->from($db->quoteName('#__users')) - ->where($db->quoteName('id') . ' = :id') - ->bind(':id', $id) + ->select('u.*') + ->from($db->quoteName('#__users', 'u')) + ->where($db->quoteName('u.id') . ' = :id') + ->bind(':id', $id, ParameterType::INTEGER) ->setLimit(1); $db->setQuery($query); - return $db->loadObject(); + $user = $db->loadObject(); + + if ($user instanceof stdClass && isset($user->id)) + { + // start admin details + $user->is_admin = false; + $user->is_admin_groups = []; + // start access + $user->access = new stdClass(); + // Initialise some variables + $query = $db->getQuery(true) + ->select($db->quoteName(array('g.id', 'g.title', 'g.params'))) + ->from($db->quoteName('#__user_usergroup_map', 'm')) + ->join('INNER', $db->quoteName('#__usergroups', 'g'), 'g.id = m.group_id') + ->where($db->quoteName('m.user_id') . ' = :user_id') + ->bind(':user_id', $user->id, ParameterType::INTEGER); + $db->setQuery($query); + + $groups = $db->loadObjectList(); + + if (is_array($groups) && count($groups) > 0) + { + // group bucket of id's + $groups_ids = []; + foreach ($groups as $group) + { + // add group ID + $groups_ids[] = $group->id; + // convert params to object + $params = json_decode($group->params); + // set the access + if (is_array($params) && count($params) > 0) + { + $counter = 0; + $checker = 0; + foreach ($params as $param) + { + // prep the area string + $area = StringHelper::safe($param->area); + // only tart object if not already set + if(empty($user->access->{$area})) + { + // start object + $user->access->{$area} = new stdClass(); + } + // make sure we have upper case + $param->access = strtoupper($param->access); + // full access to area + if ($param->access === 'CRUD') + { + $checker++; + // add the full permissions + $user->access->{$area}->create = true; + $user->access->{$area}->read = true; + $user->access->{$area}->update = true; + $user->access->{$area}->delete = true; + } + else + { + // this user has fewer permissions + // set them one at a time + if (strpos($param->access, 'C') !== false) + { + $user->access->{$area}->create = true; + } + if (strpos($param->access, 'R') !== false) + { + $user->access->{$area}->read = true; + } + if (strpos($param->access, 'U') !== false) + { + $user->access->{$area}->update = true; + } + if (strpos($param->access, 'D') !== false) + { + $user->access->{$area}->delete = true; + } + } + $counter++; + } + // if this group has full access + if ($counter == $checker) + { + // we need to know when this is an admin user + $user->is_admin = true; + // we load the ids to use in user update, so we can prevent + // admin users from removing themselves from the admin group + $user->is_admin_groups[] = $group->id; + } + } + unset($group->params); + } + // keep the group details + $user->groups = $groups; + $user->groups_ids = $groups_ids; + } + + return $user; + } + return (object) ['id' => 0, 'sendEmail' => 0, 'block' => 1, 'aid' => 0, 'guest' => 1, 'groups' => []]; } } diff --git a/week-06/project/libraries/src/User/UserFactory.php b/week-06/project/libraries/src/User/UserFactory.php index 72d5ea3..5750e7a 100644 --- a/week-06/project/libraries/src/User/UserFactory.php +++ b/week-06/project/libraries/src/User/UserFactory.php @@ -20,6 +20,8 @@ use Octoleo\CMS\Date\Date; use Octoleo\CMS\Factory; use Octoleo\CMS\Filter\InputFilter; use Octoleo\CMS\Session\MetadataManager; +use Exception; +use RuntimeException; /** * Default factory for creating User objects @@ -41,6 +43,25 @@ class UserFactory implements UserFactoryInterface */ private $authentication; + /** + * @var MetadataManager + */ + private $manager; + + /** + * The Admin Application + * + * @var AdminApplication + */ + private $app; + + /** + * The user objects. + * + * @var User[] + */ + private $users = []; + /** * @var InputFilter */ @@ -56,6 +77,7 @@ class UserFactory implements UserFactoryInterface 'password' => 'RAW', 'password2' => 'RAW' ]; + /** * @var BCryptHandler */ @@ -67,52 +89,37 @@ class UserFactory implements UserFactoryInterface * @param DatabaseInterface|null $db The database * @param AuthenticationStrategyInterface|null $authentication * - * @throws \Exception + * @throws Exception */ public function __construct( DatabaseInterface $db = null, AuthenticationStrategyInterface $authentication = null, + MetadataManager $manager = null, BCryptHandler $secure = null) { $this->db = ($db) ?: Factory::getApplication()->get(DatabaseInterface::class); $this->authentication = ($authentication) ?: Factory::getApplication()->get(AuthenticationStrategyInterface::class); + $this->manager = ($manager) ?: Factory::getApplication()->get(MetadataManager::class); $this->secure = ($secure) ?: new BCryptHandler(); } /** - * Method to get an instance of a user for the given id or session. + * Method to get an instance of a user for the given id or user in session. * - * @param int|null $id The id + * @param int|null $id The user id * * @return User * - * @throws \Exception + * @throws Exception * @since 1.0.0 */ public function getUser(?int $id = null): User { + // load the user if (empty($id)) { - /** @var \Octoleo\CMS\Application\AdminApplication $application */ - $application = Factory::getApplication(); - - $session = $application->getSession(); - - // Grab the current session ID - $sessionId = $session->getId(); - - // Get the session user ID - $query = $this->db->getQuery(true) - ->select($this->db->quoteName('userid')) - ->from($this->db->quoteName('#__session')) - ->where($this->db->quoteName('session_id') . ' = :sessionid') - ->bind(':sessionid', $sessionId) - ->setLimit(1); - $this->db->setQuery($query); - - $id = (int) $this->db->loadResult(); + return $this->loadUserBySession(); } - return $this->loadUserById($id); } @@ -123,12 +130,49 @@ class UserFactory implements UserFactoryInterface * * @return User * - * @throws \Exception + * @throws Exception * @since 1.0.0 */ public function loadUserById(int $id): User { - return new User($id); + // check if we already called for this user + if (isset($this->users[$id])) + { + return $this->users[$id]; + } + + $this->users[$id] = new User($id); + + return $this->users[$id]; + } + + /** + * Method to get an instance of a user for the session. + * + * @return User + * + * @throws Exception + * @since 1.0.0 + */ + public function loadUserBySession(): User + { + if (!$this->app instanceof AdminApplication) + { + $this->app = Factory::getApplication(); + } + // Grab the current session ID + $sessionId = $this->app->getSession()->getId(); + + // Get the session user ID + $query = $this->db->getQuery(true) + ->select($this->db->quoteName('userid')) + ->from($this->db->quoteName('#__session')) + ->where($this->db->quoteName('session_id') . ' = :sessionid') + ->bind(':sessionid', $sessionId) + ->setLimit(1); + $this->db->setQuery($query); + + return $this->loadUserById((int) $this->db->loadResult()); } /** @@ -138,7 +182,7 @@ class UserFactory implements UserFactoryInterface * * @return User * - * @throws \Exception + * @throws Exception * @since 1.0.0 */ public function loadUserByUsername(string $username): User @@ -158,47 +202,37 @@ class UserFactory implements UserFactoryInterface /** * Check if user is active * - * @param bool $purge - * * @return bool - * @throws \Exception + * @throws Exception */ - public function active(?bool $purge = true): bool + public function active(): bool { - /** @var \Octoleo\CMS\Application\AdminApplication $application */ - $application = Factory::getApplication(); + // get the user in the session + $user = $this->loadUserBySession(); - $session = $application->getSession(); + // get the user ID + $user_id = $user->get('id', 0); - // Grab the current session ID - $sessionId = $session->getId(); - - /** @var \Octoleo\CMS\Session\MetadataManager $manager */ - $manager = Factory::getContainer()->get(MetadataManager::class); - - $active = $manager->getSessionRecord($sessionId); - - // if no active session is found... - if (!$active) + // check if we have a user (and it's not blocked) + if ($user_id > 0) { - return false; - } - - // if user above 0 the active - if ($active->guest == 0 && $active->userid > 0) - { - // get user - $user = $this->loadUserById($active->userid); - // check if user has access + // 1 == blocked $blocked = $user->get('block', 1); + // 0 == not blocked if ($blocked == 0) { return true; } - } + // check if we have the application + if (!$this->app instanceof AdminApplication) + { + $this->app = Factory::getApplication(); + } + // Get the session + $session = $this->app->getSession(); + // Grab the current session ID (to purge the session) + $sessionId = $session->getId(); - if ($purge) - { // Purge the session $query = $this->db->getQuery(true) ->delete($this->db->quoteName('#__session')) @@ -208,7 +242,7 @@ class UserFactory implements UserFactoryInterface { $this->db->setQuery($query)->execute(); } - catch (\RuntimeException $e) + catch (RuntimeException $e) { // The old session is already invalidated, don't let this block logging in } @@ -237,7 +271,7 @@ class UserFactory implements UserFactoryInterface ->setLimit(1) )->loadResult(); } - catch (\RuntimeException $exception) + catch (RuntimeException $exception) { return false; } @@ -269,7 +303,7 @@ class UserFactory implements UserFactoryInterface ->bind(1, $value) )->loadResult(); } - catch (\RuntimeException $exception) + catch (RuntimeException $exception) { return false; } @@ -281,56 +315,49 @@ class UserFactory implements UserFactoryInterface return false; } - /** - * Attempt to authenticate the username and password pair. - * - * @return string|boolean A string containing a username if authentication is successful, false otherwise. - * - * @since 1.1.0 - */ - public function authenticate() - { - return $this->authentication->authenticate(); - } - /** * Attempt to login user * * @return boolean true on success * - * @throws \Exception + * @throws Exception * @since 1.0.0 */ public function login(): bool { - /** @var \Octoleo\CMS\Application\AdminApplication $application */ - $application = Factory::getApplication(); - + // check if we have the application + if (!$this->app instanceof AdminApplication) + { + $this->app = Factory::getApplication(); + } if (($username = $this->authenticate()) !== false) { + // If loadUserByUsername returned an error, then pass it back. $user = $this->loadUserByUsername($username); // If loadUserByUsername returned an error, then pass it back. - if ($user instanceof \Exception) + if ($user instanceof Exception) { - $application->enqueueMessage('Login failure', 'Error'); + $this->app->enqueueMessage('Login failure', 'Error'); return false; } - // If loadUserByUsername returned an error, then pass it back. + // check if this user is active + // 1 = blocked + // 0 = active (un blocked) $blocked = $user->get('block', 1); if ($blocked == 1) { - $application->enqueueMessage('Login failure, user is blocked. Contact your system administrator.', 'Warning'); + $this->app->enqueueMessage('Login failure, user is blocked. Contact your system administrator.', 'Warning'); return false; } - return $this->setUserSession($application, $user->toArray()); + return $this->setUserSession($user->toArray()); } // set authentication failure message - $application->enqueueMessage('Login failure, please try again.', 'Warning'); + $this->app->enqueueMessage('Login failure, please try again.', 'Warning'); return false; } @@ -339,15 +366,17 @@ class UserFactory implements UserFactoryInterface * Logout user * * @return bool - * @throws \Exception + * @throws Exception */ public function logout(): bool { - /** @var \Octoleo\CMS\Application\AdminApplication $application */ - $application = Factory::getApplication(); - - $session = $application->getSession(); - + // check if we have the application + if (!$this->app instanceof AdminApplication) + { + $this->app = Factory::getApplication(); + } + // Get the session + $session = $this->app->getSession(); // Grab the current session ID $sessionId = $session->getId(); @@ -360,7 +389,7 @@ class UserFactory implements UserFactoryInterface { $this->db->setQuery($query)->execute(); } - catch (\RuntimeException $e) + catch (RuntimeException $e) { // The old session is already invalidated, don't let this block logging in } @@ -383,7 +412,7 @@ class UserFactory implements UserFactoryInterface * * @return boolean true on success * - * @throws \Exception + * @throws Exception * @since 1.0.0 */ public function create( @@ -393,11 +422,12 @@ class UserFactory implements UserFactoryInterface string $password = null, string $password2 = null): bool { - /** @var \Octoleo\CMS\Application\AdminApplication $application */ - $application = Factory::getApplication(); - - /** @var \Joomla\Input\Input $input */ - $input = $application->getInput(); + // check if we have the application + if (!$this->app instanceof AdminApplication) + { + $this->app = Factory::getApplication(); + } + $input = $this->app->getInput(); $user = []; $user['name'] = ($name) ?: $input->getString('name', ''); @@ -405,18 +435,20 @@ class UserFactory implements UserFactoryInterface $user['email'] = ($email) ?: $input->getString('email', ''); $user['password'] = ($password) ?: $input->getString('password', ''); $user['password2'] = ($password2) ?: $input->getString('password2', ''); + // normally we don't add newly registered users to the admin group + $add_to_admin_group = false; // check if username exist if (!empty($user['username']) && $this->exist($user['username'])) { - $application->enqueueMessage('Username already exist, try another username.', 'Warning'); + $this->app->enqueueMessage('Username already exist, try another username.', 'Warning'); return false; } // check if email exist if (!empty($user['email']) && $this->exist($user['email'], 'email')) { - $application->enqueueMessage('Email already exist, try another email.', 'Warning'); + $this->app->enqueueMessage('Email already exist, try another email.', 'Warning'); return false; } @@ -437,13 +469,13 @@ class UserFactory implements UserFactoryInterface if (empty($detail)) { $valid = false; - $application->enqueueMessage($key . ' is required', 'error'); + $this->app->enqueueMessage($key . ' is required', 'error'); } // check if its valid - if (!$this->valid($key, $detail)) + elseif (!$this->valid($key, $detail)) { $valid = false; - $application->enqueueMessage($key . ' is not valid', 'error'); + $this->app->enqueueMessage($key . ' is not valid', 'error'); } } @@ -451,7 +483,7 @@ class UserFactory implements UserFactoryInterface if (isset($user['password2']) && $user['password'] != $user['password2']) { $valid = false; - $application->enqueueMessage('Passwords do not match', 'error'); + $this->app->enqueueMessage('Passwords do not match', 'error'); } unset ($user['password2']); @@ -473,7 +505,10 @@ class UserFactory implements UserFactoryInterface } else { + // this is the first account (so it's an admin account) $user['block'] = 0; + // we must add this user to the admin group + $add_to_admin_group = true; } // there are no params at this stage $user['params'] = ''; @@ -485,27 +520,58 @@ class UserFactory implements UserFactoryInterface // Insert the user $result = $this->db->insertObject('#__users', $insert, 'id'); } - catch (\RuntimeException $exception) + catch (RuntimeException $exception) { - throw new \RuntimeException($exception->getMessage(), 404); - - return false; + throw new RuntimeException($exception->getMessage(), 404); } // only set session if success and not blocked if ($result && $user['block'] == 0) { + // get the user ID $user['id'] = $this->db->insertid(); - return $this->setUserSession($application, $user); + // add to admin + if ($add_to_admin_group) + { + // build the mapped group link to admin + $group = []; + $group['user_id'] = $user['id']; + $group['group_id'] = 1; // admin group ID is normally 1 see /sq/install.sql (line 110) + + $insert = (object) $group; + + try + { + // Insert the user group link + $this->db->insertObject('#__user_usergroup_map', $insert); + } + catch (RuntimeException $exception) + { + // we ignore this... at this point + } + } + return $this->setUserSession($user); } elseif ($result) { - $application->enqueueMessage('You account has been created, an administrator will active it shortly.', 'success'); + $this->app->enqueueMessage('You account has been created, an administrator will active it shortly.', 'success'); } } return false; } + /** + * Attempt to authenticate the username and password pair. + * + * @return string|boolean A string containing a username if authentication is successful, false otherwise. + * + * @since 1.1.0 + */ + private function authenticate() + { + return $this->authentication->authenticate(); + } + /** * Attempt validate user input (BASIC) * @@ -529,16 +595,22 @@ class UserFactory implements UserFactoryInterface } /** - * @param AdminApplication $application - * @param array $user + * Method to add the user to the session + * + * @param array $user * * @return bool - * @throws \Exception + * @throws Exception */ - private function setUserSession(AdminApplication $application, array $user): bool + private function setUserSession(array $user): bool { - $session = $application->getSession(); - + // check if we have the application + if (!$this->app instanceof AdminApplication) + { + $this->app = Factory::getApplication(); + } + // Get the session + $session = $this->app->getSession(); // Grab the current session ID $oldSessionId = $session->getId(); @@ -557,18 +629,16 @@ class UserFactory implements UserFactoryInterface { $this->db->setQuery($query)->execute(); } - catch (\RuntimeException $e) + catch (RuntimeException $e) { // The old session is already invalidated, don't let this block logging in } - /** @var \Octoleo\CMS\Session\MetadataManager $manager */ - $manager = Factory::getContainer()->get(MetadataManager::class); - - $manager->createOrUpdateRecord($session, $this->loadUserById($user['id'])); + // creat or update the record for this user session + $this->manager->createOrUpdateRecord($session, $this->loadUserById($user['id'])); // show a success message - $application->enqueueMessage('Welcome ' . $user['name'] . ', you have successfully lodged in!', 'Success'); + $this->app->enqueueMessage('Welcome ' . $user['name'] . ', you have successfully lodged in!', 'Success'); return true; } diff --git a/week-06/project/libraries/src/User/UserFactoryInterface.php b/week-06/project/libraries/src/User/UserFactoryInterface.php index 3e20246..b9ee358 100644 --- a/week-06/project/libraries/src/User/UserFactoryInterface.php +++ b/week-06/project/libraries/src/User/UserFactoryInterface.php @@ -1,13 +1,17 @@ + * @created 21th April 2022 + * @author Llewellyn van der Merwe + * @git WEBD-325-45 * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Octoleo\CMS\User; +use Exception; + /** * Interface defining a factory which can create User objects * @@ -15,6 +19,18 @@ namespace Octoleo\CMS\User; */ interface UserFactoryInterface { + /** + * Method to get an instance of a user for the given id or session. + * + * @param int|null $id The id + * + * @return User + * + * @throws Exception + * @since 1.0.0 + */ + public function getUser(?int $id = null): User; + /** * Method to get an instance of a user for the given id. * @@ -38,11 +54,76 @@ interface UserFactoryInterface public function loadUserByUsername(string $username): User; /** - * Attempt to authenticate the username and password pair. + * Method to get an instance of a user for the session. * - * @return string|boolean A string containing a username if authentication is successful, false otherwise. + * @return User * - * @since 1.1.0 + * @throws Exception + * @since 1.0.0 */ - public function authenticate(); + public function loadUserBySession(): User; + + /** + * Check if user is active + * + * @return bool + * @throws Exception + */ + public function active(): bool; + + /** + * Check if we have users + * + * @return bool true if we have + */ + public function has(): bool; + + /** + * Check if a user exist based on give key value pair + * + * @param string $value + * @param string $key + * + * @return false|mixed on success return user ID + */ + public function exist(string $value, string $key = 'username'); + + /** + * Attempt to login user + * + * @return boolean true on success + * + * @throws Exception + * @since 1.0.0 + */ + public function login(): bool; + + /** + * Logout user + * + * @return bool + * @throws Exception + */ + public function logout(): bool; + + /** + * Attempt to great user + * + * @param string|null $name + * @param string|null $username + * @param string|null $email + * @param string|null $password + * @param string|null $password2 + * + * @return boolean true on success + * + * @throws Exception + * @since 1.0.0 + */ + public function create( + string $name = null, + string $username = null, + string $email = null, + string $password = null, + string $password2 = null): bool; } diff --git a/week-06/project/libraries/src/Utilities/ArrayHelper.php b/week-06/project/libraries/src/Utilities/ArrayHelper.php new file mode 100644 index 0000000..c45f5e3 --- /dev/null +++ b/week-06/project/libraries/src/Utilities/ArrayHelper.php @@ -0,0 +1,79 @@ + + * @git Joomla Component Builder + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Utilities; + + +/** + * Some array tricks helper + * + * @since 3.0.9 + */ +abstract class ArrayHelper +{ + /** + * Check if have an array with a length + * + * @input array The array to check + * + * @returns bool/int number of items in array on success + * + * @since 3.0.9 + */ + public static function check($array, $removeEmptyString = false) + { + if (is_array($array) && ($nr = count((array)$array)) > 0) + { + // also make sure the empty strings are removed + if ($removeEmptyString) + { + foreach ($array as $key => $string) + { + if (empty($string)) + { + unset($array[$key]); + } + } + return self::check($array, false); + } + return $nr; + } + return false; + } + + /** + * Merge an array of array's + * + * @input array The arrays you would like to merge + * + * @returns array on success + * + * @since 3.0.9 + */ + public static function merge($arrays) + { + if(self::check($arrays)) + { + $arrayBuket = array(); + foreach ($arrays as $array) + { + if (self::check($array)) + { + $arrayBuket = array_merge($arrayBuket, $array); + } + } + return $arrayBuket; + } + return false; + } + +} + diff --git a/week-06/project/libraries/src/Utilities/StringHelper.php b/week-06/project/libraries/src/Utilities/StringHelper.php new file mode 100644 index 0000000..f0b7b7c --- /dev/null +++ b/week-06/project/libraries/src/Utilities/StringHelper.php @@ -0,0 +1,322 @@ + + * @git Joomla Component Builder + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\Utilities; + + +/** + * Some string tricks + * + * @since 3.0.9 + */ +abstract class StringHelper +{ + + /** + * Check if we have a string with a length + * + * @input string $string The string to check + * + * @returns bool true on success + * + * @since 3.0.9 + */ + public static function check($string): bool + { + if (is_string($string) && strlen($string) > 0) + { + return true; + } + + return false; + } + + /** + * Shorten a string + * + * @input string The you would like to shorten + * + * @returns string on success + * + * @since 3.0.9 + */ + public static function shorten($string, $length = 40, $addTip = true) + { + if (self::check($string)) + { + $initial = strlen($string); + $words = preg_split('/([\s\n\r]+)/', $string, null, PREG_SPLIT_DELIM_CAPTURE); + $words_count = count((array)$words); + + $word_length = 0; + $last_word = 0; + for (; $last_word < $words_count; ++$last_word) + { + $word_length += strlen($words[$last_word]); + if ($word_length > $length) + { + break; + } + } + + $newString = implode(array_slice($words, 0, $last_word)); + $final = strlen($newString); + if ($initial != $final && $addTip) + { + $title = self::shorten($string, 400 , false); + return '' . trim($newString) . '...'; + } + elseif ($initial != $final && !$addTip) + { + return trim($newString) . '...'; + } + } + return $string; + } + + /** + * Making strings safe (various ways) + * + * @input string The you would like to make safe + * + * @returns string on success + * + * @since 3.0.9 + */ + public static function safe($string, $type = 'L', $spacer = '_', $replaceNumbers = true, $keepOnlyCharacters = true) + { + if ($replaceNumbers === true) + { + // remove all numbers and replace with english text version (works well only up to millions) + $string = self::numbers($string); + } + // 0nly continue if we have a string + if (self::check($string)) + { + // create file name without the extension that is safe + if ($type === 'filename') + { + // make sure VDM is not in the string + $string = str_replace('VDM', 'vDm', $string); + // Remove anything which isn't a word, whitespace, number + // or any of the following caracters -_() + // If you don't need to handle multi-byte characters + // you can use preg_replace rather than mb_ereg_replace + // Thanks @Łukasz Rysiak! + // $string = mb_ereg_replace("([^\w\s\d\-_\(\)])", '', $string); + $string = preg_replace("([^\w\s\d\-_\(\)])", '', $string); + + // http://stackoverflow.com/a/2021729/1429677 + return preg_replace('/\s+/', ' ', $string); + } + // remove all other characters + $string = trim($string); + $string = preg_replace('/'.$spacer.'+/', ' ', $string); + $string = preg_replace('/\s+/', ' ', $string); + // remove all and keep only characters + if ($keepOnlyCharacters) + { + $string = preg_replace("/[^A-Za-z ]/", '', $string); + } + // keep both numbers and characters + else + { + $string = preg_replace("/[^A-Za-z0-9 ]/", '', $string); + } + // select final adaptations + if ($type === 'L' || $type === 'strtolower') + { + // replace white space with underscore + $string = preg_replace('/\s+/', $spacer, $string); + // default is to return lower + return strtolower($string); + } + elseif ($type === 'W') + { + // return a string with all first letter of each word uppercase(no underscore) + return ucwords(strtolower($string)); + } + elseif ($type === 'w' || $type === 'word') + { + // return a string with all lowercase(no underscore) + return strtolower($string); + } + elseif ($type === 'Ww' || $type === 'Word') + { + // return a string with first letter of the first word uppercase and all the rest lowercase(no underscore) + return ucfirst(strtolower($string)); + } + elseif ($type === 'WW' || $type === 'WORD') + { + // return a string with all the uppercase(no underscore) + return strtoupper($string); + } + elseif ($type === 'U' || $type === 'strtoupper') + { + // replace white space with underscore + $string = preg_replace('/\s+/', $spacer, $string); + // return all upper + return strtoupper($string); + } + elseif ($type === 'F' || $type === 'ucfirst') + { + // replace white space with underscore + $string = preg_replace('/\s+/', $spacer, $string); + // return with first character to upper + return ucfirst(strtolower($string)); + } + elseif ($type === 'cA' || $type === 'cAmel' || $type === 'camelcase') + { + // convert all words to first letter uppercase + $string = ucwords(strtolower($string)); + // remove white space + $string = preg_replace('/\s+/', '', $string); + // now return first letter lowercase + return lcfirst($string); + } + // return string + return $string; + } + // not a string + return ''; + } + + /** + * Convert all int in a string to an English word string + * + * @input an string with numbers + * + * @returns a string + * + * @since 3.0.9 + */ + public static function numbers($string) + { + // set numbers array + $numbers = array(); + + // first get all numbers + preg_match_all('!\d+!', $string, $numbers); + + // check if we have any numbers + if (isset($numbers[0]) && ArrayHelper::check($numbers[0])) + { + foreach ($numbers[0] as $number) + { + $searchReplace[$number] = self::number((int)$number); + } + + // now replace numbers in string + $string = str_replace(array_keys($searchReplace), array_values($searchReplace), $string); + + // check if we missed any, strange if we did. + return self::numbers($string); + } + + // return the string with no numbers remaining. + return $string; + } + + /** + * Convert an integer into an English word string + * Thanks to Tom Nicholson + * + * @input an int + * @returns a string + * + * @since 3.0.9 + */ + public static function number($x) + { + $nwords = array( "zero", "one", "two", "three", "four", "five", "six", "seven", + "eight", "nine", "ten", "eleven", "twelve", "thirteen", + "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", + "nineteen", "twenty", 30 => "thirty", 40 => "forty", + 50 => "fifty", 60 => "sixty", 70 => "seventy", 80 => "eighty", + 90 => "ninety" ); + + if(!is_numeric($x)) + { + $w = $x; + } + elseif(fmod($x, 1) != 0) + { + $w = $x; + } + else + { + if($x < 0) + { + $w = 'minus '; + $x = -$x; + } + else + { + $w = ''; + // ... now $x is a non-negative integer. + } + + if($x < 21) // 0 to 20 + { + $w .= $nwords[$x]; + } + elseif($x < 100) // 21 to 99 + { + $w .= $nwords[10 * floor($x/10)]; + $r = fmod($x, 10); + if($r > 0) + { + $w .= ' ' . $nwords[$r]; + } + } + elseif($x < 1000) // 100 to 999 + { + $w .= $nwords[floor($x/100)] .' hundred'; + $r = fmod($x, 100); + if($r > 0) + { + $w .= ' and '. self::number($r); + } + } + elseif($x < 1000000) // 1000 to 999999 + { + $w .= self::number(floor($x/1000)) .' thousand'; + $r = fmod($x, 1000); + if($r > 0) + { + $w .= ' '; + if($r < 100) + { + $w .= 'and '; + } + $w .= self::number($r); + } + } + else // millions + { + $w .= self::number(floor($x/1000000)) .' million'; + $r = fmod($x, 1000000); + if($r > 0) + { + $w .= ' '; + if($r < 100) + { + $w .= 'and '; + } + $w .= self::number($r); + } + } + } + return $w; + } + +} + diff --git a/week-06/project/libraries/src/View/Admin/DashboardHtmlView.php b/week-06/project/libraries/src/View/Admin/DashboardHtmlView.php index 1fab3a6..b657336 100644 --- a/week-06/project/libraries/src/View/Admin/DashboardHtmlView.php +++ b/week-06/project/libraries/src/View/Admin/DashboardHtmlView.php @@ -15,12 +15,12 @@ use Joomla\Renderer\RendererInterface; use Joomla\View\HtmlView; /** - * Dashboard HTML view class for the application + * HTML view class for the application */ class DashboardHtmlView extends HtmlView { /** - * The id of item/user/menu + * The id * * @var int */ @@ -36,7 +36,7 @@ class DashboardHtmlView extends HtmlView /** * Instantiate the view. * - * @param DashboardModel $model The page model object. + * @param DashboardModel $model The model object. * @param RendererInterface $renderer The renderer object. */ public function __construct(DashboardModel $model, RendererInterface $renderer) @@ -58,7 +58,7 @@ class DashboardHtmlView extends HtmlView } /** - * Set the active dashboard + * Set the active view * * @param string $name The active page name * @@ -70,9 +70,9 @@ class DashboardHtmlView extends HtmlView } /** - * Set the active page details + * Set the active id * - * @param int $id The selected item/user/menu + * @param int $id The active id * * @return void */ diff --git a/week-06/project/libraries/src/View/Admin/ItemHtmlView.php b/week-06/project/libraries/src/View/Admin/ItemHtmlView.php index 80287bc..35429f5 100644 --- a/week-06/project/libraries/src/View/Admin/ItemHtmlView.php +++ b/week-06/project/libraries/src/View/Admin/ItemHtmlView.php @@ -15,19 +15,19 @@ use Joomla\Renderer\RendererInterface; use Joomla\View\HtmlView; /** - * Dashboard HTML view class for the application + * HTML view class for the application */ class ItemHtmlView extends HtmlView { /** - * The id of item/user/menu + * The id * * @var int */ private $id; /** - * The item model object. + * The model object. * * @var ItemModel */ @@ -36,7 +36,7 @@ class ItemHtmlView extends HtmlView /** * Instantiate the view. * - * @param ItemModel $model The page model object. + * @param ItemModel $model The model object. * @param RendererInterface $renderer The renderer object. */ public function __construct(ItemModel $model, RendererInterface $renderer) @@ -71,9 +71,9 @@ class ItemHtmlView extends HtmlView } /** - * Set the active page details + * Set the active id * - * @param int $id The selected item + * @param int $id The active id * * @return void */ diff --git a/week-06/project/libraries/src/View/Admin/ItemsHtmlView.php b/week-06/project/libraries/src/View/Admin/ItemsHtmlView.php index 368399d..14ea8e5 100644 --- a/week-06/project/libraries/src/View/Admin/ItemsHtmlView.php +++ b/week-06/project/libraries/src/View/Admin/ItemsHtmlView.php @@ -15,12 +15,12 @@ use Joomla\Renderer\RendererInterface; use Joomla\View\HtmlView; /** - * Dashboard HTML view class for the application + * HTML view class for the application */ class ItemsHtmlView extends HtmlView { /** - * The item model object. + * The model object. * * @var ItemsModel */ @@ -29,7 +29,7 @@ class ItemsHtmlView extends HtmlView /** * Instantiate the view. * - * @param ItemsModel $model The page model object. + * @param ItemsModel $model The model object. * @param RendererInterface $renderer The renderer object. */ public function __construct(ItemsModel $model, RendererInterface $renderer) diff --git a/week-06/project/libraries/src/View/Admin/MenuHtmlView.php b/week-06/project/libraries/src/View/Admin/MenuHtmlView.php index 663e5b2..a588975 100644 --- a/week-06/project/libraries/src/View/Admin/MenuHtmlView.php +++ b/week-06/project/libraries/src/View/Admin/MenuHtmlView.php @@ -13,21 +13,22 @@ namespace Octoleo\CMS\View\Admin; use Octoleo\CMS\Model\MenuModel; use Joomla\Renderer\RendererInterface; use Joomla\View\HtmlView; +use Octoleo\CMS\Model\Util\MenuInterface; /** - * Dashboard HTML view class for the application + * HTML view class for the application */ class MenuHtmlView extends HtmlView { /** - * The id of item/user/menu + * The id * * @var int */ private $id; /** - * The item model object. + * The model object. * * @var MenuModel */ @@ -36,7 +37,7 @@ class MenuHtmlView extends HtmlView /** * Instantiate the view. * - * @param MenuModel $model The page model object. + * @param MenuModel $model The model object. * @param RendererInterface $renderer The renderer object. */ public function __construct(MenuModel $model, RendererInterface $renderer) @@ -54,9 +55,16 @@ class MenuHtmlView extends HtmlView */ public function render(): string { + // set the active menus if possible + $menus = []; + if ($this->model instanceof MenuInterface) + { + $menus = $this->model->getMenus($this->id); + } $this->setData([ 'form' => $this->model->getItem($this->id), - 'items' => $this->model->getItems() + 'items' => $this->model->getItems(), + 'menus' => $menus ]); return parent::render(); } @@ -74,9 +82,9 @@ class MenuHtmlView extends HtmlView } /** - * Set the active page details + * Set the active id * - * @param int $id The selected item/user/menu + * @param int $id The active id * * @return void */ diff --git a/week-06/project/libraries/src/View/Admin/MenusHtmlView.php b/week-06/project/libraries/src/View/Admin/MenusHtmlView.php index 79a51d1..f433969 100644 --- a/week-06/project/libraries/src/View/Admin/MenusHtmlView.php +++ b/week-06/project/libraries/src/View/Admin/MenusHtmlView.php @@ -15,12 +15,12 @@ use Joomla\Renderer\RendererInterface; use Joomla\View\HtmlView; /** - * Dashboard HTML view class for the application + * HTML view class for the application */ class MenusHtmlView extends HtmlView { /** - * The item model object. + * The model object. * * @var MenusModel */ @@ -29,7 +29,7 @@ class MenusHtmlView extends HtmlView /** * Instantiate the view. * - * @param MenusModel $model The page model object. + * @param MenusModel $model The model object. * @param RendererInterface $renderer The renderer object. */ public function __construct(MenusModel $model, RendererInterface $renderer) @@ -51,9 +51,9 @@ class MenusHtmlView extends HtmlView } /** - * Set the active view + * Set the active id * - * @param string $name The active view name + * @param string $name The active id * * @return void */ diff --git a/week-06/project/libraries/src/View/Admin/UserHtmlView.php b/week-06/project/libraries/src/View/Admin/UserHtmlView.php index 7104759..e7a0278 100644 --- a/week-06/project/libraries/src/View/Admin/UserHtmlView.php +++ b/week-06/project/libraries/src/View/Admin/UserHtmlView.php @@ -15,19 +15,19 @@ use Joomla\Renderer\RendererInterface; use Joomla\View\HtmlView; /** - * Dashboard HTML view class for the application + * HTML view class for the application */ class UserHtmlView extends HtmlView { /** - * The id of user + * The id * * @var int */ private $id; /** - * The User model object. + * The model object. * * @var UserModel */ @@ -36,7 +36,7 @@ class UserHtmlView extends HtmlView /** * Instantiate the view. * - * @param UserModel $model The page model object. + * @param UserModel $model The model object. * @param RendererInterface $renderer The renderer object. */ public function __construct(UserModel $model, RendererInterface $renderer) @@ -50,10 +50,14 @@ class UserHtmlView extends HtmlView * Method to render the view * * @return string The rendered view + * @throws \Exception */ public function render(): string { - $this->setData(['form' => $this->model->getItem($this->id)]); + $this->setData([ + 'form' => $this->model->getItem($this->id), + 'groups' => $this->model->getUsergroups() + ]); return parent::render(); } @@ -70,9 +74,9 @@ class UserHtmlView extends HtmlView } /** - * Set the active page details + * Set the active id * - * @param int $id The selected user + * @param int $id The active id * * @return void */ diff --git a/week-06/project/libraries/src/View/Admin/UsergroupHtmlView.php b/week-06/project/libraries/src/View/Admin/UsergroupHtmlView.php new file mode 100644 index 0000000..cce7cec --- /dev/null +++ b/week-06/project/libraries/src/View/Admin/UsergroupHtmlView.php @@ -0,0 +1,85 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\View\Admin; + +use Octoleo\CMS\Model\UsergroupModel; +use Joomla\Renderer\RendererInterface; +use Joomla\View\HtmlView; + +/** + * HTML view class for the application + */ +class UsergroupHtmlView extends HtmlView +{ + /** + * The id + * + * @var int + */ + private $id; + + /** + * The model object. + * + * @var UsergroupModel + */ + private $model; + + /** + * Instantiate the view. + * + * @param UsergroupModel $model The model object. + * @param RendererInterface $renderer The renderer object. + */ + public function __construct(UsergroupModel $model, RendererInterface $renderer) + { + parent::__construct($renderer); + + $this->model = $model; + } + + /** + * Method to render the view + * + * @return string The rendered view + */ + public function render(): string + { + $this->setData([ + 'form' => $this->model->getItem($this->id) + ]); + return parent::render(); + } + + /** + * Set the active view + * + * @param string $name The active view name + * + * @return void + */ + public function setActiveView(string $name): void + { + $this->setLayout($this->model->setLayout($name)); + } + + /** + * Set the active id + * + * @param int $id The active id + * + * @return void + */ + public function setActiveId(int $id): void + { + $this->id = $id; + } +} diff --git a/week-06/project/libraries/src/View/Admin/UsergroupsHtmlView.php b/week-06/project/libraries/src/View/Admin/UsergroupsHtmlView.php new file mode 100644 index 0000000..ef2c7e4 --- /dev/null +++ b/week-06/project/libraries/src/View/Admin/UsergroupsHtmlView.php @@ -0,0 +1,64 @@ + + * @git WEBD-325-45 + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Octoleo\CMS\View\Admin; + +use Octoleo\CMS\Model\UsergroupsModel; +use Joomla\Renderer\RendererInterface; +use Joomla\View\HtmlView; + +/** + * HTML view class for the application + */ +class UsergroupsHtmlView extends HtmlView +{ + /** + * The model object. + * + * @var UsergroupsModel + */ + private $model; + + /** + * Instantiate the view. + * + * @param UsergroupsModel $model The model object. + * @param RendererInterface $renderer The renderer object. + */ + public function __construct(UsergroupsModel $model, RendererInterface $renderer) + { + parent::__construct($renderer); + + $this->model = $model; + } + + /** + * Method to render the view + * + * @return string The rendered view + */ + public function render(): string + { + $this->setData(['list' => $this->model->getItems()]); + return parent::render(); + } + + /** + * Set the active view + * + * @param string $name The active view name + * + * @return void + */ + public function setActiveView(string $name): void + { + $this->setLayout($this->model->setLayout($name)); + } +} diff --git a/week-06/project/libraries/src/View/Admin/UsersHtmlView.php b/week-06/project/libraries/src/View/Admin/UsersHtmlView.php index 2014369..6bbd85d 100644 --- a/week-06/project/libraries/src/View/Admin/UsersHtmlView.php +++ b/week-06/project/libraries/src/View/Admin/UsersHtmlView.php @@ -15,12 +15,12 @@ use Joomla\Renderer\RendererInterface; use Joomla\View\HtmlView; /** - * Dashboard HTML view class for the application + * HTML view class for the application */ class UsersHtmlView extends HtmlView { /** - * The item model object. + * The model object. * * @var UsersModel */ @@ -29,8 +29,8 @@ class UsersHtmlView extends HtmlView /** * Instantiate the view. * - * @param UsersModel $model The page model object. - * @param RendererInterface $renderer The renderer object. + * @param UsersModel $model The model object. + * @param RendererInterface $renderer The renderer object. */ public function __construct(UsersModel $model, RendererInterface $renderer) { diff --git a/week-06/project/libraries/src/View/Page/HomepageHtmlView.php b/week-06/project/libraries/src/View/Page/HomepageHtmlView.php deleted file mode 100644 index 492b1ac..0000000 --- a/week-06/project/libraries/src/View/Page/HomepageHtmlView.php +++ /dev/null @@ -1,133 +0,0 @@ - - * @git WEBD-325-45 - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -namespace Octoleo\CMS\View\Page; - -use Octoleo\CMS\Model\MenuInterface; -use Octoleo\CMS\Model\HomepageModel; -use Joomla\Renderer\RendererInterface; -use Joomla\View\HtmlView; -use Octoleo\CMS\Model\PageInterface; - -/** - * Page HTML view class for the application - */ -class HomepageHtmlView extends HtmlView -{ - /** - * The active page - * - * @var string - */ - private $page = ''; - - /** - * The active page details - * - * @var string - */ - private $details; - - /** - * The page model object. - * - * @var HomepageModel - */ - private $model; - - /** - * Instantiate the view. - * - * @param HomepageModel $model The page model object. - * @param RendererInterface $renderer The renderer object. - */ - public function __construct(HomepageModel $model, RendererInterface $renderer) - { - parent::__construct($renderer); - - $this->model = $model; - } - - /** - * Method to render the view - * - * @return string The rendered view - */ - public function render() - { - // set the defaults - $title = 'Home'; - $body = ''; - $menus = []; - // set the home page default template - $this->setLayout('homepage.twig'); - // we check if we have a home page - $home_page = $this->model->getHomePage(); - if ($this->model instanceof PageInterface && isset($home_page->item_id) && $home_page->item_id > 0) - { - // get the page data - $data = $this->model->getPageItemById($home_page->item_id); - if ($data->id) - { - // set the title - $title = $data->title; - // check if we have intro text we add it to full text - if (!empty($data->introtext)) - { - // TODO: for now we just merge these - $data->fulltext = $data->introtext . $data->fulltext; - } - // set the title - $body = $data->fulltext; - // set the page template - $this->setLayout('page.twig'); - } - } - - // set the menus if possible - if ($this->model instanceof MenuInterface) - { - $menus = $this->model->getMenus(); - } - - $this->setData( - [ - 'main_menu' => $menus, - 'title' => $title, - 'body' => $body - ] - ); - return parent::render(); - } - - /** - * Set the active page - * - * @param string $page The active page name - * - * @return void - */ - public function setPage(string $page): void - { - $this->page = $page; - } - - /** - * Set the active page details - * - * @param string $page The active page name - * - * @return void - */ - public function setDetails(string $details): void - { - $this->details = $details; - } -} diff --git a/week-06/project/libraries/src/View/Page/PageHtmlView.php b/week-06/project/libraries/src/View/Site/PageHtmlView.php similarity index 62% rename from week-06/project/libraries/src/View/Page/PageHtmlView.php rename to week-06/project/libraries/src/View/Site/PageHtmlView.php index 46b7745..e7b82f7 100644 --- a/week-06/project/libraries/src/View/Page/PageHtmlView.php +++ b/week-06/project/libraries/src/View/Site/PageHtmlView.php @@ -8,16 +8,17 @@ * @license GNU General Public License version 2 or later; see LICENSE.txt */ -namespace Octoleo\CMS\View\Page; +namespace Octoleo\CMS\View\Site; -use Octoleo\CMS\Model\MenuInterface; -use Octoleo\CMS\Model\PageInterface; use Octoleo\CMS\Model\PageModel; use Joomla\Renderer\RendererInterface; use Joomla\View\HtmlView; +use Octoleo\CMS\Model\Util\MenuInterface; +use Octoleo\CMS\Model\Util\PageInterface; +use Octoleo\CMS\Model\Util\HomeMenuInterface; /** - * Page HTML view class for the application + * HTML view class for the application */ class PageHtmlView extends HtmlView { @@ -29,14 +30,7 @@ class PageHtmlView extends HtmlView private $page = ''; /** - * The active page details - * - * @var string - */ - private $details; - - /** - * The page model object. + * The model object. * * @var PageModel */ @@ -45,7 +39,7 @@ class PageHtmlView extends HtmlView /** * Instantiate the view. * - * @param PageModel $model The page model object. + * @param PageModel $model The model object. * @param RendererInterface $renderer The renderer object. */ public function __construct(PageModel $model, RendererInterface $renderer) @@ -66,13 +60,39 @@ class PageHtmlView extends HtmlView $title = 'Error'; $body = ''; $menus = []; + // menu ID + $menu_id = 0; + $menu_home = false; + // set home menu title (not ideal) + $home_menu_title = 'Home'; + // we check if we have a home page + if ($this->model instanceof HomeMenuInterface) + { + $home_page = $this->model->getHomePage(); + if (isset($home_page->title)) + { + $home_menu_title = $home_page->title; + } + } // get the page data if ($this->model instanceof PageInterface) { // get the page data - $data = $this->model->getPageItemByPath($this->page); + if (empty($this->page) && isset($home_page->item_id) && $home_page->item_id > 0) + { + // this is the home menu + $data = $this->model->getPageItemById($home_page->item_id); + $menu_home = true; + } + else + { + $data = $this->model->getPageItemByPath($this->page); + } + // check if we found any data if (isset($data->id)) { + // set the menu ID + $menu_id = $data->menu_id; // set the title $title = $data->title; // check if we have intro text we add it to full text @@ -93,13 +113,16 @@ class PageHtmlView extends HtmlView // set the menus if possible if ($this->model instanceof MenuInterface) { - $menus = $this->model->getMenus(); + $menus = $this->model->getMenus($menu_id); } $this->setData( [ - 'main_menu' => $menus, + 'menus' => $menus, + 'home' => $menu_home, + 'menu_active' => $menu_id, 'title' => $title, + 'home_menu_title' => $home_menu_title, 'body' => $body ] ); diff --git a/week-06/project/sql/install.sql b/week-06/project/sql/install.sql index 9eaa39a..93136df 100644 --- a/week-06/project/sql/install.sql +++ b/week-06/project/sql/install.sql @@ -1,141 +1,249 @@ +-- phpMyAdmin SQL Dump +-- version 5.1.1 +-- https://www.phpmyadmin.net/ -- --- DATABASE STRUCTURE FOR GENERIC CMS (Adapted from Joomla!) --- -- subject to change -- --- +-- Host: mariadb_cms:3306 +-- Generation Time: Apr 22, 2022 at 03:25 PM +-- Server version: 10.6.5-MariaDB-1:10.6.5+maria~focal +-- PHP Version: 7.4.20 -SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; -SET time_zone = "+00:00"; +SET +SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +START TRANSACTION; +SET +time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; -- --- Table structure for table `llewellyn_menu` +-- Database: `vdm_io` -- -CREATE TABLE IF NOT EXISTS `llewellyn_menu` ( - `id` int NOT NULL AUTO_INCREMENT, - `title` varchar(255) NOT NULL COMMENT 'The display title of the menu item.', - `alias` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'The SEF alias of the menu item.', - `path` varchar(1024) NOT NULL COMMENT 'The computed path of the menu item based on the alias field.', - `published` tinyint NOT NULL DEFAULT 0 COMMENT 'The published state of the menu link.', - `parent_id` int unsigned NOT NULL DEFAULT 1 COMMENT 'The parent menu item in the menu tree.', - `level` int unsigned NOT NULL DEFAULT 0 COMMENT 'The relative level in the tree.', - `item_id` int unsigned NOT NULL DEFAULT 0 COMMENT 'FK to llewellyn_item.id', - `checked_out` int unsigned COMMENT 'FK to llewellyn_users.id', - `checked_out_time` datetime COMMENT 'The time the menu item was checked out.', - `params` text NOT NULL COMMENT 'JSON encoded data for the menu item.', - `lft` int NOT NULL DEFAULT 0 COMMENT 'Nested set lft.', - `rgt` int NOT NULL DEFAULT 0 COMMENT 'Nested set rgt.', - `home` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Indicates if this menu item is the home or default page.', - `publish_up` datetime, - `publish_down` datetime, - PRIMARY KEY (`id`), - KEY `idx_item` (`item_id`), - KEY `idx_left_right` (`lft`,`rgt`), - KEY `idx_alias` (`alias`(100)), - KEY `idx_path` (`path`(100)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=102; - --- --- Table structure for table `llewellyn_session` --- - -CREATE TABLE IF NOT EXISTS `llewellyn_session` ( - `session_id` varbinary(192) NOT NULL, - `guest` tinyint unsigned DEFAULT 1, - `time` int NOT NULL DEFAULT 0, - `data` mediumtext, - `userid` int DEFAULT 0, - `username` varchar(150) DEFAULT '', - PRIMARY KEY (`session_id`), - KEY `userid` (`userid`), - KEY `time` (`time`), - KEY `guest` (`guest`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; - --- --- Table structure for table `llewellyn_users` --- - -CREATE TABLE IF NOT EXISTS `llewellyn_users` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(400) NOT NULL DEFAULT '', - `username` varchar(150) NOT NULL DEFAULT '', - `email` varchar(100) NOT NULL DEFAULT '', - `password` varchar(100) NOT NULL DEFAULT '', - `block` tinyint NOT NULL DEFAULT 0, - `sendEmail` tinyint DEFAULT 0, - `registerDate` datetime NOT NULL, - `lastvisitDate` datetime, - `activation` varchar(100) NOT NULL DEFAULT '', - `params` text NOT NULL, - `lastResetTime` datetime COMMENT 'Date of last password reset', - `resetCount` int NOT NULL DEFAULT 0 COMMENT 'Count of password resets since lastResetTime', - `requireReset` tinyint NOT NULL DEFAULT 0 COMMENT 'Require user to reset password on next login', - PRIMARY KEY (`id`), - KEY `idx_name` (`name`(100)), - KEY `idx_block` (`block`), - UNIQUE KEY `idx_username` (`username`), - KEY `email` (`email`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; - --- --- Table structure for table `llewellyn_usergroups` --- - -CREATE TABLE IF NOT EXISTS `llewellyn_usergroups` ( - `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Primary Key', - `parent_id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Adjacency List Reference Id', - `lft` int NOT NULL DEFAULT 0 COMMENT 'Nested set lft.', - `rgt` int NOT NULL DEFAULT 0 COMMENT 'Nested set rgt.', - `title` varchar(100) NOT NULL DEFAULT '', - PRIMARY KEY (`id`), - UNIQUE KEY `idx_usergroup_parent_title_lookup` (`parent_id`,`title`), - KEY `idx_usergroup_title_lookup` (`title`), - KEY `idx_usergroup_adjacency_lookup` (`parent_id`), - KEY `idx_usergroup_nested_set_lookup` (`lft`,`rgt`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; - --- --- Table structure for table `llewellyn_user_usergroup_map` --- - -CREATE TABLE IF NOT EXISTS `llewellyn_user_usergroup_map` ( - `user_id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Foreign Key to llewellyn_users.id', - `group_id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Foreign Key to llewellyn_usergroups.id', - PRIMARY KEY (`user_id`,`group_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; +-- -------------------------------------------------------- -- -- Table structure for table `llewellyn_item` -- -CREATE TABLE IF NOT EXISTS `llewellyn_item` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `title` varchar(255) NOT NULL DEFAULT '', - `alias` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '', - `introtext` mediumtext NOT NULL, - `fulltext` mediumtext NOT NULL, - `state` tinyint NOT NULL DEFAULT 0, - `created` datetime NOT NULL, - `created_by` int unsigned NOT NULL DEFAULT 0, - `created_by_alias` varchar(255) NOT NULL DEFAULT '', - `modified` datetime NOT NULL, - `modified_by` int unsigned NOT NULL DEFAULT 0, - `checked_out` int unsigned, - `checked_out_time` datetime NULL DEFAULT NULL, - `publish_up` datetime NULL DEFAULT NULL, - `publish_down` datetime NULL DEFAULT NULL, - `version` int unsigned NOT NULL DEFAULT 1, - `ordering` int NOT NULL DEFAULT 0, - `metakey` text, - `metadesc` text NOT NULL, - `hits` int unsigned NOT NULL DEFAULT 0, - `metadata` text NOT NULL, - `featured` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Set if article is featured.', - PRIMARY KEY (`id`), - KEY `idx_checkout` (`checked_out`), - KEY `idx_state` (`state`), - KEY `idx_createdby` (`created_by`), - KEY `idx_alias` (`alias`(191)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; +CREATE TABLE `llewellyn_item` +( + `id` int(10) UNSIGNED NOT NULL, + `title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `alias` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '', + `introtext` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `fulltext` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `state` tinyint(4) NOT NULL DEFAULT 0, + `created` datetime NOT NULL, + `created_by` int(10) UNSIGNED NOT NULL DEFAULT 0, + `created_by_alias` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `modified` datetime NOT NULL, + `modified_by` int(10) UNSIGNED NOT NULL DEFAULT 0, + `checked_out` int(10) UNSIGNED DEFAULT NULL, + `checked_out_time` datetime DEFAULT NULL, + `publish_up` datetime DEFAULT NULL, + `publish_down` datetime DEFAULT NULL, + `version` int(10) UNSIGNED NOT NULL DEFAULT 1, + `ordering` int(11) NOT NULL DEFAULT 0, + `metakey` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `metadesc` text COLLATE utf8mb4_unicode_ci NOT NULL, + `hits` int(10) UNSIGNED NOT NULL DEFAULT 0, + `metadata` text COLLATE utf8mb4_unicode_ci NOT NULL, + `params` text COLLATE utf8mb4_unicode_ci NOT NULL, + `featured` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Set if article is featured.' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +-- -------------------------------------------------------- +-- +-- Table structure for table `llewellyn_menu` +-- + +CREATE TABLE `llewellyn_menu` +( + `id` int(11) NOT NULL, + `title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The display title of the menu item.', + `alias` varchar(400) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The SEF alias of the menu item.', + `path` varchar(1024) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The computed path of the menu item based on the alias field.', + `published` tinyint(4) NOT NULL DEFAULT 0 COMMENT 'The published state of the menu link.', + `parent_id` int(10) UNSIGNED NOT NULL DEFAULT 1 COMMENT 'The parent menu item in the menu tree.', + `level` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'The relative level in the tree.', + `item_id` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'FK to llewellyn_item.id', + `checked_out` int(10) UNSIGNED DEFAULT NULL COMMENT 'FK to llewellyn_users.id', + `checked_out_time` datetime DEFAULT NULL COMMENT 'The time the menu item was checked out.', + `params` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'JSON encoded data for the menu item.', + `lft` int(11) NOT NULL DEFAULT 0 COMMENT 'Nested set lft.', + `rgt` int(11) NOT NULL DEFAULT 0 COMMENT 'Nested set rgt.', + `home` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Indicates if this menu item is the home or default page.', + `publish_up` datetime DEFAULT NULL, + `publish_down` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `llewellyn_session` +-- + +CREATE TABLE `llewellyn_session` +( + `session_id` varbinary(192) NOT NULL, + `guest` tinyint(3) UNSIGNED DEFAULT 1, + `time` int(11) NOT NULL DEFAULT 0, + `data` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `userid` int(11) DEFAULT 0, + `username` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT '' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `llewellyn_usergroups` +-- + +CREATE TABLE `llewellyn_usergroups` +( + `id` int(10) UNSIGNED NOT NULL COMMENT 'Primary Key', + `title` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `params` text COLLATE utf8mb4_unicode_ci NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Dumping data for table `llewellyn_usergroups` +-- + +INSERT INTO `llewellyn_usergroups` (`id`, `title`, `params`) +VALUES (1, 'Administrator', + '[{\"area\":\"user\",\"access\":\"CRUD\"},{\"area\":\"usergroup\",\"access\":\"CRUD\"},{\"area\":\"menu\",\"access\":\"CRUD\"},{\"area\":\"item\",\"access\":\"CRUD\"}]'), + (2, 'Manager', + '[{\"area\":\"user\",\"access\":\"CR\"},{\"area\":\"usergroup\",\"access\":\"\"},{\"area\":\"menu\",\"access\":\"CRU\"},{\"area\":\"item\",\"access\":\"CRU\"}]'), + (3, 'Editor', + '[{\"area\":\"user\",\"access\":\"\"},{\"area\":\"usergroup\",\"access\":\"\"},{\"area\":\"menu\",\"access\":\"\"},{\"area\":\"item\",\"access\":\"CRU\"}]'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `llewellyn_users` +-- + +CREATE TABLE `llewellyn_users` +( + `id` int(11) NOT NULL, + `name` varchar(400) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `username` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `email` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `password` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `block` tinyint(4) NOT NULL DEFAULT 0, + `sendEmail` tinyint(4) DEFAULT 0, + `registerDate` datetime NOT NULL, + `lastvisitDate` datetime DEFAULT NULL, + `activation` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `params` text COLLATE utf8mb4_unicode_ci NOT NULL, + `lastResetTime` datetime DEFAULT NULL COMMENT 'Date of last password reset', + `resetCount` int(11) NOT NULL DEFAULT 0 COMMENT 'Count of password resets since lastResetTime', + `requireReset` tinyint(4) NOT NULL DEFAULT 0 COMMENT 'Require user to reset password on next login' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `llewellyn_user_usergroup_map` +-- + +CREATE TABLE `llewellyn_user_usergroup_map` +( + `user_id` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Foreign Key to llewellyn_users.id', + `group_id` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Foreign Key to llewellyn_usergroups.id' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Indexes for dumped tables +-- + +-- +-- Indexes for table `llewellyn_item` +-- +ALTER TABLE `llewellyn_item` + ADD PRIMARY KEY (`id`), +ADD KEY `idx_checkout` (`checked_out`), +ADD KEY `idx_state` (`state`), +ADD KEY `idx_createdby` (`created_by`), +ADD KEY `idx_alias` (`alias`(191)); + +-- +-- Indexes for table `llewellyn_menu` +-- +ALTER TABLE `llewellyn_menu` + ADD PRIMARY KEY (`id`), +ADD KEY `idx_item` (`item_id`), +ADD KEY `idx_left_right` (`lft`,`rgt`), +ADD KEY `idx_alias` (`alias`(100)), +ADD KEY `idx_path` (`path`(100)); + +-- +-- Indexes for table `llewellyn_session` +-- +ALTER TABLE `llewellyn_session` + ADD PRIMARY KEY (`session_id`), +ADD KEY `userid` (`userid`), +ADD KEY `time` (`time`), +ADD KEY `guest` (`guest`); + +-- +-- Indexes for table `llewellyn_usergroups` +-- +ALTER TABLE `llewellyn_usergroups` + ADD PRIMARY KEY (`id`), +ADD UNIQUE KEY `idx_usergroup_title_lookup` (`title`); + +-- +-- Indexes for table `llewellyn_users` +-- +ALTER TABLE `llewellyn_users` + ADD PRIMARY KEY (`id`), +ADD UNIQUE KEY `idx_username` (`username`), +ADD KEY `idx_name` (`name`(100)), +ADD KEY `idx_block` (`block`), +ADD KEY `email` (`email`); + +-- +-- Indexes for table `llewellyn_user_usergroup_map` +-- +ALTER TABLE `llewellyn_user_usergroup_map` + ADD PRIMARY KEY (`user_id`, `group_id`); + +-- +-- AUTO_INCREMENT for dumped tables +-- + +-- +-- AUTO_INCREMENT for table `llewellyn_item` +-- +ALTER TABLE `llewellyn_item` + MODIFY `id` int (10) UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT for table `llewellyn_menu` +-- +ALTER TABLE `llewellyn_menu` + MODIFY `id` int (11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=102; + +-- +-- AUTO_INCREMENT for table `llewellyn_usergroups` +-- +ALTER TABLE `llewellyn_usergroups` + MODIFY `id` int (10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Primary Key', AUTO_INCREMENT=3; + +-- +-- AUTO_INCREMENT for table `llewellyn_users` +-- +ALTER TABLE `llewellyn_users` + MODIFY `id` int (11) NOT NULL AUTO_INCREMENT; +COMMIT; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; \ No newline at end of file diff --git a/week-06/project/templates/admin/dashboard.twig b/week-06/project/templates/admin/dashboard.twig index efe818e..feaa006 100644 --- a/week-06/project/templates/admin/dashboard.twig +++ b/week-06/project/templates/admin/dashboard.twig @@ -7,15 +7,51 @@

Octoleo CMS Dashboard

{{ block("messages_queue", "message_queue.twig") }}
-
- -
-
- -
-
- -
+ {% set no_access = true %} + {% if user('access.user.read', false) %} + {% set no_access = false %} +
+
+
+ Users +
+
+ {% endif %} + {% if user('access.usergroup.read', false) %} + {% set no_access = false %} +
+
+
+ User Groups +
+
+ {% endif %} + {% if user('access.menu.read', false) %} + {% set no_access = false %} +
+
+
+ Menus +
+
+ {% endif %} + {% if user('access.item.read', false) %} + {% set no_access = false %} +
+
+
+ Items +
+
+ {% endif %} + {% if no_access %} +
+
+
+ No access found to any area, please contact your system administrator! +
+
+ {% endif %}
{% endblock %} \ No newline at end of file diff --git a/week-06/project/templates/admin/index.twig b/week-06/project/templates/admin/index.twig index 43bb7a7..d7e5d6d 100644 --- a/week-06/project/templates/admin/index.twig +++ b/week-06/project/templates/admin/index.twig @@ -1,5 +1,5 @@ {% extends "header.twig" %} - +{% set user = user_array() %} {% block body %} {% block bodyNavigation %}{{ block("bodyNavigation", "nav.twig") }}{% endblock %} diff --git a/week-06/project/templates/admin/item.twig b/week-06/project/templates/admin/item.twig index d9a9fcc..6e9255e 100644 --- a/week-06/project/templates/admin/item.twig +++ b/week-06/project/templates/admin/item.twig @@ -1,6 +1,6 @@ {% extends "index.twig" %} -{% block title %}Edit Item{% endblock %} +{% block title %}{% if form.id == 0 %}Create{% else %}Edit{% endif %} Item {{ form.title|default('') }}{% endblock %} {% block headJavaScriptLinks %} @@ -9,11 +9,20 @@ {% block content %}
{{ block("messages_queue", "message_queue.twig") }} + {% if form.id == 0 %} +

Create Item

+ {% else %} +

Edit Item {{ form.title|default('') }}

+ {% endif %}
-
- - Close -
+ {% if user('access.item.update', false) %} +
+ + Close +
+ {% else %} + Close + {% endif %}
diff --git a/week-06/project/templates/admin/items.twig b/week-06/project/templates/admin/items.twig index be663e0..e33d471 100644 --- a/week-06/project/templates/admin/items.twig +++ b/week-06/project/templates/admin/items.twig @@ -4,9 +4,11 @@ {% block content %}
-

Items

+

Items

{{ block("messages_queue", "message_queue.twig") }} - Create + {% if user('access.item.create', false) %} + Create + {% endif %} {% if list %} @@ -23,11 +25,17 @@ - +
- Edit - + {% if user('access.item.update', false) %} + Edit + {% else %} + Read + {% endif %} + {% if user('access.item.delete', false) %} + + {% endif %}
{{ item.title }}{{ item.title|escape('html') }} {% if item.introtext %}{{ shorten_string(item.introtext|striptags) }} {% endif %}{{ shorten_string(item.fulltext|striptags) }} {% if item.state == 1 %} @@ -49,10 +57,15 @@
{% else %}
-

There has no items been found, click create to add some.

+ {% if user('access.item.create', false) %} +

There has no items been found, click create to add some.

+ {% else %} +

There has no items been found.

+ {% endif %}
{% endif %}
+{% if user('access.item.delete', false) %} +{% endif %} {% endblock %} \ No newline at end of file diff --git a/week-06/project/templates/admin/menu.twig b/week-06/project/templates/admin/menu.twig index d53e0e7..db29567 100644 --- a/week-06/project/templates/admin/menu.twig +++ b/week-06/project/templates/admin/menu.twig @@ -1,16 +1,25 @@ {% extends "index.twig" %} -{% block title %}Edit Menu{% endblock %} +{% block title %}{% if form.id == 0 %}Create{% else %}Edit{% endif %} Menu {{ form.title|default('') }}{% endblock %} {% block content %}
{{ block("messages_queue", "message_queue.twig") }} + {% if form.id == 0 %} +

Create Menu

+ {% else %} +

Edit Menu {{ form.title|default('') }}

+ {% endif %} {% if items %} - -
- - Close -
+ + {% if user('access.menu.update', false) %} +
+ + Close +
+ {% else %} + Close + {% endif %}
@@ -43,21 +52,35 @@
+ Create More Items
+ +
+ +
+
+
-
-
+
+ {% else %} Create
diff --git a/week-06/project/templates/admin/menus.twig b/week-06/project/templates/admin/menus.twig index 9374616..0b84c4b 100644 --- a/week-06/project/templates/admin/menus.twig +++ b/week-06/project/templates/admin/menus.twig @@ -4,9 +4,11 @@ {% block content %}
-

Menus

+

Menus

{{ block("messages_queue", "message_queue.twig") }} - Create + {% if user('access.menu.create', false) %} + Create + {% endif %} {% if list %} @@ -24,17 +26,27 @@ - + - +
- Edit - + {% if user('access.menu.update', false) %} + Edit + {% else %} + Read + {% endif %} + {% if user('access.menu.delete', false) %} + + {% endif %}
{% if item.home == 1 %} {% endif %}{{ item.title }}{% if item.home == 1 %} {% endif %}{{ item.title|escape('html') }} - + {% if user('access.item.update', user('access.item.read', false)) %} + + {{ shorten_string(item.item_title, 10) }} + + {% else %} {{ shorten_string(item.item_title, 10) }} - + {% endif %} {{ item.path }}{{ item.path|escape('html') }} {% if item.published == 1 %} @@ -55,12 +67,18 @@
{% else %}
-

There has no menus been found, click create to add some.

+ {% if user('access.menu.create', false) %} +

There has no menus been found, click create to add some.

+ {% else %} +

There has no menus been found.

+ {% endif %}
{% endif %}
+{% if user('access.menu.delete', false) %} +{% endif %} {% endblock %} \ No newline at end of file diff --git a/week-06/project/templates/admin/nav.twig b/week-06/project/templates/admin/nav.twig index ab2f26b..d8d0f34 100644 --- a/week-06/project/templates/admin/nav.twig +++ b/week-06/project/templates/admin/nav.twig @@ -3,13 +3,25 @@
  • Dashboard
  • -
  • Users
  • -
  • Menus
  • -
  • Items
  • + {% if user('access.user.read', false) %} +
  • Users
  • + {% endif %} + {% if user('access.usergroup.read', false) %} +
  • User Groups
  • + {% endif %} + {% if user('access.menu.read', false) %} +
  • Menus
  • + {% endif %} + {% if user('access.item.read', false) %} +
  • Items
  • + {% endif %}
diff --git a/week-06/project/templates/admin/user.twig b/week-06/project/templates/admin/user.twig index c4c4d00..4ca6c07 100644 --- a/week-06/project/templates/admin/user.twig +++ b/week-06/project/templates/admin/user.twig @@ -1,15 +1,24 @@ {% extends "index.twig" %} -{% block title %}Edit User{% endblock %} +{% block title %}{% if form.id == 0 %}Create{% else %}Edit{% endif %} User {{ form.name|default('') }}{% endblock %} {% block content %}
- {{ block("messages_queue", "message_queue.twig") }} + {{ block("messages_queue", "message_queue.twig") }} + {% if form.id == 0 %} +

Create User

+ {% else %} +

Edit User {{ form.name|default('') }}

+ {% endif %}
-
- - Close -
+ {% if user('access.user.update', false) %} +
+ + Close +
+ {% else %} + Close + {% endif %}
@@ -18,7 +27,7 @@
  • @@ -50,8 +59,8 @@
  • -
    -
    +
    +
    +
    + +
    + {% if groups %} + {% for group in groups %} +
    + + {% endfor %} + {% endif %} +
    +
  • diff --git a/week-06/project/templates/admin/usergroup.twig b/week-06/project/templates/admin/usergroup.twig new file mode 100644 index 0000000..6ffd9c8 --- /dev/null +++ b/week-06/project/templates/admin/usergroup.twig @@ -0,0 +1,46 @@ +{% extends "index.twig" %} + +{% block title %}{% if form.id == 0 %}Create{% else %}Edit {% endif %}{{ form.title|default('') }} User Group{% endblock %} + +{% block content %} +
    + {{ block("messages_queue", "message_queue.twig") }} + {% if form.id == 0 %} +

    Create User Group

    + {% else %} +

    Edit {{ form.title|default('') }} User Group

    + {% endif %} + + {% if user('access.usergroup.update', false) %} +
    + + Close +
    + {% else %} + Close + {% endif %} +
    + +
    + +
    +
    +

    C = Create | R = Read | U = Update | D = Delete

    + Use these keys in this order, or empty for no access, or CRUD for full access, or C for just create, or RU for just read and update. Select any pre/area, have fun! +
    + {% if form.params %} + {% for params in form.params %} +
    + +
    + +
    +
    + {% endfor %} + {% endif %} +
    + + + +
    +{% endblock %} \ No newline at end of file diff --git a/week-06/project/templates/admin/usergroups.twig b/week-06/project/templates/admin/usergroups.twig new file mode 100644 index 0000000..79b260d --- /dev/null +++ b/week-06/project/templates/admin/usergroups.twig @@ -0,0 +1,82 @@ +{% extends "index.twig" %} + +{% block title %}User Groups{% endblock %} + +{% block content %} +
    +

    User Groups

    + {{ block("messages_queue", "message_queue.twig") }} + {% if user('access.usergroup.create', false) %} + Create + {% endif %} + {% if list %} + + + + + + + + + + {% for item in list %} + + + + + + {% endfor %} + +
    ActionGroup NameID
    +
    + {% if user('access.usergroup.update', false) %} + {% if item.id == 1 %}Read{% else %}Edit{% endif %} + {% else %} + Read + {% endif %} + {% if user('access.usergroup.delete', false) %} + {% if item.id != 1 %} + + {% endif %} + {% endif %} +
    +
    + {% if item.params %} +
      +
    • + {{ item.title|escape('html') }} +
      +
        + {% for areas in item.params %} +
      • {{ areas.area|upper }}: {{ areas.access|default('N') }}
      • + {% endfor %} +
      +
      +
    • +
    + {% else %} + {{ item.title|escape('html') }} + {% endif %} +
    {{ item.id }}
    + {% else %} +
    + {% if user('access.usergroup.create', false) %} +

    There has no user groups been found, click create to add some.

    + {% else %} +

    There has no user groups been found.

    + {% endif %} +
    + {% endif %} +
    +{% if user('access.usergroup.delete', false) %} + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/week-06/project/templates/admin/users.twig b/week-06/project/templates/admin/users.twig index 90b764b..e975e55 100644 --- a/week-06/project/templates/admin/users.twig +++ b/week-06/project/templates/admin/users.twig @@ -4,9 +4,11 @@ {% block content %}
    -

    Users

    +

    Users

    {{ block("messages_queue", "message_queue.twig") }} - Create + {% if user('access.user.create', false) %} + Create + {% endif %} {% if list %} @@ -14,6 +16,7 @@ + @@ -23,12 +26,56 @@ - - + + +
    Action Name EmailGroups State ID
    - Edit - + {% if user('access.user.update', false) %} + Edit + {% else %} + Read + {% endif %} + {% if user('access.user.delete', false) %} + + {% endif %}
    {{ item.name }}{{ item.email }}{{ item.name|escape('html') }}
    username: {{ item.username|escape('html') }}
    {{ item.email|escape('html') }}{% if item.groups %} +
      + {% for group in item.groups %} +
    • + {{ group.title|escape('html') }} + +
    • + {% endfor %} +
    + {% else %} + None set + {% endif %} +
    {% if item.block == 0 %} @@ -43,10 +90,15 @@
    {% else %}
    -

    There has no items been found, click create to add some.

    + {% if user('access.user.create', false) %} +

    There has no users been found, click create to add some. (this should never happen!!!)

    + {% else %} +

    There has no users been found. (this should never happen!!!)

    + {% endif %}
    {% endif %}
    +{% if user('access.user.delete', false) %} +{% endif %} {% endblock %} \ No newline at end of file diff --git a/week-06/project/templates/site/homepage.twig b/week-06/project/templates/site/homepage.twig deleted file mode 100644 index 097d8ed..0000000 --- a/week-06/project/templates/site/homepage.twig +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "index.twig" %} - -{% block content %} -
    -
    -

    Octoleo CMS

    -

    There has no home page been created for this CMS. Please come again soon...

    -
    -
    -{% endblock %} \ No newline at end of file diff --git a/week-06/project/templates/site/nav.twig b/week-06/project/templates/site/nav.twig index 89554ac..944528a 100644 --- a/week-06/project/templates/site/nav.twig +++ b/week-06/project/templates/site/nav.twig @@ -1,23 +1,132 @@ {% block bodyNavigation %} {% set center = false %} {% set right = false %} -{% if main_menu %} - {% for menu in main_menu %} - {% if menu.root and menu.position == 'center' %} +{% if menus %} + {% for menu in menus %} + {% if menu.parent == 0 and menu.position == 'center' %} {% set center = true %} - {% elseif menu.root and menu.position == 'right' %} + {% elseif menu.parent == 0 and menu.position == 'right' %} {% set right = true %} {% endif %} {% endfor %} {% endif %} -