Passed
Push — v3 ( 1e51d3...178405 )
by Andrew
25:46
created

SettingsController::actionSaveGlobal()   C

Complexity

Conditions 15
Paths 163

Size

Total Lines 70
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 0
Metric Value
cc 15
eloc 47
c 0
b 0
f 0
nc 163
nop 0
dl 0
loc 70
ccs 0
cts 60
cp 0
crap 240
rs 5.3916

How to fix   Long Method    Complexity   

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 3.x
4
 *
5
 * @link      https://nystudio107.com/
6
 * @copyright Copyright (c) 2017 nystudio107
7
 * @license   https://nystudio107.com/license
0 ignored issues
show
Coding Style introduced by
@license tag must contain a URL and a license name
Loading history...
8
 */
9
10
namespace nystudio107\seomatic\controllers;
11
12
use nystudio107\seomatic\Seomatic;
13
use nystudio107\seomatic\assetbundles\seomatic\SeomaticAsset;
14
use nystudio107\seomatic\helpers\Field as FieldHelper;
15
use nystudio107\seomatic\helpers\PullField as PullFieldHelper;
16
use nystudio107\seomatic\helpers\Schema as SchemaHelper;
17
use nystudio107\seomatic\helpers\ArrayHelper;
18
use nystudio107\seomatic\helpers\DynamicMeta as DynamicMetaHelper;
19
use nystudio107\seomatic\helpers\ImageTransform as ImageTransformHelper;
20
use nystudio107\seomatic\helpers\PluginTemplate;
21
use nystudio107\seomatic\models\MetaBundle;
22
use nystudio107\seomatic\models\MetaScript;
23
use nystudio107\seomatic\models\MetaScriptContainer;
24
use nystudio107\seomatic\services\FrontendTemplates;
25
use nystudio107\seomatic\services\MetaBundles;
26
27
use Craft;
28
use craft\elements\Asset;
29
use craft\helpers\UrlHelper;
30
use craft\models\Site;
31
use craft\web\Controller;
32
33
use yii\base\InvalidConfigException;
34
use yii\web\NotFoundHttpException;
35
use yii\web\Response;
36
37
/**
38
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
39
 * @package   Seomatic
40
 * @since     3.0.0
41
 */
42
class SettingsController extends Controller
43
{
44
    // Constants
45
    // =========================================================================
46
47
    const DOCUMENTATION_URL = 'https://github.com/nystudio107/craft-seomatic';
48
49
    const SETUP_GRADES = [
50
        ['id' => 'data1', 'name' => 'A', 'color' => '#008002'],
51
        ['id' => 'data2', 'name' => 'B', 'color' => '#9ACD31'],
52
        ['id' => 'data4', 'name' => 'C', 'color' => '#FFA500'],
53
        ['id' => 'data5', 'name' => 'D', 'color' => '#8B0100'],
54
    ];
55
56
    const SEO_SETUP_FIELDS = [
57
        'mainEntityOfPage' => 'Main Entity of Page',
58
        'seoTitle' => 'SEO Title',
59
        'seoDescription' => 'SEO Description',
60
        'seoKeywords' => 'SEO Keywords',
61
        'seoImage' => 'SEO Image',
62
        'seoImageDescription' => 'SEO Image Description',
63
    ];
64
65
    const SITE_SETUP_FIELDS = [
66
        'siteName' => 'Site Name',
67
        'twitterHandle' => 'Twitter Handle',
68
        'facebookProfileId' => 'Facebook Profile ID',
69
    ];
70
71
    const IDENTITY_SETUP_FIELDS = [
72
        'computedType' => 'Identity Entity Type',
73
        'genericName' => 'Identity Entity Name',
74
        'genericDescription' => 'Identity Entity Description',
75
        'genericUrl' => 'Identity Entity URL',
76
        'genericImage' => 'Identity Entity Brand',
77
    ];
78
79
    // Protected Properties
80
    // =========================================================================
81
82
    /**
83
     * @inheritdoc
84
     */
85
    protected $allowAnonymous = [
86
    ];
87
88
    // Public Methods
89
    // =========================================================================
90
91
    /**
92
     * Dashboard display
93
     *
94
     * @param string|null $siteHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
95
     * @param bool        $showWelcome
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
96
     *
97
     * @return Response The rendered result
98
     * @throws NotFoundHttpException
99
     * @throws \yii\web\ForbiddenHttpException
100
     */
101
    public function actionDashboard(string $siteHandle = null, bool $showWelcome = false): Response
102
    {
103
        $variables = [];
104
        // Get the site to edit
105
        $siteId = $this->getSiteIdFromHandle($siteHandle);
106
        $pluginName = Seomatic::$settings->pluginName;
107
        $templateTitle = Craft::t('seomatic', 'Dashboard');
108
        // Asset bundle
109
        try {
110
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
111
        } catch (InvalidConfigException $e) {
112
            Craft::error($e->getMessage(), __METHOD__);
113
        }
114
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
115
            '@nystudio107/seomatic/assetbundles/seomatic/dist',
116
            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

116
        /** @scrutinizer ignore-call */ 
117
        $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...
117
        );
118
        // Enabled sites
119
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
120
        $variables['controllerHandle'] = 'dashboard';
121
122
        // Basic variables
123
        $variables['fullPageForm'] = false;
124
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
125
        $variables['pluginName'] = Seomatic::$settings->pluginName;
126
        $variables['title'] = $templateTitle;
127
        $variables['docTitle'] = "{$pluginName} - {$templateTitle}";
128
        $siteHandleUri = Craft::$app->isMultiSite ? '/'.$siteHandle : '';
129
        $variables['crumbs'] = [
130
            [
131
                'label' => $pluginName,
132
                'url' => UrlHelper::cpUrl('seomatic'),
133
            ],
134
            [
135
                'label' => $templateTitle,
136
                'url' => UrlHelper::cpUrl('seomatic/dashboard'.$siteHandleUri),
137
            ],
138
        ];
139
        $variables['selectedSubnavItem'] = 'dashboard';
140
        $variables['showWelcome'] = $showWelcome;
141
        // Calulate the setup grades
142
        $variables['contentSetupStats'] = [];
143
        $variables['setupGrades'] = self::SETUP_GRADES;
144
        $numFields = \count(self::SEO_SETUP_FIELDS);
145
        $numGrades = \count(self::SETUP_GRADES);
146
        while ($numGrades--) {
147
            $variables['contentSetupStats'][] = 0;
148
        }
149
        $numGrades = \count(self::SETUP_GRADES);
150
        // Content SEO grades
151
        $variables['metaBundles'] = Seomatic::$plugin->metaBundles->getContentMetaBundlesForSiteId($siteId);
152
        $variables['contentSetupChecklistCutoff'] = floor(count($variables['metaBundles']) / 2);
153
        $variables['contentSetupChecklist'] = [];
154
        Seomatic::$plugin->metaBundles->pruneVestigialMetaBundles($variables['metaBundles']);
155
        /** @var MetaBundle $metaBundle */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
156
        foreach ($variables['metaBundles'] as $metaBundle) {
157
            $stat = 0;
158
            foreach (self::SEO_SETUP_FIELDS as $setupField => $setupLabel) {
159
                $stat += (int)!empty($metaBundle->metaGlobalVars[$setupField]);
160
                $value = $variables['contentSetupChecklist'][$setupField]['value'] ?? 0;
161
                $variables['contentSetupChecklist'][$setupField] = [
162
                    'label' => $setupLabel,
163
                    'value' => $value + (int)!empty($metaBundle->metaGlobalVars[$setupField]),
164
                ];
165
            }
166
            $stat = round($numGrades - (($stat * $numGrades) / $numFields));
167
            if ($stat >= $numGrades) {
168
                $stat = $numGrades - 1;
169
            }
170
            $variables['contentSetupStats'][$stat]++;
171
        }
172
        // Global SEO grades
173
        Seomatic::$previewingMetaContainers = true;
174
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle((int)$siteId);
175
        Seomatic::$previewingMetaContainers = false;
176
        if ($metaBundle !== null) {
177
            $stat = 0;
178
            $variables['globalSetupChecklist'] = [];
179
            foreach (self::SEO_SETUP_FIELDS as $setupField => $setupLabel) {
180
                $stat += (int)!empty($metaBundle->metaGlobalVars[$setupField]);
181
                $variables['globalSetupChecklist'][$setupField] = [
182
                    'label' => $setupLabel,
183
                    'value' => (int)!empty($metaBundle->metaGlobalVars[$setupField]),
184
                ];
185
            }
186
            $stat = round(($stat / $numFields) * 100);
187
            $variables['globalSetupStat'] = $stat;
188
            // Site Settings grades
189
            $numFields = \count(self::SITE_SETUP_FIELDS) + \count(self::IDENTITY_SETUP_FIELDS);
190
            $stat = 0;
191
            $variables['siteSetupChecklist'] = [];
192
            foreach (self::SITE_SETUP_FIELDS as $setupField => $setupLabel) {
193
                $stat += (int)!empty($metaBundle->metaSiteVars[$setupField]);
194
                $variables['siteSetupChecklist'][$setupField] = [
195
                    'label' => $setupLabel,
196
                    'value' => (int)!empty($metaBundle->metaSiteVars[$setupField]),
197
                ];
198
            }
199
            foreach (self::IDENTITY_SETUP_FIELDS as $setupField => $setupLabel) {
200
                $stat += (int)!empty($metaBundle->metaSiteVars->identity[$setupField]);
201
                $variables['siteSetupChecklist'][$setupField] = [
202
                    'label' => $setupLabel,
203
                    'value' => (int)!empty($metaBundle->metaSiteVars->identity[$setupField]),
204
                ];
205
            }
206
            $stat = round(($stat / $numFields) * 100);
207
            $variables['siteSetupStat'] = $stat;
208
        }
209
210
        // Render the template
211
        return $this->renderTemplate('seomatic/dashboard/index', $variables);
212
    }
