Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
42.13% covered (danger)
42.13%
83 / 197
44.44% covered (danger)
44.44%
12 / 27
CRAP
0.00% covered (danger)
0.00%
0 / 1
TwigExtension
42.13% covered (danger)
42.13%
83 / 197
44.44% covered (danger)
44.44%
12 / 27
964.05
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFunctions
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
1
 isImageAllowed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getLanguageDescriptor
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 isNumeric
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNow
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getSystemStatus
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toTextFormat
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 formatDateTime
87.50% covered (warning)
87.50%
14 / 16
0.00% covered (danger)
0.00%
0 / 1
3.02
 currentRoute
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
6
 currentLang
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 currentLocale
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 currentVersion
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 urlGet
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 csvProperty
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 azPrefixList
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 azPrefixListCollator
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 isValueInArray
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 remoteInclude
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
30
 getEsiFromPath
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
12
 getClientHost
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 toSortableString
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 sortByName
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 sortFirstChar
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 dumpAppProfiler
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 kindOfPayment
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3/**
4 * @package   BO Slim
5 * @copyright BerlinOnline Stadtportal GmbH & Co. KG
6 **/
7
8namespace BO\Slim;
9
10use Twig\Extension\AbstractExtension;
11
12/**
13  * Extension for Twig and Slim
14  *
15  *  @SuppressWarnings(PublicMethod)
16  *  @SuppressWarnings(TooManyMethods)
17  *  @SuppressWarnings(Coupling)
18  *  @SuppressWarnings(Complexity)
19  */
20class TwigExtension extends AbstractExtension
21{
22    /**
23     * @var \BO\Slim\Container
24     */
25    private $container;
26
27    public function __construct($container)
28    {
29        $this->container = $container;
30    }
31
32    public function getName()
33    {
34        return 'boslimExtension';
35    }
36
37    #[\Override]
38    public function getFunctions()
39    {
40        $safe = array('is_safe' => array('html'));
41        return array(
42            new \Twig\TwigFunction('urlGet', array($this, 'urlGet')),
43            new \Twig\TwigFunction('csvProperty', array($this, 'csvProperty')),
44            new \Twig\TwigFunction('azPrefixList', array($this, 'azPrefixList')),
45            new \Twig\TwigFunction('azPrefixListCollator', array($this, 'azPrefixListCollator')),
46            new \Twig\TwigFunction('isValueInArray', array($this, 'isValueInArray')),
47            new \Twig\TwigFunction('remoteInclude', array($this, 'remoteInclude'), $safe),
48            new \Twig\TwigFunction('getEsiFromPath', array($this, 'getEsiFromPath')),
49            new \Twig\TwigFunction('getLanguageDescriptor', array($this, 'getLanguageDescriptor')),
50            new \Twig\TwigFunction('currentLang', array($this, 'currentLang')),
51            new \Twig\TwigFunction('currentRoute', array($this, 'currentRoute')),
52            new \Twig\TwigFunction('currentLocale', array($this, 'currentLocale')),
53            new \Twig\TwigFunction('currentVersion', array($this, 'currentVersion')),
54            new \Twig\TwigFunction('formatDateTime', array($this, 'formatDateTime')),
55            new \Twig\TwigFunction('toTextFormat', array($this, 'toTextFormat')),
56            new \Twig\TwigFunction('getNow', array($this, 'getNow')),
57            new \Twig\TwigFunction('isNumeric', array($this, 'isNumeric')),
58            new \Twig\TwigFunction('dumpAppProfiler', array($this, 'dumpAppProfiler'), $safe),
59            new \Twig\TwigFunction('getSystemStatus', array($this, 'getSystemStatus'), $safe),
60            new \Twig\TwigFunction('getClientHost', array($this, 'getClientHost')),
61            new \Twig\TwigFunction('kindOfPayment', array($this, 'kindOfPayment')),
62            new \Twig\TwigFunction('isImageAllowed', array($this, 'isImageAllowed')),
63        );
64    }
65
66
67    public static function isImageAllowed()
68    {
69        return (isset(\App::$isImageAllowed)) ? \App::$isImageAllowed : true;
70    }
71
72    public function getLanguageDescriptor($locale = 'de')
73    {
74        $language = \App::$supportedLanguages[$locale] ?? [];
75        return $language['name'] ?? null;
76    }
77
78    public static function isNumeric($var)
79    {
80        return is_numeric($var);
81    }
82
83    public static function getNow()
84    {
85        if (\App::$now instanceof \DateTimeInterface) {
86            return \App::$now;
87        }
88        return new \DateTimeImmutable();
89    }
90
91    public static function getSystemStatus($env)
92    {
93        return getenv($env);
94    }
95
96    public function toTextFormat($string)
97    {
98        $string = \strip_tags($string, '<br />');
99        $temp = str_replace(array("<br />"), "\n", $string);
100        $lines = explode("\n", $temp);
101        $new_lines = array();
102        foreach ($lines as $line) {
103            if (!empty($line)) {
104                $new_lines[] = trim($line);
105            }
106        }
107        $result = implode("\n", $new_lines);
108        return addSlashes($result);
109    }
110
111    public function formatDateTime($dateString)
112    {
113        $dateTime = new \DateTimeImmutable(
114            $dateString->year . '-' . $dateString->month . '-' . $dateString->day,
115            new \DateTimezone('Europe/Berlin')
116        );
117        $formatDate['date']     = Helper::getFormatedDates($dateTime, "EE, dd. MMMM yyyy");
118        $formatDate['fulldate'] = Helper::getFormatedDates($dateTime, "EEEE, 'den' dd. MMMM yyyy");
119        $formatDate['weekday']  = (date('w', $dateTime->getTimestamp()) == 0) ?
120            date('w', $dateTime->getTimestamp()) + 6 :
121            date('w', $dateTime->getTimestamp()) - 1;
122        $formatDate['ym']       = $dateTime->format('Y-m');
123        $formatDate['ymd']       = $dateTime->format('Y-m-d');
124        $formatDate['ts']       = $dateTime->getTimestamp();
125        $formatDate['time']     = ($dateTime->format('H:i') != '00:00') ?
126            Helper::getFormatedDates($dateTime, 'HH:mm Uhr') :
127            false;
128        return $formatDate;
129    }
130
131    public function currentRoute($lang = null)
132    {
133        $route = array(
134            'name' => 'noroute',
135            'params' => []
136        );
137        if ($this->container->has('currentRoute')) {
138            $routeParams = $this->container->get('currentRouteParams');
139            if (null !== $lang && 'de' == $lang) {
140                unset($routeParams['lang']);
141            } elseif (\App::MULTILANGUAGE) {
142                $routeParams['lang'] = ($lang !== null) ? $lang : \App::$language->getCurrentLanguage();
143            }
144
145            $routeName = $this->container->get('currentRoute');
146            $route = array(
147                'name' => $routeName,
148                'params' => $routeParams
149            );
150        }
151        return $route;
152    }
153
154    public function currentLang()
155    {
156        return (\App::MULTILANGUAGE) ? \App::$language->getCurrentLanguage() : 'de';
157    }
158
159    public function currentLocale()
160    {
161        $locale = 'de_DE';
162        if (\App::MULTILANGUAGE) {
163            $locale = explode('.', \App::$language->getCurrentLocale());
164            $locale = reset($locale);
165        }
166        return $locale;
167    }
168
169    public function currentVersion()
170    {
171        $version = Version::getString();
172        return ($version != Version::UNKNOWN) ? $version : Git::readCurrentVersion();
173    }
174
175    public function urlGet($routeName, $params = array(), $getparams = array())
176    {
177        $url = \App::$slim->urlFor($routeName, $params);
178        $url = preg_replace('#^.*?(https?://)#', '\1', $url); // allow http:// routes
179        if ($getparams) {
180            $url .= '?' . http_build_query($getparams);
181        }
182        return Helper::proxySanitizeUri($url);
183    }
184
185    public function csvProperty($list, $property)
186    {
187        $propertylist = array();
188        foreach ($list as $item) {
189            if (!is_scalar($item) && array_key_exists($property, $item)) {
190                $propertylist[] = $item[$property];
191            }
192        }
193        return implode(',', array_unique($propertylist));
194    }
195
196    public function azPrefixList($list, $property)
197    {
198        $azList = array();
199        foreach ($list as $item) {
200            if (!is_scalar($item) && array_key_exists($property, $item)) {
201                $currentPrefix = self::sortFirstChar($item[$property]);
202                if (!array_key_exists($currentPrefix, $azList)) {
203                    $azList[$currentPrefix] = array(
204                        'prefix' => $currentPrefix,
205                        'sublist' => array(),
206                    );
207                }
208                $azList[$currentPrefix]['sublist'][] = $item;
209                uasort($azList[$currentPrefix]['sublist'], array($this,'sortByName'));
210                ksort($azList);
211            }
212        }
213        return $azList;
214    }
215
216    public function azPrefixListCollator($list, $property, $locale)
217    {
218        $collator = collator_create($locale);
219        $collator->setAttribute(\Collator::QUATERNARY, \Collator::ON);
220        $collator->setAttribute(\Collator::CASE_FIRST, \Collator::ON);
221        $collator->setAttribute(\Collator::NUMERIC_COLLATION, \Collator::ON);
222
223        if (is_array($list)) {
224            uasort($list, function ($itemA, $itemB) use ($collator, $property) {
225                return collator_compare($collator, $itemA[$property], $itemB[$property]);
226            });
227        } else {
228            $list = $list->sortWithCollator($property, $locale);
229        }
230
231        $azList = array();
232
233        foreach ($list as $item) {
234            $currentPrefix = self::sortFirstChar($item[$property]);
235            if (!array_key_exists($currentPrefix, $azList)) {
236                $azList[$currentPrefix] = array(
237                    'prefix' => $currentPrefix,
238                    'sublist' => array(),
239                );
240            }
241            $azList[$currentPrefix]['sublist'][] = $item;
242        }
243        return $azList;
244    }
245
246    public function isValueInArray($value, $params)
247    {
248        $paramsArr = explode(',', $params);
249        if (in_array($value, $paramsArr)) {
250            return true;
251        }
252        return false;
253    }
254
255    public static function remoteInclude($uri)
256    {
257        $prepend = '';
258        $append = '';
259        if (\App::DEBUG) {
260            $prepend = "<!-- include($uri) -->\n";
261            $append = "\n<!-- /include($uri) -->";
262        }
263        if (\App::ESI_ENABLED) {
264            // Varnish does not support https
265            $uri = preg_replace('#^(https?:)?//#', 'http://', $uri);
266            if (\App::DEBUG) {
267                $prepend = "<!-- replaced uri=$uri --> " . $prepend;
268            }
269            return $prepend . '<esi:include src="' . $uri . '" />' . $append;
270        } else {
271            $useragent = 'Client-' . (defined("\App::IDENTIFIER") ? constant("\App::IDENTIFIER") : 'ZMS');
272            $options = array(
273                'http' => array(
274                  'method' => "GET",
275                  'header' => "Accept-language: de\r\n" .
276                            "Cookie: zms=development\r\n" .
277                            "user-agent: $useragent \r\n"
278                )
279              );
280            $context = stream_context_create($options);
281            return $prepend . file_get_contents($uri, false, $context) . $append;
282        }
283    }
284
285    public function getEsiFromPath($path, $locale = false)
286    {
287        $localePath = ($locale && 'de' != $locale) ? '/' . $locale : '';
288        return \App::$esiBaseUrl . $localePath . \App::$$path;
289    }
290
291    public function getClientHost()
292    {
293        $request = $this->container['request'];
294        $headerList = ['host', 'x-forwarded-host'];
295        foreach ($headerList as $headername) {
296            if ($request->hasHeader($headername)) {
297                $hostname = $request->getHeaderLine($headername);
298            }
299        }
300        return $hostname;
301    }
302
303    protected static function toSortableString($string)
304    {
305        $string = strtr($string, array(
306            'Ä' => 'Ae',
307            'Ö' => 'Oe',
308            'Ü' => 'Ue',
309            'ä' => 'ae',
310            'ö' => 'oe',
311            'ü' => 'ue',
312            'ß' => 'ss',
313            '€' => 'E',
314        ));
315        return $string;
316    }
317
318    protected static function sortByName($left, $right)
319    {
320        return strcmp(
321            self::toSortableString(strtolower($left['name'])),
322            strtolower(self::toSortableString($right['name']))
323        );
324    }
325
326    protected static function sortFirstChar($string)
327    {
328        $firstChar = mb_substr($string, 0, 1);
329        $firstChar = mb_strtoupper($firstChar);
330        $firstChar = strtr($firstChar, array('Ä' => 'A', 'Ö' => 'O', 'Ü' => 'U'));
331        return $firstChar;
332    }
333
334    public function dumpAppProfiler()
335    {
336        $output = '<h2>App Profiles</h2>'
337            . ' <p>For debugging: This log contains runtime information.
338            <strong>DISABLE FOR PRODUCTION!</strong></p><ul>';
339        foreach (Profiler::$profileList as $entry) {
340            if ($entry instanceof Profiler) {
341                $output .= "<li>$entry</li>";
342            } else {
343                $output .= \Tracy\Debugger::dump($entry, true);
344            }
345        }
346        return $output . '</ul>';
347    }
348
349    public function kindOfPayment($code)
350    {
351        $result = '';
352        if ($code == 0) {
353            $result = 'eccash';
354        } elseif ($code == 1) {
355            $result = 'nocash';
356        } elseif ($code == 2) {
357            $result = 'ec';
358        } elseif ($code == 3) {
359            $result = 'cash';
360        } elseif ($code == 4) {
361            $result = 'subscribecash';
362        }
363        return $result;
364    }
365}