Controller   F
last analyzed

Complexity

Total Complexity 62

Size/Duplication

Total Lines 448
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 62
eloc 190
dl 0
loc 448
ccs 0
cts 240
cp 0
rs 3.44
c 2
b 0
f 0

30 Methods

Rating   Name   Duplication   Size   Complexity  
A filterRenderView() 0 8 2
A filterSubsubsub() 0 3 1
A filterTranslationEntries() 0 4 1
A filterTranslatorDomains() 0 3 1
A install() 0 2 1
A filterSettings() 0 3 1
A renderSettings() 0 6 1
A filterRowMeta() 0 17 3
A filterCapabilities() 0 22 3
A filterNgettextWithContext() 0 8 1
A registerWidgets() 0 2 1
A filterActionLinks() 0 7 3
A registerAsset() 0 10 4
A filterConfigPath() 0 6 2
A registerShortcodes() 0 2 1
A filterGettext() 0 5 1
A registerTinymcePopups() 0 2 1
A filterGettextWithContext() 0 6 1
A onActivation() 0 14 3
A filterDocumentation() 0 8 1
A registerLanguages() 0 5 1
A buildAssetArgs() 0 25 5
A filterFilePaths() 0 6 2
A enqueuePublicAssets() 0 7 5
B filterRoles() 0 54 5
A filterLocalizedPublicVariables() 0 4 1
A onDeactivation() 0 13 3
A enqueueAsset() 0 13 4
A filterNgettext() 0 7 1
A enqueueAdminAssets() 0 5 2

How to fix   Complexity   

Complex Class

