setGlobalFieldSourceVariables()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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

122
            Seomatic::$view->/** @scrutinizer ignore-call */ 
123
                             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...
123
        } catch (InvalidConfigException $e) {
124
            Craft::error($e->getMessage(), __METHOD__);
125
        }
126
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
127
            '@nystudio107/seomatic/web/assets/dist',
128
            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

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

255
        /** @scrutinizer ignore-call */ 
256
        $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...
256
        );
257
        // Basic variables
258
        $variables['fullPageForm'] = true;
259
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
260
        $variables['pluginName'] = Seomatic::$settings->pluginName;
261
        $variables['title'] = $templateTitle;
262
        $variables['subSectionTitle'] = $subSectionTitle;
263
        $variables['docTitle'] = "{$pluginName} - {$templateTitle} - {$subSectionTitle}";
264
        $siteHandleUri = Craft::$app->isMultiSite ? '/' . $siteHandle : '';
265
        $variables['crumbs'] = [
266
            [
267
                'label' => $pluginName,
268
                'url' => UrlHelper::cpUrl('seomatic'),
269
            ],
270
            [
271
                'label' => $templateTitle,
272
                'url' => UrlHelper::cpUrl('seomatic/global/general' . $siteHandleUri),
273
            ],
274
            [
275
                'label' => $subSectionTitle,
276
                'url' => UrlHelper::cpUrl('seomatic/global/' . $subSection . $siteHandleUri),
277
            ],
278
        ];
279
        $variables['selectedSubnavItem'] = 'global';
280
        // Pass in the pull fields
281
        $this->setGlobalFieldSourceVariables($variables);
282
        // Enabled sites
283
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
284
        $variables['controllerHandle'] = 'global' . '/' . $subSection;
285
        $variables['currentSubSection'] = $subSection;
286
        // Meta bundle settings
287
        Seomatic::$previewingMetaContainers = true;
288
        // Get the site to copy the settings from, if any
289
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
290
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
291
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always false.
Loading history...
292
        // Load the metabundle
293
        $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

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

390
                $metaBundle->metaGlobalVars->/** @scrutinizer ignore-call */ 
391
                                             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...
391
                $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

391
                $metaBundle->metaBundleSettings->/** @scrutinizer ignore-call */ 
392
                                                 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...
392
            }
393
            $templateContainers = $metaBundle->frontendTemplatesContainer->data;
394
            $robotsContainer = $templateContainers[FrontendTemplates::ROBOTS_TXT_HANDLE];
395
            if ($robotsContainer !== null && is_array($robotsTemplate)) {
396
                $robotsContainer->setAttributes($robotsTemplate);
397
                if (!$robotsContainer->validate()) {
398
                    $hasErrors = true;
399
                }
400
            }
401
            $humansContainer = $templateContainers[FrontendTemplates::HUMANS_TXT_HANDLE];
402
            if ($humansContainer !== null && is_array($humansTemplate)) {
403
                $humansContainer->setAttributes($humansTemplate);
404
                if (!$humansContainer->validate()) {
405
                    $hasErrors = true;
406
                }
407
            }
408
            $adsContainer = $templateContainers[FrontendTemplates::ADS_TXT_HANDLE];
409
            if ($adsContainer !== null && is_array($adsTemplate)) {
410
                $adsContainer->setAttributes($adsTemplate);
411
                if (!$adsContainer->validate()) {
412
                    $hasErrors = true;
413
                }
414
            }
415
            $securityContainer = $templateContainers[FrontendTemplates::SECURITY_TXT_HANDLE];
416
            if ($securityContainer !== null && is_array($securityTemplate)) {
417
                $securityContainer->setAttributes($securityTemplate);
418
                if (!$securityContainer->validate()) {
419
                    $hasErrors = true;
420
                }
421
            }
422
            if (!$metaBundle->metaGlobalVars->validate()) {
423
                $hasErrors = true;
424
            }
