HtmlHelper::metaDescription()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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