213
214
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $editedMetaBundle should have a doc-comment as per coding-style.
Loading history...
215
     * Global settings
216
     *
217
     * @param string $subSection
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 6 spaces after parameter type; 1 found
Loading history...
218
     * @param string|null $siteHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
219
     * @param null $loadFromSiteHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 8 spaces after parameter type; 1 found
Loading history...
Documentation Bug introduced by
Are you sure the doc-type for parameter $loadFromSiteHandle is correct as it would always require null to be passed?
Loading history...
220
     *
221
     * @return Response The rendered result
222
     * @throws NotFoundHttpException
223
     * @throws \yii\web\ForbiddenHttpException
224
     */
225
    public function actionGlobal(string $subSection = 'general', string $siteHandle = null, $loadFromSiteHandle = null, $editedMetaBundle = null): Response
226
    {
227
        $variables = [];
228
        $siteId = $this->getSiteIdFromHandle($siteHandle);
229
230
        $pluginName = Seomatic::$settings->pluginName;
231
        $templateTitle = Craft::t('seomatic', 'Global SEO');
232
        $subSectionTitle = Craft::t('seomatic', ucfirst($subSection));
233
        // Asset bundle
234
        try {
235
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
236
        } catch (InvalidConfigException $e) {
237
            Craft::error($e->getMessage(), __METHOD__);
238
        }
239
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
240
            '@nystudio107/seomatic/assetbundles/seomatic/dist',
241
            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

241
        /** @scrutinizer ignore-call */ 
242
        $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...
242
        );
243
        // Basic variables
244
        $variables['fullPageForm'] = true;
245
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
246
        $variables['pluginName'] = Seomatic::$settings->pluginName;
247
        $variables['title'] = $templateTitle;
248
        $variables['subSectionTitle'] = $subSectionTitle;
249
        $variables['docTitle'] = "{$pluginName} - {$templateTitle} - {$subSectionTitle}";
250
        $siteHandleUri = Craft::$app->isMultiSite ? '/'.$siteHandle : '';
251
        $variables['crumbs'] = [
252
            [
253
                'label' => $pluginName,
254
                'url' => UrlHelper::cpUrl('seomatic'),
255
            ],
256
            [
257
                'label' => $templateTitle,
258
                'url' => UrlHelper::cpUrl('seomatic/global/general'.$siteHandleUri),
259
            ],
260
            [
261
                'label' => $subSectionTitle,
262
                'url' => UrlHelper::cpUrl('seomatic/global/'.$subSection.$siteHandleUri),
263
            ],
264
        ];
265
        $variables['selectedSubnavItem'] = 'global';
266
        // Pass in the pull fields
267
        $this->setGlobalFieldSourceVariables($variables);
268
        // Enabled sites
269
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
270
        $variables['controllerHandle'] = 'global'.'/'.$subSection;
271
        $variables['currentSubSection'] = $subSection;
272
        // Meta bundle settings
273
        Seomatic::$previewingMetaContainers = true;
274
        // Get the site to copy the settings from, if any
275
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
276
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
277
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always true.
Loading history...
278
        // Load the metabundle
279
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteIdToLoad);
280
        if ($editedMetaBundle) {
281
            $metaBundle = $editedMetaBundle;
282
        }
283
        Seomatic::$previewingMetaContainers = false;
