Test Failed
Push — v5 ( ec28e0...e66c40 )
by Andrew
54:30 queued 27:51
created

SettingsController::setCrumbVariables()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 80
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 55
c 1
b 0
f 0
dl 0
loc 80
rs 8.3595
cc 6
nc 9
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
14
use craft\errors\MissingComponentException;
15
use craft\helpers\UrlHelper;
16
use craft\models\Site;
17
use craft\web\Controller;
18
use craft\web\UrlManager;
19
use DateTime;
20
use Illuminate\Support\Collection;
21
use nystudio107\seomatic\assetbundles\seomatic\SeomaticAsset;
22
use nystudio107\seomatic\autocompletes\TrackingVarsAutocomplete;
23
use nystudio107\seomatic\helpers\ArrayHelper;
24
use nystudio107\seomatic\helpers\DynamicMeta as DynamicMetaHelper;
25
use nystudio107\seomatic\helpers\Field as FieldHelper;
26
use nystudio107\seomatic\helpers\ImageTransform as ImageTransformHelper;
27
use nystudio107\seomatic\helpers\PullField as PullFieldHelper;
28
use nystudio107\seomatic\helpers\Schema as SchemaHelper;
29
use nystudio107\seomatic\models\MetaBundle;
30
use nystudio107\seomatic\models\MetaScript;
31
use nystudio107\seomatic\models\MetaScriptContainer;
32
use nystudio107\seomatic\Seomatic;
33
use nystudio107\seomatic\services\FrontendTemplates;
34
use nystudio107\seomatic\services\MetaBundles;
35
use yii\base\InvalidConfigException;
36
use yii\web\BadRequestHttpException;
37
use yii\web\ForbiddenHttpException;
38
use yii\web\NotFoundHttpException;
39
use yii\web\Response;
40
use function count;
41
use function in_array;
42
use function is_array;
43
44
/**
45
 * @author    nystudio107
46
 * @package   Seomatic
47
 * @since     3.0.0
48
 */
