Week 4: Content Management of Items/Menus/Users

This commit is contained in:
Llewellyn van der Merwe 2022-04-19 05:49:18 +02:00
parent d041433828
commit 38cb5a21d2
Signed by: Llewellyn
GPG Key ID: EFC0C720A240551C
72 changed files with 4845 additions and 518 deletions

View File

@ -45,13 +45,16 @@ try
->registerServiceProvider(new Octoleo\CMS\Service\ConfigurationProvider(LPATH_CONFIGURATION . '/octoconfig.php')) ->registerServiceProvider(new Octoleo\CMS\Service\ConfigurationProvider(LPATH_CONFIGURATION . '/octoconfig.php'))
->registerServiceProvider(new Octoleo\CMS\Service\SessionProvider) ->registerServiceProvider(new Octoleo\CMS\Service\SessionProvider)
->registerServiceProvider(new Octoleo\CMS\Service\UserProvider) ->registerServiceProvider(new Octoleo\CMS\Service\UserProvider)
->registerServiceProvider(new Octoleo\CMS\Service\InputProvider)
->registerServiceProvider(new Octoleo\CMS\Service\AdminApplicationProvider) ->registerServiceProvider(new Octoleo\CMS\Service\AdminApplicationProvider)
->registerServiceProvider(new Octoleo\CMS\Service\AdminRouterProvider)
->registerServiceProvider(new Octoleo\CMS\Service\AdminMVCProvider)
->registerServiceProvider(new Joomla\Database\Service\DatabaseProvider) ->registerServiceProvider(new Joomla\Database\Service\DatabaseProvider)
->registerServiceProvider(new Octoleo\CMS\Service\EventProvider) ->registerServiceProvider(new Octoleo\CMS\Service\EventProvider)
->registerServiceProvider(new Octoleo\CMS\Service\HttpProvider) ->registerServiceProvider(new Octoleo\CMS\Service\HttpProvider)
->registerServiceProvider(new Octoleo\CMS\Service\LoggingProvider) ->registerServiceProvider(new Octoleo\CMS\Service\LoggingProvider)
->registerServiceProvider(new Joomla\Preload\Service\PreloadProvider) ->registerServiceProvider(new Joomla\Preload\Service\PreloadProvider)
->registerServiceProvider(new Octoleo\CMS\Service\TemplatingProvider); ->registerServiceProvider(new Octoleo\CMS\Service\AdminTemplatingProvider);
// Alias the web application to Octoleo's base application class as this is the primary application for the environment // Alias the web application to Octoleo's base application class as this is the primary application for the environment
$container->alias(Joomla\Application\AbstractApplication::class, Joomla\Application\AbstractWebApplication::class); $container->alias(Joomla\Application\AbstractApplication::class, Joomla\Application\AbstractWebApplication::class);

View File

