Issues (231)

src/models/MetaScript.php (4 issues)

1
<?php
2
/**
3
 * SEOmatic plugin for Craft CMS 3.x
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 nystudio107\seomatic\Seomatic;
19
use yii\web\View;
20
use function is_string;
21
22
/**
23
 * @author    nystudio107
24
 * @package   Seomatic
25
 * @since     3.0.0
26
 */
27
class MetaScript extends NonceItem
28
{
29
    // Constants
30
    // =========================================================================
31
32
    const ITEM_TYPE = 'MetaScript';
33
34
    // Static Methods
35
    // =========================================================================
36
    /**
37
     * @var string
38
     */
39
    public $name;
40
41
    // Public Properties
42
    // =========================================================================
43
44
    /**
45
     * @var string
46
     */
47
    public $description;
48
49
    /**
50
     * @var string
51
     */
52
    public $templatePath;
53
54
    /**
55
     * @var string
56
     */
57
    public $templateString;
58
59
    /**
60
     * @var int
61
     */
62
    public $position = View::POS_HEAD;
63
64
    /**
65
     * @var string
66
     */
67
    public $bodyTemplatePath;
68
69
    /**
70
     * @var string
71
     */
72
    public $bodyTemplateString;
73
74
    /**
75
     * @var int
76
     */
77
    public $bodyPosition = View::POS_BEGIN;
78
79
    /**
80
     * @var array
81
     */
82
    public $vars;
83
84
    /**
85
     * @var array
86
     */
87
    public $dataLayer = [];
88
89
    /**
90
     * @var bool Whether this tag is deprecated or not
91
     */
92
    public $deprecated = false;
93
94
    /**
95
     * @var string The deprecation notice to display
96
     */
97
    public $deprecationNotice = '';
98
99
    /**
100
     * @var bool Whether this tag is discontinued, and should not be displayed or rendered
101
     */
102
    public $discontinued = false;
103
104
    /**
105
     * @param array $config
106
     *
107
     * @return MetaScript
108
     */
109
    public static function create(array $config = []): MetaScript
110
    {
111
        $model = new MetaScript($config);
112
        // Load $templateString from the source template if it's not set
113
        if (empty($model->templateString) && !empty($model->templatePath)) {
114
            $model->templateString = $model->loadTemplate($model->templatePath);
115
        }
116
117
        // Load $templateString from the source template if it's not set
118
        if (empty($model->bodyTemplateString) && !empty($model->bodyTemplatePath)) {
119
            $model->bodyTemplateString = $model->loadTemplate($model->bodyTemplatePath);
120
        }
121
122
        return $model;
123
    }
124
125
    // Public Methods
126
    // =========================================================================
127
128
    /**
129
     * @inheritdoc
130
     */
131
    public function init()
132
    {
133
        parent::init();
134
135
        $this->key = $this->key ?: lcfirst($this->name);
136
    }
137
138
    /**
139
     * Load the existing template into a string
140
     *
141
     * @param string $templatePath
142
     *
143
     * @return string
144
     */
145
    public function loadTemplate(string $templatePath): string
146
    {
147
        $result = '';
148
        // Try it from our plugin directory first
149
        $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

149
        $path = /** @scrutinizer ignore-type */ Craft::getAlias('@nystudio107/seomatic/templates/')
Loading history...
150
            . $templatePath;
151
        if (file_exists($path)) {
152
            $result = @file_get_contents($path);
153
        } else {
154
            // Next try it from the Craft template directory
155
            $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

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

326
                    $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...
327
                    $variables[$key]['value'] = trim($variables[$key]['value']);
328
                }
329
            }
330
        }
331
332
        return $variables;
333
    }
334
}
335