Test Failed
Push — v4 ( b350db...8df9dc )
by Andrew
21:15 queued 10:54
created

SettingsController::getCpSiteHandle()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 10
ccs 0
cts 1
cp 0
rs 10
cc 2
nc 2
nop 1
crap 6
1
<?php
2
/**
3
 * SEOmatic plugin for Craft CMS
4
 *
5
 * @link      https://nystudio107.com/
6
 * @copyright Copyright (c) 2017 nystudio107
7
 * @license   https://nystudio107.com/license
8
 */
9
10
namespace nystudio107\seomatic\controllers;
11
12
use Craft;
13
use craft\elements\Asset;
0 ignored issues
show
Bug introduced by
The type craft\elements\Asset was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use craft\errors\MissingComponentException;
15
use craft\helpers\Cp;
16
use craft\helpers\UrlHelper;
17
use craft\web\Controller;
18
use craft\web\UrlManager;
19
use DateTime;
20
use nystudio107\seomatic\assetbundles\seomatic\SeomaticAsset;
21
use nystudio107\seomatic\autocompletes\TrackingVarsAutocomplete;
22
use nystudio107\seomatic\helpers\ArrayHelper;
23
use nystudio107\seomatic\helpers\DynamicMeta as DynamicMetaHelper;
24
use nystudio107\seomatic\helpers\Field as FieldHelper;
25
use nystudio107\seomatic\helpers\ImageTransform as ImageTransformHelper;
26
use nystudio107\seomatic\helpers\PullField as PullFieldHelper;
27
use nystudio107\seomatic\helpers\Schema as SchemaHelper;
28
use nystudio107\seomatic\models\MetaBundle;
29
use nystudio107\seomatic\models\MetaScript;
30
use nystudio107\seomatic\models\MetaScriptContainer;
31
use nystudio107\seomatic\Seomatic;
32
use nystudio107\seomatic\services\FrontendTemplates;
33
use nystudio107\seomatic\services\MetaBundles;
34
use yii\base\InvalidConfigException;
35
use yii\web\BadRequestHttpException;
36
use yii\web\ForbiddenHttpException;
37
use yii\web\NotFoundHttpException;
38
use yii\web\Response;
39
use function count;
40
use function in_array;
41
use function is_array;
42
43
/**
44
 * @author    nystudio107
45
 * @package   Seomatic
46
 * @since     3.0.0
47
 */