49
class SettingsController extends Controller
50
{
51
    // Constants
52
    // =========================================================================
53
54
    public const DOCUMENTATION_URL = 'https://github.com/nystudio107/craft-seomatic';
55
56
    public const SETUP_GRADES = [
57
        ['id' => 'data1', 'name' => 'A', 'color' => '#008002'],
58
        ['id' => 'data2', 'name' => 'B', 'color' => '#9ACD31'],
59
        ['id' => 'data4', 'name' => 'C', 'color' => '#FFA500'],
60
        ['id' => 'data5', 'name' => 'D', 'color' => '#8B0100'],
61
    ];
62
63
    public const SEO_SETUP_FIELDS = [
64
        'mainEntityOfPage' => 'Main Entity of Page',
65
        'seoTitle' => 'SEO Title',
66
        'seoDescription' => 'SEO Description',
67
        'seoKeywords' => 'SEO Keywords',
68
        'seoImage' => 'SEO Image',
69
        'seoImageDescription' => 'SEO Image Description',
70
    ];
71
72
    public const SITE_SETUP_FIELDS = [
73
        'siteName' => 'Site Name',
74
        'twitterHandle' => 'Twitter Handle',
75
        'facebookProfileId' => 'Facebook Profile ID',
76
    ];
77
78
    public const IDENTITY_SETUP_FIELDS = [
79
        'computedType' => 'Identity Entity Type',
80
        'genericName' => 'Identity Entity Name',
81
        'genericDescription' => 'Identity Entity Description',
82
        'genericUrl' => 'Identity Entity URL',
83
        'genericImage' => 'Identity Entity Brand',
84
    ];
85
86
    // Protected Properties
87
    // =========================================================================
88
89
    /**
90
     * @inheritdoc
91
     */
92
    protected array|bool|int $allowAnonymous = [
93
    ];
94
95
    // Public Methods
96
    // =========================================================================
97
98
    /**
99
     * Dashboard display
100
     *
101
     * @param string|null $siteHandle
102
     * @param bool $showWelcome
103
     *
104
     * @return Response The rendered result
105
     * @throws NotFoundHttpException
106
     * @throws ForbiddenHttpException
107
     */
108
    public function actionDashboard(string $siteHandle = null, bool $showWelcome = false): Response
109
    {
110
        $variables = [];
111
        // Get the site to edit
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
        $this->setCrumbVariables($variables);
217
218
        // Render the template
219
        return $this->renderTemplate('seomatic/dashboard/index', $variables);
220
    }
221
222
    /**
223
     * Global settings
224
     *
225
     * @param string $subSection
226
     * @param string|null $siteHandle
227
     * @param string|null $loadFromSiteHandle
228
     *
229
     * @return Response The rendered result
230
     * @throws NotFoundHttpException
231
     * @throws ForbiddenHttpException
232
     */
233
    public function actionGlobal(string $subSection = 'general', string $siteHandle = null, $loadFromSiteHandle = null, $editedMetaBundle = null): Response
234
    {
235
        $variables = [];
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
        $this->setCrumbVariables($variables);
343
344
        // Render the template
345
        return $this->renderTemplate('seomatic/settings/global/' . $subSection, $variables);
346
    }
347
348
    /**
349
     * @return Response|null
350
     * @throws BadRequestHttpException
351
     * @throws MissingComponentException
352
     */
353
    public function actionSaveGlobal()
354
    {
355
        $this->requirePostRequest();
356
        $request = Craft::$app->getRequest();
357
        $siteId = $request->getParam('siteId');
358
        $globalsSettings = $request->getParam('metaGlobalVars');
359
        $bundleSettings = $request->getParam('metaBundleSettings');
360
        $robotsTemplate = $request->getParam('robotsTemplate');
361
        $humansTemplate = $request->getParam('humansTemplate');
362
        $adsTemplate = $request->getParam('adsTemplate');
363
        $securityTemplate = $request->getParam('securityTemplate');
364
        if (is_array($securityTemplate)) {
365
            if (!str_ends_with($securityTemplate['templateString'], "\n")) {
366
                $securityTemplate['templateString'] .= "\n";
367
            }
368
        }
369
        // Set the element type in the template
370
        $elementName = '';
371
372
        $hasErrors = false;
373
        // The site settings for the appropriate meta bundle
374
        Seomatic::$previewingMetaContainers = true;
375
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteId);
376
        Seomatic::$previewingMetaContainers = false;
377
        if ($metaBundle !== null) {
378
            if (is_array($globalsSettings) && is_array($bundleSettings)) {
379
                PullFieldHelper::parseTextSources($elementName, $globalsSettings, $bundleSettings);
380
                PullFieldHelper::parseImageSources($elementName, $globalsSettings, $bundleSettings, $siteId);
381
                if (!empty($bundleSettings['siteType'])) {
382
                    $globalsSettings['mainEntityOfPage'] = SchemaHelper::getSpecificEntityType($bundleSettings);
383
                }
384
                $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

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

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

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

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

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

550
        /** @scrutinizer ignore-call */ 
551
        $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...
551
        );
552
        // Enabled sites
553
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
554
        $this->cullDisabledSites($sourceBundleType, $sourceHandle, $variables);
555
        // Meta Bundle settings
556
        Seomatic::$previewingMetaContainers = true;
557
        // Get the site to copy the settings from, if any
558
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
559
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
560
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always false.
Loading history...
561
        // Load the metabundle
562
        $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
563
            $sourceBundleType,
564
            $sourceHandle,
565
            $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

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

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

683
                $metaBundle->metaSitemapVars->/** @scrutinizer ignore-call */ 
684
                                              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...
684
            }
685
686
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
687
            $metaBundle->typeId = $typeId;
688
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
689
690
            Seomatic::$plugin->clearAllCaches();
691
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic content settings saved.'));
692
        }
693
694
        return $this->redirectToPostedUrl();
695
    }
696
697
    /**
698
     * Site settings
699
     *
700
     * @param string $subSection
701
     * @param string|null $siteHandle
702
     * @param string|null $loadFromSiteHandle
703
     *
704
     * @return Response The rendered result
705
     * @throws NotFoundHttpException
706
     * @throws ForbiddenHttpException
707
     */
708
    public function actionSite(string $subSection = 'identity', string $siteHandle = null, $loadFromSiteHandle = null): Response