284
        if ($metaBundle !== null) {
285
            $variables['metaGlobalVars'] = clone $metaBundle->metaGlobalVars;
286
            $variables['metaSitemapVars'] = $metaBundle->metaSitemapVars;
287
            $variables['metaBundleSettings'] = $metaBundle->metaBundleSettings;
288
            // Template container settings
289
            $templateContainers = $metaBundle->frontendTemplatesContainer->data;
290
            $variables['robotsTemplate'] = $templateContainers[FrontendTemplates::ROBOTS_TXT_HANDLE];
291
            $variables['humansTemplate'] = $templateContainers[FrontendTemplates::HUMANS_TXT_HANDLE];
292
            // Handle an edge-case where a migration didn't work properly to add ADS_TXT_HANDLE
293
            if (!isset($templateContainers[FrontendTemplates::ADS_TXT_HANDLE])) {
294
                $globalMetaBundle = Seomatic::$plugin->metaBundles->createGlobalMetaBundleForSite($siteId, $metaBundle);
295
                $templateContainers[FrontendTemplates::ADS_TXT_HANDLE] =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
296
                    $globalMetaBundle->frontendTemplatesContainer->data[FrontendTemplates::ADS_TXT_HANDLE];
297
            }
298
            $variables['adsTemplate'] = $templateContainers[FrontendTemplates::ADS_TXT_HANDLE];
299
            // Image selectors
300
            $bundleSettings = $metaBundle->metaBundleSettings;
301
            $variables['elementType'] = Asset::class;
302
            $variables['seoImageElements'] = ImageTransformHelper::assetElementsFromIds(
303
                $bundleSettings->seoImageIds,
304
                $siteId
305
            );
306
            $variables['twitterImageElements'] = ImageTransformHelper::assetElementsFromIds(
307
                $bundleSettings->twitterImageIds,
308
                $siteId
309
            );
310
            $variables['ogImageElements'] = ImageTransformHelper::assetElementsFromIds(
311
                $bundleSettings->ogImageIds,
312
                $siteId
313
            );
314
        }
315
        // Preview the meta containers
316
        Seomatic::$plugin->metaContainers->previewMetaContainers(
317
            MetaBundles::GLOBAL_META_BUNDLE,
318
            (int)$variables['currentSiteId']
319
        );
320
        // Render the template
321
        return $this->renderTemplate('seomatic/settings/global/'.$subSection, $variables);
322
    }
323
324
    /**
325
     * @return Response
326
     * @throws \yii\web\BadRequestHttpException
327
     * @throws \craft\errors\MissingComponentException
328
     */
329
    public function actionSaveGlobal()
330
    {
331
        $this->requirePostRequest();
332
        $request = Craft::$app->getRequest();
333
        $siteId = $request->getParam('siteId');
334
        $globalsSettings = $request->getParam('metaGlobalVars');
335
        $bundleSettings = $request->getParam('metaBundleSettings');
336
        $robotsTemplate = $request->getParam('robotsTemplate');
337
        $humansTemplate = $request->getParam('humansTemplate');
338
        $adsTemplate = $request->getParam('adsTemplate');
339
340
        // Set the element type in the template
341
        $elementName = '';
342
343
        $hasErrors = false;
344
        // The site settings for the appropriate meta bundle
345
        Seomatic::$previewingMetaContainers = true;
346
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteId);
347
        Seomatic::$previewingMetaContainers = false;
348
        if ($metaBundle !== null) {
349
            if (\is_array($globalsSettings) && \is_array($bundleSettings)) {
350
                PullFieldHelper::parseTextSources($elementName, $globalsSettings, $bundleSettings);
351
                PullFieldHelper::parseImageSources($elementName, $globalsSettings, $bundleSettings, $siteId);
352
                if (!empty($bundleSettings['siteType'])) {
353
                    $globalsSettings['mainEntityOfPage'] = SchemaHelper::getSpecificEntityType($bundleSettings);
354
                }
355
                $metaBundle->metaGlobalVars->setAttributes($globalsSettings);
356
                $metaBundle->metaBundleSettings->setAttributes($bundleSettings);
357
            }
358
            $templateContainers = $metaBundle->frontendTemplatesContainer->data;
359
            $robotsContainer = $templateContainers[FrontendTemplates::ROBOTS_TXT_HANDLE];
360
            if ($robotsContainer !== null && \is_array($robotsTemplate)) {
361
                $robotsContainer->setAttributes($robotsTemplate);
362
                if (!$robotsContainer->validate()) {
363
                    $hasErrors = true;
364
                }
365
            }
366
            $humansContainer = $templateContainers[FrontendTemplates::HUMANS_TXT_HANDLE];
367
            if ($humansContainer !== null && \is_array($humansTemplate)) {
368
                $humansContainer->setAttributes($humansTemplate);
369
                if (!$humansContainer->validate()) {
370
                    $hasErrors = true;
371
                }
372
            }
373
            $adsContainer = $templateContainers[FrontendTemplates::ADS_TXT_HANDLE];
374
            if ($adsContainer !== null && \is_array($adsTemplate)) {
375
                $adsContainer->setAttributes($adsTemplate);
376
                if (!$adsContainer->validate()) {
377
                    $hasErrors = true;
378
                }
379
            }
380
381
            if ($hasErrors) {
382
                Craft::$app->getSession()->setError(Craft::t('app', "Couldn't save settings due to a Twig error."));
383
                // Send the redirect back to the template
384
                Craft::$app->getUrlManager()->setRouteParams([
385
                    'editedMetaBundle' => $metaBundle,
386
                ]);
387
388
                return null;
389
            }
390
391
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
392
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
393
394
            Seomatic::$plugin->clearAllCaches();
395
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic global settings saved.'));
396
        }
397
398
        return $this->redirectToPostedUrl();
399
    }
400
401
    /**
402
     * Content settings
403
     *
404
     * @param string|null $siteHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
405
     *
406
     * @return Response The rendered result
407
     * @throws NotFoundHttpException
408
     * @throws \yii\web\ForbiddenHttpException
409
     */
410
    public function actionContent(string $siteHandle = null): Response
411
    {
412
        $variables = [];
413
        // Get the site to edit
414
        $siteId = $this->getSiteIdFromHandle($siteHandle);
415
416
        $pluginName = Seomatic::$settings->pluginName;
417
        $templateTitle = Craft::t('seomatic', 'Content SEO');
418
        // Asset bundle
419
        try {
420
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
421
        } catch (InvalidConfigException $e) {
422
            Craft::error($e->getMessage(), __METHOD__);
423
        }
424
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
425
            '@nystudio107/seomatic/assetbundles/seomatic/dist',
426
            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

426
        /** @scrutinizer ignore-call */ 
427
        $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...
427
        );
428
        // Basic variables
429
        $variables['fullPageForm'] = false;
430
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
431
        $variables['pluginName'] = Seomatic::$settings->pluginName;
432
        $variables['title'] = $templateTitle;
433
        $variables['docTitle'] = "{$pluginName} - {$templateTitle}";
434
        $siteHandleUri = Craft::$app->isMultiSite ? '/'.$siteHandle : '';
