Week 6: Adding a User and Logging Out

This commit is contained in:
Llewellyn van der Merwe 2022-04-25 04:19:51 +02:00
parent 3a395967f1
commit dcd75b5004
Signed by: Llewellyn
GPG Key ID: EFC0C720A240551C
80 changed files with 3986 additions and 1016 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
week-06/project/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -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)
{

View File

@ -1,63 +0,0 @@
<?php
/**
* @package Octoleo CMS
*
* @created 9th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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;
}
}

View File

@ -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'],

View File

@ -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()));
}

View File

@ -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);

View File

@ -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

View File

@ -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()));
}

View File

@ -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()));
}

View File

@ -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

View File

@ -0,0 +1,262 @@
<?php
/**
* @package Octoleo CMS
*
* @created 20th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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'];
}
}

View File

@ -0,0 +1,94 @@
<?php
/**
* @package Octoleo CMS
*
* @created 20th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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;
}
}

View File

@ -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()));
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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;

View File

@ -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
{

View File

@ -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();
}
}

View File

@ -16,7 +16,7 @@ use Joomla\Model\DatabaseModelInterface;
use Joomla\Model\DatabaseModelTrait;
/**
* Model class for menus
* Model class
*/
class MenusModel implements DatabaseModelInterface
{

View File

@ -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.

View File

@ -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;

View File

@ -0,0 +1,239 @@
<?php
/**
* @package Octoleo CMS
*
* @created 21th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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;
}
}

View File

@ -0,0 +1,50 @@
<?php
/**
* @package Octoleo CMS
*
* @created 21th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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';
}
}

View File

@ -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

View File

@ -0,0 +1,37 @@
<?php
/**
* @package Octoleo CMS
*
* @created 23th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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;
}

View File

@ -0,0 +1,103 @@
<?php
/**
* @package Octoleo CMS
*
* @created 23th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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
],
];
}
}

View File

@ -0,0 +1,24 @@
<?php
/**
* @package Octoleo CMS
*
* @created 21th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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;
}

View File

@ -2,37 +2,21 @@
/**
* @package Octoleo CMS
*
* @created 9th April 2022
* @created 21th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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
*/

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1,54 @@
<?php
/**
* @package Octoleo CMS
*
* @created 21th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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');
}
}

View File

@ -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

View File

@ -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);

View File

@ -0,0 +1,44 @@
<?php
/**
* @package Octoleo CMS
*
* @created 21th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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;
}

View File

@ -0,0 +1,103 @@
<?php
/**
* @package Octoleo CMS
*
* @created 21th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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;
}
}

View File

@ -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']),
];

View File

@ -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
*

View File

@ -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

View File

@ -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()
);
}

View File

@ -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

View File

@ -14,6 +14,9 @@ use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Registry\Registry;
/**
* Configuration service provider
*/
class ConfigurationProvider implements ServiceProviderInterface
{
/**

View File

@ -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
{

View File

@ -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);

View File

@ -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
*

View File

@ -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)
);
}

View File

@ -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' => []];
}
}

View File

@ -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;
}

View File

@ -1,13 +1,17 @@
<?php
/**
* Joomla! Content Management System
* @package Octoleo CMS
*
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
* @created 21th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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;
}

View File

@ -0,0 +1,79 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 30th April, 2015
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder-Pro>
* @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;
}
}

View File

@ -0,0 +1,322 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 30th April, 2015
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder-Pro>
* @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 '<span class="hasTip" title="' . $title . '" style="cursor:help">' . trim($newString) . '...</span>';
}
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 <http://php.net/manual/en/function.strval.php#41988>
*
* @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;
}
}

View File

@ -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
*/

View File

@ -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
*/

View File

@ -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)

View File

@ -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
*/

View File

@ -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
*/

View File

@ -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
*/

View File

@ -0,0 +1,85 @@
<?php
/**
* @package Octoleo CMS
*
* @created 20th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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;
}
}

View File

@ -0,0 +1,64 @@
<?php
/**
* @package Octoleo CMS
*
* @created 20th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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));
}
}

View File

@ -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)
{

View File

@ -1,133 +0,0 @@
<?php
/**
* @package Octoleo CMS
*
* @created 18th April 2022
* @author Llewellyn van der Merwe <https://git.vdm.dev/Llewellyn>
* @git WEBD-325-45 <https://git.vdm.dev/Llewellyn/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;
}
}

View File

@ -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
]
);

View File

@ -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 */;

View File

@ -7,15 +7,51 @@
<h1 class="uk-article-title">Octoleo CMS Dashboard</h1>
{{ block("messages_queue", "message_queue.twig") }}
<div class="uk-grid-small uk-child-width-expand@s uk-text-center" uk-grid>
<div>
<div class="uk-card uk-card-default uk-card-body"><a href="{{ route() }}index.php/users">Users</a></div>
</div>
<div>
<div class="uk-card uk-card-default uk-card-body"><a href="{{ route() }}index.php/menus">Menus</a></div>
</div>
<div>
<div class="uk-card uk-card-default uk-card-body"><a href="{{ route() }}index.php/items">Items</a></div>
</div>
{% set no_access = true %}
{% if user('access.user.read', false) %}
{% set no_access = false %}
<div>
<div class="uk-card uk-card-default uk-card-body">
<a href="{{ route() }}index.php/users" uk-icon="icon: user; ratio: 7"></a><br />
<a href="{{ route() }}index.php/users">Users</a>
</div>
</div>
{% endif %}
{% if user('access.usergroup.read', false) %}
{% set no_access = false %}
<div>
<div class="uk-card uk-card-default uk-card-body">
<a href="{{ route() }}index.php/usergroups" uk-icon="icon: users; ratio: 7"></a><br />
<a href="{{ route() }}index.php/usergroups">User Groups</a>
</div>
</div>
{% endif %}
{% if user('access.menu.read', false) %}
{% set no_access = false %}
<div>
<div class="uk-card uk-card-default uk-card-body">
<a href="{{ route() }}index.php/menus" uk-icon="icon: menu; ratio: 7"></a><br />
<a href="{{ route() }}index.php/menus">Menus</a>
</div>
</div>
{% endif %}
{% if user('access.item.read', false) %}
{% set no_access = false %}
<div>
<div class="uk-card uk-card-default uk-card-body">
<a href="{{ route() }}index.php/items" uk-icon="icon: pencil; ratio: 7"></a><br />
<a href="{{ route() }}index.php/items">Items</a>
</div>
</div>
{% endif %}
{% if no_access %}
<div>
<div class="uk-card uk-card-default uk-card-body">
<span uk-icon="icon: ban; ratio: 7"></span><br />
No access found to any area, please contact your system administrator!
</div>
</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -1,5 +1,5 @@
{% extends "header.twig" %}
{% set user = user_array() %}
{% block body %}
<body id="go">
{% block bodyNavigation %}{{ block("bodyNavigation", "nav.twig") }}{% endblock %}

View File

@ -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 %}
<script src="https://cdn.ckeditor.com/ckeditor5/34.0.0/classic/ckeditor.js"></script>
@ -9,11 +9,20 @@
{% block content %}
<div class="uk-container uk-margin">
{{ block("messages_queue", "message_queue.twig") }}
{% if form.id == 0 %}
<h3><span uk-icon="icon: pencil; ratio: 1"></span> Create Item</h3>
{% else %}
<h3><span uk-icon="icon: pencil; ratio: 1"></span> Edit Item {{ form.title|default('') }}</h3>
{% endif %}
<form class="uk-form-stacked" action="{{ route() }}index.php/item{{ form.post_key|default('') }}" method="post">
<div class="uk-button-group uk-width-1-1 uk-margin">
<input type="submit" class="uk-button uk-button-primary uk-width-1-2" value="Save"/>
<a href="{{ route() }}index.php/items" type="button" class="uk-button uk-button-danger uk-width-1-2">Close</a>
</div>
{% if user('access.item.update', false) %}
<div class="uk-button-group uk-width-1-1 uk-margin">
<input type="submit" class="uk-button uk-button-primary uk-width-1-2" value="Save"/>
<a href="{{ route() }}index.php/items" type="button" class="uk-button uk-button-danger uk-width-1-2">Close</a>
</div>
{% else %}
<a href="{{ route() }}index.php/items" type="button" class="uk-button uk-button-danger uk-width-1-1">Close</a>
{% endif %}
<div>
<label class="uk-form-label">Title</label>
<div class="uk-form-controls">

View File

@ -4,9 +4,11 @@
{% block content %}
<div class="uk-container uk-margin">
<h1 class="uk-article-title">Items</h1>
<h1 class="uk-article-title"><span uk-icon="icon: pencil; ratio: 2"></span> Items</h1>
{{ block("messages_queue", "message_queue.twig") }}
<a class="uk-button uk-button-default" href="{{ route() }}index.php/item?task=create">Create</a>
{% if user('access.item.create', false) %}
<a class="uk-button uk-button-default" href="{{ route() }}index.php/item?task=create">Create</a>
{% endif %}
{% if list %}
<table class="uk-table uk-table-justify uk-table-divider">
<thead>
@ -23,11 +25,17 @@
<tr>
<td>
<div class="uk-button-group uk-width-1-1">
<a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/item?id={{ item.id }}&task=edit">Edit</a>
<button class="uk-button uk-button-default uk-button-small" onclick="confirmDeletion('{{ item.title }}', {{ item.id }});">Delete</button>
{% if user('access.item.update', false) %}
<a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/item?id={{ item.id }}&task=edit">Edit</a>
{% else %}
<a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/item?id={{ item.id }}&task=edit">Read</a>
{% endif %}
{% if user('access.item.delete', false) %}
<button class="uk-button uk-button-default uk-button-small" onclick="confirmDeletion('{{ item.title|escape('js') }}', {{ item.id }});">Delete</button>
{% endif %}
</div>
</td>
<td>{{ item.title }}</td>
<td>{{ item.title|escape('html') }}</td>
<td>{% if item.introtext %}{{ shorten_string(item.introtext|striptags) }} {% endif %}{{ shorten_string(item.fulltext|striptags) }}</td>
<td>
{% if item.state == 1 %}
@ -49,10 +57,15 @@
</table>
{% else %}
<div class="uk-alert-primary" uk-alert>
<p>There has no items been found, click create to add some.</p>
{% if user('access.item.create', false) %}
<p>There has no items been found, click create to add some.</p>
{% else %}
<p>There has no items been found.</p>
{% endif %}
</div>
{% endif %}
</div>
{% if user('access.item.delete', false) %}
<script>
function confirmDeletion(title, id){
UIkit.modal.confirm('You are about to permanently delete <b>' + title + '</b>?').then(function () {
@ -62,4 +75,5 @@
});
}
</script>
{% endif %}
{% endblock %}

View File

@ -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 %}
<div class="uk-container uk-margin">
{{ block("messages_queue", "message_queue.twig") }}
{% if form.id == 0 %}
<h3><span uk-icon="icon: menu; ratio: 1"></span> Create Menu</h3>
{% else %}
<h3><span uk-icon="icon: menu; ratio: 1"></span> Edit Menu {{ form.title|default('') }}</h3>
{% endif %}
{% if items %}
<form class="uk-form-stacked" action="{{ route() }}index.php/menu{{ form.post_key|default('') }}" method="post">
<div class="uk-button-group uk-width-1-1 uk-margin">
<input type="submit" class="uk-button uk-button-primary uk-width-1-2" value="Save"/>
<a href="{{ route() }}index.php/menus" type="button" class="uk-button uk-button-danger uk-width-1-2">Close</a>
</div>
<form id="menu-admin" class="uk-form-stacked" action="{{ route() }}index.php/menu{{ form.post_key|default('') }}" method="post">
{% if user('access.menu.update', false) %}
<div class="uk-button-group uk-width-1-1 uk-margin">
<input type="submit" class="uk-button uk-button-primary uk-width-1-2" value="Save"/>
<a href="{{ route() }}index.php/menus" type="button" class="uk-button uk-button-danger uk-width-1-2">Close</a>
</div>
{% else %}
<a href="{{ route() }}index.php/menus" type="button" class="uk-button uk-button-danger uk-width-1-1">Close</a>
{% endif %}
<div class="uk-child-width-1-2 uk-margin" uk-grid>
<div>
<label class="uk-form-label">Name (title)</label>
@ -43,21 +52,35 @@
<div class="uk-form-controls">
<select name="item_id" class="uk-select">
{% for item in items %}
<option value="{{ item.id }}" {% if item.id == form.id|default(0) %}selected{% endif %}>{{ item.title }}</option>
<option value="{{ item.id }}" {% if item.id == form.item_id|default(0) %}selected{% endif %}>{{ item.title }}</option>
{% endfor %}
</select>
</div>
<a class="uk-button uk-button-default uk-width-1-1" href="{{ route() }}index.php/item?task=create">Create More Items</a>
</div>
<div>
<label class="uk-form-label">Parent</label>
<div class="uk-form-controls">
<select id="parent_id" name="parent_id" class="uk-select" onchange="updatePosition();">
<option value="0" {% if form.parent_id == 0 %}selected{% endif %}>Root</option>
{% if menus %}
{% for menu in menus %}
<option value="{{ menu.id }}" {% if menu.id == form.parent_id|default(0) %}selected{% endif %}>{{ menu.title }}</option>
{% endfor %}
{% endif %}
</select>
</div>
</div>
<div id="home-block">
<label class="uk-form-label">Is this your home page menu item?</label>
<div class="uk-form-controls">
<select name="home" class="uk-select">
<select id="home" name="home" class="uk-select" onchange="updatePosition();">
<option value="1">Yes</option>
<option value="0" {% if form.home == 0 %}selected{% endif %}>No</option>
</select>
</div>
</div>
<div>
<div id="position-block">
<label class="uk-form-label">Position of this menu item?</label>
<div class="uk-form-controls">
<select name="position" class="uk-select">
@ -101,6 +124,26 @@
<input type="hidden" name="menu_id" value="{{ form.id|default(0) }}">
<input type="hidden" name="{{ token() }}" value="1">
</form>
<script type="text/javascript">
function updatePosition(){
// based on the parent selection we hide the position and home option
let parent = document.getElementById('parent_id').value;
if (parent > 0) {
document.getElementById('position-block').style.visibility = 'hidden';
document.getElementById('home-block').style.visibility = 'hidden';
document.getElementById('home').value = 0;
} else {
document.getElementById('position-block').style.visibility = 'visible';
document.getElementById('home-block').style.visibility = 'visible';
// based on the home selection we hide the position
let home = document.getElementById('home').value;
if (home == 1) {
document.getElementById('position-block').style.visibility = 'hidden';
}
}
}
updatePosition();
</script>
{% else %}
<a class="uk-button uk-button-default" href="{{ route() }}index.php/item?task=create">Create</a>
<div class="uk-alert-primary" uk-alert>

