summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Scheller <igor.scheller@igorshp.de>2018-09-23 19:13:19 +0200
committerIgor Scheller <igor.scheller@igorshp.de>2018-09-23 20:11:37 +0200
commit66038eda14d5d4e624b6636a6156570e3e940e49 (patch)
tree6e4b7557b7d91786ef47f22f7ddef85eed1dfb42
parent590adffa9316b98544cb8d67b03b80e44ba9c8b7 (diff)
parent9d34f371cb9c5ab0d60bd3158678b9cc9da6cc80 (diff)
Merge branch 'twig-templates'
-rw-r--r--config/routes.php9
-rw-r--r--includes/pages/guest_credits.php17
-rw-r--r--includes/sys_menu.php111
-rw-r--r--includes/sys_template.php12
-rw-r--r--includes/view/EventConfig_view.php51
-rw-r--r--resources/views/layouts/app.twig60
-rw-r--r--resources/views/layouts/parts/footer.twig36
-rw-r--r--resources/views/layouts/parts/navbar.twig74
-rw-r--r--resources/views/pages/credits.html36
-rw-r--r--resources/views/pages/credits.twig42
-rw-r--r--resources/views/pages/user-shifts.html10
-rw-r--r--src/Controllers/BaseController.php8
-rw-r--r--src/Controllers/CreditsController.php24
-rw-r--r--src/Http/RequestServiceProvider.php4
-rw-r--r--src/Http/ResponseServiceProvider.php3
-rw-r--r--src/Middleware/LegacyMiddleware.php25
-rw-r--r--src/Middleware/RequestHandler.php31
-rw-r--r--src/Renderer/Twig/Extensions/Authentication.php40
-rw-r--r--src/Renderer/Twig/Extensions/Globals.php40
-rw-r--r--src/Renderer/Twig/Extensions/Legacy.php49
-rw-r--r--src/Renderer/TwigServiceProvider.php24
-rw-r--r--templates/layouts/parts/footer.twig36
-rw-r--r--templates/layouts/parts/navbar.twig74
-rw-r--r--tests/Unit/Controllers/CreditsControllerTest.php28
-rw-r--r--tests/Unit/Http/RequestServiceProviderTest.php10
-rw-r--r--tests/Unit/Http/ResponseServiceProviderTest.php9
-rw-r--r--tests/Unit/Middleware/RequestHandlerTest.php77
-rw-r--r--tests/Unit/Renderer/Twig/Extensions/AuthenticationTest.php56
-rw-r--r--tests/Unit/Renderer/Twig/Extensions/ExtensionTest.php12
-rw-r--r--tests/Unit/Renderer/Twig/Extensions/GlobalsTest.php34
-rw-r--r--tests/Unit/Renderer/Twig/Extensions/LegacyTest.php59
-rw-r--r--tests/Unit/Renderer/TwigServiceProviderTest.php28
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, []);