@ -26,6 +26,7 @@
}, },
"require": { "require": {
"php": "^7.2.5", "php": "^7.2.5",
"ext-json": "*",
"joomla/application": "~2.0", "joomla/application": "~2.0",
"joomla/archive": "~2.0", "joomla/archive": "~2.0",
"joomla/authentication": "~2.0", "joomla/authentication": "~2.0",

View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "3442722df6289914de45ee254d28bada", "content-hash": "ce1103508736357f7992f4e8b00f7bc6",
"packages": [ "packages": [
{ {
"name": "algo26-matthias/idna-convert", "name": "algo26-matthias/idna-convert",
@ -5382,7 +5382,8 @@
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": "^7.2.5" "php": "^7.2.5",
"ext-json": "*"
}, },
"platform-dev": [], "platform-dev": [],
"platform-overrides": { "platform-overrides": {

View File

@ -44,12 +44,13 @@ try
$container = (new Joomla\DI\Container) $container = (new Joomla\DI\Container)
->registerServiceProvider(new Octoleo\CMS\Service\SiteApplicationProvider) ->registerServiceProvider(new Octoleo\CMS\Service\SiteApplicationProvider)
->registerServiceProvider(new Octoleo\CMS\Service\ConfigurationProvider(LPATH_CONFIGURATION . '/octoconfig.php')) ->registerServiceProvider(new Octoleo\CMS\Service\ConfigurationProvider(LPATH_CONFIGURATION . '/octoconfig.php'))
->registerServiceProvider(new Octoleo\CMS\Service\InputProvider)
->registerServiceProvider(new Joomla\Database\Service\DatabaseProvider) ->registerServiceProvider(new Joomla\Database\Service\DatabaseProvider)
->registerServiceProvider(new Octoleo\CMS\Service\EventProvider) ->registerServiceProvider(new Octoleo\CMS\Service\EventProvider)
->registerServiceProvider(new Octoleo\CMS\Service\HttpProvider) ->registerServiceProvider(new Octoleo\CMS\Service\HttpProvider)
->registerServiceProvider(new Octoleo\CMS\Service\LoggingProvider) ->registerServiceProvider(new Octoleo\CMS\Service\LoggingProvider)
->registerServiceProvider(new Joomla\Preload\Service\PreloadProvider) ->registerServiceProvider(new Joomla\Preload\Service\PreloadProvider)
->registerServiceProvider(new Octoleo\CMS\Service\TemplatingProvider); ->registerServiceProvider(new Octoleo\CMS\Service\SiteTemplatingProvider);
// Alias the web application to Octoleo's base application class as this is the primary application for the environment // Alias the web application to Octoleo's base application class as this is the primary application for the environment
$container->alias(Joomla\Application\AbstractApplication::class, Joomla\Application\AbstractWebApplication::class); $container->alias(Joomla\Application\AbstractApplication::class, Joomla\Application\AbstractWebApplication::class);

View File

@ -0,0 +1,28 @@
<?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;
/**
* Class for checking the user access
*
* @since 1.0.0
*/
interface AccessInterface
{
/**
* @param string $task
* @param string $default
*
* @return bool
* @throws \Exception
*/
public function allow(string $task, string $default = ''): bool;
}

View File

@ -0,0 +1,111 @@
<?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\Uri\Uri;
/**
* Trait for checking the user access
*
* @since 1.0.0
*/
trait AccessTrait
{
/**
* When access not allowed this is the path to redirect to
*
* @var string
*/
private $noAccessRedirect = '';
/**
* Check if user is allowed to access this area
*
* @param string $task
* @param string $default
*
* @return bool
* @throws \Exception
*/
public function allow(string $task = 'post', string $default = ''): bool
{
// our little access controller TODO: we can do better
$has_access = false;
/** @var \Octoleo\CMS\Application\AdminApplication $app */
$app = $this->getApplication();
/** @var \Octoleo\CMS\User\UserFactory $userFactory */
$userFactory = $app->getUserFactory();
// user actions [logout]
if ('logout' === $task)
{
if ($userFactory->logout())
{
$this->noAccessRedirect = '/';
// clear the message queue
$app->getMessageQueue(true);
}
}
// check if this is a user valid
elseif ($userFactory->active())
{
$has_access = true;
}
// user actions [access, signup]
elseif ('access' === $task || 'signup' === $task)
{
if ('access' === $task)
{
if ($userFactory->login())
{
$has_access = true;
}
}
else
{
if ($userFactory->create())
{
$has_access = true;
}
else
{
$this->noAccessRedirect = '/?account=signup';
}
}
// we by default always load the dashboard
$this->view->setActiveDashboard($default);
}
return $has_access;
}
/**
* @param string|null $target
*
* @return void
*/
private function _redirect(string $target = null)
{
// get uri request to get host
$uri = new Uri($this->getApplication()->get('uri.request'));
// get redirect path
$redirect = (!empty($target)) ? $target : $this->noAccessRedirect;
// fix the path
$path = $uri->getPath();
$path = substr($path, 0, strripos($path, '/')) . '/' . $redirect;
// redirect to the set area
$this->getApplication()->redirect($uri->getScheme() . '://' . $uri->getHost() . $path );
}
}

View File

@ -0,0 +1,26 @@
<?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;
/**
* Class for checking the form had a token
*
* @since 1.0.0
*/
interface CheckTokenInterface
{
/**
* Check the token of the form
*
* @return bool
*/
public function checkToken(): bool;
}

View File

@ -0,0 +1,36 @@
<?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;
/**
* Class for checking the form had a token
*
* @since 1.0.0
*/
trait CheckTokenTrait
{
/**
* Check the token of the form
*
* @return bool
*/
public function checkToken(): bool
{
$token = $this->getApplication()->getSession()->getToken();
$form_token = $this->getInput()->getString($token, 0);
if ($form_token == 0)
{
exit('Invalid form token');
}
return true;
}
}

View File

@ -14,18 +14,19 @@ use Joomla\Application\AbstractApplication;
use Joomla\Controller\AbstractController; use Joomla\Controller\AbstractController;
use Joomla\Input\Input; use Joomla\Input\Input;
use Joomla\Uri\Uri; use Joomla\Uri\Uri;
use Laminas\Diactoros\Response\RedirectResponse;
use Octoleo\CMS\View\Admin\DashboardHtmlView; use Octoleo\CMS\View\Admin\DashboardHtmlView;
use Laminas\Diactoros\Response\HtmlResponse; use Laminas\Diactoros\Response\HtmlResponse;
/** /**
* Controller handling the site's homepage * Controller handling the site's dashboard
* *
* @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object. * @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 DashboardController extends AbstractController class DashboardController extends AbstractController implements AccessInterface, CheckTokenInterface
{ {
use AccessTrait, CheckTokenTrait;
/** /**
* The view object. * The view object.
* *
@ -36,10 +37,10 @@ class DashboardController extends AbstractController
/** /**
* Constructor. * Constructor.
* *
* @param DashboardHtmlView $view The view object. * @param DashboardHtmlView $view The view object.
* @param Input $user The user object. * @param Input $user The user object.
* @param Input $input The input object. * @param Input $input The input object.
* @param AbstractApplication $app The application object. * @param AbstractApplication $app The application object.
*/ */
public function __construct(DashboardHtmlView $view, Input $input = null, AbstractApplication $app = null) public function __construct(DashboardHtmlView $view, Input $input = null, AbstractApplication $app = null)
{ {
@ -56,56 +57,30 @@ class DashboardController extends AbstractController
*/ */
public function execute(): bool public function execute(): bool
{ {
// our little access controller TODO: we can do better // Do not Enable browser caching
$has_access = false; $this->getApplication()->allowCache(false);
// Enable browser caching $task = $this->getInput()->getString('task', '');
$this->getApplication()->allowCache(true);
$dashboard = $this->getInput()->getString('dashboard', '');
$id = $this->getInput()->getInt('id', 0); $id = $this->getInput()->getInt('id', 0);
$this->view->setActiveDashboard($dashboard); $this->view->setActiveDashboard($task);
$this->view->setActiveId($id); $this->view->setActiveId($id);
/** @var \Octoleo\CMS\User\UserFactory $userFactory */ // validate form token
$userFactory = $this->getApplication()->getUserFactory(); if ('access' === $task || 'signup' === $task)
// user actions [access, signup]
if ('access' === $dashboard || 'signup' === $dashboard || 'logout' === $dashboard)
{ {
if ('access' === $dashboard && $userFactory->login()) $this->checkToken();
{
$has_access = true;
}
elseif ('signup' === $dashboard && $userFactory->create())
{
$has_access = true;
}
elseif ('logout' === $dashboard && $userFactory->logout())
{
$has_access = false;
}
// we by default always load the dashboard
$this->view->setActiveDashboard('dashboard');
}
elseif ($userFactory->active())
{
$has_access = true;
} }
if ($has_access) // check if user is allowed to access
if ($this->allow($task))
{ {
$this->getApplication()->setResponse(new HtmlResponse($this->view->render())); $this->getApplication()->setResponse(new HtmlResponse($this->view->render()));
} }
else else
{ {
// get uri request to get host // go to set page
$uri = new Uri($this->getApplication()->get('uri.request')); $this->_redirect();
// Redirect to the administrator area
$this->getApplication()->redirect($uri->getScheme() . '://' . $uri->getHost() . '/administrator/');
} }
return true; return true;

View File

@ -13,8 +13,8 @@ namespace Octoleo\CMS\Controller;
use Joomla\Application\AbstractApplication; use Joomla\Application\AbstractApplication;
use Joomla\Controller\AbstractController; use Joomla\Controller\AbstractController;
use Joomla\Input\Input; use Joomla\Input\Input;
use Joomla\Renderer\RendererInterface;
use Laminas\Diactoros\Response\HtmlResponse; use Laminas\Diactoros\Response\HtmlResponse;
use Octoleo\CMS\View\Page\HomepageHtmlView;
/** /**
* Controller handling the site's homepage * Controller handling the site's homepage
@ -25,24 +25,24 @@ use Laminas\Diactoros\Response\HtmlResponse;
class HomepageController extends AbstractController class HomepageController extends AbstractController
{ {
/** /**
* The template renderer. * The view object.
* *
* @var RendererInterface * @var HomepageHtmlView
*/ */
private $renderer; private $view;
/** /**
* Constructor. * Constructor.
* *
* @param RendererInterface $renderer The template renderer. * @param HomepageHtmlView $view The view object.
* @param Input $input The input object. * @param Input $input The input object.
* @param AbstractApplication $app The application object. * @param AbstractApplication $app The application object.
*/ */
public function __construct(RendererInterface $renderer, Input $input = null, AbstractApplication $app = null) public function __construct(HomepageHtmlView $view, Input $input = null, AbstractApplication $app = null)
{ {
parent::__construct($input, $app); parent::__construct($input, $app);
$this->renderer = $renderer; $this->view = $view;
} }
/** /**
@ -52,10 +52,11 @@ class HomepageController extends AbstractController
*/ */
public function execute(): bool public function execute(): bool
{ {
// Enable browser caching // Disable all cache for now
$this->getApplication()->allowCache(true); $this->getApplication()->allowCache(false);
$this->getApplication()->setResponse(new HtmlResponse($this->renderer->render('homepage.twig'))); // check if there is a home page
$this->getApplication()->setResponse(new HtmlResponse($this->view->render()));
return true; return true;
} }

View File

@ -0,0 +1,211 @@
<?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\Filter\InputFilter as InputFilterAlias;
use Joomla\Input\Input;
use Octoleo\CMS\Date\Date;
use Octoleo\CMS\Factory;
use Octoleo\CMS\Filter\InputFilter;
use Octoleo\CMS\Model\ItemModel;
use Octoleo\CMS\User\UserFactoryInterface;
use Octoleo\CMS\View\Admin\ItemHtmlView;
use Laminas\Diactoros\Response\HtmlResponse;
/**
* Controller handling the site's dashboard
*
* @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object.
* @property-read \Octoleo\CMS\Application\AdminApplication $app Application object
*/
class ItemController extends AbstractController implements AccessInterface, CheckTokenInterface
{
use AccessTrait, CheckTokenTrait;
/**
* The view object.
*
* @var ItemHtmlView
*/
private $view;
/**
* The model object.
*
* @var ItemModel
*/
private $model;
/**
* @var InputFilter
*/
private $inputFilter;
/**
* 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.
*/
public function __construct(ItemModel $model, $view, Input $input = null, AbstractApplication $app = 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
);
}
/**
* 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 ($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');
}
// go to set page
$this->_redirect('items');
return true;
}
if ('POST' === $method)
{
$id = $this->setItem();
}
$this->view->setActiveId($id);
$this->view->setActiveView('item');
// check if user is allowed to access
if ($this->allow('item'))
{
$this->getApplication()->setResponse(new HtmlResponse($this->view->render()));
}
else
{
// go to set page
$this->_redirect();
}
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 = [];
$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;
if (empty($tempItem['title']))
{
// we show a warning message
$tempItem['title'] = '';
$this->getApplication()->enqueueMessage('Title field is required.', 'error');
$can_save = false;
}
// we actually can also not continue if we don't have content
if (empty($tempItem['fulltext']))
{
// we show a warning message
$tempItem['fulltext'] = '';
$this->getApplication()->enqueueMessage('Content field is required.', 'error');
$can_save = false;
}
// can we save the item
if ($can_save)
{
/** @var \Octoleo\CMS\User\User $user */
$user = Factory::getContainer()->get(UserFactoryInterface::class)->getUser();
$user_id = (int) $user->get('id', 0);
$today = (new Date())->toSql();
return $this->model->setItem(
$tempItem['id'],
$tempItem['title'],
$tempItem['fulltext'],
$tempItem['state'],
$today,
$user_id,
$tempItem['created_by_alias'],
$today,
$user_id,
$tempItem['publish_up'],
$tempItem['publish_down'],
$tempItem['metakey'],
$tempItem['metadesc'],
$tempItem['metadata'],
$tempItem['featured']);
}
// add to model the post values
$this->model->tempItem = $tempItem;
return $tempItem['id'];
}
}

View File

@ -0,0 +1,76 @@
<?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 Octoleo\CMS\View\Admin\ItemsHtmlView;
use Laminas\Diactoros\Response\HtmlResponse;
/**
* Controller handling the site's dashboard
*
* @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object.
* @property-read \Octoleo\CMS\Application\AdminApplication $app Application object
*/
class ItemsController extends AbstractController implements AccessInterface, CheckTokenInterface
{
use AccessTrait, CheckTokenTrait;
/**
* The view object.
*
* @var ItemsHtmlView
*/
private $view;
/**
* Constructor.
*
* @param ItemsHtmlView $view The view object.
* @param Input $input The input object.
* @param AbstractApplication $app The application object.
*/
public function __construct(ItemsHtmlView $view, Input $input = null, AbstractApplication $app = null)
{
parent::__construct($input, $app);
$this->view = $view;
}
/**
* Execute the controller.
*
* @return boolean
* @throws \Exception
*/
public function execute(): bool
{
// Do not Enable browser caching
$this->getApplication()->allowCache(false);
$this->view->setActiveView('items');
// check if user is allowed to access
if ($this->allow('items'))
{
$this->getApplication()->setResponse(new HtmlResponse($this->view->render()));
}
else
{
// go to set page
$this->_redirect();
}
return true;
}
}

View File

@ -15,6 +15,7 @@ use Joomla\Controller\AbstractController;
use Joomla\Input\Input; use Joomla\Input\Input;
use Joomla\Renderer\RendererInterface; use Joomla\Renderer\RendererInterface;
use Laminas\Diactoros\Response\HtmlResponse; use Laminas\Diactoros\Response\HtmlResponse;
use Octoleo\CMS\View\Admin\DashboardHtmlView;
/** /**
* Controller handling the site's homepage * Controller handling the site's homepage
@ -31,17 +32,26 @@ class LoginController extends AbstractController
*/ */
private $renderer; private $renderer;
/**
* The view object.
*
* @var DashboardHtmlView
*/
private $view;
/** /**
* Constructor. * Constructor.
* *
* @param DashboardHtmlView $view The view object.
* @param RendererInterface $renderer The template renderer. * @param RendererInterface $renderer The template renderer.
* @param Input $input The input object. * @param Input $input The input object.
* @param AbstractApplication $app The application object. * @param AbstractApplication $app The application object.
*/ */
public function __construct(RendererInterface $renderer, Input $input = null, AbstractApplication $app = null) public function __construct(DashboardHtmlView $view, RendererInterface $renderer, Input $input = null, AbstractApplication $app = null)
{ {
parent::__construct($input, $app); parent::__construct($input, $app);
$this->view = $view;
$this->renderer = $renderer; $this->renderer = $renderer;
} }
@ -49,15 +59,29 @@ class LoginController extends AbstractController
* Execute the controller. * Execute the controller.
* *
* @return boolean * @return boolean
* @throws \Exception
*/ */
public function execute(): bool public function execute(): bool
{ {
// Enable browser caching // Do not Enable browser caching
$this->getApplication()->allowCache(true); $this->getApplication()->allowCache(false);
$task = $this->getInput()->getString('account', null); $task = $this->getInput()->getString('account', null);
if ('signup' === $task) /** @var \Octoleo\CMS\Application\AdminApplication $app */
$app = $this->getApplication();
/** @var \Octoleo\CMS\User\UserFactory $userFactory */
$userFactory = $app->getUserFactory();
// if the user is logged in we go to dashboard
if ($userFactory->active(false))
{
$this->view->setActiveDashboard('dashboard');
$this->view->setActiveId(0);
$this->getApplication()->setResponse(new HtmlResponse($this->view->render()));
}
elseif ('signup' === $task)
{ {
$this->getApplication()->setResponse(new HtmlResponse($this->renderer->render('signup.twig'))); $this->getApplication()->setResponse(new HtmlResponse($this->renderer->render('signup.twig')));
} }

View File

@ -0,0 +1,193 @@
<?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\Filter\InputFilter as InputFilterAlias;
use Joomla\Input\Input;
use Octoleo\CMS\Filter\InputFilter;
use Octoleo\CMS\Model\MenuModel;
use Laminas\Diactoros\Response\HtmlResponse;
use Octoleo\CMS\View\Admin\MenuHtmlView;
/**
* Controller handling the site's dashboard
*
* @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object.
* @property-read \Octoleo\CMS\Application\AdminApplication $app Application object
*/
class MenuController extends AbstractController implements AccessInterface, CheckTokenInterface
{
use AccessTrait, CheckTokenTrait;
/**
* The view object.
*
* @var MenuHtmlView
*/
private $view;
/**
* The model object.
*
* @var MenuModel
*/
private $model;
/**
* @var InputFilter
*/
private $inputFilter;
/**
* 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.
*/
public function __construct(MenuModel $model, MenuHtmlView $view, Input $input = null, AbstractApplication $app = 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
);
}
/**
* 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->model->delete($id))
{
$this->getApplication()->enqueueMessage('Menu was deleted!', 'success');
}
else
{
$this->getApplication()->enqueueMessage('Menu could not be deleted!', 'error');
}
// go to set page
$this->_redirect('menus');
return true;
}
if ('POST' === $method)
{
$id = $this->setItem();
}
$this->view->setActiveId($id);
$this->view->setActiveView('menu');
// check if user is allowed to access
if ($this->allow('menu'))
{
$this->getApplication()->setResponse(new HtmlResponse($this->view->render()));
}
else
{
// go to set page
$this->_redirect();
}
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 = [];
$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);
// check that we have a Title
$can_save = true;
if (empty($tempItem['title']))
{
// we show a warning message
$tempItem['title'] = '';
$this->getApplication()->enqueueMessage('Title field is required.', 'error');
$can_save = false;
}
// we actually can also not continue if we don't have content
if (empty($tempItem['item_id']) || $tempItem['item_id'] == 0)
{
// we show a warning message
$tempItem['item_id'] = 0;
$this->getApplication()->enqueueMessage('Item field is required.', 'error');
$can_save = false;
}
// can we save the item
if ($can_save)
{
return $this->model->setItem(
$tempItem['id'],
$tempItem['title'],
$tempItem['alias'],
$tempItem['item_id'],
$tempItem['path'],
$tempItem['published'],
$tempItem['publish_up'],
$tempItem['publish_down'],
$tempItem['position'],
$tempItem['home']);
}
// add to model the post values
$this->model->tempItem = $tempItem;
return $tempItem['id'];
}
}

View File

@ -0,0 +1,77 @@
<?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 Octoleo\CMS\View\Admin\MenusHtmlView;
use Laminas\Diactoros\Response\HtmlResponse;
/**
* Controller handling the site's dashboard
*
* @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object.
* @property-read \Octoleo\CMS\Application\AdminApplication $app Application object
*/
class MenusController extends AbstractController implements AccessInterface, CheckTokenInterface
{
use AccessTrait, CheckTokenTrait;
/**
* The view object.
*
* @var MenusHtmlView
*/
private $view;
/**
* 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.
*/
public function __construct(MenusHtmlView $view, Input $input = null, AbstractApplication $app = null)
{
parent::__construct($input, $app);
$this->view = $view;
}
/**
* Execute the controller.
*
* @return boolean
* @throws \Exception
*/
public function execute(): bool
{
// Do not Enable browser caching
$this->getApplication()->allowCache(false);
$this->view->setActiveView('menus');
// check if user is allowed to access
if ($this->allow('menus'))
{
$this->getApplication()->setResponse(new HtmlResponse($this->view->render()));
}
else
{
// go to set page
$this->_redirect();
}
return true;
}
}

View File

@ -36,7 +36,7 @@ class PageController extends AbstractController
/** /**
* Constructor. * Constructor.
* *
* @param PageHtmlView $view The view object. * @param PageHtmlView $view The view object.
* @param Input $input The input object. * @param Input $input The input object.
* @param AbstractApplication $app The application object. * @param AbstractApplication $app The application object.
*/ */
@ -54,8 +54,8 @@ class PageController extends AbstractController
*/ */
public function execute(): bool public function execute(): bool
{ {
// Enable browser caching // Disable all cache for now
$this->getApplication()->allowCache(true); $this->getApplication()->allowCache(false);
$page = $this->getInput()->getString('view', ''); $page = $this->getInput()->getString('view', '');
$details = $this->getInput()->getString('details', ''); $details = $this->getInput()->getString('details', '');
@ -72,7 +72,6 @@ class PageController extends AbstractController
else else
{ {
$this->view->setPage($page); $this->view->setPage($page);
$this->view->setDetails($details);
$this->getApplication()->setResponse(new HtmlResponse($this->view->render())); $this->getApplication()->setResponse(new HtmlResponse($this->view->render()));
} }

View File

@ -0,0 +1,268 @@
<?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\Filter\InputFilter as InputFilterAlias;
use Joomla\Input\Input;
use Octoleo\CMS\Date\Date;
use Joomla\Authentication\Password\BCryptHandler;
use Octoleo\CMS\Factory;
use Octoleo\CMS\Filter\InputFilter;
use Octoleo\CMS\Model\UserModel;
use Octoleo\CMS\User\UserFactoryInterface;
use Octoleo\CMS\View\Admin\UserHtmlView;
use Laminas\Diactoros\Response\HtmlResponse;
/**
* Controller handling the site's dashboard
*
* @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object.
* @property-read \Octoleo\CMS\Application\AdminApplication $app Application object
*/
class UserController extends AbstractController implements AccessInterface, CheckTokenInterface
{
use AccessTrait, CheckTokenTrait;
/**
* The view object.
*
* @var UserHtmlView
*/
private $view;
/**
* The model object.
*
* @var UserModel
*/
private $model;
/**
* @var InputFilter
*/
private $inputFilter;
/**
* @var BCryptHandler
*/
private $secure;
/**
* Constructor.
*
* @param UserModel $model The model object.
* @param UserHtmlView $view The view object.
* @param Input|null $input The input object.
* @param AbstractApplication|null $app The application object.
*/
public function __construct(UserModel $model, UserHtmlView $view, Input $input = null, AbstractApplication $app = 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();
}
/**
* 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)
{
/** @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)
{
$this->getApplication()->enqueueMessage('You can not delete your own account!', 'warning');
}
elseif ($this->model->delete($id))
{
$this->getApplication()->enqueueMessage('User was deleted!', 'success');
}
else
{
$this->getApplication()->enqueueMessage('User could not be deleted!', 'error');
}
// go to set page
$this->_redirect('users');
return true;
}
if ('POST' === $method)
{
$id = $this->setItem();
}
$this->view->setActiveId($id);
$this->view->setActiveView('user');
// check if user is allowed to access
if ($this->allow('user'))
{
$this->getApplication()->setResponse(new HtmlResponse($this->view->render()));
}
else
{
// go to set page
$this->_redirect();
}
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 = [];
$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
$tempItem['name'] = '';
$this->getApplication()->enqueueMessage('Name field is required.', 'error');
$can_save = false;
}
// check that we have a username
if (empty($tempItem['username']))
{
// we show a warning message
$tempItem['username'] = '';
$this->getApplication()->enqueueMessage('Username field is required.', 'error');
$can_save = false;
}
// check that we have an email TODO: check that we have a valid email
if (empty($tempItem['email']))
{
// we show a warning message
$tempItem['email'] = '';
$this->getApplication()->enqueueMessage('Email field is required.', 'error');
$can_save = false;
}
// check passwords
if (isset($tempItem['password2']) && $tempItem['password'] != $tempItem['password2'])
{
// we show a warning message
$tempItem['password'] = 'xxxxxxxxxx';
$tempItem['password2'] = 'xxxxxxxxxx';
$this->getApplication()->enqueueMessage('Passwords do not match.', 'error');
$can_save = false;
}
unset ($tempItem['password2']);
// do not set password that has not changed
if ($tempItem['password'] === 'xxxxxxxxxx')
{
if ($tempItem['id'] == 0)
{
// we show a warning message
$tempItem['password'] = 'xxxxxxxxxx';
$tempItem['password2'] = 'xxxxxxxxxx';
$this->getApplication()->enqueueMessage('Passwords not set.', 'error');
$can_save = false;
}
else
{
$tempItem['password'] = '';
}
}
elseif (strlen($tempItem['password']) < 7)
{
// we show a warning message
$tempItem['password'] = 'xxxxxxxxxx';
$tempItem['password2'] = 'xxxxxxxxxx';
$this->getApplication()->enqueueMessage('Passwords must be longer than 6 characters.', 'error');
$can_save = false;
}
else
{
// hash the password
$tempItem['password'] = $this->secure->hashPassword($tempItem['password']);
}
// 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)
{
// don't allow user to block self
$this->getApplication()->enqueueMessage('You can not block yourself!', 'warning');
$tempItem['block'] = 0;
}
return $this->model->setItem(
$tempItem['id'],
$tempItem['name'],
$tempItem['username'],
$tempItem['email'],
$tempItem['password'],
$tempItem['block'],
$tempItem['sendEmail'],
$today,
$tempItem['activation']);
}
// add to model the post values
$this->model->tempItem = $tempItem;
return $tempItem['id'];
}
}

View File

@ -0,0 +1,76 @@
<?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 Octoleo\CMS\View\Admin\UsersHtmlView;
use Laminas\Diactoros\Response\HtmlResponse;
/**
* Controller handling the site's dashboard
*
* @method \Octoleo\CMS\Application\AdminApplication getApplication() Get the application object.
* @property-read \Octoleo\CMS\Application\AdminApplication $app Application object
*/
class UsersController extends AbstractController implements AccessInterface, CheckTokenInterface
{
use AccessTrait, CheckTokenTrait;
/**
* The view object.
*
* @var UsersHtmlView
*/
private $view;
/**
* Constructor.
*
* @param UsersHtmlView $view The view object.
* @param Input $input The input object.
* @param AbstractApplication $app The application object.
*/
public function __construct(UsersHtmlView $view, Input $input = null, AbstractApplication $app = null)
{
parent::__construct($input, $app);
$this->view = $view;
}
/**
* Execute the controller.
*
* @return boolean
* @throws \Exception
*/
public function execute(): bool
{
// Do not Enable browser caching
$this->getApplication()->allowCache(false);
$this->view->setActiveView('users');
// check if user is allowed to access
if ($this->allow('users'))
{
$this->getApplication()->setResponse(new HtmlResponse($this->view->render()));
}
else
{
// go to set page
$this->_redirect();
}
return true;
}
}

View File

@ -31,7 +31,7 @@ class WrongCmsController extends AbstractController
// Enable browser caching // Enable browser caching
$this->getApplication()->allowCache(true); $this->getApplication()->allowCache(true);
$response = new TextResponse("This isn't the CMS you're looking for.", 404); $response = new TextResponse("This isn't the what you're looking for.", 404);
$this->getApplication()->setResponse($response); $this->getApplication()->setResponse($response);

View File

@ -147,6 +147,7 @@ abstract class Factory
->registerServiceProvider(new Service\ConfigurationProvider(LPATH_CONFIGURATION . '/octoconfig.php')) ->registerServiceProvider(new Service\ConfigurationProvider(LPATH_CONFIGURATION . '/octoconfig.php'))
->registerServiceProvider(new Service\SessionProvider) ->registerServiceProvider(new Service\SessionProvider)
->registerServiceProvider(new Service\UserProvider) ->registerServiceProvider(new Service\UserProvider)
->registerServiceProvider(new Service\InputProvider)
->registerServiceProvider(new DatabaseProvider) ->registerServiceProvider(new DatabaseProvider)
->registerServiceProvider(new Service\EventProvider) ->registerServiceProvider(new Service\EventProvider)
->registerServiceProvider(new Service\HttpProvider) ->registerServiceProvider(new Service\HttpProvider)

View File

@ -0,0 +1,67 @@
<?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\Model;
use Joomla\Database\DatabaseDriver;
use Joomla\Database\ParameterType;
use Joomla\Model\DatabaseModelInterface;
use Joomla\Model\DatabaseModelTrait;
/**
* Model class for pages
* source: https://github.com/joomla/framework.joomla.org/blob/master/src/Model/PackageModel.php
*/
class HomepageModel implements DatabaseModelInterface, MenuInterface, PageInterface
{
use DatabaseModelTrait, SiteMenuTrait, SitePageTrait;
/**
* Instantiate the model.
*
* @param DatabaseDriver $db The database adapter.
*/
public function __construct(DatabaseDriver $db)
{
$this->setDb($db);
}
/**
* @return \stdClass
*/
public function getHomePage(): \stdClass
{
$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') . ' = 1')
->setLimit(1);
try
{
$home = $db->setQuery($query)->loadObject();
}
catch (\RuntimeException $e)
{
return new \stdClass();
}
if ($home)
{
return $home;
}
return new \stdClass();
}
}

View File

@ -0,0 +1,285 @@
<?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\Model;
use Joomla\Database\DatabaseDriver;
use Joomla\Database\ParameterType;
use Joomla\Model\DatabaseModelInterface;
use Joomla\Model\DatabaseModelTrait;
use Octoleo\CMS\Date\Date;
/**
* Model class for items
*/
class ItemModel implements DatabaseModelInterface
{
use DatabaseModelTrait;
/**
* @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 string $introtext
* @param string $fulltext
* @param int $state
* @param string $created
* @param int $createdBy
* @param string $createdByAlias
* @param string $modified
* @param int $modifiedBy
* @param string $publishUp
* @param string $publishDown
* @param string $metakey
* @param string $metadesc
* @param string $metadata
* @param int $featured
*
* @return int
* @throws \Exception
*/
public function setItem(
int $id,
string $title,
string $fulltext,
int $state,
string $created,
int $createdBy,
string $createdByAlias,
string $modified,
int $modifiedBy,
string $publishUp,
string $publishDown,
string $metakey,
string $metadesc,
string $metadata,
int $featured): int
{
$db = $this->getDb();
// extract the intro text
$introtext = '';
if (strpos($fulltext, '<p>intro-text</p>'))
{
$bucket = explode('<p>intro-text</p>', $fulltext);
$introtext = array_shift($bucket);
$fulltext = implode('', $bucket);
}
$data = [
'title' => (string) $title,
'introtext' => (string) $introtext,
'fulltext' => (string) $fulltext,
'state' => (int) $state,
'created' => (string) $created,
'created_by' => (int) $createdBy,
'created_by_alias' => (string) $createdByAlias,
'modified' => (string) $modified,
'modified_by' => (int) $modifiedBy,
'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(),
'metakey' => (string) $metakey,
'metadesc' => (string) $metadesc,
'metadata' => (string) $metadata,
'featured' => (int) $featured
];
// if we have ID update
if ($id > 0)
{
$data['id'] = (int) $id;
// remove what can not now be set
unset($data['created']);
unset($data['created_by']);
// change to object
$data = (object) $data;
try
{
$db->updateObject('#__item', $data, 'id');
}
catch (\RuntimeException $exception)
{
throw new \RuntimeException($exception->getMessage(), 404);
}
return $id;
}
else
{
// remove what can not now be set
$data['modified'] = '0000-00-00 00:00:00';
$data['modified_by'] = 0;
// change to object
$data = (object) $data;
try
{
$db->insertObject('#__item', $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();
}
// to be sure ;)
$default->today_date = (new Date())->toSql();
$default->post_key = "?task=create";
$default->state = 1;
// we return the default if id not correct
if (!is_numeric($id))
{
return $default;
}
$query = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__item'))
->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";
$result->today_date = $default->today_date;
// check if we have intro text we add it to full text
if (!empty($result->introtext))
{
$result->fulltext = $result->introtext . '<p>intro-text</p>' . $result->fulltext;
}
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 linked(int $id): bool
{
$db = $this->getDb();
// first check if this item is linked to menu
$query = $db->getQuery(true)
->select($db->quoteName('title'))
->from($db->quoteName('#__menu'))
->where($db->quoteName('item_id') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
try
{
$menu = $db->setQuery($query)->loadResult();
}
catch (\RuntimeException $e)
{
// not linked... or something
return false;
}
if ($menu)
{
return true;
}
return false;
}
/**
* @param int $id
*
* @return bool
*/
public function delete(int $id): bool
{
$db = $this->getDb();
// Purge the session
$query = $db->getQuery(true)
->delete($db->quoteName('#__item'))
->where($db->quoteName('id') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
try
{
$db->setQuery($query)->execute();
}
catch (\RuntimeException $e)
{
// delete failed
return false;
}
return true;
}
}

View File

@ -0,0 +1,55 @@
<?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\Model;
use Joomla\Database\DatabaseDriver;
use Joomla\Database\ParameterType;
use Joomla\Model\DatabaseModelInterface;
use Joomla\Model\DatabaseModelTrait;
/**
* Model class for items
*/
class ItemsModel implements DatabaseModelInterface
{
use DatabaseModelTrait;
/**
* 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
{
$db = $this->getDb();
$query = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__item'));
return $db->setQuery($query)->loadObjectList('id');
}
public function setLayout(string $name): string
{
return $name . '.twig';
}
}

View File

@ -0,0 +1,28 @@
<?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\Model;
/**
* Class for getting menu items
*
* @since 1.0.0
*/
interface MenuInterface
{
/**
* Get all menu items
*
* @param string|null $active
*
* @return array
*/
public function getMenus(string $active = null): array;
}

View File

@ -0,0 +1,338 @@
<?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\Model;
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;
/**
* Model class for menu item
*/
class MenuModel implements DatabaseModelInterface
{
use DatabaseModelTrait;
/**
* @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 string $alias
* @param int $itemId
* @param string $path
* @param int $published
* @param string $publishUp
* @param string $publishDown
* @param string $position
* @param int $home
*
* @return int
* @throws \Exception
*/
public function setItem(
int $id,
string $title,
string $alias,
int $itemId,
string $path,
int $published,
string $publishUp,
string $publishDown,
string $position,
int $home): int
{
$db = $this->getDb();
// set the path if not set
$this->setPathAlias($id, $title, $path, $alias);
$data = [
'title' => (string) $title,
'alias' => (string) $alias,
'path' => (string) $path,
'item_id' => (int) $itemId,
'published' => (int) $published,
'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
];
// we set position in params
$data['params'] = json_encode(['position' => $position]);
// if we have ID update
if ($id > 0)
{
$data['id'] = (int) $id;
// change to object
$data = (object) $data;
try
{
$db->updateObject('#__menu', $data, 'id');
}
catch (\RuntimeException $exception)
{
throw new \RuntimeException($exception->getMessage(), 404);
}
}
else
{
// change to object
$data = (object) $data;
try
{
$db->insertObject('#__menu', $data);
}
catch (\RuntimeException $exception)
{
throw new \RuntimeException($exception->getMessage(), 404);
}
$id = $db->insertid();
}
// check if we have another home set
if ($data->home == 1)
{
$this->setHome($id);
}
return $id;
}
/**
* Get all published items
*
* @return array
*/
public function getItems(): array
{
$db = $this->getDb();
$query = $db->getQuery(true)
->select($db->quoteName(array('id', 'title')))
->from($db->quoteName('#__item'))
->where('state = 1');
return $db->setQuery($query)->loadObjectList('id');
}
/**
* 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();
}
// to be sure ;)
$default->today_date = (new Date())->toSql();
$default->post_key = "?task=create";
$default->published = 1;
$default->home = 0;
// we return the default if id not correct
if (!is_numeric($id))
{
return $default;
}
$query = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__menu'))
->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";
$result->today_date = $default->today_date;
// set the position
$result->params = json_decode($result->params);
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('#__menu'))
->where($db->quoteName('id') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
try
{
$db->setQuery($query)->execute();
}
catch (\RuntimeException $e)
{
// delete failed
return false;
}
return true;
}
/**
* Make sure that we have only one home
*
* @param $id
*
* @return bool
*/
private function setHome($id): bool
{
$db = $this->getDb();
// Purge the session
$query = $db->getQuery(true)
->update($db->quoteName('#__menu'))
->set($db->quoteName('home') . ' = 0')
->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
* @param string $title
* @param string $path
* @param string $alias
*/
private function setPathAlias(int $id, string $title, string &$path, string &$alias)
{
$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))
{
$seeker = $alias . '-' . $pointer;
$pointer++;
}
// update the path
$alias = $seeker;
$path = $seeker;
}
/**
* Check if an alias exist
*
* @param int $id
* @param string $alias
*
* @return bool
*/
private function exist(int $id, string $alias): bool
{
$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);
}
try
{
$id = $db->setQuery($query)->loadResult();
}
catch (\RuntimeException $e)
{
// we ignore this and just return an empty object
}
if (isset($id) && $id > 0)
{
return true;
}
return false;
}
}

View File

@ -0,0 +1,62 @@
<?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\Model;
use Joomla\Database\DatabaseDriver;
use Joomla\Database\ParameterType;
use Joomla\Model\DatabaseModelInterface;
use Joomla\Model\DatabaseModelTrait;
/**
* Model class for menus
*/
class MenusModel implements DatabaseModelInterface
{
use DatabaseModelTrait;
/**
* 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
{
$db = $this->getDb();
$query = $db->getQuery(true)
->select('a.*')
->select($db->quoteName(array('t.title'), array('item_title')))
->from($db->quoteName('#__menu', 'a'))
->join('INNER', $db->quoteName('#__item', 't'), 'a.item_id = t.id');
return $db->setQuery($query)->loadObjectList('id');
}
/**
* @param string $name
*
* @return string
*/
public function setLayout(string $name): string
{
return $name . '.twig';
}
}

View File

@ -0,0 +1,41 @@
<?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\Model;
/**
* Class for getting page data
*
* @since 1.0.0
*/
interface PageInterface
{
/**
* Get page data
*
* @param string $path The page path
*
* @return \stdClass
*
* @throws \RuntimeException
*/
public function getPageItemByPath(string $path): \stdClass;
/**
* Get page data
*
* @param int $item The item id
*
* @return \stdClass
*
* @throws \RuntimeException
*/
public function getPageItemById(int $item): \stdClass;
}

View File

@ -11,31 +11,15 @@
namespace Octoleo\CMS\Model; namespace Octoleo\CMS\Model;
use Joomla\Database\DatabaseDriver; use Joomla\Database\DatabaseDriver;
use Joomla\Database\ParameterType;
use Joomla\Model\DatabaseModelInterface; use Joomla\Model\DatabaseModelInterface;
use Joomla\Model\DatabaseModelTrait; use Joomla\Model\DatabaseModelTrait;
/** /**
* Model class for pages * Model class for pages
* source: https://github.com/joomla/framework.joomla.org/blob/master/src/Model/PackageModel.php
*/ */
class PageModel implements DatabaseModelInterface class PageModel implements DatabaseModelInterface, MenuInterface, PageInterface
{ {
use DatabaseModelTrait; use DatabaseModelTrait, SiteMenuTrait, SitePageTrait;
/**
* Array of legal pages
*
* @var array
*/
private $legalPages = ['products', 'blog', 'about-us', 'location', 'contact-us'];
/**
* Array of legal details pages
*
* @var array
*/
private $legalDetailsPages = ['yachts', 'ski-boats', 'drones'];
/** /**
* Instantiate the model. * Instantiate the model.
@ -46,42 +30,4 @@ class PageModel implements DatabaseModelInterface
{ {
$this->setDb($db); $this->setDb($db);
} }
/**
* Get a page's data
*
* @param string $pageName The page to lookup
*
* @return string
*
* @throws \RuntimeException
*/
public function getPage(string $pageName): string
{
if (!in_array($pageName, $this->legalPages))
{
throw new \RuntimeException(sprintf('Unable to find page data for the `%s`', $pageName), 404);
}
return $pageName;
}
/**
* Get a page's details data
*
* @param string $detailsName The page details to lookup
*
* @return string
*
* @throws \RuntimeException
*/
public function getDetails(string $detailsName): string
{
if (strlen($detailsName) && !in_array($detailsName, $this->legalDetailsPages))
{
throw new \RuntimeException(sprintf('Unable to find page details data for the `%s`', $detailsName), 404);
}
return $detailsName;
}
} }

View File

@ -0,0 +1,70 @@
<?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\Model;
/**
* Trait for getting menu items
*
* @since 1.0.0
*/
trait SiteMenuTrait
{
/**
* Get all menu items that are root and published and not home page
*
* @param string|null $active
*
* @return array
*/
public function getMenus($active = null): 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');
try
{
$menus = $db->setQuery($query)->loadObjectList();
}
catch (\RuntimeException $e)
{
return [];
}
if ($menus)
{
$bucket = [];
foreach ($menus as $menu)
{
$row = [];
// set the details
$row['title'] = $menu->title;
$row['path'] = $menu->path;
$row['root'] = true;
// set position
$params = (isset($menu->params) && strpos($menu->params, 'position') !== false) ? json_decode($menu->params) : null;
// default is center
$row['position'] = (is_object($params) && isset($params->position)) ? $params->position : 'center';
// add to our bucket
$bucket[] = $row;
}
return $bucket;
}
return [];
}
}

View File

@ -0,0 +1,96 @@
<?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\Model;
use Joomla\Database\ParameterType;
/**
* Trait for getting page data
*
* @since 1.0.0
*/
trait SitePageTrait
{
/**
* Get page data
*
* @param string $path The page path
*
* @return \stdClass
*
* @throws \RuntimeException
*/
public function getPageItemByPath(string $path): \stdClass
{
$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')
->bind(':path', $path)
->setLimit(1);
try
{
$page = $db->setQuery($query)->loadObject();
}
catch (\RuntimeException $e)
{
return new \stdClass();
}
if ($page)
{
return $page;
}
return new \stdClass();
}
/**
* Get page data
*
* @param int $item The item id
*
* @return \stdClass
*
* @throws \RuntimeException
*/
public function getPageItemById(int $item): \stdClass
{
$db = $this->getDb();
$query = $db->getQuery(true)
->select('a.*')
->from($db->quoteName('#__item', 'a'))
->where($db->quoteName('id') . ' = :id')
->bind(':id', $item, ParameterType::INTEGER)
->setLimit(1);
try
{
$page = $db->setQuery($query)->loadObject();
}
catch (\RuntimeException $e)
{
return new \stdClass();
}
if ($page)
{
return $page;
}
return new \stdClass();
}
}

View File

@ -0,0 +1,226 @@
<?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\Model;
use Joomla\Database\DatabaseDriver;
use Joomla\Database\ParameterType;
use Joomla\Model\DatabaseModelInterface;
use Joomla\Model\DatabaseModelTrait;
use Octoleo\CMS\Date\Date;
/**
* Model class for items
*/
class UserModel implements DatabaseModelInterface
{
use DatabaseModelTrait;
/**
* @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 $name
* @param string $username
* @param string $email
* @param string $password
* @param int $block
* @param int $sendEmail
* @param string $registerDate
* @param int $activation
*
* @return int
* @throws \Exception
*/
public function setItem(
int $id,
string $name,
string $username,
string $email,
string $password,
int $block,
int $sendEmail,
string $registerDate,
int $activation): int
{
$db = $this->getDb();
$data = [
'name' => (string) $name,
'username' => (string) $username,
'email' => (string) $email,
'block' => (int) $block,
'sendEmail' => (int) $sendEmail,
'registerDate' => (string) (empty($registerDate)) ? (new Date())->toSql() : (new Date($registerDate))->toSql(),
'activation' => (int) $activation
];
// only update password if set
if (!empty($password) && strlen($password) > 6)
{
$data['password'] = (string) $password;
}
// if we have ID update
if ($id > 0)
{
$data['id'] = (int) $id;
// we remove registration date when we update the user
unset($data['registerDate']);
// change to object
$data = (object) $data;
try
{
$db->updateObject('#__users', $data, 'id');
}
catch (\RuntimeException $exception)
{
throw new \RuntimeException($exception->getMessage(), 404);
}
return $id;
}
else
{
// change to object
$data = (object) $data;
try
{
$db->insertObject('#__users', $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();
}
// to be sure ;)
$default->today_date = (new Date())->toSql();
$default->post_key = "?task=create";
$default->block = 0;
$default->activation = 1;
$default->sendEmail = 1;
// always remove password
$default->password = 'xxxxxxxxxx';
$default->password2 = 'xxxxxxxxxx';
// we return the default if id not correct
if (!is_numeric($id))
{
return $default;
}
$query = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__users'))
->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";
$result->today_date = $default->today_date;
// always remove password
$result->password = $default->password;
$result->password2 = $default->password2;
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('#__users'))
->where($db->quoteName('id') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
try
{
$db->setQuery($query)->execute();
}
catch (\RuntimeException $e)
{
// delete failed
return false;
}
return true;
}
}

View File

@ -0,0 +1,55 @@
<?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\Model;
use Joomla\Database\DatabaseDriver;
use Joomla\Database\ParameterType;
use Joomla\Model\DatabaseModelInterface;
use Joomla\Model\DatabaseModelTrait;
/**
* Model class for users
*/
class UsersModel implements DatabaseModelInterface
{
use DatabaseModelTrait;
/**
* 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
{
$db = $this->getDb();
$query = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__users'));
return $db->setQuery($query)->loadObjectList('id');
}
public function setLayout(string $name): string
{
return $name . '.twig';
}
}

View File

@ -45,7 +45,9 @@ class FrameworkExtension extends AbstractExtension
new TwigFunction('request_uri', [FrameworkTwigRuntime::class, 'getRequestUri']), new TwigFunction('request_uri', [FrameworkTwigRuntime::class, 'getRequestUri']),
new TwigFunction('route', [FrameworkTwigRuntime::class, 'getRouteUri']), new TwigFunction('route', [FrameworkTwigRuntime::class, 'getRouteUri']),
new TwigFunction('sri', [FrameworkTwigRuntime::class, 'getSriAttributes'], ['is_safe' => ['html']]), new TwigFunction('sri', [FrameworkTwigRuntime::class, 'getSriAttributes'], ['is_safe' => ['html']]),
new TwigFunction('url', [FrameworkTwigRuntime::class, 'getRouteUrl']), new TwigFunction('message_queue', [FrameworkTwigRuntime::class, 'getMessageQueue']),
new TwigFunction('token', [FrameworkTwigRuntime::class, 'getToken']),
new TwigFunction('shorten_string', [FrameworkTwigRuntime::class, 'shortenString']),
]; ];
} }

View File

@ -10,6 +10,7 @@ namespace Octoleo\CMS\Renderer;
use Joomla\Application\AbstractApplication; use Joomla\Application\AbstractApplication;
use Joomla\Preload\PreloadManager; use Joomla\Preload\PreloadManager;
use Joomla\Session\SessionInterface;
/** /**
* Twig runtime class * Twig runtime class
@ -45,15 +46,22 @@ class FrameworkTwigRuntime
*/ */
private $sriManifestPath; private $sriManifestPath;
/**
* @var SessionInterface
*/
private $session;
/** /**
* Constructor * Constructor
* *
* @param AbstractApplication $app The application object * @param AbstractApplication $app The application object
* @param PreloadManager $preloadManager The HTTP/2 preload manager * @param PreloadManager $preloadManager The HTTP/2 preload manager
* @param string $sriManifestPath The path to the SRI manifest data * @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) public function __construct(AbstractApplication $app, PreloadManager $preloadManager, string $sriManifestPath, $session = null)
{ {
$this->session = $session;
$this->app = $app; $this->app = $app;
$this->preloadManager = $preloadManager; $this->preloadManager = $preloadManager;
$this->sriManifestPath = $sriManifestPath; $this->sriManifestPath = $sriManifestPath;
@ -93,6 +101,68 @@ class FrameworkTwigRuntime
return $this->app->get('uri.base.host') . $this->getRouteUri($route); return $this->app->get('uri.base.host') . $this->getRouteUri($route);
} }
/**
* Get form Token
*
* @return string
*/
public function getToken(): string
{
if ($this->session instanceof SessionInterface)
{
return $this->session->getToken();
}
return '';
}
/**
* Shorten a string
*
* @input string The you would like to shorten
*
* @returns string on success
*
* @since 1.0.0
*/
public function shortenString($string, $length = 100)
{
if (is_string($string) && strlen($string) > $length)
{
$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));
return trim($newString) . '...';
}
return $string;
}
/**
* Get any messages in the queue
*
* @return array
*/
public function getMessageQueue(): array
{
if (method_exists($this->app, 'getMessageQueue'))
{
return $this->app->getMessageQueue(true);
}
return [];
}
/** /**
* Get the SRI attributes for an asset * Get the SRI attributes for an asset
* *

View File

@ -11,26 +11,17 @@
namespace Octoleo\CMS\Service; namespace Octoleo\CMS\Service;
use Joomla\Application\AbstractWebApplication; use Joomla\Application\AbstractWebApplication;
use Joomla\Application\Controller\ContainerControllerResolver;
use Joomla\Application\Controller\ControllerResolverInterface; use Joomla\Application\Controller\ControllerResolverInterface;
use Joomla\Application\Web\WebClient; use Joomla\Application\Web\WebClient;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container; use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface; use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface; use Joomla\Event\DispatcherInterface;
use Joomla\Session\SessionInterface; use Joomla\Session\SessionInterface;
use Octoleo\CMS\Controller\DashboardController;
use Octoleo\CMS\Controller\LoginController;
use Octoleo\CMS\Controller\WrongCmsController;
use Octoleo\CMS\Model\DashboardModel;
use Octoleo\CMS\User\UserFactoryInterface; use Octoleo\CMS\User\UserFactoryInterface;
use Octoleo\CMS\View\Admin\DashboardHtmlView;
use Octoleo\CMS\Application\AdminApplication; use Octoleo\CMS\Application\AdminApplication;
use Joomla\Input\Input; use Joomla\Input\Input;
use Joomla\Renderer\RendererInterface;
use Joomla\Router\Router;
use Joomla\Router\RouterInterface; use Joomla\Router\RouterInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -60,138 +51,7 @@ class AdminApplicationProvider implements ServiceProviderInterface
/* /*
* Application Helpers and Dependencies * Application Helpers and Dependencies
*/ */
// This service cannot be protected as it is decorated when the debug bar is available
$container->alias(ContainerControllerResolver::class, ControllerResolverInterface::class)
->share(ControllerResolverInterface::class, [$this, 'getControllerResolverService']);
$container->share(WebClient::class, [$this, 'getWebClientService'], true); $container->share(WebClient::class, [$this, 'getWebClientService'], true);
// This service cannot be protected as it is decorated when the debug bar is available
$container->alias(RouterInterface::class, 'application.router')
->alias(Router::class, 'application.router')
->share('application.router', [$this, 'getApplicationRouterService']);
$container->share(Input::class, [$this, 'getInputClassService'], true);
/*
* MVC Layer
*/
// Controllers
$container->alias(DashboardController::class, 'controller.dashboard')
->share('controller.dashboard', [$this, 'getControllerDashboardService'], true);
$container->alias(LoginController::class, 'controller.login')
->share('controller.login', [$this, 'getControllerLoginService'], true);
$container->alias(WrongCmsController::class, 'controller.wrong.cms')
->share('controller.wrong.cms', [$this, 'getControllerWrongCmsService'], true);
// Models
$container->alias(DashboardModel::class, 'model.dashboard')
->share('model.dashboard', [$this, 'getModelDashboardService'], true);
// Views
$container->alias(DashboardHtmlView::class, 'view.dashboard.html')
->share('view.dashboard.html', [$this, 'getViewDashboardHtmlService'], true);
}
/**
* Get the `application.router` service
*
* @param Container $container The DI container.
*
* @return RouterInterface
*/
public function getApplicationRouterService(Container $container): RouterInterface
{
$router = new Router;
/*
* CMS Admin Panels
*/
$router->all(
'/index.php?dashboard=*',
DashboardController::class
);
$router->get(
'/*',
LoginController::class
);
return $router;
}
/**
* Get the `controller.login` service
*
* @param Container $container The DI container.
*
* @return LoginController
*/
public function getControllerLoginService(Container $container): LoginController
{
return new LoginController(
$container->get(RendererInterface::class),
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the `controller.dashboard` service
*
* @param Container $container The DI container.
*
* @return DashboardController
*/
public function getControllerDashboardService(Container $container): DashboardController
{
return new DashboardController(
$container->get(DashboardHtmlView::class),
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the `controller.wrong.cms` service
*
* @param Container $container The DI container.
*
* @return WrongCmsController
*/
public function getControllerWrongCmsService(Container $container): WrongCmsController
{
return new WrongCmsController(
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the Input class service
*
* @param Container $container The DI container.
*
* @return Input
*/
public function getInputClassService(Container $container): Input
{
return new Input($_REQUEST);
}
/**
* Get the `model.dashboard` service
*
* @param Container $container The DI container.
*
* @return DashboardModel
*/
public function getModelDashboardService(Container $container): DashboardModel
{
return new DashboardModel($container->get(DatabaseInterface::class));
} }
/** /**
@ -223,33 +83,6 @@ class AdminApplicationProvider implements ServiceProviderInterface
return $application; return $application;
} }
/**
* Get the controller resolver service
*
* @param Container $container The DI container.
*
* @return ControllerResolverInterface
*/
public function getControllerResolverService(Container $container): ControllerResolverInterface
{
return new ContainerControllerResolver($container);
}
/**
* Get the `view.page.html` service
*
* @param Container $container The DI container.
*
* @return DashboardHtmlView
*/
public function getViewDashboardHtmlService(Container $container): DashboardHtmlView
{
return new DashboardHtmlView(
$container->get('model.dashboard'),
$container->get('renderer')
);
}
/** /**
* Get the web client service * Get the web client service
* *

View File

@ -0,0 +1,485 @@
<?php
/**
* @package Octoleo CMS
*
* @created 14th 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\Service;
use Joomla\Application\Controller\ContainerControllerResolver;
use Joomla\Application\Controller\ControllerResolverInterface;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Octoleo\CMS\Controller\DashboardController;
use Octoleo\CMS\Controller\ItemsController;
use Octoleo\CMS\Controller\ItemController;
use Octoleo\CMS\Controller\LoginController;
use Octoleo\CMS\Controller\MenuController;
use Octoleo\CMS\Controller\MenusController;
use Octoleo\CMS\Controller\UserController;
use Octoleo\CMS\Controller\UsersController;
use Octoleo\CMS\Controller\WrongCmsController;
use Octoleo\CMS\Model\DashboardModel;
use Octoleo\CMS\Model\ItemsModel;
use Octoleo\CMS\Model\ItemModel;
use Octoleo\CMS\Model\MenusModel;
use Octoleo\CMS\Model\MenuModel;
use Octoleo\CMS\Model\UserModel;
use Octoleo\CMS\Model\UsersModel;
use Octoleo\CMS\View\Admin\DashboardHtmlView;
use Octoleo\CMS\View\Admin\ItemsHtmlView;
use Octoleo\CMS\View\Admin\ItemHtmlView;
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\Application\AdminApplication;
use Joomla\Input\Input;
use Joomla\Renderer\RendererInterface;
/**
* Model View Controller service provider
*/
class AdminMVCProvider implements ServiceProviderInterface
{
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*/
public function register(Container $container): void
{
// This service cannot be protected as it is decorated when the debug bar is available
$container->alias(ContainerControllerResolver::class, ControllerResolverInterface::class)
->share(ControllerResolverInterface::class, [$this, 'getControllerResolverService']);
// Controllers
$container->alias(DashboardController::class, 'controller.dashboard')
->share('controller.dashboard', [$this, 'getControllerDashboardService'], true);
$container->alias(UsersController::class, 'controller.users')
->share('controller.users', [$this, 'getControllerUsersService'], true);
$container->alias(UserController::class, 'controller.user')
->share('controller.user', [$this, 'getControllerUserService'], true);
$container->alias(MenusController::class, 'controller.menus')
->share('controller.menus', [$this, 'getControllerMenusService'], true);
$container->alias(MenuController::class, 'controller.menu')
->share('controller.menu', [$this, 'getControllerMenuService'], true);
$container->alias(ItemsController::class, 'controller.items')
->share('controller.items', [$this, 'getControllerItemsService'], true);
$container->alias(ItemController::class, 'controller.item')
->share('controller.item', [$this, 'getControllerItemService'], true);
$container->alias(LoginController::class, 'controller.login')
->share('controller.login', [$this, 'getControllerLoginService'], true);
$container->alias(WrongCmsController::class, 'controller.wrong.cms')
->share('controller.wrong.cms', [$this, 'getControllerWrongCmsService'], true);
// Models
$container->alias(DashboardModel::class, 'model.dashboard')
->share('model.dashboard', [$this, 'getModelDashboardService'], true);
$container->alias(UsersModel::class, 'model.users')
->share('model.users', [$this, 'getModelUsersService'], true);
$container->alias(UserModel::class, 'model.user')
->share('model.user', [$this, 'getModelUserService'], true);
$container->alias(MenusModel::class, 'model.menus')
->share('model.menus', [$this, 'getModelMenusService'], true);
$container->alias(MenuModel::class, 'model.menu')
->share('model.menu', [$this, 'getModelMenuService'], true);
$container->alias(ItemsModel::class, 'model.items')
->share('model.items', [$this, 'getModelItemsService'], true);
$container->alias(ItemModel::class, 'model.item')
->share('model.item', [$this, 'getModelItemService'], true);
// Views
$container->alias(DashboardHtmlView::class, 'view.dashboard.html')
->share('view.dashboard.html', [$this, 'getViewDashboardHtmlService'], true);
$container->alias(UsersHtmlView::class, 'view.users.html')
->share('view.users.html', [$this, 'getViewUsersHtmlService'], true);
$container->alias(UserHtmlView::class, 'view.user.html')
->share('view.user.html', [$this, 'getViewUserHtmlService'], true);
$container->alias(MenusHtmlView::class, 'view.menus.html')
->share('view.menus.html', [$this, 'getViewMenusHtmlService'], true);
$container->alias(MenuHtmlView::class, 'view.menu.html')
->share('view.menu.html', [$this, 'getViewMenuHtmlService'], true);
$container->alias(ItemsHtmlView::class, 'view.items.html')
->share('view.items.html', [$this, 'getViewItemsHtmlService'], true);
$container->alias(ItemHtmlView::class, 'view.item.html')
->share('view.item.html', [$this, 'getViewItemHtmlService'], true);
}
/**
* Get the controller resolver service
*
* @param Container $container The DI container.
*
* @return ControllerResolverInterface
*/
public function getControllerResolverService(Container $container): ControllerResolverInterface
{
return new ContainerControllerResolver($container);
}
/**
* Get the `controller.login` service
*
* @param Container $container The DI container.
*
* @return LoginController
*/
public function getControllerLoginService(Container $container): LoginController
{
return new LoginController(
$container->get(DashboardHtmlView::class),
$container->get(RendererInterface::class),
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the `controller.wrong.cms` service
*
* @param Container $container The DI container.
*
* @return WrongCmsController
*/
public function getControllerWrongCmsService(Container $container): WrongCmsController
{
return new WrongCmsController(
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the `controller.dashboard` service
*
* @param Container $container The DI container.
*
* @return DashboardController
*/
public function getControllerDashboardService(Container $container): DashboardController
{
return new DashboardController(
$container->get(DashboardHtmlView::class),
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the `model.dashboard` service
*
* @param Container $container The DI container.
*
* @return DashboardModel
*/
public function getModelDashboardService(Container $container): DashboardModel
{
return new DashboardModel($container->get(DatabaseInterface::class));
}
/**
* Get the `view.dashboard.html` service
*
* @param Container $container The DI container.
*
* @return DashboardHtmlView
*/
public function getViewDashboardHtmlService(Container $container): DashboardHtmlView
{
return new DashboardHtmlView(
$container->get('model.dashboard'),
$container->get('renderer')
);
}
/**
* Get the `controller.users` service
*
* @param Container $container The DI container.
*
* @return UsersController
*/
public function getControllerUsersService(Container $container): UsersController
{
return new UsersController(
$container->get(UsersHtmlView::class),
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the `model.users` service
*
* @param Container $container The DI container.
*
* @return UsersModel
*/
public function getModelUsersService(Container $container): UsersModel
{
return new UsersModel($container->get(DatabaseInterface::class));
}
/**
* Get the `view.users.html` service
*
* @param Container $container The DI container.
*
* @return UsersHtmlView
*/
public function getViewUsersHtmlService(Container $container): UsersHtmlView
{
return new UsersHtmlView(
$container->get('model.users'),
$container->get('renderer')
);
}
/**
* Get the `controller.user` service
*
* @param Container $container The DI container.
*
* @return UserController
*/
public function getControllerUserService(Container $container): UserController
{
return new UserController(
$container->get(UserModel::class),
$container->get(UserHtmlView::class),
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the `model.user` service
*
* @param Container $container The DI container.
*
* @return UserModel
*/
public function getModelUserService(Container $container): UserModel
{
return new UserModel($container->get(DatabaseInterface::class));
}
/**
* Get the `view.user.html` service
*
* @param Container $container The DI container.
*
* @return UserHtmlView
*/
public function getViewUserHtmlService(Container $container): UserHtmlView
{
return new UserHtmlView(
$container->get('model.user'),
$container->get('renderer')
);
}
/**
* Get the `controller.menus` service
*
* @param Container $container The DI container.
*
* @return MenusController
*/
public function getControllerMenusService(Container $container): MenusController
{
return new MenusController(
$container->get(MenusHtmlView::class),
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the `model.menus` service
*
* @param Container $container The DI container.
*
* @return MenusModel
*/
public function getModelMenusService(Container $container): MenusModel
{
return new MenusModel($container->get(DatabaseInterface::class));
}
/**
* Get the `view.menus.html` service
*
* @param Container $container The DI container.
*
* @return MenusHtmlView
*/
public function getViewMenusHtmlService(Container $container): MenusHtmlView
{
return new MenusHtmlView(
$container->get('model.menus'),
$container->get('renderer')
);
}
/**
* Get the `controller.menu` service
*
* @param Container $container The DI container.
*
* @return MenuController
*/
public function getControllerMenuService(Container $container): MenuController
{
return new MenuController(
$container->get(MenuModel::class),
$container->get(MenuHtmlView::class),
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the `model.menu` service
*
* @param Container $container The DI container.
*
* @return MenuModel
*/
public function getModelMenuService(Container $container): MenuModel
{
return new MenuModel($container->get(DatabaseInterface::class));
}
/**
* Get the `view.menu.html` service
*
* @param Container $container The DI container.
*
* @return MenuHtmlView
*/
public function getViewMenuHtmlService(Container $container): MenuHtmlView
{
return new MenuHtmlView(
$container->get('model.menu'),
$container->get('renderer')
);
}
/**
* Get the `controller.items` service
*
* @param Container $container The DI container.
*
* @return ItemsController
*/
public function getControllerItemsService(Container $container): ItemsController
{
return new ItemsController(
$container->get(ItemsHtmlView::class),
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the `model.items` service
*
* @param Container $container The DI container.
*
* @return ItemsModel
*/
public function getModelItemsService(Container $container): ItemsModel
{
return new ItemsModel($container->get(DatabaseInterface::class));
}
/**
* Get the `view.items.html` service
*
* @param Container $container The DI container.
*
* @return ItemsHtmlView
*/
public function getViewItemsHtmlService(Container $container): ItemsHtmlView
{
return new ItemsHtmlView(
$container->get('model.items'),
$container->get('renderer')
);
}
/**
* Get the `controller.item` service
*
* @param Container $container The DI container.
*
* @return ItemController
*/
public function getControllerItemService(Container $container): ItemController
{
return new ItemController(
$container->get(ItemModel::class),
$container->get(ItemHtmlView::class),
$container->get(Input::class),
$container->get(AdminApplication::class)
);
}
/**
* Get the `model.item` service
*
* @param Container $container The DI container.
*
* @return ItemModel
*/
public function getModelItemService(Container $container): ItemModel
{
return new ItemModel($container->get(DatabaseInterface::class));
}
/**
* Get the `view.item.html` service
*
* @param Container $container The DI container.
*
* @return ItemHtmlView
*/
public function getViewItemHtmlService(Container $container): ItemHtmlView
{
return new ItemHtmlView(
$container->get('model.item'),
$container->get('renderer')
);
}
}

View File

@ -0,0 +1,98 @@
<?php
/**
* @package Octoleo CMS
*
* @created 14th 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\Service;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Octoleo\CMS\Controller\DashboardController;
use Octoleo\CMS\Controller\LoginController;
use Octoleo\CMS\Controller\ItemsController;
use Octoleo\CMS\Controller\ItemController;
use Octoleo\CMS\Controller\MenuController;
use Octoleo\CMS\Controller\MenusController;
use Octoleo\CMS\Controller\UserController;
use Octoleo\CMS\Controller\UsersController;
use Joomla\Router\Router;
use Joomla\Router\RouterInterface;
/**
* Application service provider
* source: https://github.com/joomla/framework.joomla.org/blob/master/src/Service/ApplicationProvider.php
*/
class AdminRouterProvider implements ServiceProviderInterface
{
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*/
public function register(Container $container): void
{
// This service cannot be protected as it is decorated when the debug bar is available
$container->alias(RouterInterface::class, 'application.router')
->alias(Router::class, 'application.router')
->share('application.router', [$this, 'getApplicationRouterService']);
}
/**
* Get the `application.router` service
*
* @param Container $container The DI container.
*
* @return RouterInterface
*/
public function getApplicationRouterService(Container $container): RouterInterface
{
$router = new Router;
/**
* CMS Admin Panels
**/
$router->all(
'/index.php/dashboard',
DashboardController::class
);
$router->get(
'/index.php/users',
UsersController::class
);
$router->all(
'/index.php/user',
UserController::class
);
$router->get(
'/index.php/menus',
MenusController::class
);
$router->all(
'/index.php/menu',
MenuController::class
);
$router->get(
'/index.php/items',
ItemsController::class
);
$router->all(
'/index.php/item',
ItemController::class
);
$router->get(
'/*',
LoginController::class
);
return $router;
}
}

View File

@ -0,0 +1,314 @@
<?php
/**
* Joomla! Framework Website
*
* @copyright Copyright (C) 2014 - 2017 Open Source Matters, Inc. All rights reserved.
* @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License Version 2 or Later
*/
namespace Octoleo\CMS\Service;
use Joomla\Application\AbstractApplication;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Session\SessionInterface;
use Octoleo\CMS\Asset\MixPathPackage;
use Octoleo\CMS\Renderer\ApplicationContext;
use Octoleo\CMS\Renderer\FrameworkExtension;
use Octoleo\CMS\Renderer\FrameworkTwigRuntime;
use Joomla\Preload\PreloadManager;
use Joomla\Renderer\RendererInterface;
use Joomla\Renderer\TwigRenderer;
use Symfony\Component\Asset\Packages;
use Symfony\Component\Asset\PathPackage;
use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy;
use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;
use Twig\Cache\CacheInterface;
use Twig\Cache\FilesystemCache;
use Twig\Cache\NullCache;
use Twig\Environment;
use Twig\Extension\DebugExtension;
use Twig\Extension\ProfilerExtension;
use Twig\Loader\FilesystemLoader;
use Twig\Loader\LoaderInterface;
use Twig\Profiler\Profile;
use Twig\RuntimeLoader\ContainerRuntimeLoader;
/**
* Templating service provider
* source: https://github.com/joomla/framework.joomla.org/blob/master/src/Service/TemplatingProvider.php
*/
class AdminTemplatingProvider implements ServiceProviderInterface
{
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*/
public function register(Container $container): void
{
$container->alias(Packages::class, 'asset.packages')
->share('asset.packages', [$this, 'getAssetPackagesService'], true);
$container->alias(RendererInterface::class, 'renderer')
->alias(TwigRenderer::class, 'renderer')
->share('renderer', [$this, 'getRendererService'], true);
$container->alias(CacheInterface::class, 'twig.cache')
->alias(\Twig_CacheInterface::class, 'twig.cache')
->share('twig.cache', [$this, 'getTwigCacheService'], true);
$container->alias(Environment::class, 'twig.environment')
->alias(\Twig_Environment::class, 'twig.environment')
->share('twig.environment', [$this, 'getTwigEnvironmentService'], true);
$container->alias(DebugExtension::class, 'twig.extension.debug')
->alias(\Twig_Extension_Debug::class, 'twig.extension.debug')
->share('twig.extension.debug', [$this, 'getTwigExtensionDebugService'], true);
$container->alias(FrameworkExtension::class, 'twig.extension.framework')
->share('twig.extension.framework', [$this, 'getTwigExtensionFrameworkService'], true);
// This service cannot be protected as it is decorated when the debug bar is available
$container->alias(ProfilerExtension::class, 'twig.extension.profiler')
->alias(\Twig_Extension_Profiler::class, 'twig.extension.profiler')
->share('twig.extension.profiler', [$this, 'getTwigExtensionProfilerService']);
$container->alias(LoaderInterface::class, 'twig.loader')
->alias(\Twig_LoaderInterface::class, 'twig.loader')
->share('twig.loader', [$this, 'getTwigLoaderService'], true);
$container->alias(Profile::class, 'twig.profiler.profile')
->alias(\Twig_Profiler_Profile::class, 'twig.profiler.profile')
->share('twig.profiler.profile', [$this, 'getTwigProfilerProfileService'], true);
$container->alias(FrameworkTwigRuntime::class, 'twig.runtime.framework')
->share('twig.runtime.framework', [$this, 'getTwigRuntimeFrameworkService'], true);
$container->alias(ContainerRuntimeLoader::class, 'twig.runtime.loader')
->alias(\Twig_ContainerRuntimeLoader::class, 'twig.runtime.loader')
->share('twig.runtime.loader', [$this, 'getTwigRuntimeLoaderService'], true);
$this->tagTwigExtensions($container);
}
/**
* Get the `asset.packages` service
*
* @param Container $container The DI container.
*
* @return Packages
*/
public function getAssetPackagesService(Container $container): Packages
{
/** @var AbstractApplication $app */
$app = $container->get(AbstractApplication::class);
$context = new ApplicationContext($app);
$mediaPath = $app->get('uri.media.path', '/media/');
$defaultPackage = new PathPackage($mediaPath, new EmptyVersionStrategy, $context);
$mixStrategy = new MixPathPackage(
$defaultPackage,
$mediaPath,
new JsonManifestVersionStrategy(LPATH_ROOT . '/media/mix-manifest.json'),
$context
);
return new Packages(
$defaultPackage,
[
'mix' => $mixStrategy,
]
);
}
/**
* Get the `renderer` service
*
* @param Container $container The DI container.
*
* @return RendererInterface
*/
public function getRendererService(Container $container): RendererInterface
{
return new TwigRenderer($container->get('twig.environment'));
}
/**
* Get the `twig.cache` service
*
* @param Container $container The DI container.
*
* @return \Twig_CacheInterface
*/
public function getTwigCacheService(Container $container): \Twig_CacheInterface
{
/** @var \Joomla\Registry\Registry $config */
$config = $container->get('config');
// Pull down the renderer config
$cacheEnabled = $config->get('template.cache.enabled', false);
$cachePath = $config->get('template.cache.path', 'cache/twig');
$debug = $config->get('template.debug', false);
if ($debug === false && $cacheEnabled !== false)
{
return new FilesystemCache(LPATH_ROOT . '/' . $cachePath);
}
return new NullCache;
}
/**
* Get the `twig.environment` service
*
* @param Container $container The DI container.
*
* @return Environment
*/
public function getTwigEnvironmentService(Container $container): Environment
{
/** @var \Joomla\Registry\Registry $config */
$config = $container->get('config');
$debug = $config->get('template.debug', false);
$environment = new Environment(
$container->get('twig.loader'),
['debug' => $debug]
);
// Add the runtime loader
$environment->addRuntimeLoader($container->get('twig.runtime.loader'));
// Set up the environment's caching service
$environment->setCache($container->get('twig.cache'));
// Add the Twig extensions
$environment->setExtensions($container->getTagged('twig.extension'));
// Add a global tracking the debug states
$environment->addGlobal('appDebug', $config->get('debug', false));
$environment->addGlobal('fwDebug', $debug);
return $environment;
}
/**
* Get the `twig.extension.debug` service
*
* @param Container $container The DI container.
*
* @return DebugExtension
*/
public function getTwigExtensionDebugService(Container $container): DebugExtension
{
return new DebugExtension;
}
/**
* Get the `twig.extension.framework` service
*
* @param Container $container The DI container.
*
* @return FrameworkExtension
*/
public function getTwigExtensionFrameworkService(Container $container): FrameworkExtension
{
return new FrameworkExtension;
}
/**
* Get the `twig.extension.profiler` service
*
* @param Container $container The DI container.
*
* @return ProfilerExtension
*/
public function getTwigExtensionProfilerService(Container $container): ProfilerExtension
{
return new ProfilerExtension($container->get('twig.profiler.profile'));
}
/**
* Get the `twig.loader` service
*
* @param Container $container The DI container.
*
* @return \Twig_LoaderInterface
*/
public function getTwigLoaderService(Container $container): \Twig_LoaderInterface
{
return new FilesystemLoader([LPATH_TEMPLATES]);
}
/**
* Get the `twig.profiler.profile` service
*
* @param Container $container The DI container.
*
* @return Profile
*/
public function getTwigProfilerProfileService(Container $container): Profile
{
return new Profile;
}
/**
* Get the `twig.runtime.framework` service
*
* @param Container $container The DI container.
*
* @return FrameworkTwigRuntime
*/
public function getTwigRuntimeFrameworkService(Container $container): FrameworkTwigRuntime
{
return new FrameworkTwigRuntime(
$container->get(AbstractApplication::class),
$container->get(PreloadManager::class),
LPATH_ROOT . '/media/sri-manifest.json',
$container->get(SessionInterface::class)
);
}
/**
* Get the `twig.runtime.loader` service
*
* @param Container $container The DI container.
*
* @return ContainerRuntimeLoader
*/
public function getTwigRuntimeLoaderService(Container $container): ContainerRuntimeLoader
{
return new ContainerRuntimeLoader($container);
}
/**
* Tag services which are Twig extensions
*
* @param Container $container The DI container.
*
* @return void
*/
private function tagTwigExtensions(Container $container): void
{
/** @var \Joomla\Registry\Registry $config */
$config = $container->get('config');
$debug = $config->get('template.debug', false);
$twigExtensions = ['twig.extension.framework'];
if ($debug)
{
$twigExtensions[] = 'twig.extension.debug';
}
$container->tag('twig.extension', $twigExtensions);
}
}

View File

@ -43,14 +43,15 @@ class ConfigurationProvider implements ServiceProviderInterface
$this->config = new Registry(new \LConfig()); $this->config = new Registry(new \LConfig());
// Set database values based on config values // Set database values based on config values
$this->config->loadObject( (object) ['database' => [ $this->config->loadObject( (object) [
'driver' => 'mysql', 'database' => [
'host' => $this->config->get('host'), 'driver' => $this->config->get('dbdriver'),
'port' => $this->config->get('port', ''), 'host' => $this->config->get('host'),
'user' => $this->config->get('user'), 'port' => $this->config->get('port', ''),
'password' => $this->config->get('password'), 'user' => $this->config->get('user'),
'database' => $this->config->get('db'), 'password' => $this->config->get('password'),
'prefix' => $this->config->get('dbprefix') 'database' => $this->config->get('db'),
'prefix' => $this->config->get('dbprefix')
] ]
]); ]);
} }

View File

@ -0,0 +1,47 @@
<?php
/**
* @package Octoleo CMS
*
* @created 14th 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\Service;
use Joomla\DI\Container;
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
*/
class InputProvider implements ServiceProviderInterface
{
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*/
public function register(Container $container): void
{
$container->share(Input::class, [$this, 'getInputClassService'], true);
}
/**
* Get the Input class service
*
* @param Container $container The DI container.
*
* @return Input
*/
public function getInputClassService(Container $container): Input
{
return new Input($_REQUEST);
}
}

View File

@ -10,7 +10,6 @@
namespace Octoleo\CMS\Service; namespace Octoleo\CMS\Service;
use Joomla\Application\AbstractWebApplication;
use Joomla\Database\DatabaseInterface; use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container; use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface; use Joomla\DI\ServiceProviderInterface;

View File

@ -22,7 +22,9 @@ use Joomla\Event\DispatcherInterface;
use Octoleo\CMS\Controller\HomepageController; use Octoleo\CMS\Controller\HomepageController;
use Octoleo\CMS\Controller\WrongCmsController; use Octoleo\CMS\Controller\WrongCmsController;
use Octoleo\CMS\Controller\PageController; use Octoleo\CMS\Controller\PageController;
use Octoleo\CMS\Model\HomepageModel;
use Octoleo\CMS\Model\PageModel; use Octoleo\CMS\Model\PageModel;
use Octoleo\CMS\View\Page\HomepageHtmlView;
use Octoleo\CMS\View\Page\PageHtmlView; use Octoleo\CMS\View\Page\PageHtmlView;
use Octoleo\CMS\Application\SiteApplication; use Octoleo\CMS\Application\SiteApplication;
@ -71,8 +73,6 @@ class SiteApplicationProvider implements ServiceProviderInterface
->alias(Router::class, 'application.router') ->alias(Router::class, 'application.router')
->share('application.router', [$this, 'getApplicationRouterService']); ->share('application.router', [$this, 'getApplicationRouterService']);
$container->share(Input::class, [$this, 'getInputClassService'], true);
/* /*
* MVC Layer * MVC Layer
*/ */
@ -88,10 +88,16 @@ class SiteApplicationProvider implements ServiceProviderInterface
->share('controller.wrong.cms', [$this, 'getControllerWrongCmsService'], true); ->share('controller.wrong.cms', [$this, 'getControllerWrongCmsService'], true);
// Models // Models
$container->alias(HomepageModel::class, 'model.homepage')
->share('model.homepage', [$this, 'getModelHomepageService'], true);
$container->alias(PageModel::class, 'model.page') $container->alias(PageModel::class, 'model.page')
->share('model.page', [$this, 'getModelPageService'], true); ->share('model.page', [$this, 'getModelPageService'], true);
// Views // Views
$container->alias(HomepageHtmlView::class, 'view.homepage.html')
->share('view.homepage.html', [$this, 'getViewHomepageHtmlService'], true);
$container->alias(PageHtmlView::class, 'view.page.html') $container->alias(PageHtmlView::class, 'view.page.html')
->share('view.page.html', [$this, 'getViewPageHtmlService'], true); ->share('view.page.html', [$this, 'getViewPageHtmlService'], true);
} }
@ -135,16 +141,6 @@ class SiteApplicationProvider implements ServiceProviderInterface
PageController::class PageController::class
); );
$router->get(
'/:view',
PageController::class
);
$router->get(
'/:view/:details',
PageController::class
);
return $router; return $router;
} }
@ -158,7 +154,7 @@ class SiteApplicationProvider implements ServiceProviderInterface
public function getControllerHomepageService(Container $container): HomepageController public function getControllerHomepageService(Container $container): HomepageController
{ {
return new HomepageController( return new HomepageController(
$container->get(RendererInterface::class), $container->get(HomepageHtmlView::class),
$container->get(Input::class), $container->get(Input::class),
$container->get(SiteApplication::class) $container->get(SiteApplication::class)
); );
@ -196,15 +192,15 @@ class SiteApplicationProvider implements ServiceProviderInterface
} }
/** /**
* Get the Input class service * Get the `model.homepage` service
* *
* @param Container $container The DI container. * @param Container $container The DI container.
* *
* @return Input * @return HomepageModel
*/ */
public function getInputClassService(Container $container): Input public function getModelHomepageService(Container $container): HomepageModel
{ {
return new Input($_REQUEST); return new HomepageModel($container->get(DatabaseInterface::class));
} }
/** /**
@ -257,6 +253,21 @@ class SiteApplicationProvider implements ServiceProviderInterface
return new ContainerControllerResolver($container); 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 * Get the `view.page.html` service
* *

View File

@ -11,6 +11,7 @@ namespace Octoleo\CMS\Service;
use Joomla\Application\AbstractApplication; use Joomla\Application\AbstractApplication;
use Joomla\DI\Container; use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface; use Joomla\DI\ServiceProviderInterface;
use Joomla\Session\SessionInterface;
use Octoleo\CMS\Asset\MixPathPackage; use Octoleo\CMS\Asset\MixPathPackage;
use Octoleo\CMS\Renderer\ApplicationContext; use Octoleo\CMS\Renderer\ApplicationContext;
use Octoleo\CMS\Renderer\FrameworkExtension; use Octoleo\CMS\Renderer\FrameworkExtension;
@ -37,7 +38,7 @@ use Twig\RuntimeLoader\ContainerRuntimeLoader;
* Templating service provider * Templating service provider
* source: https://github.com/joomla/framework.joomla.org/blob/master/src/Service/TemplatingProvider.php * source: https://github.com/joomla/framework.joomla.org/blob/master/src/Service/TemplatingProvider.php
*/ */
class TemplatingProvider implements ServiceProviderInterface class SiteTemplatingProvider implements ServiceProviderInterface
{ {
/** /**
* Registers the service provider with a DI container. * Registers the service provider with a DI container.

View File

@ -79,6 +79,43 @@ class UserFactory implements UserFactoryInterface
$this->secure = ($secure) ?: new BCryptHandler(); $this->secure = ($secure) ?: new BCryptHandler();
} }
/**
* 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
{
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->loadUserById($id);
}
/** /**
* Method to get an instance of a user for the given id. * Method to get an instance of a user for the given id.
* *
@ -121,10 +158,12 @@ class UserFactory implements UserFactoryInterface
/** /**
* Check if user is active * Check if user is active
* *
* @param bool $purge
*
* @return bool * @return bool
* @throws \Exception * @throws \Exception
*/ */
public function active(): bool public function active(?bool $purge = true): bool
{ {
/** @var \Octoleo\CMS\Application\AdminApplication $application */ /** @var \Octoleo\CMS\Application\AdminApplication $application */
$application = Factory::getApplication(); $application = Factory::getApplication();
@ -148,30 +187,68 @@ class UserFactory implements UserFactoryInterface
// if user above 0 the active // if user above 0 the active
if ($active->guest == 0 && $active->userid > 0) if ($active->guest == 0 && $active->userid > 0)
{ {
return true; // get user
$user = $this->loadUserById($active->userid);
// check if user has access
$blocked = $user->get('block', 1);
if ($blocked == 0)
{
return true;
}
} }
// Purge the session if ($purge)
$query = $this->db->getQuery(true)
->delete($this->db->quoteName('#__session'))
->where($this->db->quoteName('session_id') . ' = :sessionid')
->bind(':sessionid', $sessionId);
try
{ {
$this->db->setQuery($query)->execute(); // Purge the session
} $query = $this->db->getQuery(true)
catch (\RuntimeException $e) ->delete($this->db->quoteName('#__session'))
{ ->where($this->db->quoteName('session_id') . ' = :sessionid')
// The old session is already invalidated, don't let this block logging in ->bind(':sessionid', $sessionId);
} try
{
$this->db->setQuery($query)->execute();
}
catch (\RuntimeException $e)
{
// The old session is already invalidated, don't let this block logging in
}
// destroy session // destroy session
$session->destroy(); $session->destroy();
}
// very basic for now.... // very basic for now....
return false; return false;
} }
/**
* Check if we have users
*
* @return bool true if we have
*/
public function has(): bool
{
try
{
$found = $this->db->setQuery(
$this->db->getQuery(true)
->select($this->db->quoteName('id'))
->from($this->db->quoteName('#__users'))
->setLimit(1)
)->loadResult();
}
catch (\RuntimeException $exception)
{
return false;
}
if ($found > 0)
{
return true;
}
return false;
}
/** /**
* Check if a user exist based on give key value pair * Check if a user exist based on give key value pair
* *
@ -242,9 +319,10 @@ class UserFactory implements UserFactoryInterface
} }
// If loadUserByUsername returned an error, then pass it back. // If loadUserByUsername returned an error, then pass it back.
if ($user->get('block', true)) $blocked = $user->get('block', 1);
if ($blocked == 1)
{ {
$application->enqueueMessage('Login failure, user is blocked.', 'Warning'); $application->enqueueMessage('Login failure, user is blocked. Contact your system administrator.', 'Warning');
return false; return false;
} }
@ -287,8 +365,8 @@ class UserFactory implements UserFactoryInterface
// The old session is already invalidated, don't let this block logging in // The old session is already invalidated, don't let this block logging in
} }
// destroy session // close session
$session->destroy(); $session->close();
// very basic for now.... // very basic for now....
return true; return true;
@ -388,7 +466,16 @@ class UserFactory implements UserFactoryInterface
// set other defaults for now // set other defaults for now
$user['sendEmail'] = 1; $user['sendEmail'] = 1;
$user['block'] = 0; // all auto created accounts are blocked (and require admin activation) except for first account
if ($this->has())
{
$user['block'] = 1;
}
else
{
$user['block'] = 0;
}
// there are no params at this stage
$user['params'] = ''; $user['params'] = '';
$insert = (object) $user; $insert = (object) $user;
@ -405,12 +492,16 @@ class UserFactory implements UserFactoryInterface
return false; return false;
} }
// only set session if success // only set session if success and not blocked
if ($result) if ($result && $user['block'] == 0)
{ {
$user['id'] = $this->db->insertid(); $user['id'] = $this->db->insertid();
return $this->setUserSession($application, $user); return $this->setUserSession($application, $user);
} }
elseif ($result)
{
$application->enqueueMessage('You account has been created, an administrator will active it shortly.', 'success');
}
} }
return false; return false;
} }
@ -476,6 +567,9 @@ class UserFactory implements UserFactoryInterface
$manager->createOrUpdateRecord($session, $this->loadUserById($user['id'])); $manager->createOrUpdateRecord($session, $this->loadUserById($user['id']));
// show a success message
$application->enqueueMessage('Welcome ' . $user['name'] . ', you have successfully lodged in!', 'Success');
return true; return true;
} }
} }

View File

@ -31,19 +31,19 @@ class DashboardHtmlView extends HtmlView
* *
* @var DashboardModel * @var DashboardModel
*/ */
private $dashboardModel; private $model;
/** /**
* Instantiate the view. * Instantiate the view.
* *
* @param DashboardModel $dashboardModel The page model object. * @param DashboardModel $model The page model object.
* @param RendererInterface $renderer The renderer object. * @param RendererInterface $renderer The renderer object.
*/ */
public function __construct(DashboardModel $dashboardModel, RendererInterface $renderer) public function __construct(DashboardModel $model, RendererInterface $renderer)
{ {
parent::__construct($renderer); parent::__construct($renderer);
$this->dashboardModel = $dashboardModel; $this->model = $model;
} }
/** /**
@ -51,7 +51,7 @@ class DashboardHtmlView extends HtmlView
* *
* @return string The rendered view * @return string The rendered view
*/ */
public function render() public function render(): string
{ {
$this->setData(['page' => $this->id]); $this->setData(['page' => $this->id]);
return parent::render(); return parent::render();
@ -66,7 +66,7 @@ class DashboardHtmlView extends HtmlView
*/ */
public function setActiveDashboard(string $name): void public function setActiveDashboard(string $name): void
{ {
$this->setLayout($this->dashboardModel->getDashboard($name)); $this->setLayout($this->model->getDashboard($name));
} }
/** /**

View File

@ -0,0 +1,84 @@
<?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\Admin;
use Octoleo\CMS\Model\ItemModel;
use Joomla\Renderer\RendererInterface;
use Joomla\View\HtmlView;
/**
* Dashboard HTML view class for the application
*/
class ItemHtmlView extends HtmlView
{
/**
* The id of item/user/menu
*
* @var int
*/
private $id;
/**
* The item model object.
*
* @var ItemModel
*/
private $model;
/**
* Instantiate the view.
*
* @param ItemModel $model The page model object.
* @param RendererInterface $renderer The renderer object.
*/
public function __construct(ItemModel $model, RendererInterface $renderer)
{
parent::__construct($renderer);
$this->model = $model;
}
/**
* Method to render the view
*
* @return string The rendered view
* @throws \Exception
*/
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 page details
*
* @param int $id The selected item
*
* @return void
*/
public function setActiveId(int $id): void
{
$this->id = $id;
}
}

View File

@ -0,0 +1,64 @@
<?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\Admin;
use Octoleo\CMS\Model\ItemsModel;
use Joomla\Renderer\RendererInterface;
use Joomla\View\HtmlView;
/**
* Dashboard HTML view class for the application
*/
class ItemsHtmlView extends HtmlView
{
/**
* The item model object.
*
* @var ItemsModel
*/
private $model;
/**
* Instantiate the view.
*
* @param ItemsModel $model The page model object.
* @param RendererInterface $renderer The renderer object.
*/
public function __construct(ItemsModel $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

@ -0,0 +1,87 @@
<?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\Admin;
use Octoleo\CMS\Model\MenuModel;
use Joomla\Renderer\RendererInterface;
use Joomla\View\HtmlView;
/**
* Dashboard HTML view class for the application
*/
class MenuHtmlView extends HtmlView
{
/**
* The id of item/user/menu
*
* @var int
*/
private $id;
/**
* The item model object.
*
* @var MenuModel
*/
private $model;
/**
* Instantiate the view.
*
* @param MenuModel $model The page model object.
* @param RendererInterface $renderer The renderer object.
*/
public function __construct(MenuModel $model, RendererInterface $renderer)
{
parent::__construct($renderer);
$this->model = $model;
}
/**
* Method to render the view
*
* @return string The rendered view
* @throws \Exception
*/
public function render(): string
{
$this->setData([
'form' => $this->model->getItem($this->id),
'items' => $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));
}
/**
* Set the active page details
*
* @param int $id The selected item/user/menu
*
* @return void
*/
public function setActiveId(int $id): void
{
$this->id = $id;
}
}

View File

@ -0,0 +1,64 @@
<?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\Admin;
use Octoleo\CMS\Model\MenusModel;
use Joomla\Renderer\RendererInterface;
use Joomla\View\HtmlView;
/**
* Dashboard HTML view class for the application
*/
class MenusHtmlView extends HtmlView
{
/**
* The item model object.
*
* @var MenusModel
*/
private $model;
/**
* Instantiate the view.
*
* @param MenusModel $model The page model object.
* @param RendererInterface $renderer The renderer object.
*/
public function __construct(MenusModel $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

@ -0,0 +1,83 @@
<?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\Admin;
use Octoleo\CMS\Model\UserModel;
use Joomla\Renderer\RendererInterface;
use Joomla\View\HtmlView;
/**
* Dashboard HTML view class for the application
*/
class UserHtmlView extends HtmlView
{
/**
* The id of user
*
* @var int
*/
private $id;
/**
* The User model object.
*
* @var UserModel
*/
private $model;
/**
* Instantiate the view.
*
* @param UserModel $model The page model object.
* @param RendererInterface $renderer The renderer object.
*/
public function __construct(UserModel $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 page details
*
* @param int $id The selected user
*
* @return void
*/
public function setActiveId(int $id): void
{
$this->id = $id;
}
}

View File

@ -0,0 +1,64 @@
<?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\Admin;
use Octoleo\CMS\Model\UsersModel;
use Joomla\Renderer\RendererInterface;
use Joomla\View\HtmlView;
/**
* Dashboard HTML view class for the application
*/
class UsersHtmlView extends HtmlView
{
/**
* The item model object.
*
* @var UsersModel
*/
private $model;
/**
* Instantiate the view.
*
* @param UsersModel $model The page model object.
* @param RendererInterface $renderer The renderer object.
*/
public function __construct(UsersModel $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

@ -0,0 +1,133 @@
<?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

@ -10,6 +10,8 @@
namespace Octoleo\CMS\View\Page; namespace Octoleo\CMS\View\Page;
use Octoleo\CMS\Model\MenuInterface;
use Octoleo\CMS\Model\PageInterface;
use Octoleo\CMS\Model\PageModel; use Octoleo\CMS\Model\PageModel;
use Joomla\Renderer\RendererInterface; use Joomla\Renderer\RendererInterface;
use Joomla\View\HtmlView; use Joomla\View\HtmlView;
@ -38,19 +40,19 @@ class PageHtmlView extends HtmlView
* *
* @var PageModel * @var PageModel
*/ */
private $pageModel; private $model;
/** /**
* Instantiate the view. * Instantiate the view.
* *
* @param PageModel $pageModel The page model object. * @param PageModel $model The page model object.
* @param RendererInterface $renderer The renderer object. * @param RendererInterface $renderer The renderer object.
*/ */
public function __construct(PageModel $pageModel, RendererInterface $renderer) public function __construct(PageModel $model, RendererInterface $renderer)
{ {
parent::__construct($renderer); parent::__construct($renderer);
$this->pageModel = $pageModel; $this->model = $model;
} }
/** /**
@ -60,10 +62,45 @@ class PageHtmlView extends HtmlView
*/ */
public function render() public function render()
{ {
// set the defaults
$title = 'Error';
$body = '';
$menus = [];
// get the page data
if ($this->model instanceof PageInterface)
{
// get the page data
$data = $this->model->getPageItemByPath($this->page);
if (isset($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;
}
else
{
throw new \RuntimeException('Trying to access a page that does not exit (' . $this->page . ')', 404);
}
}
// set the menus if possible
if ($this->model instanceof MenuInterface)
{
$menus = $this->model->getMenus();
}
$this->setData( $this->setData(
[ [
'page' => $this->pageModel->getPage($this->page), 'main_menu' => $menus,
'details' => $this->pageModel->getDetails($this->details) 'title' => $title,
'body' => $body
] ]
); );
@ -81,16 +118,4 @@ class PageHtmlView extends HtmlView
{ {
$this->page = $page; $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

@ -5,15 +5,16 @@
{% block content %} {% block content %}
<div class="uk-container uk-margin"> <div class="uk-container uk-margin">
<h1 class="uk-article-title">Octoleo CMS Dashboard</h1> <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 class="uk-grid-small uk-child-width-expand@s uk-text-center" uk-grid>
<div> <div>
<div class="uk-card uk-card-default uk-card-body"><a href="{{ url() }}index.php?dashboard=users">Users</a></div> <div class="uk-card uk-card-default uk-card-body"><a href="{{ route() }}index.php/users">Users</a></div>
</div> </div>
<div> <div>
<div class="uk-card uk-card-default uk-card-body"><a href="{{ url() }}index.php?dashboard=menus">Menus</a></div> <div class="uk-card uk-card-default uk-card-body"><a href="{{ route() }}index.php/menus">Menus</a></div>
</div> </div>
<div> <div>
<div class="uk-card uk-card-default uk-card-body"><a href="{{ url() }}index.php?dashboard=items">Items</a></div> <div class="uk-card uk-card-default uk-card-body"><a href="{{ route() }}index.php/items">Items</a></div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,10 +8,12 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
<![endif]--> <![endif]-->
<!-- UIkit CSS --> <!-- UIkit CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.13.7/dist/css/uikit.min.css" /> <link rel="stylesheet" href="{{ route() }}media/css/uikit.min.css" />
{% block headCSSLinks %}{% endblock %}
<!-- UIkit JS --> <!-- UIkit JS -->
<script src="https://cdn.jsdelivr.net/npm/uikit@3.13.7/dist/js/uikit.min.js"></script> <script src="{{ route() }}media/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit@3.13.7/dist/js/uikit-icons.min.js"></script> <script src="{{ route() }}media/js/uikit-icons.min.js"></script>
{% block headJavaScriptLinks %}{% endblock %}
<!-- End for Responsive --> <!-- End for Responsive -->
<!-- For SEO --> <!-- For SEO -->

View File

@ -2,7 +2,7 @@
{% block body %} {% block body %}
<body id="go"> <body id="go">
{% block bodyNavigation %}{% import 'nav.twig' as macros %}{{ macros.load_admin_navbar() }}{% endblock %} {% block bodyNavigation %}{{ block("bodyNavigation", "nav.twig") }}{% endblock %}
<main> <main>
{% block content %}{% endblock %} {% block content %}{% endblock %}
</main> </main>

View File

@ -0,0 +1,111 @@
{% extends "index.twig" %}
{% block title %}Edit Item{% endblock %}
{% block headJavaScriptLinks %}
<script src="https://cdn.ckeditor.com/ckeditor5/34.0.0/classic/ckeditor.js"></script>
{% endblock %}
{% block content %}
<div class="uk-container uk-margin">
{{ block("messages_queue", "message_queue.twig") }}
<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>
<div>
<label class="uk-form-label">Title</label>
<div class="uk-form-controls">
<input name="title" class="uk-input uk-width-1-1" type="text" placeholder="Add the item title here..." value="{{ form.title|default('') }}">
</div>
</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="#">Meta Details</a></li>
<li><a href="#">Publish</a></li>
</ul>
<ul class="uk-switcher switcher-container uk-margin">
<li>
<div class="uk-margin">
<label class="uk-form-label">Content</label>
<div class="uk-form-controls">
<textarea id="item-text" name="fulltext" class="uk-textarea" type="text" placeholder="Enter your item details here..." >{{ form.fulltext|default('') }}</textarea>
</div>
</div>
</li>
<li>
<div class="uk-child-width-1-2 uk-margin" uk-grid>
<div>
<label class="uk-form-label">Metakey</label>
<div class="uk-form-controls">
<input name="metakey" class="uk-input uk-width-1-1" type="text" value="{{ form.metakey|default('') }}">
</div>
</div>
<div>
<label class="uk-form-label">Metadesc</label>
<div class="uk-form-controls">
<input name="metadesc" class="uk-input uk-width-1-1" type="text" value="{{ form.metadesc|default('') }}">
</div>
</div>
</div>
<div class="uk-margin">
<label class="uk-form-label">metadata</label>
<div class="uk-form-controls">
<textarea name="metadata" class="uk-textarea" type="text" rows="10">{{ form.metadata|default('') }}</textarea>
</div>
</div>
</li>
<li>
<div class="uk-child-width-1-2 uk-margin" uk-grid>
<div>
<label class="uk-form-label">Created by Alias</label>
<div class="uk-form-controls">
<input name="created_by_alias" class="uk-input uk-width-1-1" type="text" value="{{ form.created_by_alias|default('') }}"/>
</div>
</div>
<div>
<label class="uk-form-label">Status</label>
<div class="uk-form-controls">
<select name="state" class="uk-select">
<option value="1">Published</option>
<option value="2" {% if form.state == 2 %}selected{% endif %}>Archived</option>
<option value="-1" {% if form.state == -1 %}selected{% endif %}>Trashed</option>
<option value="" {% if form.state == 0 %}selected{% endif %}>Unpublished</option>
</select>
</div>
</div>
<div>
<label class="uk-form-label">Published Up</label>
<div class="uk-form-controls">
<input name="publish_up" class="uk-input uk-width-1-1" type="datetime-local"
value="{{ form.publish_up|default(form.today_date|default('')) }}"/>
</div>
</div>
<div>
<label class="uk-form-label">Published Down</label>
<div class="uk-form-controls">
<input name="publish_down" class="uk-input uk-width-1-1" type="datetime-local"
value="{{ form.publish_down|default('') }}"/>
</div>
</div>
</div>
</li>
</ul>
<input type="hidden" name="item_id" value="{{ form.id|default(0) }}">
<input type="hidden" name="{{ token() }}" value="1">
</form>
</div>
<script>
ClassicEditor.create(document.querySelector(`#item-text`), {
toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote' ],
heading: {
options: [
{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' }
]
}
}).catch(error => { console.error(error);});
</script>
{% endblock %}

View File

@ -5,48 +5,61 @@
{% block content %} {% block content %}
<div class="uk-container uk-margin"> <div class="uk-container uk-margin">
<h1 class="uk-article-title">Items</h1> <h1 class="uk-article-title">Items</h1>
<a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=item">Create</a> {{ block("messages_queue", "message_queue.twig") }}
<a class="uk-button uk-button-default" href="{{ route() }}index.php/item?task=create">Create</a>
{% if list %}
<table class="uk-table uk-table-justify uk-table-divider"> <table class="uk-table uk-table-justify uk-table-divider">
<thead> <thead>
<tr> <tr>
<th class="uk-width-small">Table Heading</th> <th class="uk-width-small">Action</th>
<th>Table Heading</th> <th class="uk-width-small">Title</th>
<th>Table Heading</th> <th>Content</th>
<th class="uk-width-small">State</th>
<th>ID</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for item in list %}
<tr> <tr>
<td>Table Data</td> <td>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> <div class="uk-button-group uk-width-1-1">
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=item&id=1">Edit</a></td> <a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/item?id={{ item.id }}&task=edit">Edit</a>
</tr> <button class="uk-button uk-button-default uk-button-small" onclick="confirmDeletion('{{ item.title }}', {{ item.id }});">Delete</button>
<tr> </div>
<td>Table Data</td> </td>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> <td>{{ item.title }}</td>
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=item&id=2">Edit</a></td> <td>{% if item.introtext %}{{ shorten_string(item.introtext|striptags) }} {% endif %}{{ shorten_string(item.fulltext|striptags) }}</td>
</tr> <td>
<tr> {% if item.state == 1 %}
<td>Table Data</td> <button class="uk-button uk-button-primary uk-button-small uk-width-1-1">Published</button>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> {% elseif item.state == 2 %}
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=item&id=3">Edit</a></td> <button class="uk-button uk-button-small uk-width-1-1" disabled>Archived</button>
</tr> {% elseif item.state == -1 %}
<tr> <button class="uk-button uk-button-danger uk-button-small uk-width-1-1">Trashed</button>
<td>Table Data</td> {% elseif item.state == 0 %}
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> <button class="uk-button uk-button-default uk-button-small uk-width-1-1">Unpublished</button>
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=item&id=4">Edit</a></td> {% else %}
</tr> Error
<tr> {% endif %}
<td>Table Data</td> </td>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> <td>{{ item.id }}</td>
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=item&id=5">Edit</a></td>
</tr>
<tr>
<td>Table Data</td>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=item&id=6">Edit</a></td>
</tr> </tr>
{% endfor %}
</tbody> </tbody>
</table> </table>
{% else %}
<div class="uk-alert-primary" uk-alert>
<p>There has no items been found, click create to add some.</p>
</div>
{% endif %}
</div> </div>
<!-- Source: https://getuikit.com/docs/table --> <script>
function confirmDeletion(title, id){
UIkit.modal.confirm('You are about to permanently delete <b>' + title + '</b>?').then(function () {
window.open("{{ route() }}index.php/item?id=" + id + "&task=delete", "_self")
}, function () {
// we do nothing ;)
});
}
</script>
{% endblock %} {% endblock %}

View File

@ -6,13 +6,15 @@
<div class="uk-height-1-1 uk-background-cover uk-overflow-hidden uk-flex" <div class="uk-height-1-1 uk-background-cover uk-overflow-hidden uk-flex"
style="background-color: #fff; background-image: url('https://source.unsplash.com/random/1920x800/?yachts'); min-height: 500px; height: 100%"> style="background-color: #fff; background-image: url('https://source.unsplash.com/random/1920x800/?yachts'); min-height: 500px; height: 100%">
<div class="uk-width-1-2@m uk-text-center uk-margin-auto uk-margin-auto-vertical uk-overlay uk-overlay-default"> <div class="uk-width-1-2@m uk-text-center uk-margin-auto uk-margin-auto-vertical uk-overlay uk-overlay-default">
{{ block("messages_queue", "message_queue.twig") }}
<h1>Login Here</h1> <h1>Login Here</h1>
<form action="{{ url() }}index.php?dashboard=access" method="post"> <form action="{{ route() }}index.php/dashboard?task=access" method="post">
<input name="username" class="uk-input uk-width-1-2" type="text" placeholder="Username"> <input name="username" class="uk-input uk-width-1-2" type="text" placeholder="Username">
<input name="password" class="uk-input uk-width-1-2" type="password" placeholder="*************"> <input name="password" class="uk-input uk-width-1-2" type="password" placeholder="*************">
<input type="submit" class="uk-button uk-button-primary uk-width-1-2" value="Login"/> <input type="submit" class="uk-button uk-button-primary uk-width-1-2" value="Login"/>
<a class="uk-button uk-width-1-2" href="{{ url() }}?account=signup">Create Account</a> <input type="hidden" name="{{ token() }}" value="1">
</form> </form>
<a class="uk-button uk-width-1-2" href="{{ route() }}index.php?account=signup">Create Account</a>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,111 @@
{% extends "index.twig" %}
{% block title %}Edit Menu{% endblock %}
{% block content %}
<div class="uk-container uk-margin">
{{ block("messages_queue", "message_queue.twig") }}
{% 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>
<div class="uk-child-width-1-2 uk-margin" uk-grid>
<div>
<label class="uk-form-label">Name (title)</label>
<div class="uk-form-controls">
<input name="title" class="uk-input uk-width-1-1" type="text" placeholder="Add the menu title here..." value="{{ form.title|default('') }}">
</div>
</div>
<div>
<label class="uk-form-label">The computed path of the menu item based on the alias field.</label>
<div class="uk-form-controls">
<input name="path" class="uk-input uk-width-1-1" type="text" readonly value="{{ form.path|default('') }}">
</div>
</div>
</div>
<div class="uk-margin">
<label class="uk-form-label">Alias</label>
<div class="uk-form-controls">
<input name="alias" class="uk-input uk-width-1-1" type="text" placeholder="The alias computed from the title if not set" value="{{ form.alias|default('') }}">
</div>
</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="#">Publish</a></li>
</ul>
<ul class="uk-switcher switcher-container uk-margin">
<li>
<div class="uk-child-width-1-2 uk-margin" uk-grid>
<div>
<label class="uk-form-label">Item</label>
<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>
{% endfor %}
</select>
</div>
</div>
<div>
<label class="uk-form-label">Is this your home page menu item?</label>
<div class="uk-form-controls">
<select name="home" class="uk-select">
<option value="1">Yes</option>
<option value="0" {% if form.home == 0 %}selected{% endif %}>No</option>
</select>
</div>
</div>
<div>
<label class="uk-form-label">Position of this menu item?</label>
<div class="uk-form-controls">
<select name="position" class="uk-select">
<option value="center" {% if form.params.position == 'center' %}selected{% endif %}>Center</option>
<option value="right" {% if form.params.position == 'right' %}selected{% endif %}>Right</option>
</select>
</div>
</div>
</div>
</li>
<li>
<div class="uk-child-width-1-2 uk-margin" uk-grid>
<div>
<label class="uk-form-label">Published Up</label>
<div class="uk-form-controls">
<input name="publish_up" class="uk-input uk-width-1-1" type="datetime-local"
value="{{ form.publish_up|default(form.today_date|default('')) }}"/>
</div>
</div>
<div>
<label class="uk-form-label">Published Down</label>
<div class="uk-form-controls">
<input name="publish_down" class="uk-input uk-width-1-1" type="datetime-local"
value="{{ form.publish_down|default('') }}"/>
</div>
</div>
</div>
<div>
<label class="uk-form-label">Status</label>
<div class="uk-form-controls">
<select name="published" class="uk-select">
<option value="1">Published</option>
<option value="2" {% if form.published == 2 %}selected{% endif %}>Archived</option>
<option value="-1" {% if form.published == -1 %}selected{% endif %}>Trashed</option>
<option value="" {% if form.published == 0 %}selected{% endif %}>Unpublished</option>
</select>
</div>
</div>
</li>
</ul>
<input type="hidden" name="menu_id" value="{{ form.id|default(0) }}">
<input type="hidden" name="{{ token() }}" value="1">
</form>
{% else %}
<a class="uk-button uk-button-default" href="{{ route() }}index.php/item?task=create">Create</a>
<div class="uk-alert-primary" uk-alert>
<p>There has no items been found, click create to add items.</p>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -1,50 +1,71 @@
{% extends "index.twig" %} {% extends "index.twig" %}
{% block title %}Menus{% endblock %}
{% block content %} {% block content %}
<div class="uk-container uk-margin"> <div class="uk-container uk-margin">
<h1 class="uk-article-title">Menus</h1> <h1 class="uk-article-title">Menus</h1>
<a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=menu">Create</a> {{ block("messages_queue", "message_queue.twig") }}
<a class="uk-button uk-button-default" href="{{ route() }}index.php/menu?task=create">Create</a>
{% if list %}
<table class="uk-table uk-table-justify uk-table-divider"> <table class="uk-table uk-table-justify uk-table-divider">
<thead> <thead>
<tr> <tr>
<th class="uk-width-small">Table Heading</th> <th class="uk-width-small">Action</th>
<th>Table Heading</th> <th class="uk-width-small">Title</th>
<th>Table Heading</th> <th>Item</th>
<th>Path</th>
<th class="uk-width-small">State</th>
<th>ID</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> {% for item in list %}
<td>Table Data</td> <tr>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> <td>
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=menu&id=1">Edit</a></td> <div class="uk-button-group uk-width-1-1">
</tr> <a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/menu?id={{ item.id }}&task=edit">Edit</a>
<tr> <button class="uk-button uk-button-default uk-button-small" onclick="confirmDeletion('{{ item.title }}', {{ item.id }});">Delete</button>
<td>Table Data</td> </div>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> </td>
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=menu&id=2">Edit</a></td> <td>{% if item.home == 1 %}<span uk-icon="icon: home"></span> {% endif %}{{ item.title }}</td>
</tr> <td>
<tr> <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">
<td>Table Data</td> {{ shorten_string(item.item_title, 10) }}
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> </a>
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=menu&id=3">Edit</a></td> </td>
</tr> <td>{{ item.path }}</td>
<tr> <td>
<td>Table Data</td> {% if item.published == 1 %}
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> <button class="uk-button uk-button-primary uk-button-small uk-width-1-1">Published</button>
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=menu&id=4">Edit</a></td> {% elseif item.published == 2 %}
</tr> <button class="uk-button uk-button-small uk-width-1-1" disabled>Archived</button>
<tr> {% elseif item.published == -1 %}
<td>Table Data</td> <button class="uk-button uk-button-danger uk-button-small uk-width-1-1">Trashed</button>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> {% elseif item.published == 0 %}
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=menu&id=5">Edit</a></td> <button class="uk-button uk-button-default uk-button-small uk-width-1-1">Unpublished</button>
</tr> {% else %}
<tr> Error
<td>Table Data</td> {% endif %}
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> </td>
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=menu&id=6">Edit</a></td> <td>{{ item.id }}</td>
</tr> </tr>
{% endfor %}
</tbody> </tbody>
</table> </table>
</div> {% else %}
<!-- Source: https://getuikit.com/docs/table --> <div class="uk-alert-primary" uk-alert>
<p>There has no menus been found, click create to add some.</p>
</div>
{% endif %}
</div>
<script>
function confirmDeletion(title, id){
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 () {
// we do nothing ;)
});
}
</script>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,20 @@
{% block messages_queue %}
{% set message_queue = message_queue() %}
{% if message_queue|length > 0 %}
{% for messages in message_queue|sort %}
{% if messages.type == 'error' %}
{% set messages_type = 'uk-alert-danger' %}
{% elseif messages.type == 'success' %}
{% set messages_type = 'uk-alert-success' %}
{% elseif messages.type == 'warning' %}
{% set messages_type = 'uk-alert-warning' %}
{% else %}
{% set messages_type = 'uk-alert-primary' %}
{% endif %}
<div class="{{ messages_type }}" uk-alert>
<a class="uk-alert-close" uk-close></a>
<p>{{ messages.message }}</p>
</div>
{% endfor %}
{% endif %}
{% endblock %}

View File

@ -1,19 +1,18 @@
{% macro load_admin_navbar() %} {% block bodyNavigation %}
<nav class="uk-navbar-container" uk-navbar> <nav class="uk-navbar-container" uk-navbar>
<div class="uk-navbar-left"> <div class="uk-navbar-left">
<ul class="uk-navbar-nav"> <ul class="uk-navbar-nav">
<li><a href="{{ url() }}index.php?dashboard">Dashboard</a></li> <li><a href="{{ route() }}index.php/dashboard">Dashboard</a></li>
<li><a href="{{ url() }}index.php?dashboard=users">Users</a></li> <li><a href="{{ route() }}index.php/users">Users</a></li>
<li><a href="{{ url() }}index.php?dashboard=menus">Menus</a></li> <li><a href="{{ route() }}index.php/menus">Menus</a></li>
<li><a href="{{ url() }}index.php?dashboard=items">Items</a></li> <li><a href="{{ route() }}index.php/items">Items</a></li>
</ul> </ul>
</div> </div>
<div class="uk-navbar-right"> <div class="uk-navbar-right">
<ul class="uk-navbar-nav"> <ul class="uk-navbar-nav">
<li class="uk-active"><a href="{{ url() }}index.php?dashboard=logout">Logout</a></li> <li class="uk-active"><a href="{{ route() }}index.php/dashboard?task=logout">Logout</a></li>
<li class="uk-active"><a href="/">Site</a></li> <li class="uk-active"><a href="/">Site</a></li>
</ul> </ul>
</div> </div>
</nav> </nav>
{% endmacro %} {% endblock %}
<!-- Source: https://getuikit.com/docs/navbar -->

View File

@ -6,16 +6,18 @@
<div class="uk-height-1-1 uk-background-cover uk-overflow-hidden uk-flex" <div class="uk-height-1-1 uk-background-cover uk-overflow-hidden uk-flex"
style="min-height: 500px; height: 100%"> style="min-height: 500px; height: 100%">
<div class="uk-width-1-2@m uk-text-center uk-margin-auto uk-margin-auto-vertical uk-overlay uk-overlay-default"> <div class="uk-width-1-2@m uk-text-center uk-margin-auto uk-margin-auto-vertical uk-overlay uk-overlay-default">
{{ block("messages_queue", "message_queue.twig") }}
<h1>Create Account</h1> <h1>Create Account</h1>
<form action="{{ url() }}index.php?dashboard=signup" method="post"> <form action="{{ route() }}index.php/dashboard?task=signup" method="post">
<input name="name" class="uk-input uk-width-1-2" type="text" placeholder="Name"> <input name="name" class="uk-input uk-width-1-2" type="text" placeholder="Name">
<input name="username" class="uk-input uk-width-1-2" type="text" placeholder="Username"> <input name="username" class="uk-input uk-width-1-2" type="text" placeholder="Username">
<input name="email" class="uk-input uk-width-1-2" type="text" placeholder="Email"> <input name="email" class="uk-input uk-width-1-2" type="text" placeholder="Email">
<input name="password" class="uk-input uk-width-1-2" type="password" placeholder="password"> <input name="password" class="uk-input uk-width-1-2" type="password" placeholder="password">
<input name="password2" class="uk-input uk-width-1-2" type="password" placeholder="confirm password"> <input name="password2" class="uk-input uk-width-1-2" type="password" placeholder="confirm password">
<input type="hidden" name="{{ token() }}" value="1">
<input type="submit" class="uk-button uk-button-primary uk-width-1-2" value="Create Account"/> <input type="submit" class="uk-button uk-button-primary uk-width-1-2" value="Create Account"/>
<a class="uk-button uk-width-1-2" href="{{ url() }}">Login</a>
</form> </form>
<a class="uk-button uk-width-1-2" href="{{ route() }}index.php">Login</a>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,70 @@
{% extends "index.twig" %}
{% block title %}Edit User{% endblock %}
{% block content %}
<div class="uk-container uk-margin">
{{ block("messages_queue", "message_queue.twig") }}
<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>
<div>
<label class="uk-form-label">Name</label>
<div class="uk-form-controls">
<input name="name" class="uk-input uk-width-1-1" type="text" placeholder="User Full Name" value="{{ form.name|default('') }}">
</div>
</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>
</ul>
<ul class="uk-switcher switcher-container uk-margin">
<li>
<div class="uk-child-width-1-2 uk-margin" uk-grid>
<div>
<label class="uk-form-label">Email</label>
<div class="uk-form-controls">
<input name="email" class="uk-input uk-width-1-1" type="email" placeholder="User Email" value="{{ form.email|default('') }}"/>
</div>
</div>
<div>
<label class="uk-form-label">Username</label>
<div class="uk-form-controls">
<input name="username" class="uk-input uk-width-1-1" type="text" placeholder="User Username" value="{{ form.username|default('') }}"/>
</div>
</div>
<div>
<label class="uk-form-label">Password</label>
<div class="uk-form-controls">
<input name="password" class="uk-input uk-width-1-1" type="password" placeholder="User password" value="{{ form.password|default('xxxxxxxxxx') }}"/>
</div>
</div>
<div>
<label class="uk-form-label">Confirm Password</label>
<div class="uk-form-controls">
<input name="password2" class="uk-input uk-width-1-1" type="password" placeholder="User confirm password" value="{{ form.password2|default('xxxxxxxxxx') }}"/>
</div>
</div>
</div>
</li>
<li>
<div>
<div class="uk-width-1-1 uk-margin">
<label class="uk-form-label">Activation of User Access to System</label>
<div class="uk-form-controls">
<select name="block" class="uk-select">
<option value="1">Blocked</option>
<option value="0" {% if form.block == 0 %}selected{% endif %}>Active</option>
</select>
</div>
</div>
</div>
</li>
</ul>
<input type="hidden" name="user_id" value="{{ form.id|default(0) }}">
<input type="hidden" name="{{ token() }}" value="1">
</form>
</div>
{% endblock %}

View File

@ -5,28 +5,55 @@
{% block content %} {% block content %}
<div class="uk-container uk-margin"> <div class="uk-container uk-margin">
<h1 class="uk-article-title">Users</h1> <h1 class="uk-article-title">Users</h1>
<a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=user">Create</a> {{ block("messages_queue", "message_queue.twig") }}
<table class="uk-table uk-table-justify uk-table-divider"> <a class="uk-button uk-button-default" href="{{ route() }}index.php/user?task=create">Create</a>
<thead> {% if list %}
<tr> <table class="uk-table uk-table-justify uk-table-divider">
<th class="uk-width-small">Table Heading</th> <thead>
<th>Table Heading</th> <tr>
<th>Table Heading</th> <th class="uk-width-small">Action</th>
</tr> <th>Name</th>
</thead> <th class="uk-width-small">Email</th>
<tbody> <th class="uk-width-small">State</th>
<tr> <th>ID</th>
<td>Table Data</td> </tr>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> </thead>
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=user&id=1">Edit</a></td> <tbody>
</tr> {% for item in list %}
<tr> <tr>
<td>Table Data</td> <td>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td> <div class="uk-button-group uk-width-1-1">
<td><a class="uk-button uk-button-default" href="{{ url() }}index.php?dashboard=user&id=2">Edit</a></td> <a class="uk-button uk-button-default uk-button-small" href="{{ route() }}index.php/user?id={{ item.id }}&task=edit">Edit</a>
</tr> <button class="uk-button uk-button-default uk-button-small" onclick="confirmDeletion('{{ item.name }}', {{ item.id }});">Delete</button>
</tbody> </div>
</table> </td>
<td>{{ item.name }}</td>
<td>{{ item.email }}</td>
<td>
{% if item.block == 0 %}
<button class="uk-button uk-button-primary uk-button-small uk-width-1-1">Active</button>
{% else %}
<button class="uk-button uk-button-danger uk-button-small uk-width-1-1">Blocked</button>
{% endif %}
</td>
<td>{{ item.id }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="uk-alert-primary" uk-alert>
<p>There has no items been found, click create to add some.</p>
</div>
{% endif %}
</div> </div>
<!-- Source: https://getuikit.com/docs/table --> <script>
function confirmDeletion(name, id){
UIkit.modal.confirm('You are about to permanently delete <b>' + name + '</b>?').then(function () {
window.open("{{ route() }}index.php/user?id=" + id + "&task=delete", "_self")
}, function () {
// we do nothing ;)
});
}
</script>
{% endblock %} {% endblock %}

View File

@ -8,10 +8,10 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
<![endif]--> <![endif]-->
<!-- UIkit CSS --> <!-- UIkit CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.13.7/dist/css/uikit.min.css" /> <link rel="stylesheet" href="{{ route() }}media/css/uikit.min.css" />
<!-- UIkit JS --> <!-- UIkit JS -->
<script src="https://cdn.jsdelivr.net/npm/uikit@3.13.7/dist/js/uikit.min.js"></script> <script src="{{ route() }}media/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit@3.13.7/dist/js/uikit-icons.min.js"></script> <script src="{{ route() }}media/js/uikit-icons.min.js"></script>
<!-- End for Responsive --> <!-- End for Responsive -->
<!-- For SEO --> <!-- For SEO -->

View File

@ -3,8 +3,8 @@
{% block content %} {% 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-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"> <div class="uk-width-1-2@m uk-text-center uk-margin-auto uk-margin-auto-vertical uk-overlay uk-overlay-default">
<h1>Welcome</h1> <h1>Octoleo CMS</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> <p>There has no home page been created for this CMS. Please come again soon...</p>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -2,7 +2,7 @@
{% block body %} {% block body %}
<body id="go"> <body id="go">
{% block bodyNavigation %}{% import 'nav.twig' as macros %}{{ macros.load_site_navbar() }}{% endblock %} {% block bodyNavigation %}{{ block("bodyNavigation", "nav.twig") }}{% endblock %}
<main> <main>
{% block content %}{% endblock %} {% block content %}{% endblock %}
</main> </main>

View File

@ -1,28 +1,40 @@
{% macro load_site_navbar() %} {% block bodyNavigation %}
{% set center = false %}
{% set right = false %}
{% if main_menu %}
{% for menu in main_menu %}
{% if menu.root and menu.position == 'center' %}
{% set center = true %}
{% elseif menu.root and menu.position == 'right' %}
{% set right = true %}
{% endif %}
{% endfor %}
{% endif %}
<nav class="uk-navbar-container" uk-navbar> <nav class="uk-navbar-container" uk-navbar>
<div class="uk-navbar-center"> <div class="uk-navbar-center">
<ul class="uk-navbar-nav"> <ul class="uk-navbar-nav">
<li class="uk-active"><a href="{{ url() }}">Home</a></li> <li class="uk-active"><a href="{{ route() }}">Home</a></li>
<li> {% if center %}
<a href="{{ url() }}products">Products</a> {% for menu in main_menu %}
<div class="uk-navbar-dropdown"> {% if menu.root and menu.position == 'center' %}
<ul class="uk-nav uk-navbar-dropdown-nav"> <li><a href="{{ route() }}{{ menu.path }}">{{ menu.title }}</a></li>
<li class="uk-active"><a href="{{ url() }}products/yachts">Yachts</a></li> {% endif %}
<li><a href="{{ url() }}products/ski-boats">Ski Boats</a></li> {% endfor %}
<li><a href="{{ url() }}products/drones">Drones</a></li> {% endif %}
</ul>
</div>
</li>
<li><a href="{{ url() }}blog">Blog</a></li>
<li><a href="{{ url() }}about-us">About Us</a></li>
</ul> </ul>
</div> </div>
{% if right %}
<div class="uk-navbar-right"> <div class="uk-navbar-right">
<ul class="uk-navbar-nav"> <ul class="uk-navbar-nav">
<li><a href="{{ url() }}location">Location</a></li> {% if right %}
<li><a href="{{ url() }}contact-us">Contact Us</a></li> {% for menu in main_menu %}
{% if menu.root and menu.position == 'right' %}
<li><a href="{{ route() }}{{ menu.path }}">{{ menu.title }}</a></li>
{% endif %}
{% endfor %}
{% endif %}
</ul> </ul>
</div> </div>
{% endif %}
</nav> </nav>
{% endmacro %} {% endblock %}
<!-- Source: https://getuikit.com/docs/navbar -->

View File

@ -1,14 +1,14 @@
{% extends "index.twig" %} {% extends "index.twig" %}
{% block content %} {% 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-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"> <div class="uk-width-1-2@m uk-text-center uk-margin-auto uk-margin-auto-vertical uk-overlay uk-overlay-default">
<h1>{{ page }}</h1> <h1>{{ title }}</h1>
{% if details == '' %} {% if body == '' %}
<p>YOU ARE IN PAGES</p> <p>We have an error!</p>
{% else %} {% else %}
<p>You are into {{ details }}</p> {{ body|raw }}
{% endif %} {% endif %}
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}