425
426
            if ($hasErrors) {
427
                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

427
                Craft::error(/** @scrutinizer ignore-type */ print_r($metaBundle->metaGlobalVars->getErrors(), true), __METHOD__);
Loading history...
428
                Craft::$app->getSession()->setError(Craft::t('app', "Couldn't save settings due to a Twig error."));
429
                // Send the redirect back to the template
430
                /** @var UrlManager $urlManager */
431
                $urlManager = Craft::$app->getUrlManager();
432
                $urlManager->setRouteParams([
433
                    'editedMetaBundle' => $metaBundle,
434
                ]);
435
436
                return null;
437
            }
438
439
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
440
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
441
442
            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

442
            Seomatic::$plugin->/** @scrutinizer ignore-call */ 
443
                               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...
443
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic global settings saved.'));
444
        }
445
446
        return $this->redirectToPostedUrl();
447
    }
448
449
    /**
450
     * Content settings
451
     *
452
     * @param string|null $siteHandle
453
     *
454
     * @return Response The rendered result
455
     * @throws NotFoundHttpException
456
     * @throws ForbiddenHttpException
457
     */
458
    public function actionContent(string $siteHandle = null): Response
459
    {
460
        $variables = [];
461
        // Get the site to edit
462
        $siteHandle = $this->getCpSiteHandle($siteHandle);
463
        $siteId = $this->getSiteIdFromHandle($siteHandle);
464
465
        $pluginName = Seomatic::$settings->pluginName;
466
        $templateTitle = Craft::t('seomatic', 'Content SEO');
467
        // Asset bundle
468
        try {
469
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
470
        } catch (InvalidConfigException $e) {
471
            Craft::error($e->getMessage(), __METHOD__);
472
        }
473
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
474
            '@nystudio107/seomatic/web/assets/dist',
475
            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

475
        /** @scrutinizer ignore-call */ 
476
        $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...
476
        );
477
        // Basic variables
478
        $variables['fullPageForm'] = false;
479
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
480
        $variables['pluginName'] = Seomatic::$settings->pluginName;
481
        $variables['title'] = $templateTitle;
482
        $variables['docTitle'] = "{$pluginName} - {$templateTitle}";
483
        $siteHandleUri = Craft::$app->isMultiSite ? '/' . $siteHandle : '';
484
        $variables['crumbs'] = [
485
            [
486
                'label' => $pluginName,
487
                'url' => UrlHelper::cpUrl('seomatic'),
488
            ],
489
            [
490
                'label' => $templateTitle,
491
                'url' => UrlHelper::cpUrl('seomatic/content' . $siteHandleUri),
492
            ],
493
        ];
494
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
495
        $variables['controllerHandle'] = 'content';
496
        $this->setCrumbVariables($variables);
497
        $variables['selectedSubnavItem'] = 'content';
498
        $metaBundles = Seomatic::$plugin->metaBundles->getContentMetaBundlesForSiteId($siteId);
499
        Seomatic::$plugin->metaBundles->deleteVestigialMetaBundles($metaBundles);
500
501
        // Render the template
502
        return $this->renderTemplate('seomatic/settings/content/index', $variables);
503
    }
504
505
    /**
506
     * Global settings
507
     *
508
     * @param string $subSection
509
     * @param string $sourceBundleType
510
     * @param string $sourceHandle
511
     * @param string|null $siteHandle
512
     * @param string|int|null $typeId
513
     * @param string|null $loadFromSiteHandle
514
     *
515
     * @return Response The rendered result
516
     * @throws NotFoundHttpException
517
     * @throws ForbiddenHttpException
518
     */