48
class SettingsController extends Controller
49
{
50
    // Constants
51
    // =========================================================================
52
53
    public const DOCUMENTATION_URL = 'https://github.com/nystudio107/craft-seomatic';
54
55
    public const SETUP_GRADES = [
56
        ['id' => 'data1', 'name' => 'A', 'color' => '#008002'],
57
        ['id' => 'data2', 'name' => 'B', 'color' => '#9ACD31'],
58
        ['id' => 'data4', 'name' => 'C', 'color' => '#FFA500'],
59
        ['id' => 'data5', 'name' => 'D', 'color' => '#8B0100'],
60
    ];
61
62
    public const SEO_SETUP_FIELDS = [
63
        'mainEntityOfPage' => 'Main Entity of Page',
64
        'seoTitle' => 'SEO Title',
65
        'seoDescription' => 'SEO Description',
66
        'seoKeywords' => 'SEO Keywords',
67
        'seoImage' => 'SEO Image',
68
        'seoImageDescription' => 'SEO Image Description',
69
    ];
70
71
    public const SITE_SETUP_FIELDS = [
72
        'siteName' => 'Site Name',
73
        'twitterHandle' => 'Twitter Handle',
74
        'facebookProfileId' => 'Facebook Profile ID',
75
    ];
76
77
    public const IDENTITY_SETUP_FIELDS = [
78
        'computedType' => 'Identity Entity Type',
79
        'genericName' => 'Identity Entity Name',
80
        'genericDescription' => 'Identity Entity Description',
81
        'genericUrl' => 'Identity Entity URL',
82
        'genericImage' => 'Identity Entity Brand',
83
    ];
84
85
    // Protected Properties
86
    // =========================================================================
87
88
    /**
89
     * @inheritdoc
90
     */
91
    protected array|bool|int $allowAnonymous = [
92
    ];
93
94
    // Public Methods
95
    // =========================================================================
96
97
    /**
98
     * Dashboard display
99
     *
100
     * @param string|null $siteHandle
101
     * @param bool $showWelcome
102
     *
103
     * @return Response The rendered result
104
     * @throws NotFoundHttpException
105
     * @throws ForbiddenHttpException
106
     */
107
    public function actionDashboard(string $siteHandle = null, bool $showWelcome = false): Response
108
    {
109
        $variables = [];
110
        // Get the site to edit
111
        $siteHandle = $this->getCpSiteHandle($siteHandle);
112
        $siteId = $this->getSiteIdFromHandle($siteHandle);
113
        $pluginName = Seomatic::$settings->pluginName;
114
        $templateTitle = Craft::t('seomatic', 'Dashboard');
115
        // Asset bundle
116
        try {
117
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
0 ignored issues
show
Bug introduced by
The method registerAssetBundle() does not exist on null. ( Ignorable by Annotation )

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

117
            Seomatic::$view->/** @scrutinizer ignore-call */ 
118
                             registerAssetBundle(SeomaticAsset::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
118
        } catch (InvalidConfigException $e) {
119
            Craft::error($e->getMessage(), __METHOD__);
120
        }
121
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
122
            '@nystudio107/seomatic/web/assets/dist',
123
            true
0 ignored issues
show
Unused Code introduced by
The call to yii\web\AssetManager::getPublishedUrl() has too many arguments starting with true. ( Ignorable by Annotation )

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

123
        /** @scrutinizer ignore-call */ 
124
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
124
        );
125
        // Enabled sites
126
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
127
        $variables['controllerHandle'] = 'dashboard';
128
129
        // Basic variables
130
        $variables['fullPageForm'] = false;
131
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
132
        $variables['pluginName'] = Seomatic::$settings->pluginName;
133
        $variables['title'] = $templateTitle;
134
        $variables['docTitle'] = "{$pluginName} - {$templateTitle}";
135
        $siteHandleUri = Craft::$app->isMultiSite ? '/' . $siteHandle : '';
136
        $variables['crumbs'] = [
137
            [
138
                'label' => $pluginName,
139
                'url' => UrlHelper::cpUrl('seomatic'),
140
            ],
141
            [
142
                'label' => $templateTitle,
143
                'url' => UrlHelper::cpUrl('seomatic/dashboard' . $siteHandleUri),
144
            ],
145
        ];
146
        $variables['selectedSubnavItem'] = 'dashboard';
147
        $variables['showWelcome'] = $showWelcome;
148
        // Calulate the setup grades
149
        $variables['contentSetupStats'] = [];
150
        $variables['setupGrades'] = self::SETUP_GRADES;
151
        $numFields = count(self::SEO_SETUP_FIELDS);
152
        $numGrades = count(self::SETUP_GRADES);
153
        while ($numGrades--) {
154
            $variables['contentSetupStats'][] = 0;
155
        }
156
        $numGrades = count(self::SETUP_GRADES);
157
        // Content SEO grades
158
        $variables['metaBundles'] = Seomatic::$plugin->metaBundles->getContentMetaBundlesForSiteId($siteId);
159
        $variables['contentSetupChecklistCutoff'] = floor(count($variables['metaBundles']) / 2);
160
        $variables['contentSetupChecklist'] = [];
161
        Seomatic::$plugin->metaBundles->pruneVestigialMetaBundles($variables['metaBundles']);
162
        /** @var MetaBundle $metaBundle */
163
        foreach ($variables['metaBundles'] as $metaBundle) {
164
            $stat = 0;
165
            foreach (self::SEO_SETUP_FIELDS as $setupField => $setupLabel) {
166
                $stat += (int)!empty($metaBundle->metaGlobalVars[$setupField]);
167
                $value = $variables['contentSetupChecklist'][$setupField]['value'] ?? 0;
168
                $variables['contentSetupChecklist'][$setupField] = [
169
                    'label' => $setupLabel,
170
                    'value' => $value + (int)!empty($metaBundle->metaGlobalVars[$setupField]),
171
                ];
172
            }
173
            $stat = round($numGrades - (($stat * $numGrades) / $numFields));
174
            if ($stat >= $numGrades) {
175
                $stat = $numGrades - 1;
176
            }
177
            $variables['contentSetupStats'][$stat]++;
178
        }
179
        // Global SEO grades
180
        Seomatic::$previewingMetaContainers = true;
181
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle((int)$siteId);
182
        Seomatic::$previewingMetaContainers = false;
183
        if ($metaBundle !== null) {
184
            $stat = 0;
185
            $variables['globalSetupChecklist'] = [];
186
            foreach (self::SEO_SETUP_FIELDS as $setupField => $setupLabel) {
187
                $stat += (int)!empty($metaBundle->metaGlobalVars[$setupField]);
188
                $variables['globalSetupChecklist'][$setupField] = [
189
                    'label' => $setupLabel,
190
                    'value' => (int)!empty($metaBundle->metaGlobalVars[$setupField]),
191
                ];
192
            }
193
            $stat = round(($stat / $numFields) * 100);
194
            $variables['globalSetupStat'] = $stat;
195
            // Site Settings grades
196
            $numFields = count(self::SITE_SETUP_FIELDS) + count(self::IDENTITY_SETUP_FIELDS);
197
            $stat = 0;
198
            $variables['siteSetupChecklist'] = [];
199
            foreach (self::SITE_SETUP_FIELDS as $setupField => $setupLabel) {
200
                $stat += (int)!empty($metaBundle->metaSiteVars[$setupField]);
201
                $variables['siteSetupChecklist'][$setupField] = [
202
                    'label' => $setupLabel,
203
                    'value' => (int)!empty($metaBundle->metaSiteVars[$setupField]),
204
                ];
205
            }
206
            foreach (self::IDENTITY_SETUP_FIELDS as $setupField => $setupLabel) {
207
                $stat += (int)!empty($metaBundle->metaSiteVars->identity[$setupField]);
208
                $variables['siteSetupChecklist'][$setupField] = [
209
                    'label' => $setupLabel,
210
                    'value' => (int)!empty($metaBundle->metaSiteVars->identity[$setupField]),
211
                ];
212
            }
213
            $stat = round(($stat / $numFields) * 100);
214
            $variables['siteSetupStat'] = $stat;
215
        }
216
217
        // Render the template
218
        return $this->renderTemplate('seomatic/dashboard/index', $variables);
219
    }
220
221
    /**
222
     * Global settings
223
     *
224
     * @param string $subSection
225
     * @param string|null $siteHandle
226
     * @param string|null $loadFromSiteHandle
227
     *
228
     * @return Response The rendered result
229
     * @throws NotFoundHttpException
230
     * @throws ForbiddenHttpException
231
     */
232
    public function actionGlobal(string $subSection = 'general', string $siteHandle = null, $loadFromSiteHandle = null, $editedMetaBundle = null): Response
233
    {
234
        $variables = [];
235
        $siteHandle = $this->getCpSiteHandle($siteHandle);
236
        $siteId = $this->getSiteIdFromHandle($siteHandle);
237
238
        $pluginName = Seomatic::$settings->pluginName;
239
        $templateTitle = Craft::t('seomatic', 'Global SEO');
240
        $subSectionTitle = Craft::t('seomatic', ucfirst($subSection));
241
        // Asset bundle
242
        try {
243
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
244
        } catch (InvalidConfigException $e) {
245
            Craft::error($e->getMessage(), __METHOD__);
246
        }
247
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
248
            '@nystudio107/seomatic/web/assets/dist',
249
            true
0 ignored issues
show
Unused Code introduced by
The call to yii\web\AssetManager::getPublishedUrl() has too many arguments starting with true. ( Ignorable by Annotation )

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

249
        /** @scrutinizer ignore-call */ 
250
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
250
        );
251
        // Basic variables
252
        $variables['fullPageForm'] = true;
253
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
254
        $variables['pluginName'] = Seomatic::$settings->pluginName;
255
        $variables['title'] = $templateTitle;
256
        $variables['subSectionTitle'] = $subSectionTitle;
257
        $variables['docTitle'] = "{$pluginName} - {$templateTitle} - {$subSectionTitle}";
258
        $siteHandleUri = Craft::$app->isMultiSite ? '/' . $siteHandle : '';
259
        $variables['crumbs'] = [
260
            [
261
                'label' => $pluginName,
262
                'url' => UrlHelper::cpUrl('seomatic'),
263
            ],
264
            [
265
                'label' => $templateTitle,
266
                'url' => UrlHelper::cpUrl('seomatic/global/general' . $siteHandleUri),
267
            ],
268
            [
269
                'label' => $subSectionTitle,
270
                'url' => UrlHelper::cpUrl('seomatic/global/' . $subSection . $siteHandleUri),
271
            ],
272
        ];
273
        $variables['selectedSubnavItem'] = 'global';
274
        // Pass in the pull fields
275
        $this->setGlobalFieldSourceVariables($variables);
276
        // Enabled sites
277
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
278
        $variables['controllerHandle'] = 'global' . '/' . $subSection;
279
        $variables['currentSubSection'] = $subSection;
280
        // Meta bundle settings
281
        Seomatic::$previewingMetaContainers = true;
282
        // Get the site to copy the settings from, if any
283
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
284
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
285
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always false.
Loading history...
286
        // Load the metabundle
287
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteIdToLoad);
0 ignored issues
show
Bug introduced by
It seems like $siteIdToLoad can also be of type null; however, parameter $sourceSiteId of nystudio107\seomatic\ser...::getGlobalMetaBundle() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

287
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle(/** @scrutinizer ignore-type */ $siteIdToLoad);
Loading history...
288
        if ($editedMetaBundle) {
289
            $metaBundle = $editedMetaBundle;
290
        }
291
        Seomatic::$previewingMetaContainers = false;
292
        if ($metaBundle !== null) {
293
            $variables['metaGlobalVars'] = clone $metaBundle->metaGlobalVars;
294
            $variables['metaSitemapVars'] = $metaBundle->metaSitemapVars;
295
            $variables['metaBundleSettings'] = $metaBundle->metaBundleSettings;
296
            // Template container settings
297
            $templateContainers = $metaBundle->frontendTemplatesContainer->data;
298
            $variables['robotsTemplate'] = $templateContainers[FrontendTemplates::ROBOTS_TXT_HANDLE];
299
            $variables['humansTemplate'] = $templateContainers[FrontendTemplates::HUMANS_TXT_HANDLE];
300
            // Handle an edge-case where a migration didn't work properly to add ADS_TXT_HANDLE
301
            if (!isset($templateContainers[FrontendTemplates::ADS_TXT_HANDLE])) {
302
                $globalMetaBundle = Seomatic::$plugin->metaBundles->createGlobalMetaBundleForSite($siteId, $metaBundle);
303
                $templateContainers[FrontendTemplates::ADS_TXT_HANDLE] =
304
                    $globalMetaBundle->frontendTemplatesContainer->data[FrontendTemplates::ADS_TXT_HANDLE];
305
            }
306
            // Handle an edge-case where a migration didn't work properly to add ADS_TXT_HANDLE
307
            if (!isset($templateContainers[FrontendTemplates::ADS_TXT_HANDLE])) {
308
                $globalMetaBundle = Seomatic::$plugin->metaBundles->createGlobalMetaBundleForSite($siteId, $metaBundle);
309
                $templateContainers[FrontendTemplates::ADS_TXT_HANDLE] =
310
                    $globalMetaBundle->frontendTemplatesContainer->data[FrontendTemplates::ADS_TXT_HANDLE];
311
            }
312
            $variables['adsTemplate'] = $templateContainers[FrontendTemplates::ADS_TXT_HANDLE];
313
            // Handle an edge-case where a migration didn't work properly to add SECURITY_TXT_HANDLE
314
            if (!isset($templateContainers[FrontendTemplates::SECURITY_TXT_HANDLE])) {
315
                $globalMetaBundle = Seomatic::$plugin->metaBundles->createGlobalMetaBundleForSite($siteId, $metaBundle);
316
                $templateContainers[FrontendTemplates::SECURITY_TXT_HANDLE] =
317
                    $globalMetaBundle->frontendTemplatesContainer->data[FrontendTemplates::SECURITY_TXT_HANDLE];
318
            }
319
            $variables['securityTemplate'] = $templateContainers[FrontendTemplates::SECURITY_TXT_HANDLE];
320
            // Image selectors
321
            $bundleSettings = $metaBundle->metaBundleSettings;
322
            $variables['elementType'] = Asset::class;
323
            $variables['seoImageElements'] = ImageTransformHelper::assetElementsFromIds(
324
                $bundleSettings->seoImageIds,
325
                $siteId
326
            );
327
            $variables['twitterImageElements'] = ImageTransformHelper::assetElementsFromIds(
328
                $bundleSettings->twitterImageIds,
329
                $siteId
330
            );
331
            $variables['ogImageElements'] = ImageTransformHelper::assetElementsFromIds(
332
                $bundleSettings->ogImageIds,
333
                $siteId
334
            );
335
        }
