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 ( ed3c2e...98ec12 )
by Leonardo
03:45
created

Plugin   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 398
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 14
Bugs 1 Features 0
Metric Value
eloc 132
c 14
b 1
f 0
dl 0
loc 398
ccs 0
cts 123
cp 0
rs 10
wmc 30

7 Methods

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