Completed
Push — master ( 26081f...c5e2b8 )
by Alberto
14s queued 11s
created

HtmlHelper::title()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 10
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 15
rs 9.9332
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
14
namespace BEdita\WebTools\View\Helper;
15
16
use BEdita\WebTools\Utility\AssetsRevisions;
17
use Cake\Core\Configure;
18
use Cake\Utility\Hash;
19
use Cake\Utility\Inflector;
20
use Cake\View\Helper\HtmlHelper as CakeHtmlHelper;
21
22
/**
23
 * Html helper.
24
 * It extends {@see \Cake\View\Helper\HtmlHelper} Cake Html Helper
25
 */
26
class HtmlHelper extends CakeHtmlHelper
27
{
28
29
    /**
30
     * Meta data for helper
31
     */
32
    protected $metadata = [
33
        'description' => '',
34
        'author' => '',
35
        'docType' => '',
36
        'project' => [
37
            'name' => '',
38
            'version' => '',
39
        ],
40
    ];
41
42
    /**
43
     * Initialize the meta data
44
     * Merge data to $this->metadata from configure 'Meta', if set
45
     * Merge data to $this->metadata from $config['meta'], if set
46
     *
47
     * @param array $config Configuration settings for the helper.
48
     * @return void
49
     */
50
    public function initialize(array $config)
51
    {
52
        parent::initialize($config);
53
        if ($meta = Configure::read('Meta')) {
54
            $this->metadata = $meta + $this->metadata;
55
        }
56
        if (isset($config['meta'])) {
57
            $this->metadata = $config['meta'] + $this->metadata;
58
        }
59
    }
60
61
    /**
62
     * Title for template pages
63
     * If `_title` view var is set, return it
64
     * Otherwise return controller name (and action name if set)
65
     *
66
     * @return string
67
     */
68
    public function title(): string
69
    {
70
        $titleVar = $this->getView()->get('_title');
71
        if ($titleVar !== null) {
72
            return $titleVar;
73
        }
74
        $title = Inflector::humanize($this->getView()->getRequest()->getParam('controller', ''));
75
        $suffix = Inflector::humanize($this->getView()->getRequest()->getParam('action', ''));
76
        if (empty($title)) {
77
            $title = $suffix;
78
        } elseif (!empty($suffix)) {
79
            $title .= sprintf(' - %s', $suffix);
80
        }
81
82
        return $title;
83
    }
84
85
    /**
86
     * Html meta
87
     * Possible meta data:
88
     *
89
     *  - description
90
     *  - author
91
     *  - docType
92
     *  - project.name
93
     *  - project.version
94
     *
95
     * @param array $data Data for meta: 'description', 'author', 'docType', 'project' (['name' => '...', 'version' => '...'], ...)
96
     * @return string
97
     * @see HtmlHelper
98
     */
99
    public function metaAll(array $data): string
100
    {
101
        $html = '';
102
103
        // description
104
        $description = (string)$this->getMeta($data, 'description', '');
105
        $html .= $this->metaDescription($description);
106
107
        // author
108
        $author = (string)$this->getMeta($data, 'author', '');
109
        $html .= $this->metaAuthor($author);
110
111
        // css
112
        $docType = (string)$this->getMeta($data, 'docType', 'xhtml-strict');
113
        $html .= $this->metaCss($docType);
114
115
        // generator
116
        $project = (array)$this->getMeta($data, 'project', []);
117
        $html .= $this->metaGenerator($project);
118
119
        // other data
120
        $keys = ['description', 'author', 'docType', 'project'];
121
        $otherdata = array_diff_key($data, array_flip($keys));
122
        if (empty($otherdata)) {
123
            return $html;
124
        }
125
        foreach ($otherdata as $attribute) {
126
            if (!empty($otherdata[$attribute])) {
127
                $html .= $this->meta([
128
                    'name' => $attribute,
129
                    'content' => $otherdata[$attribute],
130
                ]);
131
            }
132
        }
133
134
        return $html;
135
    }
136
137
    /**
138
     * Return html meta description tag for passed description argument
139
     *
140
     * @param string|null $description The description
141
     * @return string
142
     */
143
    public function metaDescription(?string $description): string
144
    {
145
        if (empty($description)) {
146
            return '';
147
        }
148
        $html = $this->meta('description', h(strip_tags($description)));
149
        if ($html === null) {
150
            $html = '';
151
        }
152
153
        return $html;
154
    }
155
156
    /**
157
     * Return html meta author tag for passed creator argument
158
     *
159
     * @param string|null $creator The content creator
160
     * @return string
161
     */
162
    public function metaAuthor(?string $creator): string
163
    {
164
        if (empty($creator)) {
165
            $creator = (string)$this->getMeta([], 'author', '');
166
            if (empty($creator)) {
167
                return '';
168
            }
169
        }
170
        $html = $this->meta([
171
            'name' => 'author',
172
            'content' => h($creator),
173
        ]);
174
        if ($html === null) {
175
            $html = '';
176
        }
177
178
        return $html;
179
    }
180
181
    /**
182
     * Return html meta css tag for passed doc type
183
     *
184
     * @param string $docType The doc type
185
     * @return string
186
     */
187
    public function metaCss(string $docType): string
188
    {
189
        if ($docType === 'html5') {
190
            $docType = (string)$this->getMeta([], 'docType', '');
191
            if (empty($docType)) {
192
                return '';
193
            }
194
        }
195
        $html = $this->meta([
196
            'http-equiv' => 'Content-Style-Type',
197
            'content' => 'text/css',
198
        ]);
199
        if ($html === null) {
200
            $html = '';
201
        }
202
203
        return $html;
204
    }
205
206
    /**
207
     * Return html meta for generator by project name and version passed
208
     *
209
     * @param array $project The project data ('name', 'version')
210
     * @return string
211
     */
212
    public function metaGenerator(array $project): string
213
    {
214
        if (empty($project['name'])) {
215
            $project = (array)$this->getMeta([], 'project', []);
216
            if (empty($project['name'])) {
217
                return '';
218
            }
219
        }
220
        $version = '';
221
        if (!empty($project['version'])) {
222
            $version = $project['version'];
223
        }
224
        $html = $this->meta([
225
            'name' => 'generator',
226
            'content' => trim(sprintf('%s %s', $project['name'], $version)),
227
        ]);
228
        if ($html === null) {
229
            $html = '';
230
        }
231
232
        return $html;
233
    }
234
235
    /**
236
     * Return html meta for opengraph / facebook
237
     * OG fields:
238
     *
239
     *  - og:title
240
     *  - og:type
241
     *  - og:url
242
     *  - og:image
243
     *
244
     * OG optional fields:
245
     *
246
     *  - og:audio
247
     *  - og:description
248
     *  - og:determiner
249
     *  - og:locale
250
     *  - og:locale:alternate
251
     *  - og:site_name
252
     *  - og:video
253
     *
254
     * OG structured fields:
255
     *
256
     *  - og:image:url // identical to og:image
257
     *  - og:image:secure_url
258
     *  - og:image:type
259
     *  - og:image:width
260
     *  - og:image:height
261
     *  - og:image:alt
262
     *  - og:video:url // identical to og:video
263
     *  - og:video:secure_url
264
     *  - og:video:type
265
     *  - og:video:width
266
     *  - og:video:height
267
     *  - og:audio
268
     *  - og:secure_url
269
     *  - og:type
270
     *
271
     * For details @see http://ogp.me
272
     *
273
     * @param array $data The data ('title', 'type', 'image', 'url')
274
     * @return string
275
     */
276
    public function metaOpenGraph(array $data): string
277
    {
278
        $html = '';
279
        foreach ($data as $attribute => $val) {
280
            $tmp = $this->meta([
281
                'property' => sprintf('og:%s', $attribute),
282
                'content' => $val,
283
            ]);
284
            if ($tmp !== null) {
285
                $html .= $tmp;
286
            }
287
        }
288
289
        return $html;
290
    }
291
292
    /**
293
     * Return html meta for twitter
294
     * twitter fields:
295
     *
296
     *  - twitter:card
297
     *  - twitter:site
298
     *  - twitter:site:id
299
     *  - twitter:creator
300
     *  - twitter:creator:id
301
     *  - twitter:description
302
     *  - twitter:title
303
     *  - twitter:image
304
     *  - twitter:image:alt
305
     *  - twitter:player
306
     *  - twitter:player:width
307
     *  - twitter:player:height
308
     *  - twitter:player:stream
309
     *  - twitter:app:name:iphone
310
     *  - twitter:app:id:iphone
311
     *  - twitter:app:url:iphone
312
     *  - twitter:app:name:ipad
313
     *  - twitter:app:id:ipad
314
     *  - twitter:app:url:ipad
315
     *  - twitter:app:name:googleplay
316
     *  - twitter:app:id:googleplay
317
     *  - twitter:app:url:googleplay
318
     *
319
     * For details @see https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/markup.html
320
     *
321
     * @param array $data The data ('card', 'site', 'creator', 'title', 'description', 'image')
322
     * @return string
323
     */
324
    public function metaTwitter(array $data): string
325
    {
326
        $html = '';
327
        foreach ($data as $attribute => $val) {
328
            $tmp = $this->meta([
329
                'property' => sprintf('twitter:%s', $attribute),
330
                'content' => $val,
331
            ]);
332
            if ($tmp !== null) {
333
                $html .= $tmp;
334
            }
335
        }
336
337
        return $html;
338
    }
339
340
    /**
341
     * Return meta by data and field
342
     *
343
     * @param array $data The data
344
     * @param string $field The field
345
     * @param array|string|null $defaultVal The default val
346
     * @return array|string
347
     */
348
    public function getMeta(array $data, string $field, $defaultVal = null)
349
    {
350
        $meta = $data + $this->metadata;
351
352
        return Hash::get($meta, $field, $defaultVal);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Cake\Utility\Hash...a, $field, $defaultVal) also could return the type ArrayAccess which is incompatible with the documented return type array|string.
Loading history...
353
    }
354
355
    /**
356
     * Use `AssetsRevisions` class to load revisioned assets.
357
     *
358
     * {@inheritDoc}
359
     */
360
    public function script($url, array $options = []): ?string
361
    {
362
        if (is_array($url)) {
363
            $url = AssetsRevisions::getMulti($url, '.js');
364
        } else {
365
            $url = AssetsRevisions::get($url, '.js');
366
        }
367
368
        return parent::script($url, $options);
369
    }
370
371
    /**
372
     * Use `AssetsRevisions` class to load revisioned assets.
373
     *
374
     * {@inheritDoc}
375
     */
376
    public function css($url, array $options = []): ?string
377
    {
378
        if (is_array($url)) {
379
            $url = AssetsRevisions::getMulti($url, '.css');
380
        } else {
381
            $url = AssetsRevisions::get($url, '.css');
382
        }
383
384
        return parent::css($url, $options);
385
    }
386
}
387