336
        // Preview the meta containers
337
        Seomatic::$plugin->metaContainers->previewMetaContainers(
338
            MetaBundles::GLOBAL_META_BUNDLE,
339
            (int)$variables['currentSiteId']
340
        );
341
342
        // Render the template
343
        return $this->renderTemplate('seomatic/settings/global/' . $subSection, $variables);
344
    }
345
346
    /**
347
     * @return Response|null
348
     * @throws BadRequestHttpException
349
     * @throws MissingComponentException
350
     */
351
    public function actionSaveGlobal()
352
    {
353
        $this->requirePostRequest();
354
        $request = Craft::$app->getRequest();
355
        $siteId = $request->getParam('siteId');
356
        $globalsSettings = $request->getParam('metaGlobalVars');
357
        $bundleSettings = $request->getParam('metaBundleSettings');
358
        $robotsTemplate = $request->getParam('robotsTemplate');
359
        $humansTemplate = $request->getParam('humansTemplate');
360
        $adsTemplate = $request->getParam('adsTemplate');
361
        $securityTemplate = $request->getParam('securityTemplate');
362
        if (is_array($securityTemplate)) {
363
            if (!str_ends_with($securityTemplate['templateString'], "\n")) {
364
                $securityTemplate['templateString'] .= "\n";
365
            }
366
        }
367
        // Set the element type in the template
368
        $elementName = '';
369
370
        $hasErrors = false;
371
        // The site settings for the appropriate meta bundle
372
        Seomatic::$previewingMetaContainers = true;
373
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteId);
374
        Seomatic::$previewingMetaContainers = false;
375
        if ($metaBundle !== null) {
376
            if (is_array($globalsSettings) && is_array($bundleSettings)) {
377
                PullFieldHelper::parseTextSources($elementName, $globalsSettings, $bundleSettings);
378
                PullFieldHelper::parseImageSources($elementName, $globalsSettings, $bundleSettings, $siteId);
379
                if (!empty($bundleSettings['siteType'])) {
380
                    $globalsSettings['mainEntityOfPage'] = SchemaHelper::getSpecificEntityType($bundleSettings);
381
                }
382
                $metaBundle->metaGlobalVars->setAttributes($globalsSettings);
0 ignored issues
show
Bug introduced by
The method setAttributes() does not exist on null. ( Ignorable by Annotation )

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

382
                $metaBundle->metaGlobalVars->/** @scrutinizer ignore-call */ 
383
                                             setAttributes($globalsSettings);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
383
                $metaBundle->metaBundleSettings->setAttributes($bundleSettings);
0 ignored issues
show
Bug introduced by
The method setAttributes() does not exist on null. ( Ignorable by Annotation )

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

383
                $metaBundle->metaBundleSettings->/** @scrutinizer ignore-call */ 
384
                                                 setAttributes($bundleSettings);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
384
            }
385
            $templateContainers = $metaBundle->frontendTemplatesContainer->data;
386
            $robotsContainer = $templateContainers[FrontendTemplates::ROBOTS_TXT_HANDLE];
387
            if ($robotsContainer !== null && is_array($robotsTemplate)) {
388
                $robotsContainer->setAttributes($robotsTemplate);
389
                if (!$robotsContainer->validate()) {
390
                    $hasErrors = true;
391
                }
392
            }
393
            $humansContainer = $templateContainers[FrontendTemplates::HUMANS_TXT_HANDLE];
394
            if ($humansContainer !== null && is_array($humansTemplate)) {
395
                $humansContainer->setAttributes($humansTemplate);
396
                if (!$humansContainer->validate()) {
397
                    $hasErrors = true;
398
                }
399
            }
400
            $adsContainer = $templateContainers[FrontendTemplates::ADS_TXT_HANDLE];
401
            if ($adsContainer !== null && is_array($adsTemplate)) {
402
                $adsContainer->setAttributes($adsTemplate);
403
                if (!$adsContainer->validate()) {
404
                    $hasErrors = true;
405
                }
406
            }
407
            $securityContainer = $templateContainers[FrontendTemplates::SECURITY_TXT_HANDLE];
408
            if ($securityContainer !== null && is_array($securityTemplate)) {
409
                $securityContainer->setAttributes($securityTemplate);
410
                if (!$securityContainer->validate()) {
411
                    $hasErrors = true;
412
                }
413
            }
414
            if (!$metaBundle->metaGlobalVars->validate()) {
415
                $hasErrors = true;
416
            }
417
418
            if ($hasErrors) {
419
                Craft::error(print_r($metaBundle->metaGlobalVars->getErrors(), true), __METHOD__);
0 ignored issues
show
Bug introduced by
It seems like print_r($metaBundle->met...ars->getErrors(), true) can also be of type true; however, parameter $message of yii\BaseYii::error() does only seem to accept array|string, maybe add an additional type check? ( Ignorable by Annotation )

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

419
                Craft::error(/** @scrutinizer ignore-type */ print_r($metaBundle->metaGlobalVars->getErrors(), true), __METHOD__);
Loading history...
420
                Craft::$app->getSession()->setError(Craft::t('app', "Couldn't save settings due to a Twig error."));
421
                // Send the redirect back to the template
422
                /** @var UrlManager $urlManager */
423
                $urlManager = Craft::$app->getUrlManager();
424
                $urlManager->setRouteParams([
425
                    'editedMetaBundle' => $metaBundle,
426
                ]);
427
428
                return null;
429
            }
430
431
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
432
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
433
434
            Seomatic::$plugin->clearAllCaches();
0 ignored issues
show
Bug introduced by
The method clearAllCaches() does not exist on null. ( Ignorable by Annotation )

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

434
            Seomatic::$plugin->/** @scrutinizer ignore-call */ 
435
                               clearAllCaches();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
435
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic global settings saved.'));
436
        }
437
438
        return $this->redirectToPostedUrl();
439
    }
440
441
    /**
442
     * Content settings
443
     *
444
     * @param string|null $siteHandle
445
     *
446
     * @return Response The rendered result
447
     * @throws NotFoundHttpException
448
     * @throws ForbiddenHttpException
449
     */
450
    public function actionContent(string $siteHandle = null): Response
451
    {
452
        $variables = [];
453
        // Get the site to edit
454
        $siteHandle = $this->getCpSiteHandle($siteHandle);
455
        $siteId = $this->getSiteIdFromHandle($siteHandle);
456
457
        $pluginName = Seomatic::$settings->pluginName;
458
        $templateTitle = Craft::t('seomatic', 'Content SEO');
459
        // Asset bundle
460
        try {
461
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
462
        } catch (InvalidConfigException $e) {
463
            Craft::error($e->getMessage(), __METHOD__);
464
        }
465
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
466
            '@nystudio107/seomatic/web/assets/dist',
467
            true
0 ignored issues
show
Unused Code introduced by
The call to yii\web\AssetManager::getPublishedUrl() has too many arguments starting with true. ( Ignorable by Annotation )

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

467
        /** @scrutinizer ignore-call */ 
468
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
468
        );
