Passed
Push — v3 ( e59c9d...9ad5d8 )
by Andrew
48:27 queued 25:26
created

SettingsController::actionSaveGlobal()   F

Complexity

Conditions 18
Paths 487

Size

Total Lines 78
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 342

Importance

Changes 0
Metric Value
cc 18
eloc 53
nc 487
nop 0
dl 0
loc 78
ccs 0
cts 68
cp 0
crap 342
rs 1.4125
c 0
b 0
f 0

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
            // Handle an edge-case where a migration didn't work properly to add ADS_TXT_HANDLE
300
            if (!isset($templateContainers[FrontendTemplates::ADS_TXT_HANDLE])) {
301
                $globalMetaBundle = Seomatic::$plugin->metaBundles->createGlobalMetaBundleForSite($siteId, $metaBundle);
302
                $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...
303
                    $globalMetaBundle->frontendTemplatesContainer->data[FrontendTemplates::ADS_TXT_HANDLE];
304
            }
305
            $variables['securityTemplate'] = $templateContainers[FrontendTemplates::SECURITY_TXT_HANDLE];
306
            // Image selectors
307
            $bundleSettings = $metaBundle->metaBundleSettings;
308
            $variables['elementType'] = Asset::class;
309
            $variables['seoImageElements'] = ImageTransformHelper::assetElementsFromIds(
310
                $bundleSettings->seoImageIds,
311
                $siteId
312
            );
313
            $variables['twitterImageElements'] = ImageTransformHelper::assetElementsFromIds(
314
                $bundleSettings->twitterImageIds,
315
                $siteId
316
            );
317
            $variables['ogImageElements'] = ImageTransformHelper::assetElementsFromIds(
318
                $bundleSettings->ogImageIds,
319
                $siteId
320
            );
321
        }
322
        // Preview the meta containers
323
        Seomatic::$plugin->metaContainers->previewMetaContainers(
324
            MetaBundles::GLOBAL_META_BUNDLE,
325
            (int)$variables['currentSiteId']
326
        );
327
328
        // Render the template
329
        return $this->renderTemplate('seomatic/settings/global/'.$subSection, $variables);
330
    }
331
332
    /**
333
     * @return Response
334
     * @throws \yii\web\BadRequestHttpException
335
     * @throws \craft\errors\MissingComponentException
336
     */
337
    public function actionSaveGlobal()
338
    {
339
        $this->requirePostRequest();
340
        $request = Craft::$app->getRequest();
341
        $siteId = $request->getParam('siteId');
342
        $globalsSettings = $request->getParam('metaGlobalVars');
343
        $bundleSettings = $request->getParam('metaBundleSettings');
344
        $robotsTemplate = $request->getParam('robotsTemplate');
345
        $humansTemplate = $request->getParam('humansTemplate');
346
        $adsTemplate = $request->getParam('adsTemplate');
347
        $securityTemplate = $request->getParam('securityTemplate');
348
349
        // Set the element type in the template
350
        $elementName = '';
351
352
        $hasErrors = false;
353
        // The site settings for the appropriate meta bundle
354
        Seomatic::$previewingMetaContainers = true;
355
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($siteId);
356
        Seomatic::$previewingMetaContainers = false;
357
        if ($metaBundle !== null) {
358
            if (\is_array($globalsSettings) && \is_array($bundleSettings)) {
359
                PullFieldHelper::parseTextSources($elementName, $globalsSettings, $bundleSettings);
360
                PullFieldHelper::parseImageSources($elementName, $globalsSettings, $bundleSettings, $siteId);
361
                if (!empty($bundleSettings['siteType'])) {
362
                    $globalsSettings['mainEntityOfPage'] = SchemaHelper::getSpecificEntityType($bundleSettings);
363
                }
364
                $metaBundle->metaGlobalVars->setAttributes($globalsSettings);
365
                $metaBundle->metaBundleSettings->setAttributes($bundleSettings);
366
            }
367
            $templateContainers = $metaBundle->frontendTemplatesContainer->data;
368
            $robotsContainer = $templateContainers[FrontendTemplates::ROBOTS_TXT_HANDLE];
369
            if ($robotsContainer !== null && \is_array($robotsTemplate)) {
370
                $robotsContainer->setAttributes($robotsTemplate);
371
                if (!$robotsContainer->validate()) {
372
                    $hasErrors = true;
373
                }
374
            }
375
            $humansContainer = $templateContainers[FrontendTemplates::HUMANS_TXT_HANDLE];
376
            if ($humansContainer !== null && \is_array($humansTemplate)) {
377
                $humansContainer->setAttributes($humansTemplate);
378
                if (!$humansContainer->validate()) {
379
                    $hasErrors = true;
380
                }
381
            }
382
            $adsContainer = $templateContainers[FrontendTemplates::ADS_TXT_HANDLE];
383
            if ($adsContainer !== null && \is_array($adsTemplate)) {
384
                $adsContainer->setAttributes($adsTemplate);
385
                if (!$adsContainer->validate()) {
386
                    $hasErrors = true;
387
                }
388
            }
389
            $securityContainer = $templateContainers[FrontendTemplates::SECURITY_TXT_HANDLE];
390
            if ($securityContainer !== null && \is_array($securityTemplate)) {
391
                $securityContainer->setAttributes($securityTemplate);
392
                if (!$securityContainer->validate()) {
393
                    $hasErrors = true;
394
                }
395
            }
396
397
            if ($hasErrors) {
398
                Craft::$app->getSession()->setError(Craft::t('app', "Couldn't save settings due to a Twig error."));
399
                // Send the redirect back to the template
400
                Craft::$app->getUrlManager()->setRouteParams([
401
                    'editedMetaBundle' => $metaBundle,
402
                ]);
403
404
                return null;
405
            }
406
407
            Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true);
408
            Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $siteId);
409
410
            Seomatic::$plugin->clearAllCaches();
411
            Craft::$app->getSession()->setNotice(Craft::t('seomatic', 'SEOmatic global settings saved.'));
412
        }
413
414
        return $this->redirectToPostedUrl();
415
    }
416
417
    /**
418
     * Content settings
419
     *
420
     * @param string|null $siteHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
421
     *
422
     * @return Response The rendered result
423
     * @throws NotFoundHttpException
424
     * @throws \yii\web\ForbiddenHttpException
425
     */
426
    public function actionContent(string $siteHandle = null): Response
427
    {
428
        $variables = [];
429
        // Get the site to edit
430
        $siteId = $this->getSiteIdFromHandle($siteHandle);
431
432
        $pluginName = Seomatic::$settings->pluginName;
433
        $templateTitle = Craft::t('seomatic', 'Content SEO');
434
        // Asset bundle
435
        try {
436
            Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
437
        } catch (InvalidConfigException $e) {
438
            Craft::error($e->getMessage(), __METHOD__);
439
        }
440
        $variables['baseAssetsUrl'] = Craft::$app->assetManager->getPublishedUrl(
441
            '@nystudio107/seomatic/assetbundles/seomatic/dist',
442
            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

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

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

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

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

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

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