709
    {
710
        $variables = [];
711
        // Get the site to edit
712
        $siteId = $this->getSiteIdFromHandle($siteHandle);
713
714
        $pluginName = Seomatic::$settings->pluginName;
715
        $templateTitle = Craft::t('seomatic', 'Site Settings');
716
        $subSectionSuffix = '';
717
        if ($subSection === 'social') {
718
            $subSectionSuffix = ' Media';
719
        }
720
        $subSectionTitle = Craft::t('seomatic', ucfirst($subSection) . $subSectionSuffix);
721
        // Asset bundle
722
        try {
723
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
724
        } catch (InvalidConfigException $e) {
725
            Craft::error($e->getMessage(), __METHOD__);
726
        }
727
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
728
            '@nystudio107/seomatic/web/assets/dist',
729
            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

729
        /** @scrutinizer ignore-call */ 
730
        $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...
730
        );
731
        // Basic variables
732
        $variables['fullPageForm'] = true;
733
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
734
        $variables['pluginName'] = Seomatic::$settings->pluginName;
735
        $variables['title'] = $templateTitle;
736
        $variables['subSectionTitle'] = $subSectionTitle;
737
        $variables['docTitle'] = "{$pluginName} - {$templateTitle} - {$subSectionTitle}";
738
        $siteHandleUri = Craft::$app->isMultiSite ? '/' . $siteHandle : '';
739
        $variables['crumbs'] = [
740
            [
741
                'label' => $pluginName,
742
                'url' => UrlHelper::cpUrl('seomatic'),
743
            ],
744
            [
745
                'label' => $templateTitle,
746
                'url' => UrlHelper::cpUrl('seomatic/site/identity' . $siteHandleUri),
747
            ],
748
            [
749
                'label' => $subSectionTitle,
750
                'url' => UrlHelper::cpUrl('seomatic/site/' . $subSection . $siteHandleUri),
751
            ],
752
        ];
753
        $variables['selectedSubnavItem'] = 'site';
754
        $variables['currentSubSection'] = $subSection;
755
756
        // Enabled sites
757
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
758
        $variables['controllerHandle'] = 'site' . '/' . $subSection;
759
760
        // The site settings for the appropriate meta bundle
761
        Seomatic::$previewingMetaContainers = true;
762
        // Get the site to copy the settings from, if any
763
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
764
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
765
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always false.
Loading history...
766
        // Load the metabundle
767
        $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

767
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle(/** @scrutinizer ignore-type */ $siteIdToLoad);
Loading history...
768
        Seomatic::$previewingMetaContainers = false;
769
        if ($metaBundle !== null) {
770
            $variables['site'] = $metaBundle->metaSiteVars;
771
            $variables['identityImageElements'] = ImageTransformHelper::assetElementsFromIds(
772
                $variables['site']->identity->genericImageIds,
773
                $siteId
774
            );
775
            $variables['creatorImageElements'] = ImageTransformHelper::assetElementsFromIds(
776
                $variables['site']->creator->genericImageIds,
777
                $siteId
778
            );
779
        }
780
        $variables['elementType'] = Asset::class;
781
        $this->setCrumbVariables($variables);
782
783
        // Render the template
784
        return $this->renderTemplate('seomatic/settings/site/' . $subSection, $variables);
785
    }
786
787
    /**
788
     * @return Response
789
     * @throws BadRequestHttpException
790
     * @throws MissingComponentException
791
     */
792
    public function actionSaveSite(): Response
793
    {
794
        $this->requirePostRequest();
795
        $request = Craft::$app->getRequest();
796
        $siteId = $request->getParam('siteId');
797
        $siteSettings = $request->getParam('seomaticSite');
798
799
        // Make sure the twitter handle isn't prefixed with an @
800
        if (!empty($siteSettings['twitterHandle'])) {
801
            $siteSettings['twitterHandle'] = ltrim($siteSettings['twitterHandle'], '@');
802
        }
803
        // Make sure the sameAsLinks are indexed by the handle
804
        if (!empty($siteSettings['sameAsLinks'])) {
805
            $siteSettings['sameAsLinks'] = ArrayHelper::index($siteSettings['sameAsLinks'], 'handle');
806
        }
807
        // The site settings for the appropriate meta bundle
808
        Seomatic::$previewingMetaContainers = true;
809
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteId);
810
        Seomatic::$previewingMetaContainers = false;