469
        // Basic variables
470
        $variables['fullPageForm'] = false;
471
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
472
        $variables['pluginName'] = Seomatic::$settings->pluginName;
473
        $variables['title'] = $templateTitle;
474
        $variables['docTitle'] = "{$pluginName} - {$templateTitle}";
475
        $siteHandleUri = Craft::$app->isMultiSite ? '/' . $siteHandle : '';
476
        $variables['crumbs'] = [
477
            [
478
                'label' => $pluginName,
479
                'url' => UrlHelper::cpUrl('seomatic'),
480
            ],
481
            [
482
                'label' => $templateTitle,
483
                'url' => UrlHelper::cpUrl('seomatic/content' . $siteHandleUri),
484
            ],
485
        ];
486
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
487
        $variables['controllerHandle'] = 'content';
488
        $variables['selectedSubnavItem'] = 'content';
489
        $metaBundles = Seomatic::$plugin->metaBundles->getContentMetaBundlesForSiteId($siteId);
490
        Seomatic::$plugin->metaBundles->deleteVestigialMetaBundles($metaBundles);
491
492
        // Render the template
493
        return $this->renderTemplate('seomatic/settings/content/index', $variables);
494
    }
495
496
    /**
497
     * Global settings
498
     *
499
     * @param string $subSection
500
     * @param string $sourceBundleType
501
     * @param string $sourceHandle
502
     * @param string|null $siteHandle
503
     * @param string|int|null $typeId
504
     * @param string|null $loadFromSiteHandle
505
     *
506
     * @return Response The rendered result
507
     * @throws NotFoundHttpException
508
     * @throws ForbiddenHttpException
509
     */
510
    public function actionEditContent(
511
        string $subSection,
512
        string $sourceBundleType,
513
        string $sourceHandle,
514
        string $siteHandle = null,
515
               $typeId = null,
516
               $loadFromSiteHandle = null,
517
    ): Response {
518
        $variables = [];
519
        // @TODO: Let people choose an entry/categorygroup/product as the preview
520
        // Get the site to edit
521
        $siteHandle = $this->getCpSiteHandle($siteHandle);
522
        $siteId = $this->getSiteIdFromHandle($siteHandle);
523
        if (is_string($typeId)) {
524
            $typeId = (int)$typeId;
525
        }
526
        // Get the (entry) type menu
527
        $typeMenu = [];
528
        $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
529
        if ($seoElement !== null) {
530
            $typeMenu = $seoElement::typeMenuFromHandle($sourceHandle);
531
        }
532
        $variables['typeMenu'] = $typeMenu;
533
        $variables['currentTypeId'] = null;
534
        $variables['specificTypeId'] = null;
535
        if (count($typeMenu) > 1) {
536
            $currentType = reset($typeMenu);
537
            $variables['currentType'] = $typeMenu[$typeId] ?? $currentType;
538
            $variables['currentTypeId'] = $typeId ?? key($typeMenu);
539
            $typeId = (int)$variables['currentTypeId'];
540
        }
541
        // If there's only one EntryType, don't bother displaying the menu
542
        if (count($typeMenu) === 1) {
543
            $variables['typeMenu'] = [];
544
            $variables['specificTypeId'] = $typeId ?? key($typeMenu);
545
        }
546
        $pluginName = Seomatic::$settings->pluginName;
547
        // Asset bundle
548
        try {
549
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
550
        } catch (InvalidConfigException $e) {
551
            Craft::error($e->getMessage(), __METHOD__);
552
        }
553
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
554
            '@nystudio107/seomatic/web/assets/dist',
555
            true
0 ignored issues
show
Unused Code introduced by
The call to yii\web\AssetManager::getPublishedUrl() has too many arguments starting with true. ( Ignorable by Annotation )

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

555
        /** @scrutinizer ignore-call */ 
556
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
556
        );
557
        // Enabled sites
558
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
559
        $this->cullDisabledSites($sourceBundleType, $sourceHandle, $variables);
560
        // Meta Bundle settings
561
        Seomatic::$previewingMetaContainers = true;
562
        // Get the site to copy the settings from, if any
563
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
564
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
565
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always false.
Loading history...
566
        // Load the metabundle
567
        $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
568
            $sourceBundleType,
569
            $sourceHandle,
570
            $siteIdToLoad,
0 ignored issues
show
Bug introduced by
It seems like $siteIdToLoad can also be of type null; however, parameter $sourceSiteId of nystudio107\seomatic\ser...aBundleBySourceHandle() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

570
            /** @scrutinizer ignore-type */ $siteIdToLoad,
Loading history...
571
            $typeId
0 ignored issues
show
Bug introduced by
It seems like $typeId can also be of type string; however, parameter $typeId of nystudio107\seomatic\ser...aBundleBySourceHandle() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

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

571
            /** @scrutinizer ignore-type */ $typeId
Loading history...
572
        );
573
        Seomatic::$previewingMetaContainers = false;
574
        $templateTitle = '';
575
        if ($metaBundle !== null) {
576
            $variables['metaGlobalVars'] = clone $metaBundle->metaGlobalVars;
577
            $variables['metaSitemapVars'] = $metaBundle->metaSitemapVars;
578
            $variables['metaBundleSettings'] = $metaBundle->metaBundleSettings;
579
            $variables['currentSourceHandle'] = $metaBundle->sourceHandle;
580
            $variables['currentSourceBundleType'] = $metaBundle->sourceBundleType;
581
            $templateTitle = $metaBundle->sourceName;
582
        }
583
        // Basic variables
584
        $subSectionTitle = Craft::t('seomatic', ucfirst($subSection));
585
        $variables['fullPageForm'] = true;
586
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
587
        $variables['pluginName'] = Seomatic::$settings->pluginName;
588
        $variables['title'] = $templateTitle;
589
        $variables['subSectionTitle'] = $subSectionTitle;
590
        $variables['docTitle'] = "{$pluginName} - Content SEO - {$templateTitle} - {$subSectionTitle}";
591
        $siteHandleUri = Craft::$app->isMultiSite ? '/' . $siteHandle : '';
592
        $variables['siteHandleUri'] = $siteHandleUri;
593
        $variables['crumbs'] = [
594
            [
595
                'label' => $pluginName,
596
                'url' => UrlHelper::cpUrl('seomatic'),
597
            ],
598
            [
599
                'label' => 'Content SEO',
600
                'url' => UrlHelper::cpUrl('seomatic/content' . $siteHandleUri),
601
            ],
602
            [
603
                'label' => $metaBundle->sourceName . ' · ' . $subSectionTitle,
604
                'url' => UrlHelper::cpUrl("seomatic/edit-content/${subSection}/${sourceBundleType}/${sourceHandle}"),
605
            ],
606
        ];
607
        $variables['selectedSubnavItem'] = 'content';
608
        $variables['controllerHandle'] = "edit-content/${subSection}/${sourceBundleType}/${sourceHandle}";
609
        // Image selectors
610
        $variables['currentSubSection'] = $subSection;
611
        $bundleSettings = $metaBundle->metaBundleSettings;
612
        $variables['elementType'] = Asset::class;
613
        $variables['seoImageElements'] = ImageTransformHelper::assetElementsFromIds(
614
            $bundleSettings->seoImageIds,
615
            $siteId
616
        );
617
        $variables['twitterImageElements'] = ImageTransformHelper::assetElementsFromIds(
618
            $bundleSettings->twitterImageIds,
619
            $siteId
620
        );
621
        $variables['ogImageElements'] = ImageTransformHelper::assetElementsFromIds(
622
            $bundleSettings->ogImageIds,
623
            $siteId
624
        );
625
        $variables['sourceType'] = $metaBundle->sourceType;
626
        // Pass in the pull fields
627
        $groupName = ucfirst($metaBundle->sourceType);