435
        $variables['crumbs'] = [
436
            [
437
                'label' => $pluginName,
438
                'url' => UrlHelper::cpUrl('seomatic'),
439
            ],
440
            [
441
                'label' => $templateTitle,
442
                'url' => UrlHelper::cpUrl('seomatic/content'.$siteHandleUri),
443
            ],
444
        ];
445
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
446
        $variables['controllerHandle'] = 'content';
447
        $variables['selectedSubnavItem'] = 'content';
448
        $metaBundles = Seomatic::$plugin->metaBundles->getContentMetaBundlesForSiteId($siteId);
449
        Seomatic::$plugin->metaBundles->deleteVestigialMetaBundles($metaBundles);
450
451
        // Render the template
452
        return $this->renderTemplate('seomatic/settings/content/index', $variables);
453
    }
454
455
    /**
456
     * Global settings
457
     *
458
     * @param string $subSection
0 ignored issues
show
Coding Style introduced by
Expected 6 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
459
     * @param string $sourceBundleType
0 ignored issues
show
Coding Style introduced by
Expected 6 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
460
     * @param string $sourceHandle
0 ignored issues
show
Coding Style introduced by
Expected 6 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
461
     * @param string|null $siteHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
462
     * @param int|null $typeId
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
463
     * @param null $loadFromSiteHandle
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $loadFromSiteHandle is correct as it would always require null to be passed?
Loading history...
Coding Style introduced by
Expected 8 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
464
     *
465
     * @return Response The rendered result
466
     * @throws NotFoundHttpException
467
     * @throws \yii\web\ForbiddenHttpException
468
     */
469
    public function actionEditContent(
470
        string $subSection,
471
        string $sourceBundleType,
472
        string $sourceHandle,
473
        string $siteHandle = null,
474
        $typeId = null,
475
        $loadFromSiteHandle = null
476
    ): Response {
477
        $variables = [];
478
        // @TODO: Let people choose an entry/categorygroup/product as the preview
479
        // Get the site to edit
480
        $siteId = $this->getSiteIdFromHandle($siteHandle);
481
        if ($typeId !== null && is_string($typeId)) {
0 ignored issues
show
introduced by
The condition is_string($typeId) is always false.
Loading history...
482
            $typeId = (int)$typeId;
483
        }
484
        // Get the (entry) type menu
485
        $typeMenu = [];
486
        $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
487
        if ($seoElement !== null) {
488
            $typeMenu = $seoElement::typeMenuFromHandle($sourceHandle);
489
        }
490
        $variables['typeMenu'] = $typeMenu;
491
        $variables['currentTypeId'] = null;
492
        if (!empty($typeMenu)) {
493
            $currentType = reset($typeMenu);
494
            $variables['currentType'] = $typeMenu[$typeId] ?? $currentType;
495
            $variables['currentTypeId'] = $typeId ?? key($typeMenu);
496
            $typeId = (int)$variables['currentTypeId'];
497
        }
498
        $pluginName = Seomatic::$settings->pluginName;
499
        // Asset bundle
500
        try {
501
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
502
        } catch (InvalidConfigException $e) {
503
            Craft::error($e->getMessage(), __METHOD__);
504
        }
505
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
506
            '@nystudio107/seomatic/assetbundles/seomatic/dist',
507
            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

507
        /** @scrutinizer ignore-call */ 
508
        $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...
508
        );
509
        // Enabled sites
510
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
511
        $this->cullDisabledSites($sourceBundleType, $sourceHandle, $variables);
512
        // Meta Bundle settings
513
        Seomatic::$previewingMetaContainers = true;
514
        // Get the site to copy the settings from, if any
515
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
516
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
517
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always true.
Loading history...
518
        // Load the metabundle
519
        $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
520
            $sourceBundleType,
521
            $sourceHandle,
522
            $siteIdToLoad,
523
            $typeId
524
        );
525
        Seomatic::$previewingMetaContainers = false;
526
        $templateTitle = '';
527
        if ($metaBundle !== null) {
528
            $variables['metaGlobalVars'] = clone $metaBundle->metaGlobalVars;
529
            $variables['metaSitemapVars'] = $metaBundle->metaSitemapVars;
530
            $variables['metaBundleSettings'] = $metaBundle->metaBundleSettings;
531
            $variables['currentSourceHandle'] = $metaBundle->sourceHandle;
532
            $variables['currentSourceBundleType'] = $metaBundle->sourceBundleType;
533
            $templateTitle = $metaBundle->sourceName;
534
        }
535
        // Basic variables
536
        $subSectionTitle = Craft::t('seomatic', ucfirst($subSection));
537
        $variables['fullPageForm'] = true;
538
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
539
        $variables['pluginName'] = Seomatic::$settings->pluginName;
540
        $variables['title'] = $templateTitle;
541
        $variables['subSectionTitle'] = $subSectionTitle;
542
        $variables['docTitle'] = "{$pluginName} - Content SEO - {$templateTitle} - {$subSectionTitle}";
543
        $siteHandleUri = Craft::$app->isMultiSite ? '/'.$siteHandle : '';
544
        $variables['siteHandleUri'] = $siteHandleUri;
545
        $variables['crumbs'] = [
546
            [
547
                'label' => $pluginName,
548
                'url' => UrlHelper::cpUrl('seomatic'),
549
            ],
550
            [
551
                'label' => 'Content SEO',
552
                'url' => UrlHelper::cpUrl('seomatic/content'.$siteHandleUri),
553
            ],
554
            [
555
                'label' => $metaBundle->sourceName.' · '.$subSectionTitle,
556
                'url' => UrlHelper::cpUrl("seomatic/edit-content/${subSection}/${sourceBundleType}/${sourceHandle}"),
557
            ],
558
        ];
559
        $variables['selectedSubnavItem'] = 'content';
560
        $variables['controllerHandle'] = "edit-content/${subSection}/${sourceBundleType}/${sourceHandle}";
561
        // Image selectors
562
        $variables['currentSubSection'] = $subSection;
563
        $bundleSettings = $metaBundle->metaBundleSettings;
564
        $variables['elementType'] = Asset::class;
565
        $variables['seoImageElements'] = ImageTransformHelper::assetElementsFromIds(
566
            $bundleSettings->seoImageIds,
567
            $siteId
568
        );
569
        $variables['twitterImageElements'] = ImageTransformHelper::assetElementsFromIds(
570
            $bundleSettings->twitterImageIds,
571
            $siteId
572
        );
573
        $variables['ogImageElements'] = ImageTransformHelper::assetElementsFromIds(
574
            $bundleSettings->ogImageIds,
575
            $siteId
576
        );
577
        $variables['sourceType'] = $metaBundle->sourceType;
578
        // Pass in the pull fields
579
        $groupName = ucfirst($metaBundle->sourceType);
580
        $this->setContentFieldSourceVariables($sourceBundleType, $sourceHandle, $groupName, $variables);