811
        if ($metaBundle) {
812
            if (is_array($siteSettings)) {
813
                if (!empty($siteSettings['identity'])) {
814
                    $settings = $siteSettings['identity'];
815
                    $this->prepEntitySettings($settings);
816
                    $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

816
                    $metaBundle->metaSiteVars->identity->/** @scrutinizer ignore-call */ 
817
                                                         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...
817
                    $siteSettings['identity'] = $metaBundle->metaSiteVars->identity;
818
                }
819
                if (!empty($siteSettings['creator'])) {
820
                    $settings = $siteSettings['creator'];
821
                    $this->prepEntitySettings($settings);
822
                    $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

822
                    $metaBundle->metaSiteVars->creator->/** @scrutinizer ignore-call */ 
823
                                                        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...
823
                    $siteSettings['creator'] = $metaBundle->metaSiteVars->creator;
824
                }
825
                if (!empty($siteSettings['additionalSitemapUrls'])) {
826
                    $siteSettings['additionalSitemapUrlsDateUpdated'] = new DateTime();
827
                    Seomatic::$plugin->sitemaps->submitCustomSitemap($siteId);
828
                }
829
                $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

829
                $metaBundle->metaSiteVars->/** @scrutinizer ignore-call */ 
830
                                           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...
830
            }
831
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
832
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
833
834
            Seomatic::$plugin->clearAllCaches();
835
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic site settings saved.'));
836
        }
837
838
        return $this->redirectToPostedUrl();
839
    }
840
841
    /**
842
     * Plugin settings
843
     *
844
     * @return Response The rendered result
845
     * @throws ForbiddenHttpException
846
     */
847
    public function actionPlugin(): Response
848
    {
849
        // Ensure they have permission to edit the plugin settings
850
        $currentUser = Craft::$app->getUser()->getIdentity();
851
        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

851
        if (!$currentUser->/** @scrutinizer ignore-call */ can('seomatic:plugin-settings')) {
Loading history...
852
            throw new ForbiddenHttpException('You do not have permission to edit SEOmatic plugin settings.');
853
        }
854
        $general = Craft::$app->getConfig()->getGeneral();
855
        if (!$general->allowAdminChanges) {
856
            throw new ForbiddenHttpException('Unable to edit SEOmatic plugin settings because admin changes are disabled in this environment.');
857
        }
858
        // Edit the plugin settings
859
        $variables = [];
860
        $pluginName = Seomatic::$settings->pluginName;
861
        $templateTitle = Craft::t('seomatic', 'Plugin Settings');
862
        // Asset bundle
863
        try {
864
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
865
        } catch (InvalidConfigException $e) {
866
            Craft::error($e->getMessage(), __METHOD__);
867
        }
868
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
869
            '@nystudio107/seomatic/web/assets/dist',
870
            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

870
        /** @scrutinizer ignore-call */ 
871
        $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...
871
        );
872
        // Basic variables
873
        $variables['fullPageForm'] = true;
874
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
875
        $variables['pluginName'] = Seomatic::$settings->pluginName;
876
        $variables['title'] = $templateTitle;
877
        $variables['docTitle'] = "{$pluginName} - {$templateTitle}";
878
        $variables['crumbs'] = [
879
            [
880
                'label' => $pluginName,
881
                'url' => UrlHelper::cpUrl('seomatic'),
882
            ],
883
            [
884
                'label' => $templateTitle,
885
                'url' => UrlHelper::cpUrl('seomatic/plugin'),
886
            ],
887
        ];
888
        $variables['selectedSubnavItem'] = 'plugin';
889
        $variables['settings'] = Seomatic::$settings;
890
        $sites = ArrayHelper::map(Craft::$app->getSites()->getAllSites(), 'id', 'name');
891
        $variables['sites'] = $sites;
892
893
        // Render the template
894
        return $this->renderTemplate('seomatic/settings/plugin/_edit', $variables);
895
    }
896
897
    /**
898
     * Tracking settings
899
     *
900
     * @param string $subSection
901
     * @param string|null $siteHandle
902
     * @param string|null $loadFromSiteHandle
903
     *
904
     * @return Response The rendered result
905
     * @throws NotFoundHttpException
906
     * @throws ForbiddenHttpException
907
     */
908
    public function actionTracking(string $subSection = 'gtag', string $siteHandle = null, $loadFromSiteHandle = null, $editedMetaBundle = null): Response