628
        $this->setContentFieldSourceVariables($sourceBundleType, $sourceHandle, $groupName, $variables, $typeId);
629
        $uri = $this->uriFromSourceBundle($sourceBundleType, $sourceHandle, $siteId, $typeId);
630
        // Preview the meta containers
631
        Seomatic::$plugin->metaContainers->previewMetaContainers(
632
            $uri,
633
            (int)$variables['currentSiteId'],
634
            false,
635
            false
636
        );
637
638
        // Render the template
639
        return $this->renderTemplate('seomatic/settings/content/' . $subSection, $variables);
640
    }
641
642
    /**
643
     * @return Response
644
     * @throws BadRequestHttpException
645
     * @throws MissingComponentException
646
     */
647
    public function actionSaveContent(): Response
648
    {
649
        $this->requirePostRequest();
650
        $request = Craft::$app->getRequest();
651
        $sourceBundleType = $request->getParam('sourceBundleType');
652
        $sourceHandle = $request->getParam('sourceHandle');
653
        $siteId = $request->getParam('siteId');
654
        $typeId = $request->getParam('typeId') ?? null;
655
        $specificTypeId = $request->getParam('specificTypeId') ?? null;
656
        $globalsSettings = $request->getParam('metaGlobalVars');
657
        $bundleSettings = $request->getParam('metaBundleSettings');
658
        $sitemapSettings = $request->getParam('metaSitemapVars');
659
        if (is_string($typeId)) {
660
            $typeId = (int)$typeId;
661
        }
662
        // Set the element type in the template
663
        $elementName = '';
664
        $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
665
        if ($seoElement !== null) {
666
            $elementName = $seoElement::getElementRefHandle();
667
        }
668
        // The site settings for the appropriate meta bundle
669
        Seomatic::$previewingMetaContainers = true;
670
        $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
671
            $sourceBundleType,
672
            $sourceHandle,
673
            $siteId,
674
            $typeId
675
        );
676
        Seomatic::$previewingMetaContainers = false;
677
        if ($metaBundle) {
678
            if (is_array($globalsSettings) && is_array($bundleSettings)) {
679
                PullFieldHelper::parseTextSources($elementName, $globalsSettings, $bundleSettings);
680
                PullFieldHelper::parseImageSources($elementName, $globalsSettings, $bundleSettings, $siteId);
681
                if (!empty($bundleSettings['siteType'])) {
682
                    $globalsSettings['mainEntityOfPage'] = SchemaHelper::getSpecificEntityType($bundleSettings);
683
                }
684
                $metaBundle->metaGlobalVars->setAttributes($globalsSettings);
685
                $metaBundle->metaBundleSettings->setAttributes($bundleSettings);
686
            }
687
            if (is_array($sitemapSettings)) {
688
                $metaBundle->metaSitemapVars->setAttributes($sitemapSettings);
0 ignored issues
show
Bug introduced by
The method setAttributes() does not exist on null. ( Ignorable by Annotation )

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

688
                $metaBundle->metaSitemapVars->/** @scrutinizer ignore-call */ 
689
                                              setAttributes($sitemapSettings);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
689
            }
690
691
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
692
            $metaBundle->typeId = $typeId;
693
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
694
            // If there's also a specific typeId associated with this section, save the same
695
            // metabundle there too, to fix: https://github.com/nystudio107/craft-seomatic/issues/1557
696
            if (!empty($specificTypeId)) {
697
                $metaBundle->typeId = $specificTypeId;
698
                Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
699
            }
700
701
            Seomatic::$plugin->clearAllCaches();
702
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic content settings saved.'));
703
        }
704
705
        return $this->redirectToPostedUrl();
706
    }
707
708
    /**
709
     * Site settings
710
     *
711
     * @param string $subSection
712
     * @param string|null $siteHandle
713
     * @param string|null $loadFromSiteHandle
714
     *
715
     * @return Response The rendered result
716
     * @throws NotFoundHttpException
717
     * @throws ForbiddenHttpException
718
     */
719
    public function actionSite(string $subSection = 'identity', string $siteHandle = null, $loadFromSiteHandle = null): Response
720
    {
721
        $variables = [];
722
        // Get the site to edit
723
        $siteHandle = $this->getCpSiteHandle($siteHandle);
724
        $siteId = $this->getSiteIdFromHandle($siteHandle);
725
726
        $pluginName = Seomatic::$settings->pluginName;
727
        $templateTitle = Craft::t('seomatic', 'Site Settings');
728
        $subSectionSuffix = '';
729
        if ($subSection === 'social') {
730
            $subSectionSuffix = ' Media';
731
        }
732
        $subSectionTitle = Craft::t('seomatic', ucfirst($subSection) . $subSectionSuffix);
733
        // Asset bundle
734
        try {
735
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
736
        } catch (InvalidConfigException $e) {
737
            Craft::error($e->getMessage(), __METHOD__);
738
        }
739
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
740
            '@nystudio107/seomatic/web/assets/dist',
741
            true
0 ignored issues
show
Unused Code introduced by
The call to yii\web\AssetManager::getPublishedUrl() has too many arguments starting with true. ( Ignorable by Annotation )

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

741
        /** @scrutinizer ignore-call */ 
742
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
742
        );
743
        // Basic variables
744
        $variables['fullPageForm'] = true;
745
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
746
        $variables['pluginName'] = Seomatic::$settings->pluginName;
747
        $variables['title'] = $templateTitle;
748
        $variables['subSectionTitle'] = $subSectionTitle;
749
        $variables['docTitle'] = "{$pluginName} - {$templateTitle} - {$subSectionTitle}";
750
        $siteHandleUri = Craft::$app->isMultiSite ? '/' . $siteHandle : '';
751
        $variables['crumbs'] = [
752
            [
753
                'label' => $pluginName,
754
                'url' => UrlHelper::cpUrl('seomatic'),
755
            ],
756
            [
757
                'label' => $templateTitle,
758
                'url' => UrlHelper::cpUrl('seomatic/site/identity' . $siteHandleUri),
759
            ],
760
            [
761
                'label' => $subSectionTitle,
762
                'url' => UrlHelper::cpUrl('seomatic/site/' . $subSection . $siteHandleUri),
763
            ],
764
        ];
765
        $variables['selectedSubnavItem'] = 'site';
766
        $variables['currentSubSection'] = $subSection;
767
768
        // Enabled sites
769
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
770
        $variables['controllerHandle'] = 'site' . '/' . $subSection;
771
772
        // The site settings for the appropriate meta bundle
773
        Seomatic::$previewingMetaContainers = true;
774
        // Get the site to copy the settings from, if any
775
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
776
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
777
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always false.
Loading history...
778
        // Load the metabundle
779
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteIdToLoad);
0 ignored issues
show
Bug introduced by
It seems like $siteIdToLoad can also be of type null; however, parameter $sourceSiteId of nystudio107\seomatic\ser...::getGlobalMetaBundle() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

779
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle(/** @scrutinizer ignore-type */ $siteIdToLoad);
Loading history...
780
        Seomatic::$previewingMetaContainers = false;
781
        if ($metaBundle !== null) {
782
            $variables['site'] = $metaBundle->metaSiteVars;
783
            $variables['identityImageElements'] = ImageTransformHelper::assetElementsFromIds(
784
                $variables['site']->identity->genericImageIds,
785
                $siteId
786
            );
787
            $variables['creatorImageElements'] = ImageTransformHelper::assetElementsFromIds(
788
                $variables['site']->creator->genericImageIds,
789
                $siteId
790
            );
791
        }
792
        $variables['elementType'] = Asset::class;
793
794
        // Render the template
795
        return $this->renderTemplate('seomatic/settings/site/' . $subSection, $variables);
796
    }
797
798
    /**
799
     * @return Response
800
     * @throws BadRequestHttpException
801
     * @throws MissingComponentException
802
     */
803
    public function actionSaveSite(): Response