581
        $uri = $this->uriFromSourceBundle($sourceBundleType, $sourceHandle, $siteId);
582
        // Preview the meta containers
583
        Seomatic::$plugin->metaContainers->previewMetaContainers(
584
            $uri,
585
            (int)$variables['currentSiteId'],
586
            false,
587
            false
588
        );
589
590
        // Render the template
591
        return $this->renderTemplate('seomatic/settings/content/'.$subSection, $variables);
592
    }
593
594
595
    /**
596
     * @return Response
597
     * @throws \yii\web\BadRequestHttpException
598
     * @throws \craft\errors\MissingComponentException
599
     */
600
    public function actionSaveContent(): Response
601
    {
602
        $this->requirePostRequest();
603
        $request = Craft::$app->getRequest();
604
        $sourceBundleType = $request->getParam('sourceBundleType');
605
        $sourceHandle = $request->getParam('sourceHandle');
606
        $siteId = $request->getParam('siteId');
607
        $typeId = $request->getParam('typeId') ?? null;
608
        $globalsSettings = $request->getParam('metaGlobalVars');
609
        $bundleSettings = $request->getParam('metaBundleSettings');
610
        $sitemapSettings = $request->getParam('metaSitemapVars');
611
        // Set the element type in the template
612
        $elementName = '';
613
        $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
614
        if ($seoElement !== null) {
615
            $elementName = $seoElement::getElementRefHandle();
616
        }
617
        // The site settings for the appropriate meta bundle
618
        Seomatic::$previewingMetaContainers = true;
619
        $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
620
            $sourceBundleType,
621
            $sourceHandle,
622
            $siteId,
623
            $typeId
624
        );
625
        Seomatic::$previewingMetaContainers = false;
626
        if ($metaBundle) {
627
            if (\is_array($globalsSettings) && \is_array($bundleSettings)) {
628
                PullFieldHelper::parseTextSources($elementName, $globalsSettings, $bundleSettings);
629
                PullFieldHelper::parseImageSources($elementName, $globalsSettings, $bundleSettings, $siteId);
630
                if (!empty($bundleSettings['siteType'])) {
631
                    $globalsSettings['mainEntityOfPage'] = SchemaHelper::getSpecificEntityType($bundleSettings);
632
                }
633
                $metaBundle->metaGlobalVars->setAttributes($globalsSettings);
634
                $metaBundle->metaBundleSettings->setAttributes($bundleSettings);
635
            }
636
            if (\is_array($sitemapSettings)) {
637
                $metaBundle->metaSitemapVars->setAttributes($sitemapSettings);
638
            }
639
640
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
641
            $metaBundle->typeId = $typeId;
642
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
643
644
            Seomatic::$plugin->clearAllCaches();
645
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic content settings saved.'));
646
        }
647
648
        return $this->redirectToPostedUrl();
649
    }
650
651
    /**
652
     * Site settings
653
     *
654
     * @param string $subSection
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
655
     * @param string $siteHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
656
     * @param null $loadFromSiteHandle
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $loadFromSiteHandle is correct as it would always require null to be passed?
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
657
     *
658
     * @return Response The rendered result
659
     * @throws NotFoundHttpException
660
     * @throws \yii\web\ForbiddenHttpException
661
     */
662
    public function actionSite(string $subSection = 'identity', string $siteHandle = null, $loadFromSiteHandle = null): Response
663
    {
664
        $variables = [];
665
        // Get the site to edit
666
        $siteId = $this->getSiteIdFromHandle($siteHandle);
667
668
        $pluginName = Seomatic::$settings->pluginName;
669
        $templateTitle = Craft::t('seomatic', 'Site Settings');
670
        $subSectionSuffix = '';
671
        if ($subSection === 'social') {
672
            $subSectionSuffix = ' Media';
673
        }
674
        $subSectionTitle = Craft::t('seomatic', ucfirst($subSection).$subSectionSuffix);
675
        // Asset bundle
676
        try {
677
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
678
        } catch (InvalidConfigException $e) {
679
            Craft::error($e->getMessage(), __METHOD__);
680
        }
681
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
682
            '@nystudio107/seomatic/assetbundles/seomatic/dist',
683
            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

683
        /** @scrutinizer ignore-call */ 
684
        $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...
684
        );
685
        // Basic variables
686
        $variables['fullPageForm'] = true;
687
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
688
        $variables['pluginName'] = Seomatic::$settings->pluginName;
689
        $variables['title'] = $templateTitle;
690
        $variables['subSectionTitle'] = $subSectionTitle;
691
        $variables['docTitle'] = "{$pluginName} - {$templateTitle} - {$subSectionTitle}";
692
        $siteHandleUri = Craft::$app->isMultiSite ? '/'.$siteHandle : '';
693
        $variables['crumbs'] = [
694
            [
695
                'label' => $pluginName,
696
                'url' => UrlHelper::cpUrl('seomatic'),
697
            ],
698
            [
699
                'label' => $templateTitle,
700
                'url' => UrlHelper::cpUrl('seomatic/site/identity'.$siteHandleUri),
701
            ],
702
            [
703
                'label' => $subSectionTitle,
704
                'url' => UrlHelper::cpUrl('seomatic/site/'.$subSection.$siteHandleUri),
705
            ],
706
        ];
707
        $variables['selectedSubnavItem'] = 'site';
708
        $variables['currentSubSection'] = $subSection;
709
710
        // Enabled sites
711
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
712
        $variables['controllerHandle'] = 'site'.'/'.$subSection;
713
714
        // The site settings for the appropriate meta bundle
715
        Seomatic::$previewingMetaContainers = true;
716
        // Get the site to copy the settings from, if any
717
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
718
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
719
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always true.
Loading history...
720
        // Load the metabundle
721
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteIdToLoad);
722
        Seomatic::$previewingMetaContainers = false;
723
        if ($metaBundle !== null) {
724
            $variables['site'] = $metaBundle->metaSiteVars;
725
            $variables['identityImageElements'] = ImageTransformHelper::assetElementsFromIds(
726
                $variables['site']->identity->genericImageIds,
727
                $siteId
728
            );
729
            $variables['creatorImageElements'] = ImageTransformHelper::assetElementsFromIds(
730
                $variables['site']->creator->genericImageIds,
731
                $siteId
732
            );
733
        }
734
        $variables['elementType'] = Asset::class;
735
736
        // Render the template
737
        return $this->renderTemplate('seomatic/settings/site/'.$subSection, $variables);
738
    }
739
740
    /**
741
     * @return Response
742
     * @throws \yii\web\BadRequestHttpException
743
     * @throws \craft\errors\MissingComponentException
744
     */
745
    public function actionSaveSite(): Response