View File

@ -4,9 +4,11 @@
{% block content %}
<div class="uk-container uk-margin">
<h1 class="uk-article-title">Menus</h1>
<h1 class="uk-article-title"><span uk-icon="icon: menu; ratio: 2"></span> Menus</h1>
{{ block("messages_queue", "message_queue.twig") }}
<a class="uk-button uk-button-default" href="{{ route() }}index.php/menu?task=create">Create</a>
{% if user('access.menu.create', false) %}
<a class="uk-button uk-button-default" href="{{ route() }}index.php/menu?task=create">Create</a>
{% endif %}
{% if list %}
<table class="uk-table uk-table-justify uk-table-divider">
<thead>
@ -24,17 +26,27 @@
<tr>
<td>
<div class="uk-button-group uk-width-1-1">
<a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/menu?id={{ item.id }}&task=edit">Edit</a>
<button class="uk-button uk-button-default uk-button-small" onclick="confirmDeletion('{{ item.title }}', {{ item.id }});">Delete</button>
{% if user('access.menu.update', false) %}
<a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/menu?id={{ item.id }}&task=edit">Edit</a>
{% else %}
<a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/menu?id={{ item.id }}&task=edit">Read</a>
{% endif %}
{% if user('access.menu.delete', false) %}
<button class="uk-button uk-button-default uk-button-small" onclick="confirmDeletion('{{ item.title|escape('js') }}', {{ item.id }});">Delete</button>
{% endif %}
</div>
</td>
<td>{% if item.home == 1 %}<span uk-icon="icon: home"></span> {% endif %}{{ item.title }}</td>
<td>{% if item.home == 1 %}<span uk-icon="icon: home"></span> {% endif %}{{ item.title|escape('html') }}</td>
<td>
<a class="uk-button uk-button-default uk-button-small uk-width-1-1" href="{{ route() }}index.php/item?id={{ item.item_id }}&task=edit">
{% if user('access.item.update', user('access.item.read', false)) %}
<a class="uk-button uk-button-default uk-button-small uk-width-1-1" href="{{ route() }}index.php/item?id={{ item.item_id }}&task=edit">
{{ shorten_string(item.item_title, 10) }}
</a>
{% else %}
{{ shorten_string(item.item_title, 10) }}
</a>
{% endif %}
</td>
<td>{{ item.path }}</td>
<td>{{ item.path|escape('html') }}</td>
<td>
{% if item.published == 1 %}
<button class="uk-button uk-button-primary uk-button-small uk-width-1-1">Published</button>
@ -55,12 +67,18 @@
</table>
{% else %}
<div class="uk-alert-primary" uk-alert>
<p>There has no menus been found, click create to add some.</p>
{% if user('access.menu.create', false) %}
<p>There has no menus been found, click create to add some.</p>
{% else %}
<p>There has no menus been found.</p>
{% endif %}
</div>
{% endif %}
</div>
{% if user('access.menu.delete', false) %}
<script>
function confirmDeletion(title, id){
console.log(title);
UIkit.modal.confirm('You are about to permanently delete <b>' + title + '</b>?').then(function () {
window.open("{{ route() }}index.php/menu?id=" + id + "&task=delete", "_self")
}, function () {
@ -68,4 +86,5 @@
});
}
</script>
{% endif %}
{% endblock %}