804
    {
805
        $this->requirePostRequest();
806
        $request = Craft::$app->getRequest();
807
        $siteId = $request->getParam('siteId');
808
        $siteSettings = $request->getParam('seomaticSite');
809
810
        // Make sure the twitter handle isn't prefixed with an @
811
        if (!empty($siteSettings['twitterHandle'])) {
812
            $siteSettings['twitterHandle'] = ltrim($siteSettings['twitterHandle'], '@');
813
        }
814
        // Make sure the sameAsLinks are indexed by the handle
815
        if (!empty($siteSettings['sameAsLinks'])) {
816
            $siteSettings['sameAsLinks'] = ArrayHelper::index($siteSettings['sameAsLinks'], 'handle');
817
        }
818
        // The site settings for the appropriate meta bundle
819
        Seomatic::$previewingMetaContainers = true;
820
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteId);
821
        Seomatic::$previewingMetaContainers = false;
822
        if ($metaBundle) {
823
            if (is_array($siteSettings)) {
824
                if (!empty($siteSettings['identity'])) {
825
                    $settings = $siteSettings['identity'];
826
                    $this->prepEntitySettings($settings);
827
                    $metaBundle->metaSiteVars->identity->setAttributes($settings);
0 ignored issues
show
Bug introduced by
The method setAttributes() does not exist on null. ( Ignorable by Annotation )

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

827
                    $metaBundle->metaSiteVars->identity->/** @scrutinizer ignore-call */ 
828
                                                         setAttributes($settings);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
828
                    $siteSettings['identity'] = $metaBundle->metaSiteVars->identity;
829
                }
830
                if (!empty($siteSettings['creator'])) {
831
                    $settings = $siteSettings['creator'];
832
                    $this->prepEntitySettings($settings);
833
                    $metaBundle->metaSiteVars->creator->setAttributes($settings);
0 ignored issues
show
Bug introduced by
The method setAttributes() does not exist on null. ( Ignorable by Annotation )

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

833
                    $metaBundle->metaSiteVars->creator->/** @scrutinizer ignore-call */ 
834
                                                        setAttributes($settings);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
834
                    $siteSettings['creator'] = $metaBundle->metaSiteVars->creator;
835
                }
836
                if (!empty($siteSettings['additionalSitemapUrls'])) {
837
                    $siteSettings['additionalSitemapUrlsDateUpdated'] = new DateTime();
838
                    Seomatic::$plugin->sitemaps->submitCustomSitemap($siteId);
839
                }
840
                $metaBundle->metaSiteVars->setAttributes($siteSettings);
0 ignored issues
show
Bug introduced by
The method setAttributes() does not exist on null. ( Ignorable by Annotation )

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

840
                $metaBundle->metaSiteVars->/** @scrutinizer ignore-call */ 
841
                                           setAttributes($siteSettings);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
841
            }
842
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
843
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
844
845
            Seomatic::$plugin->clearAllCaches();
846
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic site settings saved.'));
847
        }
848
849
        return $this->redirectToPostedUrl();
850
    }
851
852
    /**
853
     * Plugin settings
854
     *
855
     * @return Response The rendered result
856
     * @throws ForbiddenHttpException
857
     */
858
    public function actionPlugin(): Response
859
    {
860
        // Ensure they have permission to edit the plugin settings
861
        $currentUser = Craft::$app->getUser()->getIdentity();
862
        if (!$currentUser->can('seomatic:plugin-settings')) {
0 ignored issues
show
Bug introduced by
The method can() does not exist on yii\web\IdentityInterface. It seems like you code against a sub-type of yii\web\IdentityInterface such as craft\elements\User. ( Ignorable by Annotation )

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

862
        if (!$currentUser->/** @scrutinizer ignore-call */ can('seomatic:plugin-settings')) {
Loading history...
863
            throw new ForbiddenHttpException('You do not have permission to edit SEOmatic plugin settings.');
864
        }
865
        $general = Craft::$app->getConfig()->getGeneral();
866
        if (!$general->allowAdminChanges) {
867
            throw new ForbiddenHttpException('Unable to edit SEOmatic plugin settings because admin changes are disabled in this environment.');
868
        }
869
        // Edit the plugin settings
870
        $variables = [];
871
        $pluginName = Seomatic::$settings->pluginName;
872
        $templateTitle = Craft::t('seomatic', 'Plugin Settings');
873
        // Asset bundle
874
        try {
875
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
876
        } catch (InvalidConfigException $e) {
877
            Craft::error($e->getMessage(), __METHOD__);
878
        }
879
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
880
            '@nystudio107/seomatic/web/assets/dist',
881
            true
0 ignored issues
show
Unused Code introduced by
The call to yii\web\AssetManager::getPublishedUrl() has too many arguments starting with true. ( Ignorable by Annotation )

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

881
        /** @scrutinizer ignore-call */ 
882
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
882
        );
883
        // Basic variables
884
        $variables['fullPageForm'] = true;
885
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
886
        $variables['pluginName'] = Seomatic::$settings->pluginName;
887
        $variables['title'] = $templateTitle;
888
        $variables['docTitle'] = "{$pluginName} - {$templateTitle}";
889
        $variables['crumbs'] = [
890
            [
891
                'label' => $pluginName,
892
                'url' => UrlHelper::cpUrl('seomatic'),
893
            ],
894
            [
895
                'label' => $templateTitle,
896
                'url' => UrlHelper::cpUrl('seomatic/plugin'),
897
            ],
898
        ];
899
        $variables['selectedSubnavItem'] = 'plugin';
900
        $variables['settings'] = Seomatic::$settings;
901
        $sites = ArrayHelper::map(Craft::$app->getSites()->getAllSites(), 'id', 'name');
902
        $variables['sites'] = $sites;
903
904
        // Render the template
905
        return $this->renderTemplate('seomatic/settings/plugin/_edit', $variables);
906
    }
907
908
    /**
909
     * Tracking settings
910
     *
911
     * @param string $subSection
912
     * @param string|null $siteHandle
913
     * @param string|null $loadFromSiteHandle
914
     *
915
     * @return Response The rendered result
916
     * @throws NotFoundHttpException
917
     * @throws ForbiddenHttpException
918
     */
919
    public function actionTracking(string $subSection = 'gtag', string $siteHandle = null, $loadFromSiteHandle = null, $editedMetaBundle = null): Response
920
    {
921
        $variables = [];
922
        // Get the site to edit
923
        $siteHandle = $this->getCpSiteHandle($siteHandle);
924
        $siteId = $this->getSiteIdFromHandle($siteHandle);
925
        // Enabled sites
926
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
927
        $variables['controllerHandle'] = 'tracking' . '/' . $subSection;
928
        $variables['currentSubSection'] = $subSection;
929
930
        // The script meta containers for the global meta bundle
931
        Seomatic::$previewingMetaContainers = true;
932
        // Get the site to copy the settings from, if any
933
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
934
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
935
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always false.
Loading history...
936
        // Load the metabundle
937
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteIdToLoad);
0 ignored issues
show
Bug introduced by
It seems like $siteIdToLoad can also be of type null; however, parameter $sourceSiteId of nystudio107\seomatic\ser...::getGlobalMetaBundle() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

937
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle(/** @scrutinizer ignore-type */ $siteIdToLoad);
Loading history...
938
        if ($editedMetaBundle) {
939
            $metaBundle = $editedMetaBundle;
940
        }
941
        Seomatic::$previewingMetaContainers = false;
942
        if ($metaBundle !== null) {
943
            $variables['scripts'] = Seomatic::$plugin->metaBundles->getContainerDataFromBundle(
944
                $metaBundle,
945
                MetaScriptContainer::CONTAINER_TYPE
946
            );
947
        }
948
        // Add in the variables to the autocomplete cache so they can be accessed across requests
949
        $subSectionSettings = $variables['scripts'][$subSection];
950
        $variables['codeEditorOptions'] = [
951
            TrackingVarsAutocomplete::OPTIONS_DATA_KEY => $subSectionSettings->vars,
952
        ];
953
        // Plugin and section settings
954
        $pluginName = Seomatic::$settings->pluginName;
955
        $templateTitle = Craft::t('seomatic', 'Tracking Scripts');