519
    public function actionEditContent(
520
        string $subSection,
521
        string $sourceBundleType,
522
        string $sourceHandle,
523
        string $siteHandle = null,
524
               $typeId = null,
525
               $loadFromSiteHandle = null,
526
    ): Response {
527
        $variables = [];
528
        // @TODO: Let people choose an entry/categorygroup/product as the preview
529
        // Get the site to edit
530
        $siteHandle = $this->getCpSiteHandle($siteHandle);
531
        $siteId = $this->getSiteIdFromHandle($siteHandle);
532
        if (is_string($typeId)) {
533
            $typeId = (int)$typeId;
534
        }
535
        if (empty($typeId)) {
536
            $typeId = null;
537
        }
538
        // Get the (entry) type menu
539
        $typeMenu = [];
540
        $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
541
        if ($seoElement !== null) {
542
            $typeMenu = $seoElement::typeMenuFromHandle($sourceHandle);
543
        }
544
        $variables['typeMenu'] = $typeMenu;
545
        $variables['currentTypeId'] = null;
546
        $variables['specificTypeId'] = null;
547
        if (count($typeMenu) > 1) {
548
            $currentType = reset($typeMenu);
549
            $variables['currentType'] = $typeMenu[$typeId] ?? $currentType;
550
            $variables['currentTypeId'] = $typeId ?? key($typeMenu);
551
            $typeId = (int)$variables['currentTypeId'];
552
        }
553
        // If there's only one EntryType, don't bother displaying the menu
554
        if (count($typeMenu) === 1) {
555
            $variables['typeMenu'] = [];
556
            $variables['specificTypeId'] = $typeId ?? key($typeMenu);
557
        }
558
        // If this is the sitemap section, there are no per-entry type settings
559
        if ($subSection === 'sitemap') {
560
            $variables['typeMenu'] = [];
561
            $variables['specificTypeId'] = $typeId ?? key($typeMenu);
562
            $typeId = 0;
563
        }
564
        $pluginName = Seomatic::$settings->pluginName;
565
        // Asset bundle
566
        try {
567
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
568
        } catch (InvalidConfigException $e) {
569
            Craft::error($e->getMessage(), __METHOD__);
570
        }
571
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
572
            '@nystudio107/seomatic/web/assets/dist',
573
            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

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

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

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

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

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

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

847
                    $metaBundle->metaSiteVars->identity->/** @scrutinizer ignore-call */ 
848
                                                         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...
848
                    $siteSettings['identity'] = $metaBundle->metaSiteVars->identity;
849
                }
850
                if (!empty($siteSettings['creator'])) {
851
                    $settings = $siteSettings['creator'];
852
                    $this->prepEntitySettings($settings);
853
                    $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

853
                    $metaBundle->metaSiteVars->creator->/** @scrutinizer ignore-call */ 
854
                                                        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...
854
                    $siteSettings['creator'] = $metaBundle->metaSiteVars->creator;
855
                }
856
                if (!empty($siteSettings['additionalSitemapUrls'])) {
857
                    $siteSettings['additionalSitemapUrlsDateUpdated'] = new DateTime();
858
                    Seomatic::$plugin->sitemaps->submitCustomSitemap($siteId);
859
                }
860
                $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

860
                $metaBundle->metaSiteVars->/** @scrutinizer ignore-call */ 
861
                                           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...
861
            }
862
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
863
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
864
865
            Seomatic::$plugin->clearAllCaches();
866
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic site settings saved.'));
867
        }
868
869
        return $this->redirectToPostedUrl();
870
    }
871
872
    /**
873
     * Plugin settings
874
     *
875
     * @return Response The rendered result
876
     * @throws ForbiddenHttpException
877
     */
878
    public function actionPlugin(): Response