909
    {
910
        $variables = [];
911
        // Get the site to edit
912
        $siteId = $this->getSiteIdFromHandle($siteHandle);
913
        // Enabled sites
914
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
915
        $variables['controllerHandle'] = 'tracking' . '/' . $subSection;
916
        $variables['currentSubSection'] = $subSection;
917
918
        // The script meta containers for the global meta bundle
919
        Seomatic::$previewingMetaContainers = true;
920
        // Get the site to copy the settings from, if any
921
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
922
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
923
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always false.
Loading history...
924
        // Load the metabundle
925
        $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

925
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle(/** @scrutinizer ignore-type */ $siteIdToLoad);
Loading history...
926
        if ($editedMetaBundle) {
927
            $metaBundle = $editedMetaBundle;
928
        }
929
        Seomatic::$previewingMetaContainers = false;
930
        if ($metaBundle !== null) {
931
            $variables['scripts'] = Seomatic::$plugin->metaBundles->getContainerDataFromBundle(
932
                $metaBundle,
933
                MetaScriptContainer::CONTAINER_TYPE
934
            );
935
        }
936
        // Add in the variables to the autocomplete cache so they can be accessed across requests
937
        $subSectionSettings = $variables['scripts'][$subSection];
938
        $variables['codeEditorOptions'] = [
939
            TrackingVarsAutocomplete::OPTIONS_DATA_KEY => $subSectionSettings->vars,
940
        ];
941
        // Plugin and section settings
942
        $pluginName = Seomatic::$settings->pluginName;
943
        $templateTitle = Craft::t('seomatic', 'Tracking Scripts');
944
        $subSectionTitle = $variables['scripts'][$subSection]->name;
945
        $subSectionTitle = Craft::t('seomatic', $subSectionTitle);
946
        // Asset bundle
947
        try {
948
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
949
        } catch (InvalidConfigException $e) {
950
            Craft::error($e->getMessage(), __METHOD__);
951
        }
952
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
953
            '@nystudio107/seomatic/web/assets/dist',
954
            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

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

1131
    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...
1132
    {
1133
        // Enabled sites
1134
        $sites = Craft::$app->getSites();
1135
        if (Craft::$app->getIsMultiSite()) {
1136
            // Set defaults based on the section settings
1137
            $variables['enabledSiteIds'] = [];
1138
            $variables['siteIds'] = [];
1139
1140
            foreach ($sites->getEditableSiteIds() as $editableSiteId) {
1141
                $variables['enabledSiteIds'][] = $editableSiteId;
1142
                $variables['siteIds'][] = $editableSiteId;
1143
            }
1144
1145
            // Make sure the $siteId they are trying to edit is in our array of editable sites
1146
            if (!in_array($siteId, $variables['enabledSiteIds'], false)) {
1147
                if (!empty($variables['enabledSiteIds'])) {
1148
                    $siteId = reset($variables['enabledSiteIds']);
1149
                } else {
1150
                    $this->requirePermission('editSite:' . $siteId);
1151
                }
1152
            }
1153
        }
1154
1155
        // Set the currentSiteId and currentSiteHandle
1156
        $variables['currentSiteId'] = empty($siteId) ? Craft::$app->getSites()->currentSite->id : $siteId;
1157
        $variables['currentSiteHandle'] = empty($siteHandle)
1158
            ? Craft::$app->getSites()->currentSite->handle
1159
            : $siteHandle;
1160
1161
        // Page title
1162
        $variables['showSites'] = (
1163
            Craft::$app->getIsMultiSite() &&
1164
            count($variables['enabledSiteIds'])
1165
        );
1166
    }
1167
1168
    /**
1169
     * @param array $variables
1170
     * @return void
1171
     */
1172
    protected function setCrumbVariables(array &$variables)
1173
    {
1174
        $sites = Craft::$app->getSites();
1175
        if ($variables['showSites']) {
1176
            $siteCrumbItems = [];
1177
            $siteGroups = Craft::$app->getSites()->getAllGroups();
1178
            $crumbSites = Collection::make($sites->getAllSites())
1179
                ->map(fn(Site $site) => ['site' => $site])
1180
                ->keyBy(fn(array $site) => $site['site']->id)
1181
                ->all();
1182
1183
            foreach ($siteGroups as $siteGroup) {
1184
                $groupSites = $siteGroup->getSites();
1185
1186
                if (empty($groupSites)) {
1187
                    continue;
1188
                }
1189
1190
                $groupSiteItems = array_map(fn(Site $site) => [
1191
                    'status' => $crumbSites[$site->id]['site']->status ?? null,
1192
                    'label' => Craft::t('site', $site->name),
1193
                    'url' => UrlHelper::cpUrl("seomatic/{$variables['controllerHandle']}/$site->handle"),
1194
                    'hidden' => !isset($crumbSites[$site->id]),
1195
                    'selected' => $site->id === $variables['currentSiteId'],
1196
                    'attributes' => [
1197
                        'data' => [
1198
                            'site-id' => $site->id,
1199
                        ],
1200
                    ],
1201
                ], $groupSites);
1202
1203
                if (count($siteGroups) > 1) {
1204
                    $siteCrumbItems[] = [
1205
                        'heading' => Craft::t('site', $siteGroup->name),
1206
                        'items' => $groupSiteItems,
1207
                        'hidden' => !ArrayHelper::contains($groupSiteItems, fn(array $item) => !$item['hidden']),
1208
                    ];
1209
                } else {
1210
                    array_push($siteCrumbItems, ...$groupSiteItems);
1211
                }
1212
            }
1213
1214
            if (array_key_exists('crumbs', $variables)) {
1215
                $variables['crumbs'] = [
1216
                    [
1217
                        'id' => 'language-menu',
1218
                        'icon' => 'world',
1219
                        'label' => Craft::t(
1220
                            'site',
1221
                            $sites->getSiteById((int)$variables['currentSiteId'])->name
1222
                        ),
1223
                        'menu' => [
1224
                            'items' => $siteCrumbItems,
1225
                            'label' => Craft::t('site', 'Select site'),
1226
                        ],
1227
                    ],
1228
                    ...$variables['crumbs'],
1229
                ];
1230
            } else {
1231
                $variables['crumbs'] = [
1232
                    [
1233
                        'id' => 'language-menu',
1234
                        'icon' => 'world',
1235
                        'label' => Craft::t(
1236
                            'site',
1237
                            $sites->getSiteById((int)$variables['currentSiteId'])->name
1238
                        ),
1239
                        'menu' => [
1240
                            'items' => $siteCrumbItems,
1241
                            'label' => Craft::t('site', 'Select site'),
1242
                        ],
1243
                    ],
1244
                ];
1245
            }
1246
            $variables['sitesMenuLabel'] = Craft::t(
1247
                'site',
1248
                $sites->getSiteById((int)$variables['currentSiteId'])->name
1249
            );
1250
        } else {
1251
            $variables['sitesMenuLabel'] = '';
1252
        }
1253
    }
1254
1255
    /**
1256
     * @param array $variables
1257
     */
1258
    protected function setGlobalFieldSourceVariables(array &$variables)
1259
    {
1260
        $variables['textFieldSources'] = array_merge(
1261
            ['globalsGroup' => ['optgroup' => 'Globals Fields']],
1262
            FieldHelper::fieldsOfTypeFromGlobals(
1263
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1264
                false
1265
            )
1266
        );
1267
        $variables['assetFieldSources'] = array_merge(
1268
            ['globalsGroup' => ['optgroup' => 'Globals Fields']],
1269
            FieldHelper::fieldsOfTypeFromGlobals(
1270
                FieldHelper::ASSET_FIELD_CLASS_KEY,
1271
                false
1272
            )
1273
        );
1274
    }
1275
1276
    /**
1277
     * Remove any sites for which meta bundles do not exist (they may be
1278
     * disabled for this section)
1279
     *
1280
     * @param string $sourceBundleType
1281
     * @param string $sourceHandle
1282
     * @param array $variables
1283
     */
1284
    protected function cullDisabledSites(string $sourceBundleType, string $sourceHandle, array &$variables)
1285
    {
1286
        if (isset($variables['enabledSiteIds'])) {
1287
            foreach ($variables['enabledSiteIds'] as $key => $value) {
1288
                $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
1289
                    $sourceBundleType,
1290
                    $sourceHandle,
1291
                    $value
1292
                );
1293
                if ($metaBundle === null) {
1294
                    unset($variables['enabledSiteIds'][$key]);
1295
                }
1296
            }
1297
        }
1298
    }