View File

@ -3,13 +3,25 @@
<div class="uk-navbar-left">
<ul class="uk-navbar-nav">
<li><a href="{{ route() }}index.php/dashboard">Dashboard</a></li>
<li><a href="{{ route() }}index.php/users">Users</a></li>
<li><a href="{{ route() }}index.php/menus">Menus</a></li>
<li><a href="{{ route() }}index.php/items">Items</a></li>
{% if user('access.user.read', false) %}
<li><a href="{{ route() }}index.php/users">Users</a></li>
{% endif %}
{% if user('access.usergroup.read', false) %}
<li><a href="{{ route() }}index.php/usergroups">User Groups</a></li>
{% endif %}
{% if user('access.menu.read', false) %}
<li><a href="{{ route() }}index.php/menus">Menus</a></li>
{% endif %}
{% if user('access.item.read', false) %}
<li><a href="{{ route() }}index.php/items">Items</a></li>
{% endif %}
</ul>
</div>
<div class="uk-navbar-right">
<ul class="uk-navbar-nav">
{% if user.id %}
<li class="uk-active"><a href="{{ route() }}index.php/user?id={{ user.id }}">{{ user.name }}</a></li>
{% endif %}
<li class="uk-active"><a href="{{ route() }}index.php/dashboard?task=logout">Logout</a></li>
<li class="uk-active"><a href="/">Site</a></li>
</ul>

