SettingsController::setMultiSiteVariables()   B
last analyzed

Complexity

Conditions 9
Paths 64

Size

Total Lines 43
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
eloc 25
c 0
b 0
f 0
dl 0
loc 43
ccs 0
cts 25
cp 0
rs 8.0555
cc 9
nc 64
nop 4
crap 90
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;
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
        if (empty($typeId)) {
527
            $typeId = null;
528
        }
529
        // Get the (entry) type menu
530
        $typeMenu = [];
531
        $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
532
        if ($seoElement !== null) {
533
            $typeMenu = $seoElement::typeMenuFromHandle($sourceHandle);
534
        }
535
        $variables['typeMenu'] = $typeMenu;
536
        $variables['currentTypeId'] = null;
537
        $variables['specificTypeId'] = null;
538
        if (count($typeMenu) > 1) {
539
            $currentType = reset($typeMenu);
540
            $variables['currentType'] = $typeMenu[$typeId] ?? $currentType;
541
            $variables['currentTypeId'] = $typeId ?? key($typeMenu);
542
            $typeId = (int)$variables['currentTypeId'];
543
        }
544
        // If there's only one EntryType, don't bother displaying the menu
545
        if (count($typeMenu) === 1) {
546
            $variables['typeMenu'] = [];
547
            $variables['specificTypeId'] = $typeId ?? key($typeMenu);
548
        }
549
        // If this is the sitemap section, there are no per-entry type settings
550
        if ($subSection === 'sitemap') {
551
            $variables['typeMenu'] = [];
552
            $variables['specificTypeId'] = $typeId ?? key($typeMenu);
553
            $typeId = 0;
554
        }
555
        $pluginName = Seomatic::$settings->pluginName;
556
        // Asset bundle
557
        try {
558
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
559
        } catch (InvalidConfigException $e) {
560
            Craft::error($e->getMessage(), __METHOD__);
561
        }
562
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
563
            '@nystudio107/seomatic/web/assets/dist',
564
            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

564
        /** @scrutinizer ignore-call */ 
565
        $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...
565
        );
566
        // Enabled sites
567
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
568
        $this->cullDisabledSites($sourceBundleType, $sourceHandle, $variables);
569
        // Meta Bundle settings
570
        Seomatic::$previewingMetaContainers = true;
571
        // Get the site to copy the settings from, if any
572
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
573
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
574
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always false.
Loading history...
575
        // Load the metabundle
576
        $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
577
            $sourceBundleType,
578
            $sourceHandle,
579
            $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

579
            /** @scrutinizer ignore-type */ $siteIdToLoad,
Loading history...
580
            $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

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

697
                $metaBundle->metaSitemapVars->/** @scrutinizer ignore-call */ 
698
                                              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...
698
            }
699
700
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
701
            $metaBundle->typeId = $typeId;
702
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
703
            // If there's also a specific typeId associated with this section, save the same
704
            // metabundle there too, to fix: https://github.com/nystudio107/craft-seomatic/issues/1557
705
            if (!empty($specificTypeId)) {
706
                $metaBundle->typeId = $specificTypeId;
707
                Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
708
            }
709
710
            Seomatic::$plugin->clearAllCaches();
711
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic content settings saved.'));
712
        }
713
714
        return $this->redirectToPostedUrl();
715
    }
716
717
    /**
718
     * Site settings
719
     *
720
     * @param string $subSection
721
     * @param string|null $siteHandle
722
     * @param string|null $loadFromSiteHandle
723
     *
724
     * @return Response The rendered result
725
     * @throws NotFoundHttpException
726
     * @throws ForbiddenHttpException
727
     */
728
    public function actionSite(string $subSection = 'identity', string $siteHandle = null, $loadFromSiteHandle = null): Response
729
    {
730
        $variables = [];
731
        // Get the site to edit
732
        $siteHandle = $this->getCpSiteHandle($siteHandle);
733
        $siteId = $this->getSiteIdFromHandle($siteHandle);
734
735
        $pluginName = Seomatic::$settings->pluginName;
736
        $templateTitle = Craft::t('seomatic', 'Site Settings');
737
        $subSectionSuffix = '';
738
        if ($subSection === 'social') {
739
            $subSectionSuffix = ' Media';
740
        }
741
        $subSectionTitle = Craft::t('seomatic', ucfirst($subSection) . $subSectionSuffix);
742
        // Asset bundle
743
        try {
744
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
745
        } catch (InvalidConfigException $e) {
746
            Craft::error($e->getMessage(), __METHOD__);
747
        }
748
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
749
            '@nystudio107/seomatic/web/assets/dist',
750
            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

750
        /** @scrutinizer ignore-call */ 
751
        $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...
751
        );
752
        // Basic variables
753
        $variables['fullPageForm'] = true;
754
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
755
        $variables['pluginName'] = Seomatic::$settings->pluginName;