746
    {
747
        $this->requirePostRequest();
748
        $request = Craft::$app->getRequest();
749
        $siteId = $request->getParam('siteId');
750
        $siteSettings = $request->getParam('site');
751
752
        // Make sure the twitter handle isn't prefixed with an @
753
        if (!empty($siteSettings['twitterHandle'])) {
754
            $siteSettings['twitterHandle'] = ltrim($siteSettings['twitterHandle'], '@');
755
        }
756
        // Make sure the sameAsLinks are indexed by the handle
757
        if (!empty($siteSettings['sameAsLinks'])) {
758
            $siteSettings['sameAsLinks'] = ArrayHelper::index($siteSettings['sameAsLinks'], 'handle');
759
        }
760
        // The site settings for the appropriate meta bundle
761
        Seomatic::$previewingMetaContainers = true;
762
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteId);
763
        Seomatic::$previewingMetaContainers = false;
764
        if ($metaBundle) {
765
            if (\is_array($siteSettings)) {
766
                if (!empty($siteSettings['identity'])) {
767
                    $settings = $siteSettings['identity'];
768
                    $this->prepEntitySettings($settings);
769
                    $metaBundle->metaSiteVars->identity->setAttributes($settings);
770
                    $siteSettings['identity'] = $metaBundle->metaSiteVars->identity;
771
                }
772
                if (!empty($siteSettings['creator'])) {
773
                    $settings = $siteSettings['creator'];
774
                    $this->prepEntitySettings($settings);
775
                    $metaBundle->metaSiteVars->creator->setAttributes($settings);
776
                    $siteSettings['creator'] = $metaBundle->metaSiteVars->creator;
777
                }
778
                if (!empty($siteSettings['additionalSitemapUrls'])) {
779
                    $siteSettings['additionalSitemapUrlsDateUpdated'] = new \DateTime;
780
                    Seomatic::$plugin->sitemaps->submitCustomSitemap($siteId);
781
                }
782
                $metaBundle->metaSiteVars->setAttributes($siteSettings);
783
            }
784
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
785
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
786
787
            Seomatic::$plugin->clearAllCaches();
788
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic site settings saved.'));
789
        }
790
791
        return $this->redirectToPostedUrl();
792
    }
793
794
    /**
795
     * Plugin settings
796
     *
797
     * @return Response The rendered result
798
     */
799
    public function actionPlugin(): Response
800
    {
801
        $variables = [];
802
        $pluginName = Seomatic::$settings->pluginName;
803
        $templateTitle = Craft::t('seomatic', 'Plugin Settings');
804
        // Asset bundle
805
        try {
806
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
807
        } catch (InvalidConfigException $e) {
808
            Craft::error($e->getMessage(), __METHOD__);
809
        }
810
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
811
            '@nystudio107/seomatic/assetbundles/seomatic/dist',
812
            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

812
        /** @scrutinizer ignore-call */ 
813
        $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...
813
        );
814
        // Basic variables
815
        $variables['fullPageForm'] = true;
816
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
817
        $variables['pluginName'] = Seomatic::$settings->pluginName;
818
        $variables['title'] = $templateTitle;
819
        $variables['docTitle'] = "{$pluginName} - {$templateTitle}";
820
        $variables['crumbs'] = [
821
            [
822
                'label' => $pluginName,
823
                'url' => UrlHelper::cpUrl('seomatic'),
824
            ],
825
            [
826
                'label' => $templateTitle,
827
                'url' => UrlHelper::cpUrl('seomatic/plugin'),
828
            ],
829
        ];
830
        $variables['selectedSubnavItem'] = 'plugin';
831
        $variables['settings'] = Seomatic::$settings;
832
833
        // Render the template
834
        return $this->renderTemplate('seomatic/settings/plugin/_edit', $variables);
835
    }
836
837
838
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $editedMetaBundle should have a doc-comment as per coding-style.
Loading history...
839
     * Tracking settings
840
     *
841
     * @param string $subSection
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
842
     * @param string $siteHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
843
     * @param null $loadFromSiteHandle
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $loadFromSiteHandle is correct as it would always require null to be passed?
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
844
     *
845
     * @return Response The rendered result
846
     * @throws NotFoundHttpException
847
     * @throws \yii\web\ForbiddenHttpException
848
     */
849
    public function actionTracking(string $subSection = 'gtag', string $siteHandle = null, $loadFromSiteHandle = null, $editedMetaBundle = null): Response
850
    {
851
        $variables = [];
852
        // Get the site to edit
853
        $siteId = $this->getSiteIdFromHandle($siteHandle);
854
        // Enabled sites
855
        $this->setMultiSiteVariables($siteHandle, $siteId, $variables);
856
        $variables['controllerHandle'] = 'tracking'.'/'.$subSection;
857
        $variables['currentSubSection'] = $subSection;
858
859
        // The script meta containers for the global meta bundle
860
        Seomatic::$previewingMetaContainers = true;
861
        // Get the site to copy the settings from, if any
862
        $variables['loadFromSiteHandle'] = $loadFromSiteHandle;
863
        $loadFromSiteId = $this->getSiteIdFromHandle($loadFromSiteHandle);
864
        $siteIdToLoad = $loadFromSiteHandle === null ? (int)$variables['currentSiteId'] : $loadFromSiteId;
0 ignored issues
show
introduced by
The condition $loadFromSiteHandle === null is always true.
Loading history...
865
        // Load the metabundle
866
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteIdToLoad);
867
        if ($editedMetaBundle) {
868
            $metaBundle = $editedMetaBundle;
869
        }
870
        Seomatic::$previewingMetaContainers = false;
871
        if ($metaBundle !== null) {
872
            $variables['scripts'] = Seomatic::$plugin->metaBundles->getContainerDataFromBundle(
873
                $metaBundle,
874
                MetaScriptContainer::CONTAINER_TYPE
875
            );
876
        }
877
        // Plugin and section settings
878
        $pluginName = Seomatic::$settings->pluginName;
879
        $templateTitle = Craft::t('seomatic', 'Tracking Scripts');
880
        $subSectionTitle = $variables['scripts'][$subSection]->name;
881
        $subSectionTitle = Craft::t('seomatic', $subSectionTitle);
882
        // Asset bundle
883
        try {
884
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
885
        } catch (InvalidConfigException $e) {
886
            Craft::error($e->getMessage(), __METHOD__);
887
        }
888
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
889
            '@nystudio107/seomatic/assetbundles/seomatic/dist',
890
            true
0 ignored issues
show
Unused Code introduced by
The call to yii\web\AssetManager::getPublishedUrl() has too many arguments starting with true. ( Ignorable by Annotation )

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

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

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

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

Loading history...
891
        );
892
        // Basic variables
893
        $variables['fullPageForm'] = true;
894
        $variables['docsUrl'] = self::DOCUMENTATION_URL;
895
        $variables['pluginName'] = Seomatic::$settings->pluginName;
896
        $variables['title'] = $templateTitle;
897
        $variables['subSectionTitle'] = $subSectionTitle;