View File

@ -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 %}
<div class="uk-container uk-margin">
{{ block("messages_queue", "message_queue.twig") }}
{{ block("messages_queue", "message_queue.twig") }}
{% if form.id == 0 %}
<h3><span uk-icon="icon: user; ratio: 1"></span> Create User</h3>
{% else %}
<h3><span uk-icon="icon: user; ratio: 1"></span> Edit User {{ form.name|default('') }}</h3>
{% endif %}
<form class="uk-form-stacked" action="{{ route() }}index.php/user{{ form.post_key|default('') }}" method="post">
<div class="uk-button-group uk-width-1-1 uk-margin">
<input type="submit" class="uk-button uk-button-primary uk-width-1-2" value="Save"/>
<a href="{{ route() }}index.php/users" type="button" class="uk-button uk-button-danger uk-width-1-2">Close</a>
</div>
{% if user('access.user.update', false) %}
<div class="uk-button-group uk-width-1-1 uk-margin">
<input type="submit" class="uk-button uk-button-primary uk-width-1-2" value="Save"/>
<a href="{{ route() }}index.php/users" type="button" class="uk-button uk-button-danger uk-width-1-2">Close</a>
</div>
{% else %}
<a href="{{ route() }}index.php/users" type="button" class="uk-button uk-button-danger uk-width-1-1">Close</a>
{% endif %}
<div>
<label class="uk-form-label">Name</label>
<div class="uk-form-controls">
@ -18,7 +27,7 @@
</div>
<ul class="uk-flex-center" uk-switcher="connect: .switcher-container; animation: uk-animation-slide-left-medium, uk-animation-slide-right-medium" uk-tab>
<li class="uk-active"><a href="#">Details</a></li>
<li><a href="#">Activate</a></li>
<li><a href="#">Access</a></li>
</ul>
<ul class="uk-switcher switcher-container uk-margin">
<li>
@ -50,8 +59,8 @@
</div>
</li>
<li>
<div>
<div class="uk-width-1-1 uk-margin">
<div class="uk-child-width-1-2 uk-margin" uk-grid>
<div>
<label class="uk-form-label">Activation of User Access to System</label>
<div class="uk-form-controls">
<select name="block" class="uk-select">
@ -60,6 +69,43 @@
</select>
</div>
</div>
<div>
<label class="uk-form-label">Select the Access Groups</label>
<div class="uk-form-controls">
{% if groups %}
{% for group in groups %}
<label><input class="uk-checkbox" name="groups[]" value="{{ group.id }}" type="checkbox" {% if group.id in form.groups %}checked{% endif %}>
<a href="#modal-{{ item.id }}-{{ group.title|spaceless }}" uk-toggle>{{ group.title|escape('html') }}</a></label><br />
<div id="modal-{{ item.id }}-{{ group.title|spaceless }}" uk-modal>
<div class="uk-modal-dialog uk-modal-body">
<button class="uk-modal-close-default" type="button" uk-close></button>
<div class="uk-modal-header">
<h2 class="uk-modal-title">{{ group.title|escape('html') }} Group</h2>
</div>
<div class="uk-child-width-1-2 uk-margin" uk-grid>
<div>
<b>Area access of this group</b>
<ul class="uk-list uk-list-striped">
<li>{{ group.params|map( param => "<b>#{param.area|upper}</b>: #{param.access|default('N')}" )|join('</li><li>')|raw }}</li>
</ul>
</div>
<div>
<b>Access key map</b>
<ul class="uk-list">
<li>C = Create</li>
<li>R = Read</li>
<li>U = Update</li>
<li>D = Delete</li>
<li>N = No-Access</li>
</ul>
</div>
</div>
</div>
</div>
{% endfor %}
{% endif %}
</div>
</div>
</div>
</li>
</ul>

