Passed
Pull Request — master (#1251)
by Dante
01:34
created

LayoutHelper::getCsrfToken()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 16
rs 9.6111
cc 5
nc 5
nop 0
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2018 ChannelWeb Srl, Chialab Srl
5
 *
6
 * This file is part of BEdita: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
12
 */
13
namespace App\View\Helper;
14
15
use App\Utility\CacheTools;
16
use App\Utility\Translate;
17
use Cake\Core\Configure;
18
use Cake\Utility\Hash;
19
use Cake\View\Helper;
20
21
/**
22
 * Helper for site layout
23
 *
24
 * @property \App\View\Helper\EditorsHelper $Editors
25
 * @property \Cake\View\Helper\HtmlHelper $Html
26
 * @property \App\View\Helper\LinkHelper $Link
27
 * @property \App\View\Helper\PermsHelper $Perms
28
 * @property \App\View\Helper\SystemHelper $System
29
 * @property \Cake\View\Helper\UrlHelper $Url
30
 */
31
class LayoutHelper extends Helper
32
{
33
    /**
34
     * List of helpers used by this helper
35
     *
36
     * @var array
37
     */
38
    public $helpers = ['Editors', 'Html', 'Link', 'Perms', 'System', 'Url'];
39
40
    /**
41
     * Is Dashboard
42
     *
43
     * @return bool True if visible for view
44
     */
45
    public function isDashboard(): bool
46
    {
47
        return in_array($this->_View->getName(), ['Dashboard']);
48
    }
49
50
    /**
51
     * Is Login
52
     *
53
     * @return bool True if visible for view
54
     */
55
    public function isLogin(): bool
56
    {
57
        return in_array($this->_View->getName(), ['Login']);
58
    }
59
60
    /**
61
     * Properties for various publication status
62
     *
63
     * @param array $object The object
64
     * @return string pubstatus
65
     */
66
    public function publishStatus(array $object = []): string
67
    {
68
        if (empty($object)) {
69
            return '';
70
        }
71
72
        $end = (string)Hash::get($object, 'attributes.publish_end');
73
        $start = (string)Hash::get($object, 'attributes.publish_start');
74
75
        if (!empty($end) && strtotime($end) <= time()) {
76
            return 'expired';
77
        }
78
        if (!empty($start) && strtotime($start) > time()) {
79
            return 'future';
80
        }
81
        if (!empty((string)Hash::get($object, 'meta.locked'))) {
82
            return 'locked';
83
        }
84
        if ((string)Hash::get($object, 'attributes.status') === 'draft') {
85
            return 'draft';
86
        }
87
88
        return '';
89
    }
90
91
    /**
92
     * Messages visibility
93
     *
94
     * @return bool True if visible for view
95
     */
96
    public function messages(): bool
97
    {
98
        return $this->_View->getName() != 'Login';
99
    }
100
101
    /**
102
     * Return title with object title and current module name
103
     *
104
     * @return string title with object title and current module name separated by '|'
105
     */
106
    public function title(): string
107
    {
108
        $module = (array)$this->getView()->get('currentModule');
109
        $name = (string)Hash::get($module, 'name');
110
        $object = (array)$this->getView()->get('object');
111
        $title = (string)Hash::get($object, 'attributes.title');
112
        $title = strip_tags($title);
113
114
        return empty($title) ? $name : sprintf('%s | %s', $title, $name);
115
    }
116
117
    /**
118
     * Return dashboard module link
119
     *
120
     * @param string $name The module name
121
     * @param array $module The module data
122
     * @return string
123
     */
124
    public function dashboardModuleLink(string $name, array $module): string
125
    {
126
        if (in_array($name, ['trash', 'users'])) {
127
            return '';
128
        }
129
        $label = $name === 'objects' ? __('All objects') : Hash::get($module, 'label', $name);
130
        $route = (array)Hash::get($module, 'route');
131
        $param = empty($route) ? ['_name' => 'modules:list', 'object_type' => $name, 'plugin' => null] : $route;
132
        $count = !$this->showCounter($name) ? '' : $this->moduleCount($name);
133
134
        return sprintf(
135
            '<a href="%s" class="%s"><span>%s</span>%s%s</a>',
136
            $this->Url->build($param),
137
            sprintf('dashboard-item has-background-module-%s %s', $name, Hash::get($module, 'class', '')),
138
            $this->tr($label),
139
            $this->moduleIcon($name, $module),
140
            $count
141
        );
142
    }
143
144
    /**
145
     * Show counter for module
146
     *
147
     * @param string $name The module name
148
     * @return bool
149
     */
150
    public function showCounter(string $name): bool
151
    {
152
        $counters = Configure::read('UI.modules.counters', ['trash']);
153
154
        return is_array($counters) ? in_array($name, $counters) : $counters === 'all';
155
    }
156
157
    /**
158
     * Return module count span.
159
     *
160
     * @param string $name The module name
161
     * @param string|null $moduleClass The module class
162
     * @return string
163
     */
164
    public function moduleCount(string $name, ?string $moduleClass = null): string
165
    {
166
        $count = CacheTools::getModuleCount($name);
167
168
        return sprintf('<span class="module-count">%s</span>', $count);
169
    }
170
171
    /**
172
     * Return module icon.
173
     *
174
     * @param string $name The module name
175
     * @param array $module The module data
176
     * @return string
177
     */
178
    public function moduleIcon(string $name, array $module): string
179
    {
180
        if (Hash::get($module, 'hints.multiple_types') && !Hash::get($module, 'class')) {
181
            return '<app-icon icon="carbon:grid" :style="{ width: \'28px\', height: \'28px\' }"></app-icon>';
182
        }
183
        $icon = (string)Configure::read(sprintf('Modules.%s.icon', $name));
184
        if (!empty($icon)) {
185
            return sprintf('<app-icon icon="%s" :style="{ width: \'28px\', height: \'28px\' }"></app-icon>', $icon);
186
        }
187
        $map = [
188
            'audio' => 'carbon:document-audio',
189
            'categories' => 'carbon:collapse-categories',
190
            'documents' => 'carbon:document',
191
            'events' => 'carbon:event',
192
            'files' => 'carbon:document-blank',
193
            'folders' => 'carbon:tree-view',
194
            'images' => 'carbon:image',
195
            'links' => 'carbon:link',
196
            'locations' => 'carbon:location',
197
            'news' => 'carbon:calendar',
198
            'profiles' => 'carbon:person',
199
            'publications' => 'carbon:wikis',
200
            'tags' => 'carbon:tag',
201
            'users' => 'carbon:user',
202
            'videos' => 'carbon:video',
203
        ];
204
        if (!in_array($name, array_keys($map))) {
205
            return '';
206
        }
207
208
        return sprintf('<app-icon icon="%s" :style="{ width: \'28px\', height: \'28px\' }"></app-icon>', $map[$name]);
209
    }
210
211
    /**
212
     * Return module css class(es).
213
     * If object is locked by parents, return base class plus 'locked' class.
214
     * If object is locked by concurrent editors, return 'concurrent-editors' class plus publish status class.
215
     * If object is not locked, return base class plus publish status class.
216
     * Publish status class is 'expired', 'future', 'locked' or 'draft'.
217
     *
218
     * @return string
219
     */
220
    public function moduleClass(): string
221
    {
222
        $moduleClasses = ['app-module-box'];
223
        $object = (array)$this->getView()->get('object');
224
        if (!empty($object) && $this->Perms->isLockedByParents((string)Hash::get($object, 'id'))) {
225
            $moduleClasses[] = 'locked';
226
227
            return trim(implode(' ', $moduleClasses));
228
        }
229
        $editors = $this->Editors->list();
230
        if (count($editors) > 1) {
231
            $moduleClasses = ['app-module-box-concurrent-editors'];
232
        }
233
        $moduleClasses[] = $this->publishStatus($object);
234
235
        return trim(implode(' ', $moduleClasses));
236
    }
237
238
    /**
239
     * Module main link
240
     *
241
     * @return string The link
242
     */
243
    public function moduleLink(): string
244
    {
245
        $currentModule = (array)$this->getView()->get('currentModule');
246
        if (!empty($currentModule) && !empty($currentModule['name'])) {
247
            $name = $currentModule['name'];
248
            $label = Hash::get($currentModule, 'label', $name);
249
            $counters = Configure::read('UI.modules.counters', ['trash']);
250
            $showCounter = is_array($counters) ? in_array($name, $counters) : $counters === 'all';
251
            $count = !$showCounter ? '' : $this->moduleCount($name);
252
253
            return sprintf(
254
                '<a href="%s" class="%s"><span class="mr-05">%s</span>%s%s</a>',
255
                $this->Url->build(['_name' => 'modules:list', 'object_type' => $name]),
256
                sprintf('module-item has-background-module-%s', $name),
257
                $this->tr($label),
258
                $this->moduleIcon($name, $currentModule),
259
                $count
260
            );
261
        }
262
263
        // if no `currentModule` has been set a `moduleLink` must be set in controller otherwise current link is displayed
264
        return $this->Html->link(
265
            $this->tr($this->getView()->getName()),
266
            (array)$this->getView()->get('moduleLink'),
267
            ['class' => $this->commandLinkClass()]
268
        );
269
    }
270
271
    /**
272
     * Return module index default view type
273
     *
274
     * @return string
275
     */
276
    public function moduleIndexDefaultViewType(): string
277
    {
278
        $module = (array)$this->getView()->get('currentModule');
279
        $name = (string)Hash::get($module, 'name');
280
        $schema = (array)$this->_View->get('schema');
281
        $defaultType = in_array('DateRanges', (array)Hash::get($schema, 'associations')) ? 'calendar' : 'list';
282
        $defaultType = $name === 'folders' ? 'tree' : $defaultType;
283
284
        return $defaultType;
285
    }
286
287
    /**
288
     * Return module index view type
289
     *
290
     * @return string
291
     */
292
    public function moduleIndexViewType(): string
293
    {
294
        $query = (array)$this->getView()->getRequest()->getQueryParams();
295
296
        return (string)Hash::get($query, 'view_type', $this->moduleIndexDefaultViewType());
297
    }
298
299
    /**
300
     * Return module index view types
301
     *
302
     * @return array
303
     */
304
    public function moduleIndexViewTypes(): array
305
    {
306
        $defaultType = $this->moduleIndexDefaultViewType();
307
        $defaultList = $defaultType === 'calendar' ? ['calendar', 'list'] : ['list'];
308
        $defaultList = $defaultType === 'tree' ? ['tree', 'list'] : $defaultList;
309
310
        return $defaultList;
311
    }
312
313
    /**
314
     * Return style class for command link
315
     *
316
     * @return string
317
     */
318
    protected function commandLinkClass(): string
319
    {
320
        $moduleClasses = [
321
            'UserProfile' => 'has-background-black icon-user',
322
            'Import' => 'has-background-black icon-download-alt',
323
            'ObjectTypes' => 'has-background-black',
324
            'Relations' => 'has-background-black',
325
            'PropertyTypes' => 'has-background-black',
326
            'Categories' => 'has-background-black',
327
            'Appearance' => 'has-background-black',
328
            'Applications' => 'has-background-black',
329
            'AsyncJobs' => 'has-background-black',
330
            'Config' => 'has-background-black',
331
            'Endpoints' => 'has-background-black',
332
            'Roles' => 'has-background-black',
333
            'RolesModules' => 'has-background-black',
334
            'EndpointPermissions' => 'has-background-black',
335
            'Tags' => 'has-background-module-tags',
336
            'ObjectsHistory' => 'has-background-black',
337
            'SystemInfo' => 'has-background-black',
338
            'UserAccesses' => 'has-background-black',
339
            'Statistics' => 'has-background-black',
340
        ];
341
342
        return (string)Hash::get($moduleClasses, $this->_View->getName(), 'commands-menu__module');
343
    }
344
345
    /**
346
     * Get translated val by input string, using plugins (if any) translations.
347
     *
348
     * @param string $input The input string
349
     * @return string|null
350
     */
351
    public function tr(string $input): ?string
352
    {
353
        return Translate::get($input);
354
    }
355
356
    /**
357
     * Return configuration items to create JSON BEDITA object
358
     *
359
     * @return array
360
     */
361
    public function metaConfig(): array
362
    {
363
        return [
364
            'base' => $this->Link->baseUrl(),
365
            'currentModule' => $this->getView()->get('currentModule', ['name' => 'home']),
366
            'template' => $this->getView()->getTemplate(),
367
            'modules' => array_keys($this->getView()->get('modules', [])),
368
            'plugins' => \App\Plugin::loadedAppPlugins(),
369
            'uploadable' => $this->getView()->get('uploadable', []),
370
            'locale' => \Cake\I18n\I18n::getLocale(),
371
            'csrfToken' => $this->getCsrfToken(),
372
            'maxFileSize' => $this->System->getMaxFileSize(),
373
            'canReadUsers' => $this->Perms->canRead('users'),
374
            'canSave' => $this->Perms->canSave(),
375
            'cloneConfig' => (array)Configure::read('Clone'),
376
            'placeholdersConfig' => $this->System->placeholdersConfig(),
377
            'uploadConfig' => $this->System->uploadConfig(),
378
            'relationsSchema' => $this->getView()->get('relationsSchema', []),
379
            'richeditorConfig' => (array)Configure::read('Richeditor'),
380
            'richeditorByPropertyConfig' => (array)Configure::read('UI.richeditor', []),
381
        ];
382
    }
383
384
    /**
385
     * Get csrf token, searching in: request params, data, attributes and cookies
386
     *
387
     * @return string|null
388
     */
389
    public function getCsrfToken(): ?string
390
    {
391
        if (!empty($this->getView()->getRequest()->getParam('_csrfToken'))) {
392
            return (string)$this->getView()->getRequest()->getParam('_csrfToken');
393
        }
394
        if (!empty($this->getView()->getRequest()->getData('_csrfToken'))) {
395
            return (string)$this->getView()->getRequest()->getData('_csrfToken');
396
        }
397
        if (!empty($this->getView()->getRequest()->getAttribute('csrfToken'))) {
398
            return (string)$this->getView()->getRequest()->getAttribute('csrfToken');
399
        }
400
        if (!empty($this->getView()->getRequest()->getCookie('csrfToken'))) {
401
            return (string)$this->getView()->getRequest()->getCookie('csrfToken');
402
        }
403
404
        return null;
405
    }
406
407
    /**
408
     * Trash link by type.
409
     *
410
     * @param string|null $type The object type, if any.
411
     * @return string
412
     */
413
    public function trashLink(?string $type): string
414
    {
415
        if (empty($type) || $type === 'trash' || $type === 'translations') {
416
            return '';
417
        }
418
419
        $modules = (array)$this->_View->get('modules');
420
        if (!Hash::check($modules, sprintf('%s.hints.object_type', $type))) {
421
            return '';
422
        }
423
424
        $classes = sprintf('button icon icon-only-icon has-text-module-%s', $type);
425
        $title = $this->tr($type) . __(' in Trashcan');
426
        $filter = ['type' => [$type]];
427
428
        return $this->Html->link(
429
            sprintf('<span class="is-sr-only">%s</span><app-icon icon="carbon:trash-can"></app-icon>', __('Trash')),
430
            ['_name' => 'trash:list', '?' => compact('filter')],
431
            ['class' => $classes, 'title' => $title, 'escape' => false]
432
        );
433
    }
434
435
    /**
436
     * Return properties group for given property
437
     *
438
     * @param string $needle The property to search
439
     * @return ?string
440
     */
441
    public function propertyGroup(string $needle): ?string
442
    {
443
        $properties = (array)$this->getView()->get('properties');
444
        foreach ($properties as $group => $props) {
445
            $keys = array_keys($props);
446
            if (in_array($needle, $keys)) {
447
                return $group;
448
            }
449
        }
450
451
        return null;
452
    }
453
}
454