Controller   F
last analyzed

Complexity

Total Complexity 60

Size/Duplication

Total Lines 433
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 60
eloc 182
dl 0
loc 433
ccs 0
cts 218
cp 0
rs 3.6
c 2
b 0
f 0

29 Methods

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