879
    {
880
        // Ensure they have permission to edit the plugin settings
881
        $currentUser = Craft::$app->getUser()->getIdentity();
882
        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

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

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

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

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

1179
    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...
1180
    {
1181
        // Enabled sites
1182
        $sites = Craft::$app->getSites();
1183
        if (Craft::$app->getIsMultiSite()) {
1184
            // Set defaults based on the section settings
1185
            $variables['enabledSiteIds'] = [];
1186
            $variables['siteIds'] = [];
1187
1188
            foreach ($sites->getEditableSiteIds() as $editableSiteId) {
1189
                $variables['enabledSiteIds'][] = $editableSiteId;
1190
                $variables['siteIds'][] = $editableSiteId;
1191
            }
1192
1193
            // Make sure the $siteId they are trying to edit is in our array of editable sites
1194
            if (!in_array($siteId, $variables['enabledSiteIds'], false)) {
1195
                if (!empty($variables['enabledSiteIds'])) {
1196
                    $siteId = reset($variables['enabledSiteIds']);
1197
                } else {
1198
                    $this->requirePermission('editSite:' . $siteId);
1199
                }
1200
            }
1201
        }
1202
1203
        // Set the currentSiteId and currentSiteHandle
1204
        $variables['currentSiteId'] = empty($siteId) ? Craft::$app->getSites()->currentSite->id : $siteId;
1205
        $variables['currentSiteHandle'] = empty($siteHandle)
1206
            ? Craft::$app->getSites()->currentSite->handle
1207
            : $siteHandle;
1208
1209
        // Page title
1210
        $variables['showSites'] = (
1211
            Craft::$app->getIsMultiSite() &&
1212
            count($variables['enabledSiteIds'])
1213
        );
1214
    }
1215
1216
    /**
1217
     * @param array $variables
1218
     * @return void
1219
     */
1220
    protected function setCrumbVariables(array &$variables)
1221
    {
1222
        $sites = Craft::$app->getSites();
1223
        if (!array_key_exists('crumbs', $variables)) {
1224
            $variables['crumbs'] = [];
1225
        }
1226
        // Handle adding in the Sites menu if there are multiple sites
1227
        if ($variables['showSites']) {
1228
            $siteCrumbItems = [];
1229
            $siteGroups = Craft::$app->getSites()->getAllGroups();
1230
            $crumbSites = Collection::make($sites->getAllSites())
1231
                ->map(fn(Site $site) => ['site' => $site])
1232
                ->keyBy(fn(array $site) => $site['site']->id)
1233
                ->filter(fn(array $site) => in_array($site['site']->id, $variables['enabledSiteIds']))
1234
                ->all();
1235
1236
            foreach ($siteGroups as $siteGroup) {
1237
                $groupSites = $siteGroup->getSites();
1238
1239
                if (empty($groupSites)) {
1240
                    continue;
1241
                }
1242
1243
                $groupSiteItems = array_map(fn(Site $site) => [
1244
                    'status' => $crumbSites[$site->id]['site']->status ?? null,
1245
                    'label' => Craft::t('site', $site->name),
1246
                    'url' => UrlHelper::cpUrl("seomatic/{$variables['controllerHandle']}?site=$site->handle"),
1247
                    'hidden' => !isset($crumbSites[$site->id]),
1248
                    'selected' => $site->id === $variables['currentSiteId'],
1249
                    'attributes' => [
1250
                        'data' => [
1251
                            'site-id' => $site->id,
1252
                        ],
1253
                    ],
1254
                ], $groupSites);
1255
1256
                if (count($siteGroups) > 1) {
1257
                    $siteCrumbItems[] = [
1258
                        'heading' => Craft::t('site', $siteGroup->name),
1259
                        'items' => $groupSiteItems,
1260
                        'hidden' => !ArrayHelper::contains($groupSiteItems, fn(array $item) => !$item['hidden']),
1261
                    ];
1262
                } else {
1263
                    array_push($siteCrumbItems, ...$groupSiteItems);
1264
                }
1265
            }
1266
            // Add in the breadcrumbs
1267
            $variables['crumbs'] = [
1268
                [
1269
                    'id' => 'language-menu',
1270
                    'icon' => 'world',
1271
                    'label' => Craft::t(
1272
                        'site',
1273
                        $sites->getSiteById((int)$variables['currentSiteId'])->name
1274
                    ),
1275
                    'menu' => [
1276
                        'items' => $siteCrumbItems,
1277
                        'label' => Craft::t('site', 'Select site'),
1278
                    ],
1279
                ],
1280
                ...$variables['crumbs'],
1281
            ];
1282
1283
            $variables['sitesMenuLabel'] = Craft::t(
1284
                'site',
1285
                $sites->getSiteById((int)$variables['currentSiteId'])->name
1286
            );
1287
        } else {
1288
            $variables['sitesMenuLabel'] = '';
1289
        }
1290
        // Handle adding in the Entry Types menu if there are multiple entry types
1291
        if (isset($variables['typeMenu']) && !empty($variables['typeMenu'])) {
1292
            $typeCrumbItems = [];
1293
            foreach ($variables['typeMenu'] as $key => $value) {
1294
                $typeCrumbItems[] = [
1295
                    'status' => null,
1296
                    'url' => UrlHelper::url("seomatic/{$variables['controllerHandle']}{$variables['siteHandleUri']}", [
1297
                        'site' => $variables['currentSiteHandle'],
1298
                        'typeId' => $key,
1299
                    ]),
1300
                    'label' => $value,
1301
                    'selected' => $variables['currentTypeId'] === $key,
1302
                ];
1303
            }
1304
            $variables['crumbs'][] =
1305
                [
1306
                    'id' => 'types-menu',
1307
                    'icon' => 'list',
1308
                    'label' => $variables['typeMenu'][$variables['currentTypeId']],
1309
                    'menu' => [
1310
                        'items' => $typeCrumbItems,
1311
                        'label' => Craft::t('seomatic', 'Entry Types'),
1312
                    ],
1313
                ];
1314
        }
1315
    }
1316
1317
    /**
1318
     * @param array $variables
1319
     */
1320
    protected function setGlobalFieldSourceVariables(array &$variables)
1321
    {
1322
        $variables['textFieldSources'] = array_merge(
1323
            ['globalsGroup' => ['optgroup' => 'Globals Fields']],
1324
            FieldHelper::fieldsOfTypeFromGlobals(
1325
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1326
                false
1327
            )
1328
        );
1329
        $variables['assetFieldSources'] = array_merge(
1330
            ['globalsGroup' => ['optgroup' => 'Globals Fields']],
1331
            FieldHelper::fieldsOfTypeFromGlobals(
1332
                FieldHelper::ASSET_FIELD_CLASS_KEY,
1333
                false
1334
            )
1335
        );
1336
    }
1337
1338
    /**
1339
     * Remove any sites for which meta bundles do not exist (they may be
1340
     * disabled for this section)
1341
     *
1342
     * @param class-string<SeoElementInterface> $seoElement
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<SeoElementInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<SeoElementInterface>.
Loading history...
1343
     * @param string $sourceBundleType
1344
     * @param string $sourceHandle
1345
     * @param array $variables
1346
     */
1347
    protected function cullDisabledSites($seoElement, string $sourceBundleType, string $sourceHandle, array &$variables)
1348
    {
1349
        /** @var Section|ProductType|null $section */
1350
        $section = $seoElement::sourceModelFromHandle($sourceHandle);
1351
        $sectionSiteIds = [];
1352
        if ($section) {
1353
            $sectionSettings = $section->getSiteSettings();
1354
            foreach ($sectionSettings as $sectionSetting) {
1355
                $sectionSiteIds[] = $sectionSetting->siteId;
1356
            }
1357
        }
1358
        if (isset($variables['enabledSiteIds'])) {
1359
            foreach ($variables['enabledSiteIds'] as $key => $value) {
1360
                $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
1361
                    $sourceBundleType,
1362
                    $sourceHandle,
1363
                    $value
1364
                );
1365
                // Make sure the site exists for this Section
1366
                if (!in_array($value, $sectionSiteIds, true)) {
1367
                    unset($variables['enabledSiteIds'][$key]);
1368
                }
1369
                if ($metaBundle === null) {
1370
                    unset($variables['enabledSiteIds'][$key]);
1371
                }
1372
            }
1373
        }
1374
    }
