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 ( 5f2c26...67714c )
by Leonardo
11:03
created

Plugin::deactivate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 11
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 24
ccs 0
cts 10
cp 0
crap 6
rs 9.9
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\Admin\MenuPages\SettingsMenuPage;
19
use GraphQLAPI\GraphQLAPI\Facades\UserSettingsManagerFacade;
20
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLEndpointPostType;
21
use GraphQLAPI\GraphQLAPI\EditorScripts\AbstractEditorScript;
22
use PoP\ComponentModel\Facades\Instances\InstanceManagerFacade;
23
use GraphQLAPI\GraphQLAPI\BlockCategories\AbstractBlockCategory;
24
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLPersistedQueryPostType;
25
use GraphQLAPI\GraphQLAPI\Admin\TableActions\ModuleListTableAction;
26
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLCacheControlListPostType;
27
use GraphQLAPI\GraphQLAPI\EndpointResolvers\AbstractEndpointResolver;
28
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLAccessControlListPostType;
29
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLSchemaConfigurationPostType;
30
use GraphQLAPI\GraphQLAPI\PostTypes\GraphQLFieldDeprecationListPostType;
31
use GraphQLAPI\GraphQLAPI\ModuleResolvers\EndpointFunctionalityModuleResolver;
32
use GraphQLAPI\GraphQLAPI\ModuleResolvers\VersioningFunctionalityModuleResolver;
33
use GraphQLAPI\GraphQLAPI\ModuleResolvers\PerformanceFunctionalityModuleResolver;
34
use GraphQLAPI\GraphQLAPI\ModuleResolvers\AccessControlFunctionalityModuleResolver;
35
use GraphQLAPI\GraphQLAPI\ModuleResolvers\UserInterfaceFunctionalityModuleResolver;
36
use GraphQLAPI\GraphQLAPI\Blocks\AccessControlRuleBlocks\AccessControlUserRolesBlock;
37
use GraphQLAPI\GraphQLAPI\Blocks\AccessControlRuleBlocks\AccessControlUserStateBlock;
38
use GraphQLAPI\GraphQLAPI\ModuleResolvers\PluginManagementFunctionalityModuleResolver;
39
use GraphQLAPI\GraphQLAPI\Blocks\AccessControlRuleBlocks\AccessControlDisableAccessBlock;
40
use GraphQLAPI\GraphQLAPI\ModuleResolvers\SchemaConfigurationFunctionalityModuleResolver;
41
use GraphQLAPI\GraphQLAPI\Blocks\AccessControlRuleBlocks\AccessControlUserCapabilitiesBlock;
42
43
class Plugin
44
{
45
    /**
46
     * Plugin's namespace
47
     */
48
    public const NAMESPACE = __NAMESPACE__;
49
50
    /**
51
     * Store the plugin version in the Options table, to track when
52
     * the plugin is installed/updated
53
     */
54
    public const OPTION_PLUGIN_VERSION = 'graphql-api-plugin-version';
55
56
    /**
57
     * Plugin set-up, executed immediately when loading the plugin.
58
     * There are three stages for this plugin, and for each extension plugin:
59
     * `setup`, `initialize` and `boot`.
60
     *
61
     * This is because:
62
     *
63
     * - The plugin must execute its logic before the extensions
64
     * - The services can't be booted before all services have been initialized
65
     *
66
     * To attain the needed order, we execute them using hook "plugins_loaded":
67
     *
68
     * 1. GraphQL API => setup(): immediately
69
     * 2. GraphQL API extensions => setup(): priority 0
70
     * 3. GraphQL API => initialize(): priority 5
71
     * 4. GraphQL API extensions => initialize(): priority 10
72
     * 5. GraphQL API => boot(): priority 15
73
     * 6. GraphQL API extensions => boot(): priority 20
74
     *
75
     * @return void
76
     */
77
    public function setup(): void
78
    {
79
        // Functions to execute when activating/deactivating the plugin
80
        \register_deactivation_hook(\GRAPHQL_API_PLUGIN_FILE, [$this, 'deactivate']);
81
        /**
82
         * PoP depends on hook "init" to set-up the endpoint rewrite,
83
         * as in function `addRewriteEndpoints` in `AbstractEndpointHandler`
84
         * However, activating the plugin takes place AFTER hooks "plugins_loaded"
85
         * and "init". Hence, the code cannot flush the rewrite_rules when the plugin
86
         * is activated, and any non-default GraphQL endpoint is not set.
87
         *
88
         * The solution (hack) is to check if the plugin has just been installed,
89
         * and then apply the logic, on every request in the admin!
90
         *
91
         * @see https://developer.wordpress.org/reference/functions/register_activation_hook/#process-flow
92
         */
93
        \register_activation_hook(
94
            \GRAPHQL_API_PLUGIN_FILE,
95
            function (): void {
96
                // By removing the option (in case it already exists from a previously-installed version),
97
                // the next request will know the plugin was just installed
98
                \update_option(self::OPTION_PLUGIN_VERSION, false);
99
                // This is the proper activation logic
100
                $this->activate();
101
            }
102
        );
103
        \add_action(
104
            'admin_init',
105
            function (): void {
106
                // If there is no version stored, it's the first screen after activating the plugin
107
                $isPluginJustActivated = \get_option(self::OPTION_PLUGIN_VERSION) === false;
108
                if (!$isPluginJustActivated) {
109
                    return;
110
                }
111
                // Update to the current version
112
                \update_option(self::OPTION_PLUGIN_VERSION, \GRAPHQL_API_VERSION);
113
                // Required logic after plugin is activated
114
                \flush_rewrite_rules();
115
            }
116
        );
117
        /**
118
         * Show an admin notice with a link to the latest release notes
119
         */
120
        \add_action(
121
            'admin_init',
122
            function (): void {
123
                // Do not execute when doing Ajax, since we can't show the one-time
124
                // admin notice to the user then
125
                if (\wp_doing_ajax()) {
126
                    return;
127
                }
128
                // Check if the plugin has been updated: if the stored version in the DB
129
                // and the current plugin's version are different
130
                $storedVersion = \get_option(self::OPTION_PLUGIN_VERSION, \GRAPHQL_API_VERSION);
131
                if ($storedVersion == \GRAPHQL_API_VERSION) {
132
                    return;
133
                }
134
                // Update to the current version
135
                \update_option(self::OPTION_PLUGIN_VERSION, \GRAPHQL_API_VERSION);
136
                // Admin notice: Check if it is enabled
137
                $userSettingsManager = UserSettingsManagerFacade::getInstance();
138
                if (!$userSettingsManager->getSetting(
139
                    PluginManagementFunctionalityModuleResolver::GENERAL,
140
                    PluginManagementFunctionalityModuleResolver::OPTION_ADD_RELEASE_NOTES_ADMIN_NOTICE
141
                )) {
142
                    return;
143
                }
144
                // Show admin notice only when updating MAJOR or MINOR versions. No need for PATCH versions
145
                $currentMinorReleaseVersion = $this->getMinorReleaseVersion(\GRAPHQL_API_VERSION);
146
                $previousMinorReleaseVersion = $this->getMinorReleaseVersion($storedVersion);
0 ignored issues
show
Bug introduced by
It seems like $storedVersion can also be of type false; however, parameter $version of GraphQLAPI\GraphQLAPI\Pl...etMinorReleaseVersion() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

146
                $previousMinorReleaseVersion = $this->getMinorReleaseVersion(/** @scrutinizer ignore-type */ $storedVersion);
Loading history...
147
                if ($currentMinorReleaseVersion == $previousMinorReleaseVersion) {
148
                    return;
149
                }
150
                // All checks passed, show the release notes
151
                $this->showReleaseNotesInAdminNotice();
152
            }
153
        );
154
155
        /**
156
         * Wait until "plugins_loaded" to initialize the plugin, because:
157
         *
158
         * - ModuleListTableAction requires `wp_verify_nonce`, loaded in pluggable.php
159
         * - Allow other plugins to inject their own functionality
160
         */
161
        \add_action('plugins_loaded', [$this, 'initialize'], 5);
162
        \add_action('plugins_loaded', [$this, 'boot'], 15);
163
    }
164
165
    /**
166
     * Add a notice with a link to the latest release note,
167
     * to open in a modal window
168
     */
169
    protected function showReleaseNotesInAdminNotice(): void
170
    {
171
        // Load the assets to open in a modal
172
        \add_action('admin_enqueue_scripts', function () {
173
            /**
174
             * Hack to open the modal thickbox iframe with the documentation
175
             */
176
            \wp_enqueue_style(
177
                'thickbox'
178
            );
179
            \wp_enqueue_script(
180
                'plugin-install'
181
            );
182
        });
183
        // Add the admin notice
184
        \add_action('admin_notices', function () {
185
            $instanceManager = InstanceManagerFacade::getInstance();
186
            /**
187
             * @var AboutMenuPage
188
             */
189
            $aboutMenuPage = $instanceManager->getInstance(AboutMenuPage::class);
190
            // Calculate the minor release version.
191
            // Eg: if current version is 0.6.3, minor version is 0.6
192
            $minorReleaseVersion = $this->getMinorReleaseVersion(\GRAPHQL_API_VERSION);
193
            $releaseNotesURL = \admin_url(sprintf(
194
                'admin.php?page=%s&%s=%s&%s=%s&TB_iframe=true',
195
                $aboutMenuPage->getScreenID(),
196
                RequestParams::TAB,
197
                RequestParams::TAB_DOCS,
198
                RequestParams::DOC,
199
                sprintf(
200
                    'release-notes/%s',
201
                    $minorReleaseVersion
202
                )
203
            ));
204
            /**
205
             * @var SettingsMenuPage
206
             */
207
            $settingsMenuPage = $instanceManager->getInstance(SettingsMenuPage::class);
208
            $moduleRegistry = ModuleRegistryFacade::getInstance();
209
            $generalSettingsURL = \admin_url(sprintf(
210
                'admin.php?page=%s&tab=%s',
211
                $settingsMenuPage->getScreenID(),
212
                $moduleRegistry
213
                    ->getModuleResolver(PluginManagementFunctionalityModuleResolver::GENERAL)
214
                    ->getID(PluginManagementFunctionalityModuleResolver::GENERAL)
215
            ));
216
            _e(sprintf(
217
                '<div class="notice notice-success is-dismissible">' .
218
                '<p>%s</p>' .
219
                '</div>',
220
                sprintf(
221
                    __('Plugin <strong>GraphQL API for WordPress</strong> has been updated to version <code>%s</code>. <strong><a href="%s" class="%s">Check out what\'s new</a></strong> | <a href="%s">Disable this admin notice in the Settings</a>', 'graphql-api'),
222
                    \GRAPHQL_API_VERSION,
223
                    $releaseNotesURL,
224
                    'thickbox open-plugin-details-modal',
225
                    $generalSettingsURL
226
                )
227
            ));
228
        });
229
    }
230
231
    /**
232
     * Given a version in semver (MAJOR.MINOR.PATCH),
233
     * return the minor version (MAJOR.MINOR)
234
     */
235
    protected function getMinorReleaseVersion(string $version): string
236
    {
237
        $versionParts = explode('.', $version);
238
        return $versionParts[0] . '.' . $versionParts[1];
239
    }
240
241
    /**
242
     * Plugin initialization, executed on hook "plugins_loaded"
243
     * to wait for all extensions to be loaded
244
     *
245
     * @return void
246
     */
247
    public function initialize(): void
248
    {
249
        /**
250
         * Watch out! If we are in the Modules page and enabling/disabling
251
         * a module, then already take that new state!
252
         * This is because `maybeProcessAction`, which is where modules are
253
         * enabled/disabled, must be executed before PluginConfiguration::initialize(),
254
         * which is where the plugin reads if a module is enabled/disabled as to
255
         * set the environment constants.
256
         *
257
         * This is mandatory, because only when it is enabled, can a module
258
         * have its state persisted when calling `flush_rewrite`
259
         */
260
        if (\is_admin()) {
261
            // We can't use the InstanceManager, since at this stage it hasn't
262
            // been initialized yet
263
            // We can create a new instances of ModulesMenuPage because
264
            // its instantiation produces no side-effects
265
            $modulesMenuPage = new ModulesMenuPage();
266
            if (isset($_GET['page']) && $_GET['page'] == $modulesMenuPage->getScreenID()) {
267
                // Instantiating ModuleListTableAction DOES have side-effects,
268
                // but they are needed, and won't be repeated when instantiating
269
                // the class through the Container Builder
270
                $moduleListTable = new ModuleListTableAction();
271
                $moduleListTable->maybeProcessAction();
272
            }
273
        }
274
275
        // Configure the plugin. This defines hooks to set environment variables,
276
        // so must be executed
277
        // before those hooks are triggered for first time
278
        // (in ComponentConfiguration classes)
279
        PluginConfiguration::initialize();
280
281
        // Component configuration
282
        $componentClassConfiguration = PluginConfiguration::getComponentClassConfiguration();
283
        $skipSchemaComponentClasses = PluginConfiguration::getSkippingSchemaComponentClasses();
284
285
        // Initialize the plugin's Component and, with it,
286
        // all its dependencies from PoP
287
        ComponentLoader::initializeComponents(
288
            [
289
                \GraphQLAPI\GraphQLAPI\Component::class,
290
            ],
291
            $componentClassConfiguration,
292
            $skipSchemaComponentClasses
293
        );
294
    }
295
296
    /**
297
     * Plugin initialization, executed on hook "plugins_loaded"
298
     * to wait for all extensions to be loaded
299
     */
300
    public function boot(): void
301
    {
302
        // Boot all PoP components, from this plugin and all extensions
303
        ComponentLoader::bootComponents();
304
305
        $instanceManager = InstanceManagerFacade::getInstance();
306
        $moduleRegistry = ModuleRegistryFacade::getInstance();
307
        /**
308
         * Initialize classes for the admin panel
309
         */
310
        if (\is_admin()) {
311
            /**
312
             * Initialize all the services
313
             */
314
            $menuServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\Admin\\Menus');
315
            foreach ($menuServiceClasses as $serviceClass) {
316
                /**
317
                 * @var AbstractMenu
318
                 */
319
                $service = $instanceManager->getInstance($serviceClass);
320
                $service->initialize();
321
            }
322
            $endpointResolverServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\Admin\\EndpointResolvers');
323
            foreach ($endpointResolverServiceClasses as $serviceClass) {
324
                /**
325
                 * @var AbstractEndpointResolver
326
                 */
327
                $service = $instanceManager->getInstance($serviceClass);
328
                $service->initialize();
329
            }
330
        }
331
332
        /**
333
         * Taxonomies must be initialized before Post Types
334
         */
335
        $taxonomyServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\Taxonomies');
336
        foreach ($taxonomyServiceClasses as $serviceClass) {
337
            /**
338
             * @var AbstractTaxonomy
339
             */
340
            $service = $instanceManager->getInstance($serviceClass);
341
            $service->initialize();
342
        }
343
        /**
344
         * Initialize Post Types manually to control in what order they are added to the menu
345
         */
346
        $postTypeServiceClassModules = [
347
            GraphQLEndpointPostType::class => EndpointFunctionalityModuleResolver::CUSTOM_ENDPOINTS,
348
            GraphQLPersistedQueryPostType::class => EndpointFunctionalityModuleResolver::PERSISTED_QUERIES,
349
            GraphQLSchemaConfigurationPostType::class => SchemaConfigurationFunctionalityModuleResolver::SCHEMA_CONFIGURATION,
350
            GraphQLAccessControlListPostType::class => AccessControlFunctionalityModuleResolver::ACCESS_CONTROL,
351
            GraphQLCacheControlListPostType::class => PerformanceFunctionalityModuleResolver::CACHE_CONTROL,
352
            GraphQLFieldDeprecationListPostType::class => VersioningFunctionalityModuleResolver::FIELD_DEPRECATION,
353
        ];
354
        foreach ($postTypeServiceClassModules as $serviceClass => $module) {
355
            // Check that the corresponding module is enabled
356
            if ($moduleRegistry->isModuleEnabled($module)) {
357
                /**
358
                 * @var AbstractPostType
359
                 */
360
                $service = $instanceManager->getInstance($serviceClass);
361
                $service->initialize();
362
            }
363
        }
364
        /**
365
         * Editor Scripts
366
         * They are all used to show the Welcome Guide
367
         */
368
        if ($moduleRegistry->isModuleEnabled(UserInterfaceFunctionalityModuleResolver::WELCOME_GUIDES)) {
369
            $editorScriptServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\EditorScripts');
370
            foreach ($editorScriptServiceClasses as $serviceClass) {
371
                /**
372
                 * @var AbstractEditorScript
373
                 */
374
                $service = $instanceManager->getInstance($serviceClass);
375
                $service->initialize();
376
            }
377
        }
378
        /**
379
         * Blocks
380
         * The GraphiQL Block may be overriden to GraphiQLWithExplorerBlock
381
         */
382
        $blockServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\Blocks', false);
383
        foreach ($blockServiceClasses as $serviceClass) {
384
            /**
385
             * @var AbstractBlock
386
             */
387
            $service = $instanceManager->getInstance($serviceClass);
388
            $service->initialize();
389
        }
390
        /**
391
         * Access Control Nested Blocks
392
         * Register them one by one, as to disable them if module is disabled
393
         */
394
        $accessControlRuleBlockServiceClassModules = [
395
            AccessControlDisableAccessBlock::class => AccessControlFunctionalityModuleResolver::ACCESS_CONTROL_RULE_DISABLE_ACCESS,
396
            AccessControlUserStateBlock::class => AccessControlFunctionalityModuleResolver::ACCESS_CONTROL_RULE_USER_STATE,
397
            AccessControlUserRolesBlock::class => AccessControlFunctionalityModuleResolver::ACCESS_CONTROL_RULE_USER_ROLES,
398
            AccessControlUserCapabilitiesBlock::class => AccessControlFunctionalityModuleResolver::ACCESS_CONTROL_RULE_USER_CAPABILITIES,
399
        ];
400
        foreach ($accessControlRuleBlockServiceClassModules as $serviceClass => $module) {
401
            if ($moduleRegistry->isModuleEnabled($module)) {
402
                /**
403
                 * @var AbstractBlock
404
                 */
405
                $service = $instanceManager->getInstance($serviceClass);
406
                $service->initialize();
407
            }
408
        }
409
        /**
410
         * Block categories
411
         */
412
        $blockCategoryServiceClasses = ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\BlockCategories');
413
        foreach ($blockCategoryServiceClasses as $serviceClass) {
414
            /**
415
             * @var AbstractBlockCategory
416
             */
417
            $service = $instanceManager->getInstance($serviceClass);
418
            $service->initialize();
419
        }
420
    }
421
422
    /**
423
     * Get permalinks to work when activating the plugin
424
     *
425
     * @see    https://codex.wordpress.org/Function_Reference/register_post_type#Flushing_Rewrite_on_Activation
426
     * @return void
427
     */
428
    public function activate(): void
429
    {
430
        // Initialize the timestamp
431
        $userSettingsManager = UserSettingsManagerFacade::getInstance();
432
        $userSettingsManager->storeTimestamp();
433
    }
434
435
    /**
436
     * Remove permalinks when deactivating the plugin
437
     *
438
     * @see    https://developer.wordpress.org/plugins/plugin-basics/activation-deactivation-hooks/
439
     * @return void
440
     */
441
    public function deactivate(): void
442
    {
443
        // First, unregister the post type, so the rules are no longer in memory.
444
        $instanceManager = InstanceManagerFacade::getInstance();
445
        $postTypeObjects = array_map(
446
            function ($serviceClass) use ($instanceManager): AbstractPostType {
447
                /**
448
                 * @var AbstractPostType
449
                 */
450
                $postTypeObject = $instanceManager->getInstance($serviceClass);
451
                return $postTypeObject;
452
            },
453
            ContainerBuilderUtils::getServiceClassesUnderNamespace(__NAMESPACE__ . '\\PostTypes')
454
        );
455
        foreach ($postTypeObjects as $postTypeObject) {
456
            $postTypeObject->unregisterPostType();
457
        }
458
459
        // Then, clear the permalinks to remove the post type's rules from the database.
460
        \flush_rewrite_rules();
461
462
        // Remove the timestamp
463
        $userSettingsManager = UserSettingsManagerFacade::getInstance();
464
        $userSettingsManager->removeTimestamp();
465
    }
466
}
467