Complex classes like Controller often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Controller, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace GeminiLabs\SiteReviews\Addons;
4
5
use GeminiLabs\SiteReviews\Controllers\AbstractController;
6
use GeminiLabs\SiteReviews\Database\OptionManager;
7
use GeminiLabs\SiteReviews\Helpers\Arr;
8
use GeminiLabs\SiteReviews\Helpers\Cast;
9
use GeminiLabs\SiteReviews\Helpers\Str;
10
use GeminiLabs\SiteReviews\Install;
11
use GeminiLabs\SiteReviews\Modules\Assets\AssetCss;
12
use GeminiLabs\SiteReviews\Modules\Assets\AssetJs;
13
use GeminiLabs\SiteReviews\Modules\Html\Template;
14
use GeminiLabs\SiteReviews\Modules\Translation;
15
use GeminiLabs\SiteReviews\Modules\Translator;
16
use GeminiLabs\SiteReviews\Role;
17
18
abstract class Controller extends AbstractController
19
{
20
    /**
21
     * @action admin_enqueue_scripts
22
     */
23
    public function enqueueAdminAssets(): void
24
    {
25
        if ($this->isReviewAdminPage()) {
26
            $this->enqueueAsset('css', ['suffix' => 'admin']);
27
            $this->enqueueAsset('js', ['suffix' => 'admin']);
28
        }
29
    }
30
31
    /**
32
     * @action wp_enqueue_scripts
33
     */
34
    public function enqueuePublicAssets(): void
35
    {
36
        if (!glsr(AssetCss::class)->canOptimize() || !glsr(AssetCss::class)->isOptimized()) {
37
            $this->enqueueAsset('css');
38
        }
39
        if (!glsr(AssetJs::class)->canOptimize() || !glsr(AssetJs::class)->isOptimized()) {
40
            $this->enqueueAsset('js', ['in_footer' => true]);
41
        }
42
    }
43
44
    /**
45
     * @filter plugin_action_links_{$this->app()->id}/{$this->app()->id}.php
46
     */
47
    public function filterActionLinks(array $links): array
48
    {
49
        $actions = [];
50
        if (glsr()->hasPermission('settings') && !empty($this->app()->config('settings'))) {
51
            $actions['settings'] = glsr_admin_link("settings.addons.{$this->app()->slug}", _x('Settings', 'admin-text', 'site-reviews'));
52
        }
53
        return array_merge($actions, $links);
54
    }
55
56
    /**
57
     * @filter site-reviews/capabilities
58
     */
59
    public function filterCapabilities(array $capabilities): array
60
    {
61
        if (!$this->app()->post_type) { // @phpstan-ignore-line
62
            return $capabilities;
63
        }
64
        $defaults = [
65
            'create_posts',
66
            'delete_others_posts',
67
            'delete_posts',
68
            'delete_private_posts',
69
            'delete_published_posts',
70
            'edit_others_posts',
71
            'edit_posts',
72
            'edit_private_posts',
73
            'edit_published_posts',
74
            'publish_posts',
75
            'read_private_posts',
76
        ];
77
        foreach ($defaults as $capability) {
78
            $capabilities[] = str_replace('post', $this->app()->post_type, $capability);
79
        }
80
        return $capabilities;
81
    }
82
83
    /**
84
     * @filter site-reviews/config
85
     */
86
    public function filterConfigPath(string $path): string
87
    {
88
        $prefix = trailingslashit($this->app()->id);
89
        return str_contains($path, $prefix)
90
            ? $prefix.str_replace($prefix, '', $path)
91
            : $path;
92
    }
93
94
    /**
95
     * @filter site-reviews/addon/documentation
96
     */
97
    public function filterDocumentation(array $documentation): array
98
    {
99
        $notice = glsr()->build('views/partials/addons/support-notice', [
100
            'addon_id' => $this->app()->id,
101
        ]);
102
        $support = $this->app()->build('views/documentation');
103
        $documentation[$this->app()->id] = $notice.$support;
104
        return $documentation;
105
    }
106
107
    /**
108
     * @filter site-reviews/path
109
     */
110
    public function filterFilePaths(string $path, string $file): string
111
    {
112
        $addonPrefix = trailingslashit($this->app()->id);
113
        return str_starts_with($file, $addonPrefix)
114
            ? $this->app()->path(Str::replaceFirst($addonPrefix, '', $file))
115
            : $path;
116
    }
117
118
    /**
119
     * @param string $translation
120
     * @param string $single
121
     *
122
     * @filter gettext_{$this->app()->id}
123
     */
124
    public function filterGettext($translation, $single): string
125
    {
126
        $translation = Cast::toString($translation);
127
        return glsr(Translator::class)->translate($translation, $this->app()->id, [
128
            'single' => Cast::toString($single),
129
        ]);
130
    }
131
132
    /**
133
     * @param string $translation
134
     * @param string $single
135
     * @param string $context
136
     *
137
     * @filter gettext_with_context_{$this->app()->id}
138
     */
139
    public function filterGettextWithContext($translation, $single, $context): string
140
    {
141
        $translation = Cast::toString($translation);
142
        return glsr(Translator::class)->translate($translation, $this->app()->id, [
143
            'context' => Cast::toString($context),
144
            'single' => Cast::toString($single),
145
        ]);
146
    }
147
148
    /**
149
     * @filter site-reviews/enqueue/public/localize
150
     */
151
    public function filterLocalizedPublicVariables(array $variables): array
152
    {
153
        $variables['addons'][$this->app()->id] = null;
154
        return $variables;
155
    }
156
157
    /**
158
     * @param string $translation
159
     * @param string $single
160
     * @param string $plural
161
     * @param int    $number
162
     *
163
     * @filter ngettext_{$this->app()->id}
164
     */
165
    public function filterNgettext($translation, $single, $plural, $number): string
166
    {
167
        $translation = Cast::toString($translation);
168
        return glsr(Translator::class)->translate($translation, $this->app()->id, [
169
            'number' => Cast::toInt($number),
170
            'plural' => Cast::toString($plural),
171
            'single' => Cast::toString($single),
172
        ]);
173
    }
174
175
    /**
176
     * @param string $translation
177
     * @param string $single
178
     * @param string $plural
179
     * @param int    $number
180
     * @param string $context
181
     *
182
     * @filter ngettext_with_context_{$this->app()->id}
183
     */
184
    public function filterNgettextWithContext($translation, $single, $plural, $number, $context): string
185
    {
186
        $translation = Cast::toString($translation);
187
        return glsr(Translator::class)->translate($translation, $this->app()->id, [
188
            'context' => Cast::toString($context),
189
            'number' => Cast::toInt($number),
190
            'plural' => Cast::toString($plural),
191
            'single' => Cast::toString($single),
192
        ]);
193
    }
194
195
    /**
196
     * @filter {$this->app()->id}/render/view
197
     */
198
    public function filterRenderView(string $view): string
199
    {
200
        $style = glsr(OptionManager::class)->get('settings.general.style', 'default');
201
        $styledView = sprintf('views/styles/%s/%s', $style, basename($view));
202
        if (file_exists($this->app()->file($styledView))) {
203
            return $styledView;
204
        }
205
        return $view;
206
    }
207
208
    /**
209
     * @filter site-reviews/roles
210
     */
211
    public function filterRoles(array $roles): array
212
    {
213
        if (!$this->app()->post_type) { // @phpstan-ignore-line
214
            return $roles;
215
        }
216
        $defaults = [
217
            'administrator' => [
218
                'create_posts',
219
                'delete_others_posts',
220
                'delete_posts',
221
                'delete_private_posts',
222
                'delete_published_posts',
223
                'edit_others_posts',
224
                'edit_posts',
225
                'edit_private_posts',
226
                'edit_published_posts',
227
                'publish_posts',
228
                'read_private_posts',
229
            ],
230
            'editor' => [
231
                'create_posts',
232
                'delete_others_posts',
233
                'delete_posts',
234
                'delete_private_posts',
235
                'delete_published_posts',
236
                'edit_others_posts',
237
                'edit_posts',
238
                'edit_private_posts',
239
                'edit_published_posts',
240
                'publish_posts',
241
                'read_private_posts',
242
            ],
243
            'author' => [
244
                'create_posts',
245
                'delete_posts',
246
                'delete_published_posts',
247
                'edit_posts',
248
                'edit_published_posts',
249
                'publish_posts',
250
            ],
251
            'contributor' => [
252
                'delete_posts',
253
                'edit_posts',
254
            ],
255
        ];
256
        foreach ($defaults as $role => $capabilities) {
257
            if (!array_key_exists($role, $roles)) {
258
                continue;
259
            }
260
            foreach ($capabilities as $capability) {
261
                $roles[$role][] = str_replace('post', $this->app()->post_type, $capability);
262
            }
263
        }
264
        return $roles;
265
    }
266
267
    /**
268
     * @filter plugin_row_meta
269
     */
270
    public function filterRowMeta(array $links, string $file): array
271
    {
272
        if ($file !== $this->app()->basename) {
273
            return $links;
274
        }
275
        $actions = [];
276
        if (glsr()->hasPermission('documentation')) {
277
            $actions['documentation'] = glsr_admin_link('documentation.addons', [
278
                'data-expand' => "#addon-{$this->app()->id}",
279
                'text' => _x('Documentation', 'admin-text', 'site-reviews'),
280
            ]);
281
        }
282
        $actions['support'] = glsr_premium_link('support', [
283
            'aria-label' => _x('Visit premium customer support', 'admin-text', 'site-reviews'),
284
            'text' => _x('Support', 'admin-text', 'site-reviews'),
285
        ]);
286
        return array_merge($links, $actions);
287
    }
288
289
    /**
290
     * @filter site-reviews/settings
291
     */
292
    public function filterSettings(array $settings): array
293
    {
294
        return array_merge($this->app()->config('settings'), $settings);
295
    }
296
297
    /**
298
     * @filter site-reviews/addon/subsubsub
299
     */
300
    public function filterSubsubsub(array $subsubsub): array
301
    {
302
        return $subsubsub;
303
    }
304
305
    /**
306
     * @filter site-reviews/translation/entries
307
     */
308
    public function filterTranslationEntries(array $entries): array
309
    {
310
        $potFile = $this->app()->path("{$this->app()->languages}/{$this->app()->id}.pot");
311
        return glsr(Translation::class)->extractEntriesFromPotFile($potFile, $this->app()->id, $entries);
312
    }
313
314
    /**
315
     * @filter site-reviews/translator/domains
316
     */
317
    public function filterTranslatorDomains(array $domains): array
318
    {
319
        return [...$domains, $this->app()->id];
320
    }
321
322
    /**
323
     * @action {$this->app()->id}/activated
324
     */
325
    public function install(): void
326
    {
327
    }
328
329
    /**
330
     * @action admin_init
331
     */
332
    public function onActivation(): void
333
    {
334
        $option = glsr()->prefix."activated_{$this->app()->id}";
335
        if (empty(get_option($option))) {
336
            update_option($option, true);
337
            if ($this->app()->post_type) { // @phpstan-ignore-line
338
                glsr(Role::class)->reset($this->filterRoles([
339
                    'administrator' => [],
340
                    'author' => [],
341
                    'contributor' => [],
342
                    'editor' => [],
343
                ]));
344
            }
345
            $this->app()->action('activated');
346
        }
347
    }
348
349
    /**
350
     * @action deactivate_{$this->app()->basename}
351
     */
352
    public function onDeactivation(bool $isNetworkDeactivation): void
353
    {
354
        $option = glsr()->prefix."activated_{$this->app()->id}";
355
        if (!$isNetworkDeactivation) {
356
            delete_option($option);
357
            $this->app()->action('deactivated');
358
            return;
359
        }
360
        foreach (glsr(Install::class)->sites() as $siteId) {
361
            switch_to_blog($siteId);
362
            delete_option($option);
363
            $this->app()->action('deactivated');
364
            restore_current_blog();
365
        }
366
    }
367
368
    /**
369
     * @action init
370
     */
371
    public function registerLanguages(): void
372
    {
373
        $path = plugin_basename($this->app()->path());
374
        $path = trailingslashit("{$path}/{$this->app()->languages}");
375
        load_plugin_textdomain($this->app()->id, false, $path);
376
    }
377
378
    /**
379
     * @action init
380
     */
381
    public function registerShortcodes(): void
382
    {
383
    }
384
385
    /**
386
     * @action admin_init
387
     */
388
    public function registerTinymcePopups(): void
389
    {
390
    }
391
392
    /**
393
     * @action widgets_init
394
     */
395
    public function registerWidgets(): void
396
    {
397
    }
398
399
    /**
400
     * @action site-reviews/settings/{$this->app()->slug}
401
     */
402
    public function renderSettings(string $rows): void
403
    {
404
        glsr(Template::class)->render("{$this->app()->id}/views/settings", [
405
            'context' => [
406
                'rows' => $rows,
407
                'title' => $this->app()->name,
408
            ],
409
        ]);
410
    }
411
412
    protected function buildAssetArgs(string $ext, array $args = []): array
413
    {
414
        $args = wp_parse_args($args, [
415
            'defer' => true,
416
            'in_footer' => false,
417
            'suffix' => '',
418
        ]);
419
        $suffix = Str::prefix($args['suffix'], '-');
420
        $path = "assets/{$this->app()->id}{$suffix}.{$ext}";
421
        if (!file_exists($this->app()->path($path)) || !in_array($ext, ['css', 'js'])) {
422
            return [];
423
        }
424
        $suffix = Str::prefix($args['suffix'], '/');
425
        $dependencies = Arr::get($args, 'dependencies', [glsr()->id.$suffix]);
426
        $handle = $this->app()->id.$suffix;
427
        $funcArgs = [
428
            $handle,
429
            $this->app()->url($path),
430
            Arr::consolidate($dependencies),
431
            $this->app()->version,
432
        ];
433
        if ('js' === $ext && wp_validate_boolean($args['in_footer'])) {
434
            $funcArgs[] = true; // load script in the footer
435
        }
436
        return $funcArgs;
437
    }
438
439
    protected function enqueueAsset(string $extension, array $args = []): void
440
    {
441
        $defer = Arr::get($args, 'defer', false);
442
        if ($args = $this->buildAssetArgs($extension, $args)) {
443
            if ('js' === $extension) {
444
                call_user_func_array('wp_register_script', $args);
445
                call_user_func('wp_enqueue_script', $args[0]);
446
            } else {
447
                call_user_func_array('wp_register_style', $args);
448
                call_user_func('wp_enqueue_style', $args[0]);
449
            }
450
            if (wp_validate_boolean($defer)) {
451
                wp_script_add_data($args[0], 'strategy', 'defer');
452
            }
453
        }
454
    }
455
456
    protected function registerAsset(string $extension, array $args = []): void
457
    {
458
        $defer = Arr::get($args, 'defer', false);
459
        if ($args = $this->buildAssetArgs($extension, $args)) {
460
            $function = 'js' === $extension
461
                ? 'wp_register_script'
462
                : 'wp_register_style';
463
            call_user_func_array($function, $args);
464
            if (wp_validate_boolean($defer)) {
465
                wp_script_add_data($args[0], 'strategy', 'defer');
466
            }
467
        }
468
    }
469
}
470