GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( b11886...81ba60 )
by Leonardo
14:42
created

Plugin::setup()   B

Complexity

Conditions 6
Paths 1

Size

Total Lines 77
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 8
Bugs 1 Features 0
Metric Value
cc 6
eloc 29
c 8
b 1
f 0
nc 1
nop 0
dl 0
loc 77
ccs 0
cts 30
cp 0
crap 42
rs 8.8337

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQLAPI\GraphQLAPI;
6
7
use PoP\Engine\ComponentLoader;
8
use GraphQLAPI\GraphQLAPI\PluginConfiguration;
9
use GraphQLAPI\GraphQLAPI\Blocks\AbstractBlock;
10
use GraphQLAPI\GraphQLAPI\General\RequestParams;
11
use GraphQLAPI\GraphQLAPI\Admin\Menus\AbstractMenu;
12
use GraphQLAPI\GraphQLAPI\PostTypes\AbstractPostType;
13
use GraphQLAPI\GraphQLAPI\Taxonomies\AbstractTaxonomy;
14
use GraphQLAPI\GraphQLAPI\Facades\ModuleRegistryFacade;
15
use PoP\ComponentModel\Container\ContainerBuilderUtils;
16
use GraphQLAPI\GraphQLAPI\Admin\MenuPages\AboutMenuPage;
17
use GraphQLAPI\GraphQLAPI\Admin\MenuPages\ModulesMenuPage;
18
use GraphQLAPI\GraphQLAPI\Facades\UserSettingsManagerFacade;
19
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLEndpointPostType;
20
use GraphQLAPI\GraphQLAPI\EditorScripts\AbstractEditorScript;
21
use PoP\ComponentModel\Facades\Instances\InstanceManagerFacade;
22
use GraphQLAPI\GraphQLAPI\BlockCategories\AbstractBlockCategory;
23
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLPersistedQueryPostType;
24
use GraphQLAPI\GraphQLAPI\Admin\TableActions\ModuleListTableAction;
25
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLCacheControlListPostType;
26
use GraphQLAPI\GraphQLAPI\EndpointResolvers\AbstractEndpointResolver;
27
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLAccessControlListPostType;
28
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLSchemaConfigurationPostType;
29
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLFieldDeprecationListPostType;
30
use GraphQLAPI\GraphQLAPI\ModuleResolvers\EndpointFunctionalityModuleResolver;
31
use GraphQLAPI\GraphQLAPI\ModuleResolvers\VersioningFunctionalityModuleResolver;
32
use GraphQLAPI\GraphQLAPI\ModuleResolvers\PerformanceFunctionalityModuleResolver;
33
use GraphQLAPI\GraphQLAPI\ModuleResolvers\AccessControlFunctionalityModuleResolver;
34
use GraphQLAPI\GraphQLAPI\ModuleResolvers\UserInterfaceFunctionalityModuleResolver;
35
use GraphQLAPI\GraphQLAPI\Blocks\AccessControlRuleBlocks\AccessControlUserRolesBlock;
36
use GraphQLAPI\GraphQLAPI\Blocks\AccessControlRuleBlocks\AccessControlUserStateBlock;
37
use GraphQLAPI\GraphQLAPI\Blocks\AccessControlRuleBlocks\AccessControlDisableAccessBlock;
38
use GraphQLAPI\GraphQLAPI\ModuleResolvers\SchemaConfigurationFunctionalityModuleResolver;
39
use GraphQLAPI\GraphQLAPI\Blocks\AccessControlRuleBlocks\AccessControlUserCapabilitiesBlock;
40
use WP_Upgrader;
41
42
class Plugin
43
{
44
    /**
45
     * Plugin's namespace
46
     */
47
    public const NAMESPACE = __NAMESPACE__;
48
49
    /**
50
     * Transient to indicate if plugin was just updated
51
     */
52
    public const TRANSIENT_PLUGIN_UPDATED = 'graphql-api-plugin-updated';
53
54
    /**
55
     * Plugin set-up, executed immediately when loading the plugin.
56
     * There are three stages for this plugin, and for each extension plugin:
57
     * `setup`, `initialize` and `boot`.
58
     *
59
     * This is because:
60
     *
61
     * - The plugin must execute its logic before the extensions
62
     * - The services can't be booted before all services have been initialized
63
     *
64
     * To attain the needed order, we execute them using hook "plugins_loaded":
65
     *
66
     * 1. GraphQL API => setup(): immediately
67
     * 2. GraphQL API extensions => setup(): priority 0
68
     * 3. GraphQL API => initialize(): priority 5
69
     * 4. GraphQL API extensions => initialize(): priority 10
70
     * 5. GraphQL API => boot(): priority 15
71
     * 6. GraphQL API extensions => boot(): priority 20
72
     *
73
     * @return void
74
     */
75
    public function setup(): void
76
    {
77
        // Functions to execute when activating/deactivating the plugin
78
        \register_deactivation_hook(\GRAPHQL_API_PLUGIN_FILE, [$this, 'deactivate']);
79
        /**
80
         * PoP depends on hook "init" to set-up the endpoint rewrite,
81
         * as in function `addRewriteEndpoints` in `AbstractEndpointHandler`
82
         * However, activating the plugin takes place AFTER hooks "plugins_loaded"
83
         * and "init". Hence, the code cannot flush the rewrite_rules when the plugin
84
         * is activated, and any non-default GraphQL endpoint is not set.
85
         *
86
         * The solution (hack) is to check if the plugin has just been installed,
87
         * and then apply the logic, on every request in the admin!
88
         *
89
         * @see https://developer.wordpress.org/reference/functions/register_activation_hook/#process-flow
90
         */
91
        $isPluginJustActivatedOptionName = 'graphql-api-activated-plugin';
92
        \register_activation_hook(
93
            \GRAPHQL_API_PLUGIN_FILE,
94
            function () use ($isPluginJustActivatedOptionName): void {
95
                // Flag to indicate that the plugin has been activated
96
                \add_option($isPluginJustActivatedOptionName, true);
97
                // This is the proper activation logic
98
                $this->activate();
99
            }
100
        );
101
        \add_action(
102
            'admin_init',
103
            function () use ($isPluginJustActivatedOptionName): void {
104
                // If the flag is there, it's the first screen after activating the plugin
105
                if (\is_admin()) {
106
                    $isPluginJustActivated = \get_option($isPluginJustActivatedOptionName) == true;
107
                    if ($isPluginJustActivated) {
108
                        // Delete the flag
109
                        \delete_option($isPluginJustActivatedOptionName);
110
                        // Required logic after plugin is activated
111
                        \flush_rewrite_rules();
112
                    }
113
114
                    // On updates only: Show a link to view the new version's release notes
115
                    if (!\wp_doing_ajax()) {
116
                        // Check if there is a transient indicating that the plugin was updated
117
                        $pluginUpdatedTransient = \get_transient(self::TRANSIENT_PLUGIN_UPDATED);
118
                        if ($pluginUpdatedTransient) {
119
                            delete_transient(self::TRANSIENT_PLUGIN_UPDATED);
120
                            // If updating either MAJOR or MINOR versions, show admin notice
121
                            // No need for PATCH versions
122
                            $currentMinorReleaseVersion = $this->getMinorReleaseVersion(\GRAPHQL_API_VERSION);
123
                            $previousMinorReleaseVersion = $this->getMinorReleaseVersion($pluginUpdatedTransient);
124
                            if ($currentMinorReleaseVersion != $previousMinorReleaseVersion) {
125
                                $this->showReleaseNotesInAdminNotice();
126
                            }
127
                        }
128
                    }
129
                }
130
            }
131
        );
132
        /**
133
         * On updates only: Show a link to view the new version's release notes
134
         *
135
         * @see https://codex.wordpress.org/Plugin_API/Action_Reference/upgrader_process_complete
136
         */
137
        add_action(
138
            'upgrader_process_complete',
139
            [$this, 'checkIsPluginUpgraded'],
140
            10,
141
            2
142
        );
143
144
        /**
145
         * Wait until "plugins_loaded" to initialize the plugin, because:
146
         *
147
         * - ModuleListTableAction requires `wp_verify_nonce`, loaded in pluggable.php
148
         * - Allow other plugins to inject their own functionality
149
         */
150
        \add_action('plugins_loaded', [$this, 'initialize'], 5);
151
        \add_action('plugins_loaded', [$this, 'boot'], 15);
152
    }
153
154
    /**
155
     * This function runs when WordPress completes its upgrade process
156
     * If this plugin is updated, it sets a transient flag
157
     *
158
     * @param array<string, mixed> $options
159
     * @see https://codex.wordpress.org/Plugin_API/Action_Reference/upgrader_process_complete
160
     */
161
    public function checkIsPluginUpgraded(WP_Upgrader $upgrader_object, array $options): void
162
    {
163
        // If an update has taken place and the updated type is plugins and the plugins element exists,
164
        // or an install of a new version of the plugin
165
        if ($options['type'] == 'plugin' && (
166
            (
167
                $options['action'] == 'update'
168
                && isset($options['plugins'])
169
                && in_array(\GRAPHQL_API_BASE_NAME, $options['plugins'])
170
            ) || (
171
                $options['action'] == 'install'
172
                && $upgrader_object->result['destination_name'] == \GRAPHQL_API_PLUGIN_NAME
0 ignored issues
show
Bug introduced by
The constant GRAPHQL_API_PLUGIN_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
173
            )
174
        )) {
175
            // Set a transient to record that the plugin has just been updated
176
            // The value is the plugin's current version when the upgrade happens,
177
            // i.e. the version to be replaced
178
            \set_transient(self::TRANSIENT_PLUGIN_UPDATED, \GRAPHQL_API_VERSION);
179
        }
180
    }
181
182
    /**
183
     * Add a notice with a link to the latest release note,
184
     * to open in a modal window
185
     */
186
    protected function showReleaseNotesInAdminNotice(): void
187
    {
188
        // Load the assets to open in a modal
189
        \add_action('admin_enqueue_scripts', function () {
190
            /**
191
             * Hack to open the modal thickbox iframe with the documentation
192
             */
193
            \wp_enqueue_style(
194
                'thickbox'
195
            );
196
            \wp_enqueue_script(
197
                'plugin-install'
198
            );
199
        });
200
        // Add the admin notice
201
        \add_action('admin_notices', function () {
202
            $instanceManager = InstanceManagerFacade::getInstance();
203
            /**
204
             * @var AboutMenuPage
205
             */
206
            $aboutMenuPage = $instanceManager->getInstance(AboutMenuPage::class);
207
            // Calculate the minor release version.
208
            // Eg: if current version is 0.6.3, minor version is 0.6
209
            $minorReleaseVersion = $this->getMinorReleaseVersion(\GRAPHQL_API_VERSION);
210
            $url = \admin_url(sprintf(
211
                'admin.php?page=%s&%s=%s&%s=%s&TB_iframe=true',
212
                $aboutMenuPage->getScreenID(),
213
                RequestParams::TAB,
214
                RequestParams::TAB_DOCS,
215
                RequestParams::DOC,
216
                sprintf(
217
                    'release-notes/%s',
218
                    $minorReleaseVersion
219
                )
220
            ));
221
            _e(sprintf(
222
                '<div class="notice notice-success is-dismissible">' .
223
                '<p>%s</p>' .
224
                '</div>',
225
                sprintf(
226
                    __('Plugin <strong>GraphQL API for WordPress</strong> has been updated to version <code>%s</code>. <a href="%s" class="%s">Check out what\'s new</a>.', 'graphql-api'),
227
                    \GRAPHQL_API_VERSION,
228
                    $url,
229
                    'thickbox open-plugin-details-modal'
230
                )
231
            ));
232
        });
233
    }
234
235
    /**
236
     * Given a version in semver (MAJOR.MINOR.PATCH),
237
     * return the minor version (MAJOR.MINOR)
238
     */
239
    protected function getMinorReleaseVersion(string $version): string
240
    {
241
        $versionParts = explode('.', $version);
242
        return $versionParts[0] . '.' . $versionParts[1];
243
    }
244
245
    /**
246
     * Plugin initialization, executed on hook "plugins_loaded"
247
     * to wait for all extensions to be loaded
248
     *
249
     * @return void
250
     */
251
    public function initialize(): void
252
    {
253
        /**
254
         * Watch out! If we are in the Modules page and enabling/disabling
255
         * a module, then already take that new state!
256
         * This is because `maybeProcessAction`, which is where modules are
257
         * enabled/disabled, must be executed before PluginConfiguration::initialize(),
258
         * which is where the plugin reads if a module is enabled/disabled as to
259
         * set the environment constants.
260
         *
261
         * This is mandatory, because only when it is enabled, can a module
262
         * have its state persisted when calling `flush_rewrite`
263
         */
264
        if (\is_admin()) {
265
            // We can't use the InstanceManager, since at this stage it hasn't
266
            // been initialized yet
267
            // We can create a new instances of ModulesMenuPage because
268
            // its instantiation produces no side-effects
269
            $modulesMenuPage = new ModulesMenuPage();
270
            if (isset($_GET['page']) && $_GET['page'] == $modulesMenuPage->getScreenID()) {
271
                // Instantiating ModuleListTableAction DOES have side-effects,
272
                // but they are needed, and won't be repeated when instantiating
273
                // the class through the Container Builder
274
                $moduleListTable = new ModuleListTableAction();
275
                $moduleListTable->maybeProcessAction();
276
            }
277
        }
278
279
        // Configure the plugin. This defines hooks to set environment variables,
280
        // so must be executed
281
        // before those hooks are triggered for first time
282
        // (in ComponentConfiguration classes)
283
        PluginConfiguration::initialize();
284
285
        // Component configuration
286
        $componentClassConfiguration = PluginConfiguration::getComponentClassConfiguration();
287
        $skipSchemaComponentClasses = PluginConfiguration::getSkippingSchemaComponentClasses();
288
289
        // Initialize the plugin's Component and, with it,
290
        // all its dependencies from PoP
291
        ComponentLoader::initializeComponents(
292
            [
293
                \GraphQLAPI\GraphQLAPI\Component::class,
294
            ],
295
            $componentClassConfiguration,
296
            $skipSchemaComponentClasses
297
        );
298
    }
299
300
    /**
301
     * Plugin initialization, executed on hook "plugins_loaded"
302
     * to wait for all extensions to be loaded
303
     */
304
    public function boot(): void
305
    {
306
        // Boot all PoP components, from this plugin and all extensions
307
        ComponentLoader::bootComponents();
308
309
        $instanceManager = InstanceManagerFacade::getInstance();
310
        $moduleRegistry = ModuleRegistryFacade::getInstance();
311
        /**
312
         * Initialize classes for the admin panel
313
         */
314
        if (\is_admin()) {
315
            /**
316
             * Initialize all the services
317
             */
318
            $menuServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\Admin\\Menus');
319
            foreach ($menuServiceClasses as $serviceClass) {
320
                /**
321
                 * @var AbstractMenu
322
                 */
323
                $service = $instanceManager->getInstance($serviceClass);
324
                $service->initialize();
325
            }
326
            $endpointResolverServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\Admin\\EndpointResolvers');
327
            foreach ($endpointResolverServiceClasses as $serviceClass) {
328
                /**
329
                 * @var AbstractEndpointResolver
330
                 */
331
                $service = $instanceManager->getInstance($serviceClass);
332
                $service->initialize();
333
            }
334
        }
335
336
        /**
337
         * Taxonomies must be initialized before Post Types
338
         */
339
        $taxonomyServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\Taxonomies');
340
        foreach ($taxonomyServiceClasses as $serviceClass) {
341
            /**
342
             * @var AbstractTaxonomy
343
             */
344
            $service = $instanceManager->getInstance($serviceClass);
345
            $service->initialize();
346
        }
347
        /**
348
         * Initialize Post Types manually to control in what order they are added to the menu
349
         */
350
        $postTypeServiceClassModules = [
351
            GraphQLEndpointPostType::class => EndpointFunctionalityModuleResolver::CUSTOM_ENDPOINTS,
352
            GraphQLPersistedQueryPostType::class => EndpointFunctionalityModuleResolver::PERSISTED_QUERIES,
353
            GraphQLSchemaConfigurationPostType::class => SchemaConfigurationFunctionalityModuleResolver::SCHEMA_CONFIGURATION,
354
            GraphQLAccessControlListPostType::class => AccessControlFunctionalityModuleResolver::ACCESS_CONTROL,
355
            GraphQLCacheControlListPostType::class => PerformanceFunctionalityModuleResolver::CACHE_CONTROL,
356
            GraphQLFieldDeprecationListPostType::class => VersioningFunctionalityModuleResolver::FIELD_DEPRECATION,
357
        ];
358
        foreach ($postTypeServiceClassModules as $serviceClass => $module) {
359
            // Check that the corresponding module is enabled
360
            if ($moduleRegistry->isModuleEnabled($module)) {
361
                /**
362
                 * @var AbstractPostType
363
                 */
364
                $service = $instanceManager->getInstance($serviceClass);
365
                $service->initialize();
366
            }
367
        }
368
        /**
369
         * Editor Scripts
370
         * They are all used to show the Welcome Guide
371
         */
372
        if ($moduleRegistry->isModuleEnabled(UserInterfaceFunctionalityModuleResolver::WELCOME_GUIDES)) {
373
            $editorScriptServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\EditorScripts');
374
            foreach ($editorScriptServiceClasses as $serviceClass) {
375
                /**
376
                 * @var AbstractEditorScript
377
                 */
378
                $service = $instanceManager->getInstance($serviceClass);
379
                $service->initialize();
380
            }
381
        }
382
        /**
383
         * Blocks
384
         * The GraphiQL Block may be overriden to GraphiQLWithExplorerBlock
385
         */
386
        $blockServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\Blocks', false);
387
        foreach ($blockServiceClasses as $serviceClass) {
388
            /**
389
             * @var AbstractBlock
390
             */
391
            $service = $instanceManager->getInstance($serviceClass);
392
            $service->initialize();
393
        }
394
        /**
395
         * Access Control Nested Blocks
396
         * Register them one by one, as to disable them if module is disabled
397
         */
398
        $accessControlRuleBlockServiceClassModules = [
399
            AccessControlDisableAccessBlock::class => AccessControlFunctionalityModuleResolver::ACCESS_CONTROL_RULE_DISABLE_ACCESS,
400
            AccessControlUserStateBlock::class => AccessControlFunctionalityModuleResolver::ACCESS_CONTROL_RULE_USER_STATE,
401
            AccessControlUserRolesBlock::class => AccessControlFunctionalityModuleResolver::ACCESS_CONTROL_RULE_USER_ROLES,
402
            AccessControlUserCapabilitiesBlock::class => AccessControlFunctionalityModuleResolver::ACCESS_CONTROL_RULE_USER_CAPABILITIES,
403
        ];
404
        foreach ($accessControlRuleBlockServiceClassModules as $serviceClass => $module) {
405
            if ($moduleRegistry->isModuleEnabled($module)) {
406
                /**
407
                 * @var AbstractBlock
408
                 */
409
                $service = $instanceManager->getInstance($serviceClass);
410
                $service->initialize();
411
            }
412
        }
413
        /**
414
         * Block categories
415
         */
416
        $blockCategoryServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\BlockCategories');
417
        foreach ($blockCategoryServiceClasses as $serviceClass) {
418
            /**
419
             * @var AbstractBlockCategory
420
             */
421
            $service = $instanceManager->getInstance($serviceClass);
422
            $service->initialize();
423
        }
424
    }
425
426
    /**
427
     * Get permalinks to work when activating the plugin
428
     *
429
     * @see    https://codex.wordpress.org/Function_Reference/register_post_type#Flushing_Rewrite_on_Activation
430
     * @return void
431
     */
432
    public function activate(): void
433
    {
434
        // Initialize the timestamp
435
        $userSettingsManager = UserSettingsManagerFacade::getInstance();
436
        $userSettingsManager->storeTimestamp();
437
    }
438
439
    /**
440
     * Remove permalinks when deactivating the plugin
441
     *
442
     * @see    https://developer.wordpress.org/plugins/plugin-basics/activation-deactivation-hooks/
443
     * @return void
444
     */
445
    public function deactivate(): void
446
    {
447
        // First, unregister the post type, so the rules are no longer in memory.
448
        $instanceManager = InstanceManagerFacade::getInstance();
449
        $postTypeObjects = array_map(
450
            function ($serviceClass) use ($instanceManager): AbstractPostType {
451
                /**
452
                 * @var AbstractPostType
453
                 */
454
                $postTypeObject = $instanceManager->getInstance($serviceClass);
455
                return $postTypeObject;
456
            },
457
            ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\PostTypes')
458
        );
459
        foreach ($postTypeObjects as $postTypeObject) {
460
            $postTypeObject->unregisterPostType();
461
        }
462
463
        // Then, clear the permalinks to remove the post type's rules from the database.
464
        \flush_rewrite_rules();
465
466
        // Remove the timestamp
467
        $userSettingsManager = UserSettingsManagerFacade::getInstance();
468
        $userSettingsManager->removeTimestamp();
469
    }
470
}
471