1375
1376
    /**
1377
     * @param string $sourceBundleType
1378
     * @param string $sourceHandle
1379
     * @param string $groupName
1380
     * @param array $variables
1381
     * @param int|string|null $typeId
1382
     */
1383
    protected function setContentFieldSourceVariables(
1384
        string $sourceBundleType,
1385
        string $sourceHandle,
1386
        string $groupName,
1387
        array  &$variables,
1388
               $typeId = null,
1389
    ) {
1390
        $variables['textFieldSources'] = array_merge(
1391
            ['entryGroup' => ['optgroup' => $groupName . ' Fields'], 'title' => 'Title'],
1392
            FieldHelper::fieldsOfTypeFromSource(
1393
                $sourceBundleType,
1394
                $sourceHandle,
1395
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1396
                false,
1397
                $typeId
1398
            )
1399
        );
1400
        $variables['assetFieldSources'] = array_merge(
1401
            ['entryGroup' => ['optgroup' => $groupName . ' Fields']],
1402
            FieldHelper::fieldsOfTypeFromSource(
1403
                $sourceBundleType,
1404
                $sourceHandle,
1405
                FieldHelper::ASSET_FIELD_CLASS_KEY,
1406
                false,
1407
                $typeId
1408
            )
1409
        );
1410
        $variables['assetVolumeTextFieldSources'] = array_merge(
1411
            ['entryGroup' => ['optgroup' => 'Asset Volume Fields'], '' => '--', 'title' => 'Title'],
1412
            array_merge(
1413
                FieldHelper::fieldsOfTypeFromAssetVolumes(
1414
                    FieldHelper::TEXT_FIELD_CLASS_KEY,
1415
                    false
1416
                )
1417
            )
1418
        );
1419
        $variables['userFieldSources'] = array_merge(
1420
            ['entryGroup' => ['optgroup' => 'User Fields']],
1421
            FieldHelper::fieldsOfTypeFromUsers(
1422
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1423
                false
1424
            )
1425
        );
1426
    }