1299
1300
    /**
1301
     * @param string $sourceBundleType
1302
     * @param string $sourceHandle
1303
     * @param string $groupName
1304
     * @param array $variables
1305
     */
1306
    protected function setContentFieldSourceVariables(
1307
        string $sourceBundleType,
1308
        string $sourceHandle,
1309
        string $groupName,
1310
        array  &$variables,
1311
    ) {
1312
        $variables['textFieldSources'] = array_merge(
1313
            ['entryGroup' => ['optgroup' => $groupName . ' Fields'], 'title' => 'Title'],
1314
            FieldHelper::fieldsOfTypeFromSource(
1315
                $sourceBundleType,
1316
                $sourceHandle,
1317
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1318
                false
1319
            )
1320
        );
1321
        $variables['assetFieldSources'] = array_merge(
1322
            ['entryGroup' => ['optgroup' => $groupName . ' Fields']],
1323
            FieldHelper::fieldsOfTypeFromSource(
1324
                $sourceBundleType,
1325
                $sourceHandle,
1326
                FieldHelper::ASSET_FIELD_CLASS_KEY,
1327
                false
1328
            )
1329
        );
1330
        $variables['assetVolumeTextFieldSources'] = array_merge(
1331
            ['entryGroup' => ['optgroup' => 'Asset Volume Fields'], '' => '--', 'title' => 'Title'],
1332
            array_merge(
1333
                FieldHelper::fieldsOfTypeFromAssetVolumes(
1334
                    FieldHelper::TEXT_FIELD_CLASS_KEY,
1335
                    false
1336
                )
1337
            )
1338
        );
1339
        $variables['userFieldSources'] = array_merge(
1340
            ['entryGroup' => ['optgroup' => 'User Fields']],
1341
            FieldHelper::fieldsOfTypeFromUsers(
1342
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1343
                false
1344
            )
1345
        );
1346
    }