View File

@ -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 %}
<div class="uk-container uk-margin">
{{ block("messages_queue", "message_queue.twig") }}
{% if form.id == 0 %}
<h3><span uk-icon="icon: users; ratio: 1"></span> Create User Group</h3>
{% else %}
<h3><span uk-icon="icon: users; ratio: 1"></span> Edit {{ form.title|default('') }} User Group</h3>
{% endif %}
<form class="uk-form-stacked" action="{{ route() }}index.php/usergroup{{ form.post_key|default('') }}" method="post">
{% if user('access.usergroup.update', false) %}
<div class="uk-button-group uk-width-1-1 uk-margin">
<input type="submit" class="uk-button uk-button-primary uk-width-1-2" value="Save"/>
<a href="{{ route() }}index.php/usergroups" type="button" class="uk-button uk-button-danger uk-width-1-2">Close</a>
</div>
{% else %}
<a href="{{ route() }}index.php/usergroups" type="button" class="uk-button uk-button-danger uk-width-1-1">Close</a>
{% endif %}
<div>
<label class="uk-form-label">User Group Name</label>
<div class="uk-form-controls">
<input name="title" class="uk-input uk-width-1-1" type="text" placeholder="Add Group Name" value="{{ form.title|default('') }}">
</div>
</div>
<h3>C = Create | R = Read | U = Update | D = Delete</h3>
<small>Use these keys in this order, or <b>empty</b> for no access, or <b>CRUD</b> for full access, or <b>C</b> for just create, or <b>RU</b> for just read and update. Select any pre/area, have fun!</small>
<div class="uk-child-width-1-2 uk-margin" uk-grid>
{% if form.params %}
{% for params in form.params %}
<div>
<label class="uk-form-label">{{ params.area|escape('html')|upper }} AREA</label>
<div class="uk-form-controls">
<input name="params[{{ params.area }}]" class="uk-input uk-width-1-1" type="text" placeholder="CRUD" value="{{ params.access|default('')|upper }}">
</div>
</div>
{% endfor %}
{% endif %}
</div>
<input type="hidden" name="usergroup_id" value="{{ form.id|default(0) }}">
<input type="hidden" name="{{ token() }}" value="1">
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,82 @@
{% extends "index.twig" %}
{% block title %}User Groups{% endblock %}
{% block content %}
<div class="uk-container uk-margin">
<h1 class="uk-article-title"><span uk-icon="icon: users; ratio: 2"></span> User Groups</h1>
{{ block("messages_queue", "message_queue.twig") }}
{% if user('access.usergroup.create', false) %}
<a class="uk-button uk-button-default" href="{{ route() }}index.php/usergroup?task=create">Create</a>
{% endif %}
{% if list %}
<table class="uk-table uk-table-justify uk-table-divider">
<thead>
<tr>
<th class="uk-width-small">Action</th>
<th>Group Name</th>
<th>ID</th>
</tr>
</thead>
<tbody>
{% for item in list %}
<tr>
<td>
<div class="uk-button-group uk-width-1-1">
{% if user('access.usergroup.update', false) %}
<a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/usergroup?id={{ item.id }}&task=edit">{% if item.id == 1 %}Read{% else %}Edit{% endif %}</a>
{% else %}
<a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/usergroup?id={{ item.id }}&task=edit">Read</a>
{% endif %}
{% if user('access.usergroup.delete', false) %}
{% if item.id != 1 %}
<button class="uk-button uk-button-default uk-button-small" onclick="confirmDeletion('{{ item.title|escape('js') }}', {{ item.id }});">Delete</button>
{% endif %}
{% endif %}
</div>
</td>
<td>
{% if item.params %}
<ul uk-accordion>
<li>
<a class="uk-accordion-title" href="#">{{ item.title|escape('html') }}</a>
<div class="uk-accordion-content">
<ul class="uk-list">
{% for areas in item.params %}
<li><span uk-icon="icon: chevron-double-right"></span> {{ areas.area|upper }}: <b>{{ areas.access|default('N') }}</b></li>
{% endfor %}
</ul>
</div>
</li>
</ul>
{% else %}
{{ item.title|escape('html') }}
{% endif %}
</td>
<td>{{ item.id }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="uk-alert-primary" uk-alert>
{% if user('access.usergroup.create', false) %}
<p>There has no user groups been found, click create to add some.</p>
{% else %}
<p>There has no user groups been found.</p>
{% endif %}
</div>
{% endif %}
</div>
{% if user('access.usergroup.delete', false) %}
<script>
function confirmDeletion(name, id){
UIkit.modal.confirm('You are about to permanently delete <b>' + name + '</b>?').then(function () {
window.open("{{ route() }}index.php/usergroup?id=" + id + "&task=delete", "_self")
}, function () {
// we do nothing ;)
});
}
</script>
{% endif %}
{% endblock %}

View File

@ -4,9 +4,11 @@
{% block content %}
<div class="uk-container uk-margin">
<h1 class="uk-article-title">Users</h1>
<h1 class="uk-article-title"><span uk-icon="icon: user; ratio: 2"></span> Users</h1>
{{ block("messages_queue", "message_queue.twig") }}
<a class="uk-button uk-button-default" href="{{ route() }}index.php/user?task=create">Create</a>
{% if user('access.user.create', false) %}
<a class="uk-button uk-button-default" href="{{ route() }}index.php/user?task=create">Create</a>
{% endif %}
{% if list %}
<table class="uk-table uk-table-justify uk-table-divider">
<thead>
@ -14,6 +16,7 @@
<th class="uk-width-small">Action</th>
<th>Name</th>
<th class="uk-width-small">Email</th>
<th>Groups</th>
<th class="uk-width-small">State</th>
<th>ID</th>
</tr>
@ -23,12 +26,56 @@
<tr>
<td>
<div class="uk-button-group uk-width-1-1">
<a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/user?id={{ item.id }}&task=edit">Edit</a>
<button class="uk-button uk-button-default uk-button-small" onclick="confirmDeletion('{{ item.name }}', {{ item.id }});">Delete</button>
{% if user('access.user.update', false) %}
<a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/user?id={{ item.id }}&task=edit">Edit</a>
{% else %}
<a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/user?id={{ item.id }}&task=edit">Read</a>
{% endif %}
{% if user('access.user.delete', false) %}
<button class="uk-button uk-button-default uk-button-small" onclick="confirmDeletion('{{ item.name|escape('js') }}', {{ item.id }});">Delete</button>
{% endif %}
</div>
</td>
<td>{{ item.name }}</td>
<td>{{ item.email }}</td>
<td>{{ item.name|escape('html') }}<br /><small>username: {{ item.username|escape('html') }}</small></td>
<td>{{ item.email|escape('html') }}</td>
<td>{% if item.groups %}
<ul class="uk-list uk-list-collapse">
{% for group in item.groups %}
<li>
<a href="#modal-{{ item.id }}-{{ group.title|spaceless }}" uk-toggle>{{ group.title|escape('html') }}</a>
<div id="modal-{{ item.id }}-{{ group.title|spaceless }}" uk-modal>
<div class="uk-modal-dialog uk-modal-body">
<button class="uk-modal-close-default" type="button" uk-close></button>
<div class="uk-modal-header">
<h2 class="uk-modal-title">{{ group.title|escape('html') }} Group</h2>
</div>
<div class="uk-child-width-1-2 uk-margin" uk-grid>
<div>
<b>Area access of this group</b>
<ul class="uk-list uk-list-striped">
<li>{{ group.params|map( param => "<b>#{param.area|upper}</b>: #{param.access|default('N')}" )|join('</li><li>')|raw }}</li>
</ul>
</div>
<div>
<b>Access key map</b>
<ul class="uk-list">
<li>C = Create</li>
<li>R = Read</li>
<li>U = Update</li>
<li>D = Delete</li>
<li>N = No-Access</li>
</ul>
</div>
</div>
</div>
</div>
</li>
{% endfor %}
</ul>
{% else %}
None set
{% endif %}
</td>
<td>
{% if item.block == 0 %}
<button class="uk-button uk-button-primary uk-button-small uk-width-1-1">Active</button>
@ -43,10 +90,15 @@
</table>
{% else %}
<div class="uk-alert-primary" uk-alert>
<p>There has no items been found, click create to add some.</p>
{% if user('access.user.create', false) %}
<p>There has no users been found, click create to add some. (this should never happen!!!)</p>
{% else %}
<p>There has no users been found. (this should never happen!!!)</p>
{% endif %}
</div>
{% endif %}
</div>
{% if user('access.user.delete', false) %}
<script>
function confirmDeletion(name, id){
UIkit.modal.confirm('You are about to permanently delete <b>' + name + '</b>?').then(function () {
@ -56,4 +108,5 @@
});
}
</script>
{% endif %}
{% endblock %}

View File

@ -1,10 +0,0 @@
{% extends "index.twig" %}
{% block content %}
<div class="uk-margin-remove-top uk-height-large uk-background-cover uk-overflow-hidden uk-flex" style="background-image: url('https://source.unsplash.com/random/1920x500/?yachts');">
<div class="uk-width-1-2@m uk-text-center uk-margin-auto uk-margin-auto-vertical uk-overlay uk-overlay-default">
<h1>Octoleo CMS</h1>
<p>There has no home page been created for this CMS. Please come again soon...</p>
</div>
</div>
{% endblock %}

View File

@ -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 %}
<nav class="uk-navbar-container" uk-navbar>
{% if center and right %}
<div class="uk-position-relative">
{% endif %}
<nav class="uk-navbar-container" uk-navbar{% if center and right %}="dropbar: true"{% endif %}>
<div class="uk-navbar-center">
<ul class="uk-navbar-nav">
<li class="uk-active"><a href="{{ route() }}">Home</a></li>
<li{% if home %} class="uk-active"{% endif %}><a href="{{ route() }}">{{ home_menu_title|default('Home') }}</a></li>
{% if center %}
{% for menu in main_menu %}
{% if menu.root and menu.position == 'center' %}
<li><a href="{{ route() }}{{ menu.path }}">{{ menu.title }}</a></li>
{% for menu in menus %}
{% if menu.parent == 0 and menu.position == 'center' %}
<li{% if menu_active == menu.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu.path }}">{{ menu.title|escape('html') }}</a>
{% set load_menu_one = false %}
{% for menu_one in menus %}
{% if menu_one.parent == menu.id %}
{% set load_menu_one = true %}
{% endif %}
{% endfor %}
{% if load_menu_one %}
<div class="uk-navbar-dropdown">
<ul class="uk-nav uk-navbar-dropdown-nav">
{% for menu_one in menus %}
{% if menu_one.parent == menu.id %}
<li{% if menu_active == menu_one.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_one.path }}">{{ menu_one.title|escape('html') }}</a>
{% set load_menu_two = false %}
{% for menu_two in menus %}
{% if menu_two.parent == menu_one.id %}
{% set load_menu_two = true %}
{% endif %}
{% endfor %}
{% if load_menu_two %}
<ul class="uk-nav-sub">
{% for menu_two in menus %}
{% if menu_two.parent == menu_one.id %}
<li{% if menu_active == menu_two.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_two.path }}">{{ menu_two.title|escape('html') }}</a>
{% set load_menu_three = false %}
{% for menu_three in menus %}
{% if menu_three.parent == menu_two.id %}
{% set load_menu_three = true %}
{% endif %}
{% endfor %}
{% if load_menu_three %}
<ul class="uk-nav-sub">
{% for menu_three in menus %}
{% if menu_three.parent == menu_two.id %}
<li{% if menu_active == menu_three.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_three.path }}">{{ menu_three.title|escape('html') }}</a>
{% set load_menu_four = false %}
{% for menu_four in menus %}
{% if menu_four.parent == menu_three.id %}
{% set load_menu_four = true %}
{% endif %}
{% endfor %}
{% if load_menu_four %}
<ul class="uk-nav-sub">
{% for menu_four in menus %}
{% if menu_four.parent == menu_three.id %}
<li{% if menu_active == menu_four.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_four.path }}">{{ menu_four.title|escape('html') }}</a>
{% set load_menu_five = false %}
{% for menu_five in menus %}
{% if menu_five.parent == menu_four.id %}
{% set load_menu_five = true %}
{% endif %}
{% endfor %}
{% if load_menu_five %}
<ul class="uk-nav-sub">
{% for menu_five in menus %}
{% if menu_five.parent == menu_four.id %}
<li{% if menu_active == menu_five.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_five.path }}">{{ menu_five.title|escape('html') }}</a>
{% set load_menu_six = false %}
{% for menu_six in menus %}
{% if menu_six.parent == menu_five.id %}
{% set load_menu_six = true %}
{% endif %}
{% endfor %}
{% if load_menu_six %}
<ul class="uk-nav-sub">
{% for menu_six in menus %}
{% if menu_six.parent == menu_five.id %}
<li{% if menu_active == menu_six.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_six.path }}">{{ menu_six.title|escape('html') }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
</li>
{% endif %}
{% endfor %}
{% endif %}
@ -27,9 +136,115 @@
<div class="uk-navbar-right">
<ul class="uk-navbar-nav">
{% if right %}
{% for menu in main_menu %}
{% if menu.root and menu.position == 'right' %}
<li><a href="{{ route() }}{{ menu.path }}">{{ menu.title }}</a></li>
{% for menu in menus %}
{% if menu.parent == 0 and menu.position == 'right' %}
<li{% if menu_active == menu.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu.path }}">{{ menu.title|escape('html') }}</a>
{% set load_menu_one = false %}
{% for menu_one in menus %}
{% if menu_one.parent == menu.id %}
{% set load_menu_one = true %}
{% endif %}
{% endfor %}
{% if load_menu_one %}
<div class="uk-navbar-dropdown">
<ul class="uk-nav uk-navbar-dropdown-nav">
{% for menu_one in menus %}
{% if menu_one.parent == menu.id %}
<li{% if menu_active == menu_one.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_one.path }}">{{ menu_one.title|escape('html') }}</a>
{% set load_menu_two = false %}
{% for menu_two in menus %}
{% if menu_two.parent == menu_one.id %}
{% set load_menu_two = true %}
{% endif %}
{% endfor %}
{% if load_menu_two %}
<ul class="uk-nav-sub">
{% for menu_two in menus %}
{% if menu_two.parent == menu_one.id %}
<li{% if menu_active == menu_two.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_two.path }}">{{ menu_two.title|escape('html') }}</a>
{% set load_menu_three = false %}
{% for menu_three in menus %}
{% if menu_three.parent == menu_two.id %}
{% set load_menu_three = true %}
{% endif %}
{% endfor %}
{% if load_menu_three %}
<ul class="uk-nav-sub">
{% for menu_three in menus %}
{% if menu_three.parent == menu_two.id %}
<li{% if menu_active == menu_three.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_three.path }}">{{ menu_three.title|escape('html') }}</a>
{% set load_menu_four = false %}
{% for menu_four in menus %}
{% if menu_four.parent == menu_three.id %}
{% set load_menu_four = true %}
{% endif %}
{% endfor %}
{% if load_menu_four %}
<ul class="uk-nav-sub">
{% for menu_four in menus %}
{% if menu_four.parent == menu_three.id %}
<li{% if menu_active == menu_four.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_four.path }}">{{ menu_four.title|escape('html') }}</a>
{% set load_menu_five = false %}
{% for menu_five in menus %}
{% if menu_five.parent == menu_four.id %}
{% set load_menu_five = true %}
{% endif %}
{% endfor %}
{% if load_menu_five %}
<ul class="uk-nav-sub">
{% for menu_five in menus %}
{% if menu_five.parent == menu_four.id %}
<li{% if menu_active == menu_five.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_five.path }}">{{ menu_five.title|escape('html') }}</a>
{% set load_menu_six = false %}
{% for menu_six in menus %}
{% if menu_six.parent == menu_five.id %}
{% set load_menu_six = true %}
{% endif %}
{% endfor %}
{% if load_menu_six %}
<ul class="uk-nav-sub">
{% for menu_six in menus %}
{% if menu_six.parent == menu_five.id %}
<li{% if menu_active == menu_six.id %} class="uk-active"{% endif %}>
<a href="{{ route() }}{{ menu_six.path }}">{{ menu_six.title|escape('html') }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
</li>
{% endif %}
{% endfor %}
{% endif %}
@ -37,4 +252,7 @@
</div>
{% endif %}
</nav>
{% if center and right %}
</div>
{% endif %}
{% endblock %}

View File

@ -1,14 +1,19 @@
{% extends "index.twig" %}
{% block title %}{{ title|escape('html') }}{% endblock %}
{% block content %}
{% if body == '' %}
<div class="uk-container uk-margin">
<h2>We Couldn't Find It</h2>
<p class="lead">Sorry, we couldn't find the page matching your request. Try using the navigation to find what you were looking for?</p>
</div>
{% else %}
<div class="uk-margin-remove-top uk-background-cover uk-flex" style="background-image: url('https://source.unsplash.com/random/1920x500/?yachts');">
<div class="uk-width-1-2@m uk-text-center uk-margin-auto uk-margin-auto-vertical uk-overlay uk-overlay-default">
<h1>{{ title }}</h1>
{% if body == '' %}
<p>We have an error!</p>
{% else %}
<h1>{{ title|escape('html') }}</h1>
{{ body|raw }}
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}