diff options
author | Igor Scheller <igor.scheller@igorshp.de> | 2018-09-23 19:13:19 +0200 |
---|---|---|
committer | Igor Scheller <igor.scheller@igorshp.de> | 2018-09-23 20:11:37 +0200 |
commit | 66038eda14d5d4e624b6636a6156570e3e940e49 (patch) | |
tree | 6e4b7557b7d91786ef47f22f7ddef85eed1dfb42 | |
parent | 590adffa9316b98544cb8d67b03b80e44ba9c8b7 (diff) | |
parent | 9d34f371cb9c5ab0d60bd3158678b9cc9da6cc80 (diff) |
Merge branch 'twig-templates'
32 files changed, 822 insertions, 307 deletions
diff --git a/config/routes.php b/config/routes.php index 5296dbc7..2267bc88 100644 --- a/config/routes.php +++ b/config/routes.php @@ -1,14 +1,7 @@ <?php use FastRoute\RouteCollector; -use Psr\Http\Message\ServerRequestInterface; /** @var RouteCollector $route */ -/** Demo route endpoint, TODO: Remove */ -$route->addRoute('GET', '/hello/{name}', function ($request) { - /** @var ServerRequestInterface $request */ - $name = $request->getAttribute('name'); - - return response(sprintf('Hello %s!', htmlspecialchars($name))); -}); +$route->get('/credits', 'CreditsController@index'); diff --git a/includes/pages/guest_credits.php b/includes/pages/guest_credits.php deleted file mode 100644 index 8388c386..00000000 --- a/includes/pages/guest_credits.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @return string - */ -function credits_title() -{ - return __('Credits'); -} - -/** - * @return string - */ -function guest_credits() -{ - return view(__DIR__ . '/../../resources/views/pages/credits.html'); -} diff --git a/includes/sys_menu.php b/includes/sys_menu.php index e303d055..ca2ab411 100644 --- a/includes/sys_menu.php +++ b/includes/sys_menu.php @@ -41,69 +41,6 @@ function header_render_hints() } /** - * Renders the header toolbar containing search, login/logout, user and settings links. - * - * @return string - */ -function header_toolbar() -{ - global $page, $privileges, $user; - - $toolbar_items = []; - - if (isset($user)) { - $toolbar_items[] = toolbar_item_link( - page_link_to('shifts', ['action' => 'next']), - 'time', - User_shift_state_render($user) - ); - } - - if (!isset($user) && in_array('register', $privileges) && config('registration_enabled')) { - $toolbar_items[] = toolbar_item_link( - page_link_to('register'), - 'plus', - register_title(), - $page == 'register' - ); - } - - if (in_array('login', $privileges)) { - $toolbar_items[] = toolbar_item_link( - page_link_to('login'), - 'log-in', - login_title(), - $page == 'login' - ); - } - - if (isset($user) && in_array('user_messages', $privileges)) { - $toolbar_items[] = toolbar_item_link( - page_link_to('user_messages'), - 'envelope', - user_unread_messages() - ); - } - - $toolbar_items[] = header_render_hints(); - if (in_array('user_myshifts', $privileges)) { - $toolbar_items[] = toolbar_item_link( - page_link_to('users', ['action' => 'view']), - '', - '<span class="icon-icon_angel"></span> ' . $user['Nick'], - $page == 'users' - ); - } - - $user_submenu = make_user_submenu(); - if (count($user_submenu) > 0) { - $toolbar_items[] = toolbar_dropdown('', '', $user_submenu); - } - - return toolbar($toolbar_items, true); -} - -/** * @return array */ function make_user_submenu() @@ -120,7 +57,7 @@ function make_user_submenu() $user_submenu[] = toolbar_item_link( page_link_to('user_settings'), 'list-alt', - settings_title(), + __('Settings'), $page == 'user_settings' ); } @@ -129,7 +66,7 @@ function make_user_submenu() $user_submenu[] = toolbar_item_link( page_link_to('logout'), 'log-out', - logout_title(), + __('Logout'), $page == 'logout' ); } @@ -146,11 +83,11 @@ function make_navigation() $menu = []; $pages = [ - 'news' => news_title(), - 'user_meetings' => meetings_title(), - 'user_shifts' => shifts_title(), - 'angeltypes' => angeltypes_title(), - 'user_questions' => questions_title() + 'news' => __('News'), + 'user_meetings' => __('Meetings'), + 'user_shifts' => __('Shifts'), + 'angeltypes' => __('Angeltypes'), + 'user_questions' => __('Ask the Heaven'), ]; foreach ($pages as $menu_page => $title) { @@ -163,18 +100,18 @@ function make_navigation() $admin_menu = []; $admin_pages = [ - 'admin_arrive' => admin_arrive_title(), - 'admin_active' => admin_active_title(), - 'admin_user' => admin_user_title(), - 'admin_free' => admin_free_title(), - 'admin_questions' => admin_questions_title(), - 'shifttypes' => shifttypes_title(), - 'admin_shifts' => admin_shifts_title(), - 'admin_rooms' => admin_rooms_title(), - 'admin_groups' => admin_groups_title(), - 'admin_import' => admin_import_title(), - 'admin_log' => admin_log_title(), - 'admin_event_config' => event_config_title() + 'admin_arrive' => __('Arrived angels'), + 'admin_active' => __('Active angels'), + 'admin_user' => __('All Angels'), + 'admin_free' => __('Free angels'), + 'admin_questions' => __('Answer questions'), + 'shifttypes' => __('Shifttypes'), + 'admin_shifts' => __('Create shifts'), + 'admin_rooms' => __('Rooms'), + 'admin_groups' => __('Grouprights'), + 'admin_import' => __('Frab import'), + 'admin_log' => __('Log'), + 'admin_event_config' => __('Event config'), ]; foreach ($admin_pages as $menu_page => $title) { @@ -192,7 +129,7 @@ function make_navigation() $menu[] = toolbar_dropdown('', __('Admin'), $admin_menu); } - return toolbar($menu); + return '<ul class="nav navbar-nav">' . join("\n", $menu) . '</ul>'; } /** @@ -250,11 +187,3 @@ function make_language_select() } return $items; } - -/** - * @return string - */ -function make_menu() -{ - return make_navigation(); -} diff --git a/includes/sys_template.php b/includes/sys_template.php index 98ed1207..dc87e94e 100644 --- a/includes/sys_template.php +++ b/includes/sys_template.php @@ -150,18 +150,6 @@ function heading($content, $number = 1) } /** - * Render a toolbar. - * - * @param array $items - * @param bool $right - * @return string - */ -function toolbar($items = [], $right = false) -{ - return '<ul class="nav navbar-nav' . ($right ? ' navbar-right' : '') . '">' . join("\n", $items) . '</ul>'; -} - -/** * @param string[] $items * @return string */ diff --git a/includes/view/EventConfig_view.php b/includes/view/EventConfig_view.php index 2525182a..4290ab0a 100644 --- a/includes/view/EventConfig_view.php +++ b/includes/view/EventConfig_view.php @@ -61,57 +61,6 @@ function EventConfig_countdown_page($event_config) } /** - * Converts event name and start+end date into a line of text. - * - * @param array $event_config - * @return string - */ -function EventConfig_info($event_config) -{ - if (empty($event_config)) { - return ''; - } - - // Event name, start+end date are set - if ( - !is_null($event_config['event_name']) - && !is_null($event_config['event_start_date']) - && !is_null($event_config['event_end_date']) - ) { - return sprintf( - __('%s, from %s to %s'), - $event_config['event_name'], - date(__('Y-m-d'), $event_config['event_start_date']), - date(__('Y-m-d'), $event_config['event_end_date']) - ); - } - - // Event name, start date are set - if (!is_null($event_config['event_name']) && !is_null($event_config['event_start_date'])) { - return sprintf( - __('%s, starting %s'), $event_config['event_name'], - date(__('Y-m-d'), $event_config['event_start_date']) - ); - } - - // Event start+end date are set - if (!is_null($event_config['event_start_date']) && !is_null($event_config['event_end_date'])) { - return sprintf( - __('Event from %s to %s'), - date(__('Y-m-d'), $event_config['event_start_date']), - date(__('Y-m-d'), $event_config['event_end_date']) - ); - } - - // Only event name is set - if (!is_null($event_config['event_name'])) { - return sprintf($event_config['event_name']); - } - - return ''; -} - -/** * Render edit page for event config. * * @param string $event_name The event name diff --git a/resources/views/layouts/app.twig b/resources/views/layouts/app.twig index aa45484d..d8f8d10f 100644 --- a/resources/views/layouts/app.twig +++ b/resources/views/layouts/app.twig @@ -11,8 +11,12 @@ <link rel="stylesheet" type="text/css" href="{{ asset('assets/theme' ~ theme ~ '.css') }}"/> <script type="text/javascript" src="{{ asset('assets/vendor.js') }}"></script> - {% if atom_feed -%} - <link href="{{ url('atom', atom_feed) }}" type="application/atom+xml" rel="alternate" title="Atom Feed"> + {% if page() in ['news', 'user-meetings', '/'] and is_user() -%} + {% set parameters = {'key': user.api_key} -%} + {% if page() == 'user-meetings' -%} + {% set parameters = parameters|merge({'meetings': 1}) -%} + {% endif %} + <link href="{{ url('atom', parameters) }}" type="application/atom+xml" rel="alternate" title="Atom Feed"> {% endif %} {% endblock %} @@ -20,54 +24,24 @@ <body> {% block body %} - <div class="navbar navbar-default navbar-fixed-top"> - {% block header %} - <div class="container-fluid"> - <div class="navbar-header"> - <button type="button" class="navbar-toggle collapsed" - data-toggle="collapse" data-target="#navbar-collapse-1"> - <span class="sr-only">Toggle navigation</span> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - </button> - <a class="navbar-brand" href="{{ url('/') }}"> - <span class="icon-icon_angel"></span> <strong class="visible-lg-inline">ENGELSYSTEM</strong> - </a> - </div> - - {% block navbar %} - <div class="collapse navbar-collapse" id="navbar-collapse-1"> - {{ menu|raw }} - {{ header_toolbar|raw }} - </div> - {% endblock %} - </div> - {% endblock %} - </div> + {% block header %} + {% include "layouts/parts/navbar.twig" %} + {% endblock %} <div class="container-fluid"> - <div class="row">{% block content %}{{ content|raw }}{% endblock %}</div> + <div class="row" id="content"> + {% block content %} + {{ content|raw }} + {% endblock %} + </div> <div class="row" id="footer"> {% block footer %} - <div class="col-md-12"> - <hr/> - <div class="text-center footer" style="margin-bottom: 10px;"> - {% block eventinfo %} - {{ event_info|raw }} - {% endblock %} - <a href="{{ config('faq_url') }}">FAQ</a> - · <a href="{{ config('contact_email') }}"> - <span class="glyphicon glyphicon-envelope"></span>Contact - </a> - · <a href="https://github.com/engelsystem/engelsystem/issues">Bugs / Features</a> - · <a href="https://github.com/engelsystem/engelsystem/">Development Platform</a> - · <a href="{{ url('credits') }}">Credits</a> - </div> - </div> + {% include "layouts/parts/footer.twig" %} {% endblock %} </div> </div> + + {% block scripts %}{% endblock %} {% endblock %} </body> diff --git a/resources/views/layouts/parts/footer.twig b/resources/views/layouts/parts/footer.twig new file mode 100644 index 00000000..e986963a --- /dev/null +++ b/resources/views/layouts/parts/footer.twig @@ -0,0 +1,36 @@ +<div class="col-md-12"> + <hr/> + <div class="text-center footer" style="margin-bottom: 10px;"> + {% block eventinfo %} + {% if event_config.event_name %} + {% if event_config.event_start_date and event_config.event_end_date %} + {{ __('%s, from %s to %s', [ + event_config.event_name, + date(event_config.event_start_date).format(__('Y-m-d')), + date(event_config.event_end_date).format(__('Y-m-d')) + ]) }} + {% elseif event_config.event_start_date %} + {{ __('%s, starting %s', [ + event_config.event_name, + date(event_config.event_start_date).format(__('Y-m-d')) + ]) }} + {% else %} + {{ event_config.event_name }} + {% endif %} <br> + {% elseif event_config.event_start_date and event_config.event_end_date %} + {{ __('Event from %s to %s', [ + date(event_config.event_start_date).format(__('Y-m-d')), + date(event_config.event_end_date).format(__('Y-m-d')) + ]) }} <br> + {% endif %} + {% endblock %} + + <a href="{{ config('faq_url') }}">{{ __('FAQ') }}</a> + · <a href="{{ config('contact_email') }}"> + <span class="glyphicon glyphicon-envelope"></span> {{ __('Contact') }} + </a> + · <a href="https://github.com/engelsystem/engelsystem/issues">{{ __('Bugs / Features') }}</a> + · <a href="https://github.com/engelsystem/engelsystem/">{{ __('Development Platform') }}</a> + · <a href="{{ url('credits') }}">{{ __('Credits') }}</a> + </div> +</div> diff --git a/resources/views/layouts/parts/navbar.twig b/resources/views/layouts/parts/navbar.twig new file mode 100644 index 00000000..a718fc48 --- /dev/null +++ b/resources/views/layouts/parts/navbar.twig @@ -0,0 +1,74 @@ +{% import _self as elements %} + +{% macro toolbar_item(label, link, active_page, icon) %} + <li{% if page() == active_page %} class="active"{% endif %}> + <a href="{{ link }}"> + {% if icon %}<span class="glyphicon {{ icon }}"></span>{% endif %} + {{ label|raw }} + </a> + </li> +{% endmacro %} + +<div class="navbar navbar-default navbar-fixed-top"> + <div class="container-fluid"> + {% block navbar %} + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" + data-toggle="collapse" data-target="#navbar-collapse-1"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" href="{{ url('/') }}"> + <span class="icon-icon_angel"></span> <strong class="visible-lg-inline">ENGELSYSTEM</strong> + </a> + </div> + + {% block menu %} + <div class="collapse navbar-collapse" id="navbar-collapse-1"> + {% block menu_items %} + {{ menu() }} + {% endblock %} + + {% block menu_toolbar %} + <ul class="nav navbar-nav navbar-right"> + + {% if is_user() %} + {{ elements.toolbar_item(menuUserShiftState(user), url('shifts', {'action': 'next'}), '', 'glyphicon-time') }} + {% elseif has_permission_to('register') and config('registration_enabled') %} + {{ elements.toolbar_item(__('Register'), url('register'), 'register', 'glyphicon-plus') }} + {% endif %} + + {% if has_permission_to('login') %} + {{ elements.toolbar_item(__('Login'), url('login'), 'login', 'glyphicon-log-in') }} + {% endif %} + + {% if is_user() and has_permission_to('user_messages') %} + {{ elements.toolbar_item(menuUserMessages(), url('user-messages'), 'user-messages', 'glyphicon-envelope') }} + {% endif %} + + {{ menuUserHints() }} + + {% if has_permission_to('user_myshifts') %} + {{ elements.toolbar_item(user.Nick, url('users', {'action': 'view'}), 'users', 'icon-icon_angel') }} + {% endif %} + + {% if has_permission_to('user_settings') or has_permission_to('logout') %} + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown"> + <span class="caret"></span> + </a> + <ul class="dropdown-menu" role="menu"> + {{ menuUserSubmenu()|join(" ")|raw }} + </ul> + </li> + {% endif %} + + </ul> + {% endblock %} + </div> + {% endblock %} + {% endblock %} + </div> +</div> diff --git a/resources/views/pages/credits.html b/resources/views/pages/credits.html deleted file mode 100644 index 4e247113..00000000 --- a/resources/views/pages/credits.html +++ /dev/null @@ -1,36 +0,0 @@ -<div class="container"> - <h1>Credits</h1> - <div class="row"> - <div class="col-md-4"> - <h2>Source code</h2> - <p> - The original system was written by <a href="https://github.com/cookieBerlin/engelsystem">cookie</a>. - It was then completely rewritten and enhanced by - <a href="https://notrademark.de">msquare</a> (maintainer), - <a href="https://myigel.name">MyIgel</a>, - <a href="https://mortzu.de">mortzu</a>, - <a href="https://jplitza.de">jplitza</a> and - <a href="https://github.com/gnomus">gnomus</a>. - </p> - <p> - Please look at the <a href="https://github.com/engelsystem/engelsystem/graphs/contributors"> - contributor list on github</a> for a more complete version. - </p> - </div> - <div class="col-md-4"> - <h2>Hosting</h2> - <p> - Webspace, development platform and domain on <a href="https://engelsystem.de">engelsystem.de</a> - is currently provided by <a href="https://www.wybt.net/">would you buy this?</a> (ichdasich) - and adminstrated by <a href="https://mortzu.de">mortzu</a>, - <a href="http://derf.homelinux.org">derf</a> and ichdasich. - </p> - </div> - <div class="col-md-4"> - <h2>Translation</h2> - <p> - Many thanks for the german translation: <a href="http://e7p.de">e7p</a> - </p> - </div> - </div> -</div> diff --git a/resources/views/pages/credits.twig b/resources/views/pages/credits.twig new file mode 100644 index 00000000..ff2bf873 --- /dev/null +++ b/resources/views/pages/credits.twig @@ -0,0 +1,42 @@ +{% extends "layouts/app.twig" %} + +{% block title %}{{ __('Credits') }}{% endblock %} + +{% block content %} + <div class="container"> + <h1>Credits</h1> + <div class="row"> + <div class="col-md-4"> + <h2>Source code</h2> + <p> + The original system was written by <a href="https://github.com/cookieBerlin/engelsystem">cookie</a>. + It was then completely rewritten and enhanced by + <a href="https://notrademark.de">msquare</a> (maintainer), + <a href="https://myigel.name">MyIgel</a>, + <a href="https://mortzu.de">mortzu</a>, + <a href="https://jplitza.de">jplitza</a> and + <a href="https://github.com/gnomus">gnomus</a>. + </p> + <p> + Please look at the <a href="https://github.com/engelsystem/engelsystem/graphs/contributors"> + contributor list on github</a> for a more complete version. + </p> + </div> + <div class="col-md-4"> + <h2>Hosting</h2> + <p> + Webspace, development platform and domain on <a href="https://engelsystem.de">engelsystem.de</a> + is currently provided by <a href="https://www.wybt.net/">would you buy this?</a> (ichdasich) + and adminstrated by <a href="https://mortzu.de">mortzu</a>, + <a href="http://derf.homelinux.org">derf</a> and ichdasich. + </p> + </div> + <div class="col-md-4"> + <h2>Translation</h2> + <p> + Many thanks for the german translation: <a href="http://e7p.de">e7p</a> + </p> + </div> + </div> + </div> +{% endblock %} diff --git a/resources/views/pages/user-shifts.html b/resources/views/pages/user-shifts.html index b1b7ac1a..2278cc07 100644 --- a/resources/views/pages/user-shifts.html +++ b/resources/views/pages/user-shifts.html @@ -61,21 +61,23 @@ </div> </div> <div class="col-md-6"> - <button class="btn btn-info btn-sm" style="margin-top: 20px; margin-bottom:10px" type="button" data-toggle="collapse" - data-target="#collapseRoomSelect" aria-expanded="false" - aria-controls="collapseRoomSelect"> + <button class="btn btn-info btn-sm" style="margin-top: 20px; margin-bottom:10px" type="button" + data-toggle="collapse" + data-target="#collapseRoomSelect" aria-expanded="false" + aria-controls="collapseRoomSelect" + > collapse/show filters </button> <div class="collapse in row" id="collapseRoomSelect"> <div class="col-xs-4 col-xxs-12">%room_select%</div> <div class="col-xs-4 col-xxs-12">%type_select%</div> <div class="col-xs-4 col-xxs-12">%filled_select%</div> - <div class="col-sm-12"><p>%task_notice%</p></div> </div> </div> </div> <div class="row"> <div class="col-md-12"> + <div><p>%task_notice%</p></div> <input class="btn btn-primary" type="submit" style="width:75%; margin-bottom: 20px" value="%filter%"> </div> </div> diff --git a/src/Controllers/BaseController.php b/src/Controllers/BaseController.php new file mode 100644 index 00000000..6a27a066 --- /dev/null +++ b/src/Controllers/BaseController.php @@ -0,0 +1,8 @@ +<?php + +namespace Engelsystem\Controllers; + +abstract class BaseController +{ + +} diff --git a/src/Controllers/CreditsController.php b/src/Controllers/CreditsController.php new file mode 100644 index 00000000..568811c7 --- /dev/null +++ b/src/Controllers/CreditsController.php @@ -0,0 +1,24 @@ +<?php + +namespace Engelsystem\Controllers; + +use Engelsystem\Http\Response; + +class CreditsController extends BaseController +{ + /** @var Response */ + protected $response; + + public function __construct(Response $response) + { + $this->response = $response; + } + + /** + * @return Response + */ + public function index() + { + return $this->response->withView('pages/credits.twig'); + } +} diff --git a/src/Http/RequestServiceProvider.php b/src/Http/RequestServiceProvider.php index bbf2579c..37f8c604 100644 --- a/src/Http/RequestServiceProvider.php +++ b/src/Http/RequestServiceProvider.php @@ -3,6 +3,7 @@ namespace Engelsystem\Http; use Engelsystem\Container\ServiceProvider; +use Symfony\Component\HttpFoundation\Request as SymfonyRequest; class RequestServiceProvider extends ServiceProvider { @@ -18,6 +19,9 @@ class RequestServiceProvider extends ServiceProvider /** @var Request $request */ $request = $this->app->call([Request::class, 'createFromGlobals']); $this->setTrustedProxies($request, $trustedProxies); + + $this->app->instance(Request::class, $request); + $this->app->instance(SymfonyRequest::class, $request); $this->app->instance('request', $request); } diff --git a/src/Http/ResponseServiceProvider.php b/src/Http/ResponseServiceProvider.php index f0d238ef..c1489f06 100644 --- a/src/Http/ResponseServiceProvider.php +++ b/src/Http/ResponseServiceProvider.php @@ -3,12 +3,15 @@ namespace Engelsystem\Http; use Engelsystem\Container\ServiceProvider; +use Symfony\Component\HttpFoundation\Response as SymfonyResponse; class ResponseServiceProvider extends ServiceProvider { public function register() { $response = $this->app->make(Response::class); + $this->app->instance(Response::class, $response); + $this->app->instance(SymfonyResponse::class, $response); $this->app->instance('response', $response); } } diff --git a/src/Middleware/LegacyMiddleware.php b/src/Middleware/LegacyMiddleware.php index f4d6bbcc..ebf456eb 100644 --- a/src/Middleware/LegacyMiddleware.php +++ b/src/Middleware/LegacyMiddleware.php @@ -18,7 +18,6 @@ class LegacyMiddleware implements MiddlewareInterface 'angeltypes', 'api', 'atom', - 'credits', 'ical', 'login', 'public_dashboard', @@ -60,6 +59,7 @@ class LegacyMiddleware implements MiddlewareInterface ): ResponseInterface { global $user; global $privileges; + global $page; /** @var Request $appRequest */ $appRequest = $this->container->get('request'); @@ -248,11 +248,6 @@ class LegacyMiddleware implements MiddlewareInterface $title = admin_log_title(); $content = admin_log(); return [$title, $content]; - case 'credits': - require_once realpath(__DIR__ . '/../../includes/pages/guest_credits.php'); - $title = credits_title(); - $content = guest_credits(); - return [$title, $content]; } require_once realpath(__DIR__ . '/../../includes/pages/guest_start.php'); @@ -271,27 +266,13 @@ class LegacyMiddleware implements MiddlewareInterface */ protected function renderPage($page, $title, $content) { - global $user; - $event_config = EventConfig(); - $parameters = [ - 'key' => (isset($user) ? $user['api_key'] : ''), - ]; - - if ($page == 'user_meetings') { - $parameters['meetings'] = 1; - } - if (!empty($page) && is_int($page)) { return response($content, (int)$page); } return response(view('layouts/app', [ - 'title' => $title, - 'atom_feed' => ($page == 'news' || $page == 'user_meetings') ? $parameters : [], - 'menu' => make_menu(), - 'content' => msg() . $content, - 'header_toolbar' => header_toolbar(), - 'event_info' => EventConfig_info($event_config) . ' <br />' + 'title' => $title, + 'content' => msg() . $content, ]), 200); } } diff --git a/src/Middleware/RequestHandler.php b/src/Middleware/RequestHandler.php index e1381abf..ebe1ff9e 100644 --- a/src/Middleware/RequestHandler.php +++ b/src/Middleware/RequestHandler.php @@ -35,7 +35,7 @@ class RequestHandler implements MiddlewareInterface public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $requestHandler = $request->getAttribute('route-request-handler'); - $requestHandler = $this->resolveMiddleware($requestHandler); + $requestHandler = $this->resolveRequestHandler($requestHandler); if ($requestHandler instanceof MiddlewareInterface) { return $requestHandler->process($request, $handler); @@ -47,4 +47,33 @@ class RequestHandler implements MiddlewareInterface throw new InvalidArgumentException('Unable to process request handler of type ' . gettype($requestHandler)); } + + /** + * @param string|callable|MiddlewareInterface|RequestHandlerInterface $handler + * @return MiddlewareInterface|RequestHandlerInterface + */ + protected function resolveRequestHandler($handler) + { + if (is_string($handler) && strpos($handler, '@') !== false) { + list($class, $method) = explode('@', $handler, 2); + if (!class_exists($class) && !$this->container->has($class)) { + $class = sprintf('Engelsystem\\Controllers\\%s', $class); + } + + $handler = [$class, $method]; + } + + if ( + is_array($handler) + && is_string($handler[0]) + && ( + class_exists($handler[0]) + || $this->container->has($handler[0]) + ) + ) { + $handler[0] = $this->container->make($handler[0]); + } + + return $this->resolveMiddleware($handler); + } } diff --git a/src/Renderer/Twig/Extensions/Authentication.php b/src/Renderer/Twig/Extensions/Authentication.php new file mode 100644 index 00000000..6a72d825 --- /dev/null +++ b/src/Renderer/Twig/Extensions/Authentication.php @@ -0,0 +1,40 @@ +<?php + +namespace Engelsystem\Renderer\Twig\Extensions; + +use Twig_Extension as TwigExtension; +use Twig_Function as TwigFunction; + +class Authentication extends TwigExtension +{ + /** + * @return TwigFunction[] + */ + public function getFunctions() + { + return [ + new TwigFunction('is_user', [$this, 'isAuthenticated']), + new TwigFunction('is_guest', [$this, 'isGuest']), + new TwigFunction('has_permission_to', [$this, 'checkAuth']), + ]; + } + + public function isAuthenticated() + { + global $user; + + return !empty($user); + } + + public function isGuest() + { + return !$this->isAuthenticated(); + } + + public function checkAuth($privilege) + { + global $privileges; + + return in_array($privilege, $privileges); + } +} diff --git a/src/Renderer/Twig/Extensions/Globals.php b/src/Renderer/Twig/Extensions/Globals.php index f9bffbc8..1a4df42c 100644 --- a/src/Renderer/Twig/Extensions/Globals.php +++ b/src/Renderer/Twig/Extensions/Globals.php @@ -2,6 +2,7 @@ namespace Engelsystem\Renderer\Twig\Extensions; +use Carbon\Carbon; use Twig_Extension as TwigExtension; use Twig_Extension_GlobalsInterface as GlobalsInterface; @@ -16,8 +17,45 @@ class Globals extends TwigExtension implements GlobalsInterface { global $user; + $eventConfig = $this->getEventConfig(); + if (empty($eventConfig)) { + $eventConfig = []; + } + return [ - 'user' => isset($user) ? $user : [], + 'user' => isset($user) ? $user : [], + 'event_config' => $this->filterEventConfig($eventConfig), ]; } + + /** + * @return array + * @codeCoverageIgnore + */ + protected function getEventConfig() + { + return EventConfig(); + } + + /** + * @param $eventConfig + * @return mixed + */ + protected function filterEventConfig($eventConfig) + { + array_walk($eventConfig, function (&$value, $key) { + if (is_null($value) || !in_array($key, [ + 'buildup_start_date', + 'event_start_date', + 'event_end_date', + 'teardown_end_date', + ])) { + return; + } + + $value = Carbon::createFromTimestamp($value); + }); + + return $eventConfig; + } } diff --git a/src/Renderer/Twig/Extensions/Legacy.php b/src/Renderer/Twig/Extensions/Legacy.php new file mode 100644 index 00000000..79de32cb --- /dev/null +++ b/src/Renderer/Twig/Extensions/Legacy.php @@ -0,0 +1,49 @@ +<?php + +namespace Engelsystem\Renderer\Twig\Extensions; + +use Engelsystem\Http\Request; +use Twig_Extension as TwigExtension; +use Twig_Function as TwigFunction; + +class Legacy extends TwigExtension +{ + /** @var Request */ + protected $request; + + /** + * @param Request $request + */ + public function __construct(Request $request) + { + $this->request = $request; + } + + /** + * @return TwigFunction[] + */ + public function getFunctions() + { + $isSafeHtml = ['is_safe' => ['html']]; + return [ + new TwigFunction('menu', 'make_navigation', $isSafeHtml), + new TwigFunction('menuUserShiftState', 'User_shift_state_render', $isSafeHtml), + new TwigFunction('menuUserMessages', 'user_unread_messages', $isSafeHtml), + new TwigFunction('menuUserHints', 'header_render_hints', $isSafeHtml), + new TwigFunction('menuUserSubmenu', 'make_user_submenu', $isSafeHtml), + new TwigFunction('page', [$this, 'getPage']), + ]; + } + + /** + * @return string + */ + public function getPage() + { + if ($this->request->has('p')) { + return $this->request->get('p'); + } + + return $this->request->path(); + } +} diff --git a/src/Renderer/TwigServiceProvider.php b/src/Renderer/TwigServiceProvider.php index c70fb303..49a0eb90 100644 --- a/src/Renderer/TwigServiceProvider.php +++ b/src/Renderer/TwigServiceProvider.php @@ -2,26 +2,32 @@ namespace Engelsystem\Renderer; +use Engelsystem\Config\Config as EngelsystemConfig; use Engelsystem\Container\ServiceProvider; +use Engelsystem\Renderer\Twig\Extensions\Authentication; use Engelsystem\Renderer\Twig\Extensions\Assets; use Engelsystem\Renderer\Twig\Extensions\Config; use Engelsystem\Renderer\Twig\Extensions\Globals; +use Engelsystem\Renderer\Twig\Extensions\Legacy; use Engelsystem\Renderer\Twig\Extensions\Session; use Engelsystem\Renderer\Twig\Extensions\Translation; use Engelsystem\Renderer\Twig\Extensions\Url; use Twig_Environment as Twig; +use Twig_Extension_Core as TwigCore; use Twig_LoaderInterface as TwigLoaderInterface; class TwigServiceProvider extends ServiceProvider { /** @var array */ protected $extensions = [ - 'assets' => Assets::class, - 'config' => Config::class, - 'globals' => Globals::class, - 'session' => Session::class, - 'url' => Url::class, - 'translation' => Translation::class, + 'assets' => Assets::class, + 'authentication' => Authentication::class, + 'config' => Config::class, + 'globals' => Globals::class, + 'session' => Session::class, + 'legacy' => Legacy::class, + 'translation' => Translation::class, + 'url' => Url::class, ]; public function register() @@ -46,6 +52,8 @@ class TwigServiceProvider extends ServiceProvider protected function registerTwigEngine() { $viewsPath = $this->app->get('path.views'); + /** @var EngelsystemConfig $config */ + $config = $this->app->get('config'); $twigLoader = $this->app->make(TwigLoader::class, ['paths' => $viewsPath]); $this->app->instance(TwigLoader::class, $twigLoader); @@ -56,6 +64,10 @@ class TwigServiceProvider extends ServiceProvider $this->app->instance(Twig::class, $twig); $this->app->instance('twig.environment', $twig); + /** @var TwigCore $twigCore */ + $twigCore = $twig->getExtension(TwigCore::class); + $twigCore->setTimezone($config->get('timezone')); + $twigEngine = $this->app->make(TwigEngine::class); $this->app->instance('renderer.twigEngine', $twigEngine); $this->app->tag('renderer.twigEngine', ['renderer.engine']); diff --git a/templates/layouts/parts/footer.twig b/templates/layouts/parts/footer.twig new file mode 100644 index 00000000..cf201281 --- /dev/null +++ b/templates/layouts/parts/footer.twig @@ -0,0 +1,36 @@ +<div class="col-md-12"> + <hr/> + <div class="text-center footer" style="margin-bottom: 10px;"> + {% block eventinfo %} + {% if event_config.event_name %} + {% if event_config.event_start_date and event_config.event_end_date %} + {{ __('%s, from %s to %s', [ + event_config.event_name, + date(event_config.event_start_date).format(__('Y-m-d')), + date(event_config.event_end_date).format(__('Y-m-d')) + ]) }} + {% elseif event_config.event_start_date %} + {{ __('%s, starting %s', [ + event_config.event_name, + date(event_config.event_start_date).format(__('Y-m-d')) + ]) }} + {% else %} + {{ event_config.event_name }} + {% endif %} <br> + {% elseif event_config.event_start_date and event_config.event_end_date %} + {{ __('Event from %s to %s', [ + date(event_config.event_start_date).format(__('Y-m-d')), + date(event_config.event_end_date).format(__('Y-m-d')) + ]) }} <br> + {% endif %} + {% endblock %} + + <a href="{{ config('faq_url') }}">{{ __('FAQ') }}</a> + · <a href="{{ config('contact_email') }}"> + <span class="glyphicon glyphicon-envelope"></span> {{ __('Contact') }} + </a> + · <a href="https://github.com/engelsystem/engelsystem/issues">{{ __('Bugs / Features') }}</a> + · <a href="https://github.com/engelsystem/engelsystem/">{{ __('Development Platform') }}</a> + · <a href="{{ url('credits') }}">{{ __('Credits') }}</a> + </div> +</div> diff --git a/templates/layouts/parts/navbar.twig b/templates/layouts/parts/navbar.twig new file mode 100644 index 00000000..a718fc48 --- /dev/null +++ b/templates/layouts/parts/navbar.twig @@ -0,0 +1,74 @@ +{% import _self as elements %} + +{% macro toolbar_item(label, link, active_page, icon) %} + <li{% if page() == active_page %} class="active"{% endif %}> + <a href="{{ link }}"> + {% if icon %}<span class="glyphicon {{ icon }}"></span>{% endif %} + {{ label|raw }} + </a> + </li> +{% endmacro %} + +<div class="navbar navbar-default navbar-fixed-top"> + <div class="container-fluid"> + {% block navbar %} + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" + data-toggle="collapse" data-target="#navbar-collapse-1"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" href="{{ url('/') }}"> + <span class="icon-icon_angel"></span> <strong class="visible-lg-inline">ENGELSYSTEM</strong> + </a> + </div> + + {% block menu %} + <div class="collapse navbar-collapse" id="navbar-collapse-1"> + {% block menu_items %} + {{ menu() }} + {% endblock %} + + {% block menu_toolbar %} + <ul class="nav navbar-nav navbar-right"> + + {% if is_user() %} + {{ elements.toolbar_item(menuUserShiftState(user), url('shifts', {'action': 'next'}), '', 'glyphicon-time') }} + {% elseif has_permission_to('register') and config('registration_enabled') %} + {{ elements.toolbar_item(__('Register'), url('register'), 'register', 'glyphicon-plus') }} + {% endif %} + + {% if has_permission_to('login') %} + {{ elements.toolbar_item(__('Login'), url('login'), 'login', 'glyphicon-log-in') }} + {% endif %} + + {% if is_user() and has_permission_to('user_messages') %} + {{ elements.toolbar_item(menuUserMessages(), url('user-messages'), 'user-messages', 'glyphicon-envelope') }} + {% endif %} + + {{ menuUserHints() }} + + {% if has_permission_to('user_myshifts') %} + {{ elements.toolbar_item(user.Nick, url('users', {'action': 'view'}), 'users', 'icon-icon_angel') }} + {% endif %} + + {% if has_permission_to('user_settings') or has_permission_to('logout') %} + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown"> + <span class="caret"></span> + </a> + <ul class="dropdown-menu" role="menu"> + {{ menuUserSubmenu()|join(" ")|raw }} + </ul> + </li> + {% endif %} + + </ul> + {% endblock %} + </div> + {% endblock %} + {% endblock %} + </div> +</div> diff --git a/tests/Unit/Controllers/CreditsControllerTest.php b/tests/Unit/Controllers/CreditsControllerTest.php new file mode 100644 index 00000000..6f0200f2 --- /dev/null +++ b/tests/Unit/Controllers/CreditsControllerTest.php @@ -0,0 +1,28 @@ +<?php + +namespace Unit\Controllers; + +use Engelsystem\Controllers\CreditsController; +use Engelsystem\Http\Response; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class CreditsControllerTest extends TestCase +{ + /** + * @covers \Engelsystem\Controllers\CreditsController::__construct + * @covers \Engelsystem\Controllers\CreditsController::index + */ + public function testIndex() + { + /** @var Response|MockObject $response */ + $response = $this->createMock(Response::class); + + $response->expects($this->once()) + ->method('withView') + ->with('pages/credits.twig'); + + $controller = new CreditsController($response); + $controller->index(); + } +} diff --git a/tests/Unit/Http/RequestServiceProviderTest.php b/tests/Unit/Http/RequestServiceProviderTest.php index eddf7ee5..4e9bb4e0 100644 --- a/tests/Unit/Http/RequestServiceProviderTest.php +++ b/tests/Unit/Http/RequestServiceProviderTest.php @@ -8,6 +8,7 @@ use Engelsystem\Http\Request; use Engelsystem\Http\RequestServiceProvider; use Engelsystem\Test\Unit\ServiceProviderTest; use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Symfony\Component\HttpFoundation\Request as SymfonyRequest; class RequestServiceProviderTest extends ServiceProviderTest { @@ -45,9 +46,16 @@ class RequestServiceProviderTest extends ServiceProviderTest $this->setExpects($app, 'call', [[Request::class, 'createFromGlobals']], $request); $this->setExpects($app, 'get', ['config'], $config); - $this->setExpects($app, 'instance', ['request', $request]); $this->setExpects($config, 'get', ['trusted_proxies'], $configuredProxies); + $app->expects($this->exactly(3)) + ->method('instance') + ->withConsecutive( + [Request::class, $request], + [SymfonyRequest::class, $request], + ['request', $request] + ); + /** @var ServiceProvider|MockObject $serviceProvider */ $serviceProvider = $this->getMockBuilder(RequestServiceProvider::class) ->setConstructorArgs([$app]) diff --git a/tests/Unit/Http/ResponseServiceProviderTest.php b/tests/Unit/Http/ResponseServiceProviderTest.php index 52e95714..6ce1f4fe 100644 --- a/tests/Unit/Http/ResponseServiceProviderTest.php +++ b/tests/Unit/Http/ResponseServiceProviderTest.php @@ -6,6 +6,7 @@ use Engelsystem\Http\Response; use Engelsystem\Http\ResponseServiceProvider; use Engelsystem\Test\Unit\ServiceProviderTest; use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Symfony\Component\HttpFoundation\Response as SymfonyResponse; class ResponseServiceProviderTest extends ServiceProviderTest { @@ -21,7 +22,13 @@ class ResponseServiceProviderTest extends ServiceProviderTest $app = $this->getApp(); $this->setExpects($app, 'make', [Response::class], $response); - $this->setExpects($app, 'instance', ['response', $response]); + $app->expects($this->exactly(3)) + ->method('instance') + ->withConsecutive( + [Response::class, $response], + [SymfonyResponse::class, $response], + ['response', $response] + ); $serviceProvider = new ResponseServiceProvider($app); $serviceProvider->register(); diff --git a/tests/Unit/Middleware/RequestHandlerTest.php b/tests/Unit/Middleware/RequestHandlerTest.php index 896b55c3..cb5fc4a6 100644 --- a/tests/Unit/Middleware/RequestHandlerTest.php +++ b/tests/Unit/Middleware/RequestHandlerTest.php @@ -38,15 +38,12 @@ class RequestHandlerTest extends TestCase public function testProcess() { /** @var Application|MockObject $container */ - $container = $this->createMock(Application::class); /** @var ServerRequestInterface|MockObject $request */ - $request = $this->getMockForAbstractClass(ServerRequestInterface::class); /** @var RequestHandlerInterface|MockObject $handler */ - $handler = $this->getMockForAbstractClass(RequestHandlerInterface::class); /** @var ResponseInterface|MockObject $response */ - $response = $this->getMockForAbstractClass(ResponseInterface::class); + /** @var MiddlewareInterface|MockObject $middlewareInterface */ + list($container, $request, $handler, $response, $middlewareInterface) = $this->getMocks(); - $middlewareInterface = $this->getMockForAbstractClass(MiddlewareInterface::class); $requestHandlerInterface = $this->getMockForAbstractClass(RequestHandlerInterface::class); $request->expects($this->exactly(3)) @@ -57,10 +54,10 @@ class RequestHandlerTest extends TestCase /** @var RequestHandler|MockObject $middleware */ $middleware = $this->getMockBuilder(RequestHandler::class) ->setConstructorArgs([$container]) - ->setMethods(['resolveMiddleware']) + ->setMethods(['resolveRequestHandler']) ->getMock(); $middleware->expects($this->exactly(3)) - ->method('resolveMiddleware') + ->method('resolveRequestHandler') ->with('FooBarClass') ->willReturnOnConsecutiveCalls( $middlewareInterface, @@ -86,4 +83,70 @@ class RequestHandlerTest extends TestCase $this->expectException(InvalidArgumentException::class); $middleware->process($request, $handler); } + + /** + * @covers \Engelsystem\Middleware\RequestHandler::resolveRequestHandler + */ + public function testResolveRequestHandler() + { + /** @var Application|MockObject $container */ + /** @var ServerRequestInterface|MockObject $request */ + /** @var RequestHandlerInterface|MockObject $handler */ + /** @var ResponseInterface|MockObject $response */ + /** @var MiddlewareInterface|MockObject $middlewareInterface */ + list($container, $request, $handler, $response, $middlewareInterface) = $this->getMocks(); + + $className = 'Engelsystem\\Controllers\\FooBarTestController'; + + $request->expects($this->exactly(1)) + ->method('getAttribute') + ->with('route-request-handler') + ->willReturn('FooBarTestController@showStuff'); + + /** @var RequestHandler|MockObject $middleware */ + $middleware = $this->getMockBuilder(RequestHandler::class) + ->setConstructorArgs([$container]) + ->setMethods(['resolveMiddleware']) + ->getMock(); + $middleware->expects($this->once()) + ->method('resolveMiddleware') + ->with([$middlewareInterface, 'showStuff']) + ->willReturn($middlewareInterface); + + $middlewareInterface->expects($this->once()) + ->method('process') + ->with($request, $handler) + ->willReturn($response); + + $container->expects($this->exactly(2)) + ->method('has') + ->withConsecutive(['FooBarTestController'], [$className]) + ->willReturnOnConsecutiveCalls(false, true); + $container->expects($this->once()) + ->method('make') + ->with($className) + ->willReturn($middlewareInterface); + + $return = $middleware->process($request, $handler); + $this->assertEquals($return, $response); + } + + /** + * @return array + */ + protected function getMocks(): array + { + /** @var Application|MockObject $container */ + $container = $this->createMock(Application::class); + /** @var ServerRequestInterface|MockObject $request */ + $request = $this->getMockForAbstractClass(ServerRequestInterface::class); + /** @var RequestHandlerInterface|MockObject $handler */ + $handler = $this->getMockForAbstractClass(RequestHandlerInterface::class); + /** @var ResponseInterface|MockObject $response */ + $response = $this->getMockForAbstractClass(ResponseInterface::class); + /** @var MiddlewareInterface $middlewareInterface */ + $middlewareInterface = $this->getMockForAbstractClass(MiddlewareInterface::class); + + return [$container, $request, $handler, $response, $middlewareInterface]; + } } diff --git a/tests/Unit/Renderer/Twig/Extensions/AuthenticationTest.php b/tests/Unit/Renderer/Twig/Extensions/AuthenticationTest.php new file mode 100644 index 00000000..0a72c0e7 --- /dev/null +++ b/tests/Unit/Renderer/Twig/Extensions/AuthenticationTest.php @@ -0,0 +1,56 @@ +<?php + +namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions; + +use Engelsystem\Renderer\Twig\Extensions\Authentication; + +class AuthenticationTest extends ExtensionTest +{ + /** + * @covers \Engelsystem\Renderer\Twig\Extensions\Authentication::getFunctions + */ + public function testGetFunctions() + { + $extension = new Authentication(); + $functions = $extension->getFunctions(); + + $this->assertExtensionExists('is_user', [$extension, 'isAuthenticated'], $functions); + $this->assertExtensionExists('is_guest', [$extension, 'isGuest'], $functions); + $this->assertExtensionExists('has_permission_to', [$extension, 'checkAuth'], $functions); + } + + /** + * @covers \Engelsystem\Renderer\Twig\Extensions\Authentication::isAuthenticated + * @covers \Engelsystem\Renderer\Twig\Extensions\Authentication::isGuest + */ + public function testIsAuthenticated() + { + global $user; + $user = []; + + $extension = new Authentication(); + + $this->assertFalse($extension->isAuthenticated()); + $this->assertTrue($extension->isGuest()); + + $user = ['lorem' => 'ipsum']; + $this->assertTrue($extension->isAuthenticated()); + $this->assertFalse($extension->isGuest()); + } + + /** + * @covers \Engelsystem\Renderer\Twig\Extensions\Authentication::checkAuth + */ + public function testCheckAuth() + { + global $privileges; + $privileges = []; + + $extension = new Authentication(); + + $this->assertFalse($extension->checkAuth('foo.bar')); + + $privileges = ['foo.bar']; + $this->assertTrue($extension->checkAuth('foo.bar')); + } +} diff --git a/tests/Unit/Renderer/Twig/Extensions/ExtensionTest.php b/tests/Unit/Renderer/Twig/Extensions/ExtensionTest.php index e1c5a378..ef7dba7a 100644 --- a/tests/Unit/Renderer/Twig/Extensions/ExtensionTest.php +++ b/tests/Unit/Renderer/Twig/Extensions/ExtensionTest.php @@ -2,8 +2,10 @@ namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Twig_Function as TwigFunction; +use Twig_Node as TwigNode; abstract class ExtensionTest extends TestCase { @@ -35,7 +37,7 @@ abstract class ExtensionTest extends TestCase * @param callable $callback * @param TwigFunction[] $functions */ - protected function assertExtensionExists($name, $callback, $functions) + protected function assertExtensionExists($name, $callback, $functions, $options = []) { foreach ($functions as $function) { if ($function->getName() != $name) { @@ -43,6 +45,14 @@ abstract class ExtensionTest extends TestCase } $this->assertEquals($callback, $function->getCallable()); + + if (isset($options['is_save'])) { + /** @var TwigNode|MockObject $twigNode */ + $twigNode = $this->createMock(TwigNode::class); + + $this->assertArraySubset($options['is_save'], $function->getSafe($twigNode)); + } + return; } diff --git a/tests/Unit/Renderer/Twig/Extensions/GlobalsTest.php b/tests/Unit/Renderer/Twig/Extensions/GlobalsTest.php index 6cc3a4da..cdd9cd2a 100644 --- a/tests/Unit/Renderer/Twig/Extensions/GlobalsTest.php +++ b/tests/Unit/Renderer/Twig/Extensions/GlobalsTest.php @@ -2,24 +2,54 @@ namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions; +use Carbon\Carbon; use Engelsystem\Renderer\Twig\Extensions\Globals; +use PHPUnit\Framework\MockObject\MockObject; class GlobalsTest extends ExtensionTest { /** * @covers \Engelsystem\Renderer\Twig\Extensions\Globals::getGlobals + * @covers \Engelsystem\Renderer\Twig\Extensions\Globals::filterEventConfig */ public function testGetGlobals() { - $extension = new Globals(); + global $user; + $user = []; + + /** @var Globals|MockObject $extension */ + $extension = $this->getMockBuilder(Globals::class) + ->setMethods(['getEventConfig']) + ->getMock(); + + $extension->expects($this->exactly(2)) + ->method('getEventConfig') + ->willReturnOnConsecutiveCalls( + null, + [ + 'lorem' => 'ipsum', + 'event_end_date' => 1234567890, + ] + ); + $globals = $extension->getGlobals(); $this->assertGlobalsExists('user', [], $globals); + $this->assertGlobalsExists('event_config', [], $globals); - global $user; $user['foo'] = 'bar'; $globals = $extension->getGlobals(); $this->assertGlobalsExists('user', ['foo' => 'bar'], $globals); + $this->assertGlobalsExists('event_config', ['lorem' => 'ipsum'], $globals); + + $config = $globals['event_config']; + $this->assertArrayHasKey('event_end_date', $config); + /** @var Carbon $eventEndDate */ + $eventEndDate = $config['event_end_date']; + $this->assertInstanceOf(Carbon::class, $eventEndDate); + + $eventEndDate->setTimezone('UTC'); + $this->assertEquals('2009-02-13 23:31:30', $eventEndDate->format('Y-m-d H:i:s')); } } diff --git a/tests/Unit/Renderer/Twig/Extensions/LegacyTest.php b/tests/Unit/Renderer/Twig/Extensions/LegacyTest.php new file mode 100644 index 00000000..613af0da --- /dev/null +++ b/tests/Unit/Renderer/Twig/Extensions/LegacyTest.php @@ -0,0 +1,59 @@ +<?php + +namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions; + +use Engelsystem\Http\Request; +use Engelsystem\Renderer\Twig\Extensions\Legacy; +use PHPUnit\Framework\MockObject\MockObject; + +class LegacyTest extends ExtensionTest +{ + /** + * @covers \Engelsystem\Renderer\Twig\Extensions\Legacy::getFunctions + */ + public function testGetFunctions() + { + $isSafeHtml = ['is_safe' => ['html']]; + /** @var Request|MockObject $request */ + $request = $this->createMock(Request::class); + + $extension = new Legacy($request); + $functions = $extension->getFunctions(); + + $this->assertExtensionExists('menu', 'make_navigation', $functions, $isSafeHtml); + $this->assertExtensionExists('menuUserShiftState', 'User_shift_state_render', $functions, $isSafeHtml); + $this->assertExtensionExists('menuUserMessages', 'user_unread_messages', $functions, $isSafeHtml); + $this->assertExtensionExists('menuUserHints', 'header_render_hints', $functions, $isSafeHtml); + $this->assertExtensionExists('menuUserSubmenu', 'make_user_submenu', $functions, $isSafeHtml); + $this->assertExtensionExists('page', [$extension, 'getPage'], $functions); + } + + /** + * @covers \Engelsystem\Renderer\Twig\Extensions\Legacy::getPage + * @covers \Engelsystem\Renderer\Twig\Extensions\Legacy::__construct + */ + public function testIsAuthenticated() + { + /** @var Request|MockObject $request */ + $request = $this->createMock(Request::class); + + $extension = new Legacy($request); + + $request->expects($this->exactly(2)) + ->method('has') + ->with('p') + ->willReturnOnConsecutiveCalls(true, false); + + $request->expects($this->once()) + ->method('get') + ->with('p') + ->willReturn('foo-bar'); + + $request->expects($this->once()) + ->method('path') + ->willReturn('batz'); + + $this->assertEquals('foo-bar', $extension->getPage()); + $this->assertEquals('batz', $extension->getPage()); + } +} diff --git a/tests/Unit/Renderer/TwigServiceProviderTest.php b/tests/Unit/Renderer/TwigServiceProviderTest.php index 3cd0da4d..0d632633 100644 --- a/tests/Unit/Renderer/TwigServiceProviderTest.php +++ b/tests/Unit/Renderer/TwigServiceProviderTest.php @@ -2,6 +2,7 @@ namespace Engelsystem\Test\Unit\Renderer; +use Engelsystem\Config\Config; use Engelsystem\Renderer\TwigEngine; use Engelsystem\Renderer\TwigLoader; use Engelsystem\Renderer\TwigServiceProvider; @@ -10,6 +11,7 @@ use PHPUnit\Framework\MockObject\MockObject; use ReflectionClass as Reflection; use stdClass; use Twig_Environment as Twig; +use Twig_Extension_Core as TwigCore; use Twig_ExtensionInterface as ExtensionInterface; use Twig_LoaderInterface as TwigLoaderInterface; @@ -97,6 +99,12 @@ class TwigServiceProviderTest extends ServiceProviderTest $twigLoader = $this->createMock(TwigLoader::class); /** @var Twig|MockObject $twig */ $twig = $this->createMock(Twig::class); + /** @var Config|MockObject $config */ + $config = $this->createMock(Config::class); + /** @var TwigCore|MockObject $twigCore */ + $twigCore = $this->getMockBuilder(stdClass::class) + ->setMethods(['setTimezone']) + ->getMock(); $app = $this->getApp(['make', 'instance', 'tag', 'get']); @@ -125,13 +133,27 @@ class TwigServiceProviderTest extends ServiceProviderTest ['renderer.twigEngine', $twigEngine] ); - $app->expects($this->once()) + $app->expects($this->exactly(2)) ->method('get') - ->with('path.views') - ->willReturn($viewsPath); + ->withConsecutive(['path.views'], ['config']) + ->willReturnOnConsecutiveCalls($viewsPath, $config); $this->setExpects($app, 'tag', ['renderer.twigEngine', ['renderer.engine']]); + $config->expects($this->once()) + ->method('get') + ->with('timezone') + ->willReturn('The/World'); + + $twig->expects($this->once()) + ->method('getExtension') + ->with(TwigCore::class) + ->willReturn($twigCore); + + $twigCore->expects($this->once()) + ->method('setTimezone') + ->with('The/World'); + $serviceProvider = new TwigServiceProvider($app); $this->setExtensionsTo($serviceProvider, []); |