756
        $variables['title'] = $templateTitle;
757
        $variables['subSectionTitle'] = $subSectionTitle;
758
        $variables['docTitle'] = "{$pluginName} - {$templateTitle} - {$subSectionTitle}";
759
        $siteHandleUri = Craft::$app->isMultiSite ? '/' . $siteHandle : '';
760
        $variables['crumbs'] = [
761
            [
762
                'label' => $pluginName,
763
                'url' => UrlHelper::cpUrl('seomatic'),
764
            ],
765
            [
766
                'label' => $templateTitle,
767
                'url' => UrlHelper::cpUrl('seomatic/site/identity' . $siteHandleUri),
768
            ],
769
            [
770
                'label' => $subSectionTitle,
771
                'url' => UrlHelper::cpUrl('seomatic/site/' . $subSection . $siteHandleUri),
772
            ],
773
        ];
774
        $variables['selectedSubnavItem'] = 'site';
775
        $variables['currentSubSection'] = $subSection;
776
777
        // Enabled sites
778
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
779
        $variables['controllerHandle'] = 'site' . '/' . $subSection;
780
781
        // The site settings for the appropriate meta bundle
782
        Seomatic::$previewingMetaContainers = true;
783
        // Get the site to copy the settings from, if any
784
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
785
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
786
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always false.
Loading history...
787
        // Load the metabundle
788
        $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

788
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle(/** @scrutinizer ignore-type */ $siteIdToLoad);
Loading history...
789
        Seomatic::$previewingMetaContainers = false;
790
        if ($metaBundle !== null) {
791
            $variables['site'] = $metaBundle->metaSiteVars;
792
            $variables['identityImageElements'] = ImageTransformHelper::assetElementsFromIds(
793
                $variables['site']->identity->genericImageIds,
794
                $siteId
795
            );
796
            $variables['creatorImageElements'] = ImageTransformHelper::assetElementsFromIds(
797
                $variables['site']->creator->genericImageIds,
798
                $siteId
799
            );
800
        }
801
        $variables['elementType'] = Asset::class;
802
803
        // Render the template
804
        return $this->renderTemplate('seomatic/settings/site/' . $subSection, $variables);
805
    }
806
807
    /**
808
     * @return Response
809
     * @throws BadRequestHttpException
810
     * @throws MissingComponentException
811
     */
812
    public function actionSaveSite(): Response
813
    {
814
        $this->requirePostRequest();
815
        $request = Craft::$app->getRequest();
816
        $siteId = $request->getParam('siteId');
817
        $siteSettings = $request->getParam('seomaticSite');
818
819
        // Make sure the twitter handle isn't prefixed with an @
820
        if (!empty($siteSettings['twitterHandle'])) {
821
            $siteSettings['twitterHandle'] = ltrim($siteSettings['twitterHandle'], '@');
822
        }
823
        // Make sure the sameAsLinks are indexed by the handle
824
        if (!empty($siteSettings['sameAsLinks'])) {
825
            $siteSettings['sameAsLinks'] = ArrayHelper::index($siteSettings['sameAsLinks'], 'handle');
826
        }
827
        // The site settings for the appropriate meta bundle
828
        Seomatic::$previewingMetaContainers = true;
829
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteId);
830
        Seomatic::$previewingMetaContainers = false;
831
        if ($metaBundle) {
832
            if (is_array($siteSettings)) {
833
                if (!empty($siteSettings['identity'])) {
834
                    $settings = $siteSettings['identity'];
835
                    $this->prepEntitySettings($settings);
836
                    $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

836
                    $metaBundle->metaSiteVars->identity->/** @scrutinizer ignore-call */ 
837
                                                         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...
837
                    $siteSettings['identity'] = $metaBundle->metaSiteVars->identity;
838
                }
839
                if (!empty($siteSettings['creator'])) {
840
                    $settings = $siteSettings['creator'];
841
                    $this->prepEntitySettings($settings);
842
                    $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

842
                    $metaBundle->metaSiteVars->creator->/** @scrutinizer ignore-call */ 
843
                                                        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...
843
                    $siteSettings['creator'] = $metaBundle->metaSiteVars->creator;
844
                }
845
                if (!empty($siteSettings['additionalSitemapUrls'])) {
846
                    $siteSettings['additionalSitemapUrlsDateUpdated'] = new DateTime();
847
                    Seomatic::$plugin->sitemaps->submitCustomSitemap($siteId);
848
                }
849
                $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

849
                $metaBundle->metaSiteVars->/** @scrutinizer ignore-call */ 
850
                                           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...
850
            }
851
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
852
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
853
854
            Seomatic::$plugin->clearAllCaches();
855
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic site settings saved.'));
856
        }
857
858
        return $this->redirectToPostedUrl();
859
    }
860
861
    /**
862
     * Plugin settings
863
     *
864
     * @return Response The rendered result
865
     * @throws ForbiddenHttpException
866
     */