898
        $variables['docTitle'] = "{$pluginName} - {$templateTitle} - {$subSectionTitle}";
899
        $siteHandleUri = Craft::$app->isMultiSite ? '/'.$siteHandle : '';
900
        $variables['crumbs'] = [
901
            [
902
                'label' => $pluginName,
903
                'url' => UrlHelper::cpUrl('seomatic'),
904
            ],
905
            [
906
                'label' => $templateTitle,
907
                'url' => UrlHelper::cpUrl('seomatic/tracking'),
908
            ],
909
            [
910
                'label' => $subSectionTitle,
911
                'url' => UrlHelper::cpUrl('seomatic/tracking/'.$subSection.$siteHandleUri),
912
            ],
913
        ];
914
        $variables['selectedSubnavItem'] = 'tracking';
915
916
        // Render the template
917
        return $this->renderTemplate('seomatic/settings/tracking/_edit', $variables);
918
    }
919
920
    /**
921
     * @return Response
922
     * @throws \yii\web\BadRequestHttpException
923
     * @throws \craft\errors\MissingComponentException
924
     */
925
    public function actionSaveTracking()
926
    {
927
        $this->requirePostRequest();
928
        $request = Craft::$app->getRequest();
929
        $siteId = $request->getParam('siteId');
930
        $scriptSettings = $request->getParam('scripts');
931
932
        // The site settings for the appropriate meta bundle
933
        Seomatic::$previewingMetaContainers = true;
934
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteId);
935
        Seomatic::$previewingMetaContainers = false;
936
        $hasErrors = false;
937
        if ($metaBundle) {
938
            /** @var array $scriptSettings */
0 ignored issues
show
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
939
            foreach ($scriptSettings as $scriptHandle => $scriptData) {
940
                foreach ($metaBundle->metaContainers as $metaContainer) {
941
                    if ($metaContainer::CONTAINER_TYPE === MetaScriptContainer::CONTAINER_TYPE) {
942
                        $data = $metaContainer->getData($scriptHandle);
943
                        /** @var MetaScript $data */
0 ignored issues
show
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
944
                        if ($data) {
945
                            /** @var array $scriptData */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
946
                            foreach ($scriptData as $key => $value) {
947
                                if (\is_array($value)) {
948
                                    foreach ($value as $varsKey => $varsValue) {
949
                                        $data->$key[$varsKey]['value'] = $varsValue;
950
                                    }
951
                                } else {
952
                                    $data->$key = $value;
953
                                }
954
                            }
955
                            if (!$data->validate()) {
956
                                $hasErrors = true;
957
                            }
958
                        }
959
                    }
960
                }
961
            }
962
            if ($hasErrors) {
963
                Craft::$app->getSession()->setError(Craft::t('app', "Couldn't save tracking settings due to a Twig error."));
964
                // Send the redirect back to the template
965
                Craft::$app->getUrlManager()->setRouteParams([
966
                    'editedMetaBundle' => $metaBundle,
967
                ]);
968
969
                return null;
970
            }
971
972
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
973
974
            Seomatic::$plugin->clearAllCaches();
975
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic site settings saved.'));
976
        }
977
978
        return $this->redirectToPostedUrl();
979
    }
980
981
    /**
982
     * Saves a plugin’s settings.
983
     *
984
     * @return Response|null
985
     * @throws NotFoundHttpException if the requested plugin cannot be found
986
     * @throws \yii\web\BadRequestHttpException
987
     * @throws \craft\errors\MissingComponentException
988
     */
989
    public function actionSavePluginSettings()
990
    {
991
        $this->requirePostRequest();
992
        $pluginHandle = Craft::$app->getRequest()->getRequiredBodyParam('pluginHandle');
993
        $settings = Craft::$app->getRequest()->getBodyParam('settings', []);
994
        $plugin = Craft::$app->getPlugins()->getPlugin($pluginHandle);
995
996
        if ($plugin === null) {
997
            throw new NotFoundHttpException('Plugin not found');
998
        }
999
1000
        if (!Craft::$app->getPlugins()->savePluginSettings($plugin, $settings)) {
1001
            Craft::$app->getSession()->setError(Craft::t('app', "Couldn't save plugin settings."));
1002
1003
            // Send the plugin back to the template
1004
            Craft::$app->getUrlManager()->setRouteParams([
1005
                'plugin' => $plugin,
1006
            ]);
1007
1008
            return null;
1009
        }
1010
1011
        Seomatic::$plugin->clearAllCaches();
1012
        Craft::$app->getSession()->setNotice(Craft::t('app', 'Plugin settings saved.'));
1013
1014
        return $this->redirectToPostedUrl();
1015
    }
1016
1017
    // Protected Methods
1018
    // =========================================================================
1019
1020
    /**
1021
     * @param array $variables
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1022
     */
1023
    protected function setGlobalFieldSourceVariables(array &$variables)
1024
    {
1025
        $variables['textFieldSources'] = array_merge(
1026
            ['globalsGroup' => ['optgroup' => 'Globals Fields']],
1027
            FieldHelper::fieldsOfTypeFromGlobals(
1028
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1029
                false
1030
            )
1031
        );
1032
        $variables['assetFieldSources'] = array_merge(
1033
            ['globalsGroup' => ['optgroup' => 'Globals Fields']],
1034
            FieldHelper::fieldsOfTypeFromGlobals(
1035
                FieldHelper::ASSET_FIELD_CLASS_KEY,
1036
                false
1037
            )
1038
        );
1039
    }
1040
1041
    /**
1042
     * @param string $sourceBundleType
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1043
     * @param string $sourceHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1044
     * @param string $groupName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1045
     * @param array  $variables
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1046
     */
1047
    protected function setContentFieldSourceVariables(
1048
        string $sourceBundleType,
1049
        string $sourceHandle,
1050
        string $groupName,
1051
        array &$variables
1052
    ) {
1053
        $variables['textFieldSources'] = array_merge(
1054
            ['entryGroup' => ['optgroup' => $groupName.' Fields'], 'title' => 'Title'],
1055
            FieldHelper::fieldsOfTypeFromSource(
1056
                $sourceBundleType,
1057
                $sourceHandle,
1058
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1059
                false
1060
            )
1061
        );
1062
        $variables['assetFieldSources'] = array_merge(
1063
            ['entryGroup' => ['optgroup' => $groupName.' Fields']],
1064
            FieldHelper::fieldsOfTypeFromSource(
1065
                $sourceBundleType,
1066
                $sourceHandle,
1067
                FieldHelper::ASSET_FIELD_CLASS_KEY,
1068
                false
1069
            )
1070
        );
1071
        $variables['assetVolumeTextFieldSources'] = array_merge(
1072
            ['entryGroup' => ['optgroup' => 'Asset Volume Fields'], '' => '--', 'title' => 'Title'],
1073
            array_merge(
1074
                FieldHelper::fieldsOfTypeFromAssetVolumes(
1075
                    FieldHelper::TEXT_FIELD_CLASS_KEY,
1076
                    false
1077
                )
1078
            )
1079
        );
1080
        $variables['userFieldSources'] = array_merge(
1081
            ['entryGroup' => ['optgroup' => 'User Fields']],
1082
            FieldHelper::fieldsOfTypeFromUsers(
1083
                FieldHelper::TEXT_FIELD_CLASS_KEY,
1084
                false
1085
            )
1086
        );
1087
    }
