Issues (257)

src/models/MetaScript.php (4 issues)

1
<?php
2
/**
3
 * SEOmatic plugin for Craft CMS
4
 *
5
 * A turnkey SEO implementation for Craft CMS that is comprehensive, powerful,
6
 * and flexible
7
 *
8
 * @link      https://nystudio107.com
9
 * @copyright Copyright (c) 2017 nystudio107
10
 */
11
12
namespace nystudio107\seomatic\models;
13
14
use Craft;
15
use nystudio107\codeeditor\validators\TwigTemplateValidator;
16
use nystudio107\seomatic\base\NonceItem;
17
use nystudio107\seomatic\helpers\PluginTemplate as PluginTemplateHelper;
18
use yii\web\View;
19
use function is_string;
20
21
/**
22
 * @author    nystudio107
23
 * @package   Seomatic
24
 * @since     3.0.0
25
 */
26
class MetaScript extends NonceItem
27
{
28
    // Constants
29
    // =========================================================================
30
31
    public const ITEM_TYPE = 'MetaScript';
32
33
    // Static Methods
34
    // =========================================================================
35
    /**
36
     * @var string
37
     */
38
    public $name;
39
40
    // Public Properties
41
    // =========================================================================
42
43
    /**
44
     * @var string
45
     */
46
    public $description;
47
48
    /**
49
     * @var string
50
     */
51
    public $templatePath;
52
53
    /**
54
     * @var string
55
     */
56
    public $templateString;
57
58
    /**
59
     * @var int
60
     */
61
    public $position = View::POS_HEAD;
62
63
    /**
64
     * @var string
65
     */
66
    public $bodyTemplatePath;
67
68
    /**
69
     * @var string
70
     */
71
    public $bodyTemplateString;
72
73
    /**
74
     * @var int
75
     */
76
    public $bodyPosition = View::POS_BEGIN;
77
78
    /**
79
     * @var array
80
     */
81
    public $vars;
82
83
    /**
84
     * @var array
85
     */
86
    public $dataLayer = [];
87
88
    /**
89
     * @var bool Whether this tag is deprecated or not
90
     */
91
    public $deprecated = false;
92
93
    /**
94
     * @var string The deprecation notice to display
95
     */
96
    public $deprecationNotice = '';
97
98
    /**
99
     * @var bool Whether this tag is discontinued, and should not be displayed or rendered
100
     */
101
    public $discontinued = false;
102
103
    /**
104
     * @param array $config
105
     *
106
     * @return MetaScript
107
     */
108
    public static function create(array $config = []): MetaScript
109
    {
110
        $model = new MetaScript($config);
111
        // Load $templateString from the source template if it's not set
112
        if (empty($model->templateString) && !empty($model->templatePath)) {
113
            $model->templateString = $model->loadTemplate($model->templatePath);
114
        }
115
116
        // Load $templateString from the source template if it's not set
117
        if (empty($model->bodyTemplateString) && !empty($model->bodyTemplatePath)) {
118
            $model->bodyTemplateString = $model->loadTemplate($model->bodyTemplatePath);
119
        }
120
121
        return $model;
122
    }
123
124
    // Public Methods
125
    // =========================================================================
126
127
    /**
128
     * @inheritdoc
129
     */
130
    public function init(): void
131
    {
132
        parent::init();
133
134
        $this->key = $this->key ?: lcfirst($this->name);
135
    }
136
137
    /**
138
     * Load the existing template into a string
139
     *
140
     * @param string $templatePath
141
     *
142
     * @return string
143
     */
144
    public function loadTemplate(string $templatePath): string
145
    {
146
        $result = '';
147
        // Try it from our plugin directory first
148
        $path = Craft::getAlias('@nystudio107/seomatic/templates/')
0 ignored issues
show
Are you sure Craft::getAlias('@nystud...7/seomatic/templates/') of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

148
        $path = /** @scrutinizer ignore-type */ Craft::getAlias('@nystudio107/seomatic/templates/')
Loading history...
149
            . $templatePath;
150
        if (file_exists($path)) {
151
            $result = @file_get_contents($path);
152
        } else {
153
            // Next try it from the Craft template directory
154
            $path = Craft::getAlias('@templates/')
0 ignored issues
show
Are you sure Craft::getAlias('@templates/') of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

154
            $path = /** @scrutinizer ignore-type */ Craft::getAlias('@templates/')
Loading history...
155
                . $templatePath;
156
            if (file_exists($path)) {
157
                $result = @file_get_contents($path);
158
            }
159
        }
160
161
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
162
    }
163
164
    /**
165
     * @inheritdoc
166
     */
167
    public function rules(): array
168
    {
169
        $rules = parent::rules();
170
        $rules = array_merge($rules, [
171
            [
172
                [
173
                    'name',
174
                    'description',
175
                    'templatePath',
176
                    'templateString',
177
                    'bodyTemplatePath',
178
                    'bodyTemplateString',
179
                ],
180
                'string',
181
            ],
182
            [
183
                [
184
                    'position',
185
                ],
186
                'integer',
187
            ],
188
            [
189
                [
190
                    'name',
191
                    'description',
192
                    'position',
193
                ],
194
                'required',
195
            ],
196
            [
197
                [
198
                    'templateString',
199
                    'bodyTemplateString',
200
                ],
201
                TwigTemplateValidator::class,
202
            ],
203
            [['vars'], 'safe'],
204
            [['dataLayer'], 'safe'],
205
        ]);
206
207
        return $rules;
208
    }
209
210
    /**
211
     * @inheritdoc
212
     */
213
    public function fields(): array
214
    {
215
        $fields = parent::fields();
216
        switch ($this->scenario) {
217
            case 'render':
218
                $fields = array_diff_key(
219
                    $fields,
220
                    array_flip([
221
                        'name',
222
                        'description',
223
                        'templatePath',
224
                        'templateString',
225
                        'position',
226
                        'bodyTemplatePath',
227
                        'bodyTemplateString',
228
                        'bodyPosition',
229
                        'vars',
230
                        'dataLayer',
231
                    ])
232
                );
233
                break;
234
        }
235
236
        return $fields;
237
    }
238
239
    /**
240
     * @inheritdoc
241
     */
242
    public function prepForRender(&$data): bool
243
    {
244
        $shouldRender = parent::prepForRender($data);
245
        if ($this->discontinued) {
246
            return false;
247
        }
248
249
        return $shouldRender;
250
    }
251
252
    /**
253
     * Render the script body HTML
254
     *
255
     * @param array $params
256
     *
257
     * @return string
258
     */
259
    public function renderBodyHtml(array $params = []): string
260
    {
261
        $html = '';
262
        if (!empty($this->bodyTemplatePath) && $this->prepForRender($params)) {
263
            $variables = $this->normalizeScriptVars();
264
            if (!empty($this->bodyTemplateString)) {
265
                $html = PluginTemplateHelper::renderStringTemplate($this->bodyTemplateString, $variables);
266
            }
267
        }
268
269
        return $html;
270
    }
271
272
    /**
273
     * @inheritdoc
274
     */
275
    public function render(array $params = []): string
276
    {
277
        $html = '';
278
        if (!empty($this->templatePath) && $this->prepForRender($params)) {
279
            $variables = $this->normalizeScriptVars();
280
            $html = PluginTemplateHelper::renderStringTemplate($this->templateString, $variables);
281
        }
282
283
        if (empty($html) && !empty($this->templatePath)) {
284
            $html = '/* ' . $this->name . Craft::t('seomatic', ' script did not render') . ' */' . PHP_EOL;
285
        }
286
287
        return $html;
288
    }
289
290
    /**
291
     * @inheritdoc
292
     */
293
    public function renderAttributes(array $params = []): array
294
    {
295
        $attributes = [];
296
297
        if ($this->prepForRender($options)) {
298
            $variables = $this->normalizeScriptVars();
299
            if (!empty($this->templateString)) {
300
                $attributes['script']
301
                    = PluginTemplateHelper::renderStringTemplate($this->templateString, $variables);
302
            }
303
            if (!empty($this->bodyTemplateString)) {
304
                $attributes['bodyScript']
305
                    = PluginTemplateHelper::renderStringTemplate($this->bodyTemplateString, $variables);
306
            }
307
        }
308
309
        return $attributes;
310
    }
311
312
    /**
313
     * Normalize the script variables by parsing them as environment variables, and trimming whitespace
314
     *
315
     * @return array
316
     */
317
    private function normalizeScriptVars(): array
318
    {
319
        $variables = array_merge($this->vars, [
320
            'dataLayer' => $this->dataLayer,
321
        ]);
322
        foreach ($variables as $key => $value) {
323
            if (!empty($value['value']) && is_string($value['value'])) {
324
                $variables[$key]['value'] = Craft::parseEnv($value['value']);
0 ignored issues
show
Deprecated Code introduced by
The function Craft::parseEnv() has been deprecated: in 3.7.29. [[App::parseEnv()]] should be used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

324
                $variables[$key]['value'] = /** @scrutinizer ignore-deprecated */ Craft::parseEnv($value['value']);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
325
                $variables[$key]['value'] = trim($variables[$key]['value']);
326
            }
327
        }
328
329
        return $variables;
330
    }
331
}
332