1347
1348
    /**
1349
     * @param string $sourceBundleType
1350
     * @param string $sourceHandle
1351
     * @param null|int $siteId
1352
     *
1353
     * @return string
1354
     */
1355
    protected function uriFromSourceBundle(string $sourceBundleType, string $sourceHandle, $siteId): string
1356
    {
1357
        $uri = null;
1358
        // Pick an Element to be used for the preview
1359
        if ($sourceBundleType === MetaBundles::GLOBAL_META_BUNDLE) {
1360
            $uri = MetaBundles::GLOBAL_META_BUNDLE;
1361
        } else {
1362
            $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
1363
            if ($seoElement !== null) {
1364
                $uri = $seoElement::previewUri($sourceHandle, $siteId);
1365
            }
1366
        }
1367
        // Special-case for the __home__ slug, and default to /
1368
        if (($uri === '__home__') || ($uri === null)) {
1369
            $uri = '/';
1370
        }
1371
1372
        return $uri;
1373
    }
1374
1375
    /**
1376
     * Prep the entity settings for saving to the db
1377
     *
1378
     * @param array &$settings
1379
     */
1380
    protected function prepEntitySettings(&$settings)
1381
    {
1382
        DynamicMetaHelper::normalizeTimes($settings['localBusinessOpeningHours']);
1383
        if (!empty($settings['siteType'])) {
1384
            $settings['computedType'] = SchemaHelper::getSpecificEntityType($settings);
1385
        } else {
1386
            $settings['computedType'] = 'WebPage';
1387
        }
1388
        // Empty out the entity image settings to ensure the image gets removed if it no longer exists
1389
        $settings['genericImage'] = '';
1390
        $settings['genericImageWidth'] = '';
1391
        $settings['genericImageHeight'] = '';
1392
        if (!empty($settings['genericImageIds'])) {
1393
            $asset = Craft::$app->getAssets()->getAssetById($settings['genericImageIds'][0]);
1394
            if ($asset !== null) {
1395
                $settings['genericImage'] = $asset->getUrl();
1396
                $settings['genericImageWidth'] = $asset->getWidth();
1397
                $settings['genericImageHeight'] = $asset->getHeight();
1398
            }
1399
        }
1400
    }
1401
}
1402