956
        $subSectionTitle = $variables['scripts'][$subSection]->name;
957
        $subSectionTitle = Craft::t('seomatic', $subSectionTitle);
958
        // Asset bundle
959
        try {
960
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
961
        } catch (InvalidConfigException $e) {
962
            Craft::error($e->getMessage(), __METHOD__);
963
        }
964
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
965
            '@nystudio107/seomatic/web/assets/dist',
966
            true
0 ignored issues
show
Unused Code introduced by
The call to yii\web\AssetManager::getPublishedUrl() has too many arguments starting with true. ( Ignorable by Annotation )

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

966
        /** @scrutinizer ignore-call */ 
967
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
967
        );
968
        // Basic variables
969
        $variables['fullPageForm'] = true;
970
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
971
        $variables['pluginName'] = Seomatic::$settings->pluginName;
972
        $variables['title'] = $templateTitle;
973
        $variables['subSectionTitle'] = $subSectionTitle;
974
        $variables['docTitle'] = "{$pluginName} - {$templateTitle} - {$subSectionTitle}";
975
        $siteHandleUri = Craft::$app->isMultiSite ? '/' . $siteHandle : '';
976
        $variables['crumbs'] = [
977
            [
978
                'label' => $pluginName,
979
                'url' => UrlHelper::cpUrl('seomatic'),
980
            ],
981
            [
982
                'label' => $templateTitle,
983
                'url' => UrlHelper::cpUrl('seomatic/tracking'),
984
            ],
985
            [
986
                'label' => $subSectionTitle,
987
                'url' => UrlHelper::cpUrl('seomatic/tracking/' . $subSection . $siteHandleUri),
988
            ],
989
        ];
990
        $variables['selectedSubnavItem'] = 'tracking';
991
992
        // Render the template
993
        return $this->renderTemplate('seomatic/settings/tracking/_edit', $variables);
994
    }
995
996
    /**
997
     * @return Response|null
998
     * @throws BadRequestHttpException
999
     * @throws MissingComponentException
1000
     */
1001
    public function actionSaveTracking()
1002
    {
1003
        $this->requirePostRequest();
1004
        $request = Craft::$app->getRequest();
1005
        $siteId = $request->getParam('siteId');
1006
        $scriptSettings = $request->getParam('scripts');
1007
1008
        // The site settings for the appropriate meta bundle
1009
        Seomatic::$previewingMetaContainers = true;
1010
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteId);
1011
        Seomatic::$previewingMetaContainers = false;
1012
        $hasErrors = false;
1013
        if ($metaBundle) {
1014
            /** @var array $scriptSettings */
1015
            foreach ($scriptSettings as $scriptHandle => $scriptData) {
1016
                foreach ($metaBundle->metaContainers as $metaContainer) {
1017
                    if ($metaContainer::CONTAINER_TYPE === MetaScriptContainer::CONTAINER_TYPE) {
1018
                        $data = $metaContainer->getData($scriptHandle);
1019
                        /** @var MetaScript|null $data */
1020
                        if ($data) {
1021
                            /** @var array $scriptData */
1022
                            foreach ($scriptData as $key => $value) {
1023
                                if (is_array($value) && $key !== 'tagAttrs') {
1024
                                    foreach ($value as $varsKey => $varsValue) {
1025
                                        $data->$key[$varsKey]['value'] = $varsValue;
1026
                                    }
1027
                                } else {
1028
                                    $data->$key = $value;
1029
                                }
1030
                            }
1031
                            if (!$data->validate()) {
1032
                                $hasErrors = true;
1033
                            }
1034
                        }
1035
                    }
1036
                }
1037
            }
1038
            if ($hasErrors) {
1039
                Craft::$app->getSession()->setError(Craft::t('app', "Couldn't save tracking settings due to a Twig error."));
1040
                // Send the redirect back to the template
1041
                /** @var UrlManager $urlManager */
1042
                $urlManager = Craft::$app->getUrlManager();
1043
                $urlManager->setRouteParams([
1044
                    'editedMetaBundle' => $metaBundle,
1045
                ]);
1046
1047
                return null;
1048
            }
1049
1050
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
1051
1052
            Seomatic::$plugin->clearAllCaches();
1053
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic site settings saved.'));
1054
        }
1055
1056
        return $this->redirectToPostedUrl();
1057
    }
1058
1059
    /**
1060
     * Saves a plugin’s settings.
1061
     *
1062
     * @return Response|null
1063
     * @throws NotFoundHttpException if the requested plugin cannot be found
1064
     * @throws BadRequestHttpException
1065
     * @throws MissingComponentException
1066
     */
1067
    public function actionSavePluginSettings()
1068
    {
1069
        // Ensure they have permission to edit the plugin settings
1070
        $currentUser = Craft::$app->getUser()->getIdentity();
1071
        if (!$currentUser->can('seomatic:plugin-settings')) {
1072
            throw new ForbiddenHttpException('You do not have permission to edit SEOmatic plugin settings.');
1073
        }
1074
        $general = Craft::$app->getConfig()->getGeneral();
1075
        if (!$general->allowAdminChanges) {
1076
            throw new ForbiddenHttpException('Unable to edit SEOmatic plugin settings because admin changes are disabled in this environment.');
1077
        }
1078
        // Save the plugin settings
1079
        $this->requirePostRequest();
1080
        $pluginHandle = Craft::$app->getRequest()->getRequiredBodyParam('pluginHandle');
1081
        $settings = Craft::$app->getRequest()->getBodyParam('settings', []);
1082
        $plugin = Craft::$app->getPlugins()->getPlugin($pluginHandle);
1083
1084
        if ($plugin === null) {
1085
            throw new NotFoundHttpException('Plugin not found');
1086
        }
1087
1088
        if (!Craft::$app->getPlugins()->savePluginSettings($plugin, $settings)) {
1089
            Craft::$app->getSession()->setError(Craft::t('app', "Couldn't save plugin settings."));
1090
1091
            // Send the redirect back to the template
1092
            /** @var UrlManager $urlManager */
1093
            $urlManager = Craft::$app->getUrlManager();
1094
            $urlManager->setRouteParams([
1095
                'plugin' => $plugin,
1096
            ]);
1097
1098
            return null;
1099
        }
1100
1101
        Seomatic::$plugin->clearAllCaches();
1102
        Craft::$app->getSession()->setNotice(Craft::t('app', 'Plugin settings saved.'));
1103
1104
        return $this->redirectToPostedUrl();
1105
    }
1106
1107
    // Protected Methods
1108
    // =========================================================================
1109
1110
    /**
1111
     * @param $siteHandle
1112
     * @return string|null
1113
     */
1114
    protected function getCpSiteHandle($siteHandle)
1115
    {
1116
        // As of Craft 4, the site query parameter is appended to CP urls to indicate the current
1117
        // site that is being edited, so respect it
1118
        $cpSite = Cp::requestedSite();
1119
        if ($cpSite) {
1120
            return $cpSite->handle;
1121
        }
1122
1123
        return $siteHandle;
1124
    }
1125
1126
    /**
1127
     * Return a siteId from a siteHandle
1128
     *
1129
     * @param string|null $siteHandle
1130
     *
1131
     * @return int|null
1132
     * @throws NotFoundHttpException
1133
     */
1134
    protected function getSiteIdFromHandle($siteHandle)
1135
    {
1136
        // Get the site to edit
1137
        if ($siteHandle !== null) {
1138
            $site = Craft::$app->getSites()->getSiteByHandle($siteHandle);
1139
            if (!$site) {
1140
                throw new NotFoundHttpException('Invalid site handle: ' . $siteHandle);
1141
            }
1142
            $siteId = $site->id;
1143
        } else {
1144
            $siteId = Craft::$app->getSites()->currentSite->id;
1145
        }
1146
1147
        return $siteId;
1148
    }
1149
1150
    /**
1151
     * @param $siteHandle
1152
     * @param $siteId
1153
     * @param array $variables
1154
     * @param $element
1155
     * @return void
1156
     * @throws ForbiddenHttpException
1157
     */