1427
1428
    /**
1429
     * @param string $sourceBundleType
1430
     * @param string $sourceHandle
1431
     * @param null|int $siteId
1432
     * @param int|string|null $typeId
1433
     *
1434
     * @return string
1435
     */
1436
    protected function uriFromSourceBundle(string $sourceBundleType, string $sourceHandle, $siteId, $typeId): string
1437
    {
1438
        $uri = null;
1439
        // Pick an Element to be used for the preview
1440
        if ($sourceBundleType === MetaBundles::GLOBAL_META_BUNDLE) {
1441
            $uri = MetaBundles::GLOBAL_META_BUNDLE;
1442
        } else {
1443
            $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
1444
            if ($seoElement !== null) {
1445
                $uri = $seoElement::previewUri($sourceHandle, $siteId, $typeId);
1446
            }
1447
        }
1448
        // Special-case for the __home__ slug, and default to /
1449
        if (($uri === '__home__') || ($uri === null)) {
1450
            $uri = '/';
1451
        }
1452
1453
        return $uri;
1454
    }
1455
1456
    /**
1457
     * Prep the entity settings for saving to the db
1458
     *
1459
     * @param array &$settings
1460
     */
1461
    protected function prepEntitySettings(&$settings)
1462
    {
1463
        DynamicMetaHelper::normalizeTimes($settings['localBusinessOpeningHours']);
1464
        if (!empty($settings['siteType'])) {
1465
            $settings['computedType'] = SchemaHelper::getSpecificEntityType($settings);
1466
        } else {
1467
            $settings['computedType'] = 'WebPage';
1468
        }
1469
        // Empty out the entity image settings to ensure the image gets removed if it no longer exists
1470
        $settings['genericImage'] = '';
1471
        $settings['genericImageWidth'] = '';
1472
        $settings['genericImageHeight'] = '';
1473
        if (!empty($settings['genericImageIds'])) {
1474
            $asset = Craft::$app->getAssets()->getAssetById($settings['genericImageIds'][0]);
1475
            if ($asset !== null) {
1476
                $settings['genericImage'] = $asset->getUrl();
1477
                $settings['genericImageWidth'] = $asset->getWidth();
1478
                $settings['genericImageHeight'] = $asset->getHeight();
1479
            }
1480
        }
1481
    }
1482
}
1483