867
    public function actionPlugin(): Response
868
    {
869
        // Ensure they have permission to edit the plugin settings
870
        $currentUser = Craft::$app->getUser()->getIdentity();
871
        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

871
        if (!$currentUser->/** @scrutinizer ignore-call */ can('seomatic:plugin-settings')) {
Loading history...
872
            throw new ForbiddenHttpException('You do not have permission to edit SEOmatic plugin settings.');
873
        }
874
        $general = Craft::$app->getConfig()->getGeneral();
875
        if (!$general->allowAdminChanges) {
876
            throw new ForbiddenHttpException('Unable to edit SEOmatic plugin settings because admin changes are disabled in this environment.');
877
        }
878
        // Edit the plugin settings
879
        $variables = [];
880
        $pluginName = Seomatic::$settings->pluginName;
881
        $templateTitle = Craft::t('seomatic', 'Plugin Settings');
882
        // Asset bundle
883
        try {
884
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
885
        } catch (InvalidConfigException $e) {
886
            Craft::error($e->getMessage(), __METHOD__);
887
        }
888
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
889
            '@nystudio107/seomatic/web/assets/dist',
890
            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

890
        /** @scrutinizer ignore-call */ 
891
        $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...
891
        );
892
        // Basic variables
893
        $variables['fullPageForm'] = true;
894
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
895
        $variables['pluginName'] = Seomatic::$settings->pluginName;
896
        $variables['title'] = $templateTitle;
897
        $variables['docTitle'] = "{$pluginName} - {$templateTitle}";
898
        $variables['crumbs'] = [
899
            [
900
                'label' => $pluginName,
901
                'url' => UrlHelper::cpUrl('seomatic'),
902
            ],
903
            [
904
                'label' => $templateTitle,
905
                'url' => UrlHelper::cpUrl('seomatic/plugin'),
906
            ],
907
        ];
908
        $variables['selectedSubnavItem'] = 'plugin';
909
        $variables['settings'] = Seomatic::$settings;
910
        $sites = ArrayHelper::map(Craft::$app->getSites()->getAllSites(), 'id', 'name');
911
        $variables['sites'] = $sites;
912
913
        // Render the template
914
        return $this->renderTemplate('seomatic/settings/plugin/_edit', $variables);
915
    }
916
917
    /**
918
     * Tracking settings
919
     *
920
     * @param string $subSection
921
     * @param string|null $siteHandle
922
     * @param string|null $loadFromSiteHandle
923
     *
924
     * @return Response The rendered result
925
     * @throws NotFoundHttpException
926
     * @throws ForbiddenHttpException
927
     */
928
    public function actionTracking(string $subSection = 'gtag', string $siteHandle = null, $loadFromSiteHandle = null, $editedMetaBundle = null): Response
929
    {
930
        $variables = [];
931
        // Get the site to edit
932
        $siteHandle = $this->getCpSiteHandle($siteHandle);
933
        $siteId = $this->getSiteIdFromHandle($siteHandle);
934
        // Enabled sites
935
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
936
        $variables['controllerHandle'] = 'tracking' . '/' . $subSection;
937
        $variables['currentSubSection'] = $subSection;
938
939
        // The script meta containers for the global meta bundle
940
        Seomatic::$previewingMetaContainers = true;
941
        // Get the site to copy the settings from, if any
942
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
943
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
944
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always false.
Loading history...
945
        // Load the metabundle
946
        $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

946
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle(/** @scrutinizer ignore-type */ $siteIdToLoad);
Loading history...
947
        if ($editedMetaBundle) {
948
            $metaBundle = $editedMetaBundle;
949
        }
950
        Seomatic::$previewingMetaContainers = false;
951
        if ($metaBundle !== null) {
952
            $variables['scripts'] = Seomatic::$plugin->metaBundles->getContainerDataFromBundle(
953
                $metaBundle,
954
                MetaScriptContainer::CONTAINER_TYPE
955
            );
956
        }
957
        // Add in the variables to the autocomplete cache so they can be accessed across requests
958
        $subSectionSettings = $variables['scripts'][$subSection];
959
        $variables['codeEditorOptions'] = [
960
            TrackingVarsAutocomplete::OPTIONS_DATA_KEY => $subSectionSettings->vars,
961
        ];
962
        // Plugin and section settings
963
        $pluginName = Seomatic::$settings->pluginName;
964
        $templateTitle = Craft::t('seomatic', 'Tracking Scripts');
965
        $subSectionTitle = $variables['scripts'][$subSection]->name;
966
        $subSectionTitle = Craft::t('seomatic', $subSectionTitle);
967
        // Asset bundle
968
        try {
969
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
970
        } catch (InvalidConfigException $e) {
971
            Craft::error($e->getMessage(), __METHOD__);
972
        }
973
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
974
            '@nystudio107/seomatic/web/assets/dist',
975
            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

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

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