1088
1089
    /**
1090
     * @param string   $sourceBundleType
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1091
     * @param string   $sourceHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1092
     * @param null|int $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1093
     *
1094
     * @return string
1095
     */
1096
    protected function uriFromSourceBundle(string $sourceBundleType, string $sourceHandle, $siteId): string
1097
    {
1098
        $uri = null;
1099
        // Pick an Element to be used for the preview
1100
        if ($sourceBundleType === MetaBundles::GLOBAL_META_BUNDLE) {
1101
            $uri = MetaBundles::GLOBAL_META_BUNDLE;
1102
        } else {
1103
            $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
1104
            if ($seoElement !== null) {
1105
                $uri = $seoElement::previewUri($sourceHandle, $siteId);
1106
            }
1107
        }
1108
        // Special-case for the __home__ slug, and default to /
1109
        if (($uri === '__home__') || ($uri === null)) {
1110
            $uri = '/';
1111
        }
1112
1113
        return $uri;
1114
    }
1115
1116
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $element should have a doc-comment as per coding-style.
Loading history...
1117
     * @param string $siteHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1118
     * @param        $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1119
     * @param        $variables
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1120
     *
1121
     * @throws \yii\web\ForbiddenHttpException
1122
     */
1123
    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

1123
    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...
1124
    {
1125
        // Enabled sites
1126
        $sites = Craft::$app->getSites();
1127
        if (Craft::$app->getIsMultiSite()) {
1128
            // Set defaults based on the section settings
1129
            $variables['enabledSiteIds'] = [];
1130
            $variables['siteIds'] = [];
1131
1132
            /** @var Site $site */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
1133
            foreach ($sites->getEditableSiteIds() as $editableSiteId) {
1134
                $variables['enabledSiteIds'][] = $editableSiteId;
1135
                $variables['siteIds'][] = $editableSiteId;
1136
            }
1137
1138
            // Make sure the $siteId they are trying to edit is in our array of editable sites
1139
            if (!\in_array($siteId, $variables['enabledSiteIds'], false)) {
1140
                if (!empty($variables['enabledSiteIds'])) {
1141
                    $siteId = reset($variables['enabledSiteIds']);
1142
                } else {
1143
                    $this->requirePermission('editSite:'.$siteId);
1144
                }
1145
            }
1146
        }
1147
1148
        // Set the currentSiteId and currentSiteHandle
1149
        $variables['currentSiteId'] = empty($siteId) ? Craft::$app->getSites()->currentSite->id : $siteId;
1150
        $variables['currentSiteHandle'] = empty($siteHandle)
1151
            ? Craft::$app->getSites()->currentSite->handle
1152
            : $siteHandle;
1153
1154
        // Page title
1155
        $variables['showSites'] = (
1156
            Craft::$app->getIsMultiSite() &&
1157
            \count($variables['enabledSiteIds'])
1158
        );
1159
1160
        if ($variables['showSites']) {
1161
            $variables['sitesMenuLabel'] = Craft::t(
1162
                'site',
1163
                $sites->getSiteById((int)$variables['currentSiteId'])->name
1164
            );
1165
        } else {
1166
            $variables['sitesMenuLabel'] = '';
1167
        }
1168
    }
1169
1170
    /**
1171
     * Remove any sites for which meta bundles do not exist (they may be
1172
     * disabled for this section)
1173
     *
1174
     * @param string $sourceBundleType
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1175
     * @param string $sourceHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1176
     * @param array  $variables
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1177
     */
1178
    protected function cullDisabledSites(string $sourceBundleType, string $sourceHandle, array &$variables)
1179
    {
1180
        if (isset($variables['enabledSiteIds'])) {
1181
            foreach ($variables['enabledSiteIds'] as $key => $value) {
1182
                $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
1183
                    $sourceBundleType,
1184
                    $sourceHandle,
1185
                    $value
1186
                );
1187
                if ($metaBundle === null) {
1188
                    unset($variables['enabledSiteIds'][$key]);
1189
                }
1190
            }
1191
        }
1192
    }
1193
1194
    /**
1195
     * Return a siteId from a siteHandle
1196
     *
1197
     * @param string $siteHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1198
     *
1199
     * @return int|null
1200
     * @throws NotFoundHttpException
1201
     */
1202
    protected function getSiteIdFromHandle($siteHandle)
1203
    {
1204
        // Get the site to edit
1205
        if ($siteHandle !== null) {
0 ignored issues
show
introduced by
The condition $siteHandle !== null is always true.
Loading history...
1206
            $site = Craft::$app->getSites()->getSiteByHandle($siteHandle);
1207
            if (!$site) {
1208
                throw new NotFoundHttpException('Invalid site handle: '.$siteHandle);
1209
            }
1210
            $siteId = $site->id;
1211
        } else {
1212
            $siteId = Craft::$app->getSites()->currentSite->id;
1213
        }
1214
1215
        return $siteId;
1216
    }
1217
1218
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $settings should have a doc-comment as per coding-style.
Loading history...
1219
     * Prep the entity settings for saving to the db
1220
     *
1221
     * @param array &$settings
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter &$settings does not match actual variable name $settings
Loading history...
1222
     */
1223
    protected function prepEntitySettings(&$settings)
1224
    {
1225
        DynamicMetaHelper::normalizeTimes($settings['localBusinessOpeningHours']);
1226
        if (!empty($settings['siteType'])) {
1227
            $settings['computedType'] = SchemaHelper::getSpecificEntityType($settings);
1228
        } else {
1229
            $settings['computedType'] = 'WebPage';
1230
        }
1231
        // Empty out the entity image settings to ensure the image gets removed if it no longer exists
1232
        $settings['genericImage'] = '';
1233
        $settings['genericImageWidth'] = '';
1234
        $settings['genericImageHeight'] = '';
1235
        if (!empty($settings['genericImageIds'])) {
1236
            $asset = Craft::$app->getAssets()->getAssetById($settings['genericImageIds'][0]);
1237
            if ($asset !== null) {
1238
                $settings['genericImage'] = $asset->getUrl();
1239
                $settings['genericImageWidth'] = $asset->getWidth();
1240
                $settings['genericImageHeight'] = $asset->getHeight();
1241
            }
1242
        }
1243
    }
1244
}
1245