1158
    protected function setMultiSiteVariables($siteHandle, &$siteId, array &$variables, $element = null)
0 ignored issues
show
Unused Code introduced by
The parameter $element is not used and could be removed. ( Ignorable by Annotation )

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

1158
    protected function setMultiSiteVariables($siteHandle, &$siteId, array &$variables, /** @scrutinizer ignore-unused */ $element = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1159
    {
1160
        // Enabled sites
1161
        $sites = Craft::$app->getSites();
1162
        if (Craft::$app->getIsMultiSite()) {
1163
            // Set defaults based on the section settings
1164
            $variables['enabledSiteIds'] = [];
1165
            $variables['siteIds'] = [];
1166
1167
            foreach ($sites->getEditableSiteIds() as $editableSiteId) {
1168
                $variables['enabledSiteIds'][] = $editableSiteId;
1169
                $variables['siteIds'][] = $editableSiteId;
1170
            }
1171
1172
            // Make sure the $siteId they are trying to edit is in our array of editable sites
1173
            if (!in_array($siteId, $variables['enabledSiteIds'], false)) {
1174
                if (!empty($variables['enabledSiteIds'])) {
1175
                    $siteId = reset($variables['enabledSiteIds']);
1176
                } else {
1177
                    $this->requirePermission('editSite:' . $siteId);
1178
                }
1179
            }
1180
        }
1181
1182
        // Set the currentSiteId and currentSiteHandle
1183
        $variables['currentSiteId'] = empty($siteId) ? Craft::$app->getSites()->currentSite->id : $siteId;
1184
        $variables['currentSiteHandle'] = empty($siteHandle)
1185
            ? Craft::$app->getSites()->currentSite->handle
1186
            : $siteHandle;
1187
1188
        // Page title
1189
        $variables['showSites'] = (
1190
            Craft::$app->getIsMultiSite() &&
1191
            count($variables['enabledSiteIds'])
1192
        );
1193
1194
        if ($variables['showSites']) {
1195
            $variables['sitesMenuLabel'] = Craft::t(
1196
                'site',
1197
                $sites->getSiteById((int)$variables['currentSiteId'])->name
1198
            );
1199
        } else {
1200
            $variables['sitesMenuLabel'] = '';
1201
        }
1202
    }
1203
1204
    /**
1205
     * @param array $variables
1206
     */
1207
    protected function setGlobalFieldSourceVariables(array &$variables)
1208
    {
1209
        $variables['textFieldSources'] = array_merge(
1210
            ['globalsGroup' => ['optgroup' => 'Globals Fields']],
1211
            FieldHelper::fieldsOfTypeFromGlobals(
1212
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1213
                false
1214
            )
1215
        );
1216
        $variables['assetFieldSources'] = array_merge(
1217
            ['globalsGroup' => ['optgroup' => 'Globals Fields']],
1218
            FieldHelper::fieldsOfTypeFromGlobals(
1219
                FieldHelper::ASSET_FIELD_CLASS_KEY,
1220
                false
1221
            )
1222
        );
1223
    }
1224
1225
    /**
1226
     * Remove any sites for which meta bundles do not exist (they may be
1227
     * disabled for this section)
1228
     *
1229
     * @param string $sourceBundleType
1230
     * @param string $sourceHandle
1231
     * @param array $variables
1232
     */
1233
    protected function cullDisabledSites(string $sourceBundleType, string $sourceHandle, array &$variables)
1234
    {
1235
        if (isset($variables['enabledSiteIds'])) {
1236
            foreach ($variables['enabledSiteIds'] as $key => $value) {
1237
                $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
1238
                    $sourceBundleType,
1239
                    $sourceHandle,
1240
                    $value
1241
                );
1242
                if ($metaBundle === null) {
1243
                    unset($variables['enabledSiteIds'][$key]);
1244
                }
1245
            }
1246
        }
1247
    }
1248
1249
    /**
1250
     * @param string $sourceBundleType
1251
     * @param string $sourceHandle
1252
     * @param string $groupName
1253
     * @param array $variables
1254
     * @param int|string|null $typeId
1255
     */
1256
    protected function setContentFieldSourceVariables(
1257
        string $sourceBundleType,
1258
        string $sourceHandle,
1259
        string $groupName,
1260
        array  &$variables,
1261
               $typeId = null,
1262
    ) {
1263
        $variables['textFieldSources'] = array_merge(
1264
            ['entryGroup' => ['optgroup' => $groupName . ' Fields'], 'title' => 'Title'],
1265
            FieldHelper::fieldsOfTypeFromSource(
1266
                $sourceBundleType,
1267
                $sourceHandle,
1268
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1269
                false,
1270
                $typeId
1271
            )
1272
        );
1273
        $variables['assetFieldSources'] = array_merge(
1274
            ['entryGroup' => ['optgroup' => $groupName . ' Fields']],
1275
            FieldHelper::fieldsOfTypeFromSource(
1276
                $sourceBundleType,
1277
                $sourceHandle,
1278
                FieldHelper::ASSET_FIELD_CLASS_KEY,
1279
                false,
1280
                $typeId
1281
            )
1282
        );
1283
        $variables['assetVolumeTextFieldSources'] = array_merge(
1284
            ['entryGroup' => ['optgroup' => 'Asset Volume Fields'], '' => '--', 'title' => 'Title'],
1285
            array_merge(
1286
                FieldHelper::fieldsOfTypeFromAssetVolumes(
1287
                    FieldHelper::TEXT_FIELD_CLASS_KEY,
1288
                    false
1289
                )
1290
            )
1291
        );
1292
        $variables['userFieldSources'] = array_merge(
1293
            ['entryGroup' => ['optgroup' => 'User Fields']],
1294
            FieldHelper::fieldsOfTypeFromUsers(
1295
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1296
                false
1297
            )
1298
        );
1299
    }
1300
1301
    /**
1302
     * @param string $sourceBundleType
1303
     * @param string $sourceHandle
1304
     * @param null|int $siteId
1305
     * @param int|string|null $typeId
1306
     *
1307
     * @return string
1308
     */
1309
    protected function uriFromSourceBundle(string $sourceBundleType, string $sourceHandle, $siteId, $typeId): string
1310
    {
1311
        $uri = null;
1312
        // Pick an Element to be used for the preview
1313
        if ($sourceBundleType === MetaBundles::GLOBAL_META_BUNDLE) {
1314
            $uri = MetaBundles::GLOBAL_META_BUNDLE;
1315
        } else {
1316
            $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
1317
            if ($seoElement !== null) {
1318
                $uri = $seoElement::previewUri($sourceHandle, $siteId, $typeId);
1319
            }
1320
        }
1321
        // Special-case for the __home__ slug, and default to /
1322
        if (($uri === '__home__') || ($uri === null)) {
1323
            $uri = '/';
1324
        }
1325
1326
        return $uri;
1327
    }
1328
1329
    /**
1330
     * Prep the entity settings for saving to the db
1331
     *
1332
     * @param array &$settings
1333
     */
1334
    protected function prepEntitySettings(&$settings)
1335
    {
1336
        DynamicMetaHelper::normalizeTimes($settings['localBusinessOpeningHours']);
1337
        if (!empty($settings['siteType'])) {
1338
            $settings['computedType'] = SchemaHelper::getSpecificEntityType($settings);
1339
        } else {
1340
            $settings['computedType'] = 'WebPage';
1341
        }
1342
        // Empty out the entity image settings to ensure the image gets removed if it no longer exists
1343
        $settings['genericImage'] = '';
1344
        $settings['genericImageWidth'] = '';
1345
        $settings['genericImageHeight'] = '';
1346
        if (!empty($settings['genericImageIds'])) {
1347
            $asset = Craft::$app->getAssets()->getAssetById($settings['genericImageIds'][0]);
1348
            if ($asset !== null) {
1349
                $settings['genericImage'] = $asset->getUrl();
1350
                $settings['genericImageWidth'] = $asset->getWidth();
1351
                $settings['genericImageHeight'] = $asset->getHeight();
1352
            }
1353
        }
1354
    }
1355
}
1356