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.

AbstractPostType::maybeAddExcerptAsDescription()   A
last analyzed

Complexity

Conditions 6
Paths 4

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 15
nc 4
nop 1
dl 0
loc 28
ccs 0
cts 9
cp 0
crap 42
rs 9.2222
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQLAPI\GraphQLAPI\PostTypes;
6
7
use WP_Post;
8
use GraphQLAPI\GraphQLAPI\ConditionalOnEnvironment\Admin\Services\Menus\Menu;
9
use GraphQLAPI\GraphQLAPI\General\CPTUtils;
10
use PoP\ComponentModel\State\ApplicationState;
11
use GraphQLAPI\GraphQLAPI\Security\UserAuthorization;
12
use GraphQLAPI\GraphQLAPI\Facades\ModuleRegistryFacade;
13
use GraphQLAPI\GraphQLAPI\Facades\UserSettingsManagerFacade;
14
use GraphQLAPI\GraphQLAPI\ModuleResolvers\EndpointFunctionalityModuleResolver;
15
use GraphQLAPI\GraphQLAPI\ModuleResolvers\UserInterfaceFunctionalityModuleResolver;
16
17
abstract class AbstractPostType
18
{
19
    /**
20
     * Add the hook to initialize the different post types
21
     *
22
     * @return void
23
     */
24
    public function initialize(): void
25
    {
26
        $postType = $this->getPostType();
27
        \add_action(
28
            'init',
29
            [$this, 'initPostType']
30
        );
31
        \add_action(
32
            'init',
33
            [$this, 'maybeLockGutenbergTemplate']
34
        );
35
        \add_filter(
36
            'allowed_block_types',
37
            [$this, 'allowGutenbergBlocksForCustomPostType'],
38
            10,
39
            2
40
        );
41
42
        /**
43
         * Add the excerpt, which is the description of the
44
         * different CPTs (GraphQL query/ACL/CCL)
45
         * */
46
        if ($this->isExcerptAsDescriptionEnabled() && $this->usePostExcerptAsDescription()) {
47
            // Execute last as to always add the description at the top
48
            \add_filter(
49
                'the_content',
50
                [$this, 'maybeAddExcerptAsDescription'],
51
                PHP_INT_MAX
52
            );
53
            // Add the custom columns to the post type
54
            add_filter(
55
                "manage_{$postType}_posts_columns",
56
                [$this, 'setTableColumns']
57
            );
58
            add_action(
59
                "manage_{$postType}_posts_custom_column",
60
                [$this, 'resolveCustomColumn'],
61
                10,
62
                2
63
            );
64
        }
65
66
        /**
67
         * Add extra actions to the CPT table.
68
         * If they are hierarchical, they use hook "page_row_actions"
69
         */
70
        \add_filter(
71
            'post_row_actions',
72
            [$this, 'maybeAddPostTypeTableActions'],
73
            10,
74
            2
75
        );
76
        \add_filter(
77
            'page_row_actions',
78
            [$this, 'maybeAddPostTypeTableActions'],
79
            10,
80
            2
81
        );
82
83
        /**
84
         * Regenerate the timestamp when saving this CPT
85
         */
86
        if ($this->regenerateTimestampOnSave()) {
87
            // Ideally: Only do it if the post is published,
88
            // or was published and had its status changed
89
            // Otherwise, editing and saving a draft would also
90
            // update the timestamp, and there's no need for that
91
            // since it's not added to the schema
92
            // However, the old status is not provided through this hook,
93
            // so just check it's not being automatically saved in the editor
94
            // ("auto-draft" status)
95
            // This is also saving "draft" to "draft" for which there's no need,
96
            // but can't avoid it
97
            \add_action(
98
                "save_post_{$postType}",
99
                function ($postID, $post): void {
100
                    if ($post->post_status != 'auto-draft') {
101
                        $userSettingsManager = UserSettingsManagerFacade::getInstance();
102
                        $userSettingsManager->storeTimestamp();
103
                    }
104
                },
105
                10,
106
                2
107
            );
108
        }
109
    }
110
111
    /**
112
     * Indicate if, whenever this CPT is created/updated,
113
     * the timestamp must be regenerated
114
     *
115
     * @return boolean
116
     */
117
    protected function regenerateTimestampOnSave(): bool
118
    {
119
        return false;
120
    }
121
122
    /**
123
     * @param array<string, string> $columns
124
     * @return array<string, string>
125
     */
126
    public function setTableColumns(array $columns): array
127
    {
128
        // Add the description column after the title
129
        $titlePos = array_search('title', array_keys($columns));
130
        return array_merge(
131
            array_slice(
132
                $columns,
133
                0,
134
                $titlePos + 1,
135
                true
136
            ),
137
            [
138
                'description' => \__('Description', 'graphql-api'),
139
            ],
140
            array_slice(
141
                $columns,
142
                $titlePos + 1,
143
                null,
144
                true
145
            )
146
        );
147
    }
148
149
    // Add the data to the custom columns for the book post type:
150
    public function resolveCustomColumn(string $column, int $post_id): void
151
    {
152
        switch ($column) {
153
            case 'description':
154
                /**
155
                 * @var WP_Post|null
156
                 */
157
                $post = \get_post($post_id);
158
                if (!is_null($post)) {
159
                    echo CPTUtils::getCustomPostDescription($post);
0 ignored issues
show
Bug introduced by
It seems like $post can also be of type array; however, parameter $post of GraphQLAPI\GraphQLAPI\Ge...CustomPostDescription() does only seem to accept WP_Post, 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

159
                    echo CPTUtils::getCustomPostDescription(/** @scrutinizer ignore-type */ $post);
Loading history...
160
                }
161
                break;
162
        }
163
    }
164
165
    /**
166
     * Add extra actions to the Custom Post Type list
167
     *
168
     * @see https://developer.wordpress.org/reference/hooks/post_row_actions/
169
     * @param array<string, string> $actions
170
     * @param WP_Post $post
171
     * @return array<string, string>
172
     */
173
    public function maybeAddPostTypeTableActions(array $actions, $post): array
174
    {
175
        if ($post->post_type == $this->getPostType()) {
176
            $actions = \array_merge(
177
                $actions,
178
                $this->getPostTypeTableActions($post)
179
            );
180
        }
181
        return $actions;
182
    }
183
184
    /**
185
     * Get actions to add for this CPT
186
     *
187
     * @param WP_Post $post
188
     * @return array<string, string>
189
     */
190
    protected function getPostTypeTableActions($post): array
191
    {
192
        return [];
193
    }
194
195
    /**
196
     * Indicate if the excerpt must be used as the CPT's description and rendered when rendering the post
197
     *
198
     * @return boolean
199
     */
200
    public function usePostExcerptAsDescription(): bool
201
    {
202
        return false;
203
    }
204
205
    /**
206
     * Block align class
207
     */
208
    public function getAlignClass(): string
209
    {
210
        return 'aligncenter';
211
    }
212
213
    /**
214
     * Render the excerpt as the description for the current CPT
215
     * Can enable/disable through environment variable
216
     */
217
    public function maybeAddExcerptAsDescription(string $content): string
218
    {
219
        $moduleRegistry = ModuleRegistryFacade::getInstance();
220
        /**
221
         * Check if it is enabled and it is this CPT...
222
         */
223
        if (
224
            $moduleRegistry->isModuleEnabled(UserInterfaceFunctionalityModuleResolver::EXCERPT_AS_DESCRIPTION)
225
            && UserAuthorization::canAccessSchemaEditor()
226
            && \is_singular($this->getPostType())
227
        ) {
228
            /**
229
             * Add the excerpt (if not empty) as description of the GraphQL query
230
             */
231
            $vars = ApplicationState::getVars();
232
            $customPost = $vars['routing-state']['queried-object'];
233
            // Make sure there is a post (eg: it has not been deleted)
234
            if ($customPost !== null) {
235
                if ($excerpt = CPTUtils::getCustomPostDescription($customPost)) {
236
                    $content = \sprintf(
237
                        \__('<p class="%s"><strong>Description: </strong>%s</p>'),
238
                        $this->getAlignClass(),
239
                        $excerpt
240
                    ) . $content;
241
                }
242
            }
243
        }
244
        return $content;
245
    }
246
247
    /**
248
     * Custom Post Type singular name
249
     *
250
     * @return string
251
     */
252
    abstract protected function getPostTypeName(): string;
253
    /**
254
     * Custom Post Type under which it will be registered
255
     * From documentation: Max. 20 characters and may only contain lowercase alphanumeric characters,
256
     * dashes, and underscores.
257
     * @see https://codex.wordpress.org/Function_Reference/register_post_type#Parameters
258
     *
259
     * @return string
260
     */
261
    protected function getPostType(): string
262
    {
263
        return strtolower(str_replace(' ', '-', $this->getPostTypeName()));
264
    }
265
    /**
266
     * Custom Post Type plural name
267
     *
268
     * @param bool $uppercase Indicate if the name must be uppercase (for starting a sentence) or, otherwise, lowercase
269
     * @return string
270
     */
271
    protected function getPostTypePluralNames(bool $uppercase): string
272
    {
273
        $postTypeName = $this->getPostTypeName();
274
        if ($uppercase) {
275
            return $postTypeName;
276
        }
277
        return strtolower($postTypeName);
278
    }
279
280
    /**
281
     * Indicate if to make the Custom Post Type public.
282
     * By default it's false because, for configuration CPTs (Access Control Lists,
283
     * Cache Control Lists, Schema Configuration, etc), this data is private,
284
     * must not be exposed.
285
     *
286
     * @return boolean
287
     */
288
    protected function isPublic(): bool
289
    {
290
        return false;
291
    }
292
293
    /**
294
     * Is the excerpt used as description for the CPT?
295
     *
296
     * @return bool
297
     */
298
    protected function isExcerptAsDescriptionEnabled(): bool
299
    {
300
        $moduleRegistry = ModuleRegistryFacade::getInstance();
301
        return $moduleRegistry->isModuleEnabled(UserInterfaceFunctionalityModuleResolver::EXCERPT_AS_DESCRIPTION);
302
    }
303
304
    /**
305
     * Is the API Hierarchy Module enabled?
306
     *
307
     * @return bool
308
     */
309
    protected function isAPIHierarchyModuleEnabled(): bool
310
    {
311
        $moduleRegistry = ModuleRegistryFacade::getInstance();
312
        return $moduleRegistry->isModuleEnabled(EndpointFunctionalityModuleResolver::API_HIERARCHY);
313
    }
314
315
    /**
316
     * Hierarchical
317
     *
318
     * @return bool
319
     */
320
    protected function isHierarchical(): bool
321
    {
322
        return false;
323
    }
324
325
    /**
326
     * Show in admin bar
327
     *
328
     * @return bool
329
     */
330
    protected function showInAdminBar(): bool
331
    {
332
        return false;
333
    }
334
335
    /**
336
     * If provided, rewrite the slug
337
     *
338
     * @return string|null
339
     */
340
    protected function getSlugBase(): ?string
341
    {
342
        return null;
343
    }
344
345
    /**
346
     * Arguments for registering the post type
347
     *
348
     * @return array<string, mixed>
349
     */
350
    protected function getPostTypeArgs(): array
351
    {
352
        $name_uc = $this->getPostTypeName();
353
        $names_uc = $this->getPostTypePluralNames(true);
354
        $names_lc = $this->getPostTypePluralNames(false);
355
        // Configuration CPTs are to configure data, and not to be directly accessible by themselves
356
        // Then, do not make them public, but still allow to access them
357
        // This way, executing query `{ posts(postTypes:["graphql-acl"]) }` will fail,
358
        // and we execute instead `{ accessControlLists }` which can be @validated
359
        $securityPostTypeArgs = array(
360
            'public' => $this->isPublic(),
361
            'show_in_nav_menus' => true,
362
            'show_ui' => true,
363
            'publicly_queryable' => true,
364
        );
365
        $canAccessSchemaEditor = UserAuthorization::canAccessSchemaEditor();
366
        $postTypeArgs = array_merge(
367
            $securityPostTypeArgs,
368
            array(
369
                'label' => $this->getPostTypeName(),
370
                'labels' => $this->getPostTypeLabels($name_uc, $names_uc, $names_lc),
371
                'capability_type' => $canAccessSchemaEditor ? 'post' : '',
372
                'hierarchical' => $this->isAPIHierarchyModuleEnabled() && $this->isHierarchical(),
373
                'exclude_from_search' => true,
374
                'show_in_admin_bar' => $this->showInAdminBar(),
375
                'show_in_menu' => $canAccessSchemaEditor ? Menu::getName() : false,
376
                'show_in_rest' => true,
377
                'supports' => [
378
                    'title',
379
                    'editor',
380
                    // 'author',
381
                    'revisions',
382
                    // 'custom-fields',
383
                ],
384
            )
385
        );
386
        if ($slugBase = $this->getSlugBase()) {
387
            $postTypeArgs['rewrite'] = ['slug' => $slugBase];
388
        }
389
        if ($taxonomies = $this->getTaxonomies()) {
390
            $postTypeArgs['taxonomies'] = $taxonomies;
391
        }
392
        if ($this->isAPIHierarchyModuleEnabled() && $this->isHierarchical()) {
393
            $postTypeArgs['supports'][] = 'page-attributes';
394
        }
395
        if ($this->isExcerptAsDescriptionEnabled() && $this->usePostExcerptAsDescription()) {
396
            $postTypeArgs['supports'][] = 'excerpt';
397
        }
398
        if ($template = $this->getGutenbergTemplate()) {
399
            $postTypeArgs['template'] = $template;
400
        }
401
        return $postTypeArgs;
402
    }
403
404
    /**
405
     * Labels for registering the post type
406
     *
407
     * @param string $name_uc Singular name uppercase
408
     * @param string $names_uc Plural name uppercase
409
     * @param string $names_lc Plural name lowercase
410
     * @return array<string, string>
411
     */
412
    protected function getPostTypeLabels(string $name_uc, string $names_uc, string $names_lc): array
413
    {
414
        return array(
415
            'name'               => $names_uc,
416
            'singular_name'      => $name_uc,
417
            'add_new'            => sprintf(\__('Add New %s', 'graphql-api'), $name_uc),
418
            'add_new_item'       => sprintf(\__('Add New %s', 'graphql-api'), $name_uc),
419
            'edit_item'          => sprintf(\__('Edit %s', 'graphql-api'), $name_uc),
420
            'new_item'           => sprintf(\__('New %s', 'graphql-api'), $name_uc),
421
            'all_items'          => $names_uc,//sprintf(\__('All %s', 'graphql-api'), $names_uc),
422
            'view_item'          => sprintf(\__('View %s', 'graphql-api'), $name_uc),
423
            'search_items'       => sprintf(\__('Search %s', 'graphql-api'), $names_uc),
424
            'not_found'          => sprintf(\__('No %s found', 'graphql-api'), $names_lc),
425
            'not_found_in_trash' => sprintf(\__('No %s found in Trash', 'graphql-api'), $names_lc),
426
            'parent_item_colon'  => sprintf(\__('Parent %s:', 'graphql-api'), $name_uc),
427
        );
428
    }
429
430
    /**
431
     * Initialize the post type
432
     *
433
     * @return void
434
     */
435
    public function initPostType(): void
436
    {
437
        $this->registerPostType();
438
    }
439
440
    /**
441
     * Register the post type
442
     *
443
     * @return void
444
     */
445
    public function registerPostType(): void
446
    {
447
        \register_post_type($this->getPostType(), $this->getPostTypeArgs());
448
    }
449
450
    /**
451
     * Unregister the post type
452
     *
453
     * @return void
454
     */
455
    public function unregisterPostType(): void
456
    {
457
        \unregister_post_type($this->getPostType());
458
    }
459
460
    /**
461
     * Taxonomies
462
     *
463
     * @return string[]
464
     */
465
    protected function getTaxonomies(): array
466
    {
467
        return [];
468
    }
469
470
    /**
471
     * Lock down the Custom Post Type to use the given Gutenberg templates
472
     *
473
     * @return void
474
     * @see https://developer.wordpress.org/block-editor/developers/block-api/block-templates/#locking
475
     */
476
    public function maybeLockGutenbergTemplate(): void
477
    {
478
        if (!empty($this->getGutenbergTemplate()) && $this->lockGutenbergTemplate()) {
479
            $post_type_object = \get_post_type_object($this->getPostType());
480
            if (!is_null($post_type_object)) {
481
                $post_type_object->template_lock = 'all';
482
            }
483
        }
484
    }
485
486
    /**
487
     * Restrict the Gutenberg blocks available for this Custom Post Type
488
     *
489
     * @param string[]|bool $allowedBlocks The list of blocks, or `true` for all of them
490
     * @return string[]|bool The list of blocks, or `true` for all of them
491
     */
492
    public function allowGutenbergBlocksForCustomPostType($allowedBlocks, WP_Post $post)
493
    {
494
        /**
495
         * Check it is this CPT
496
         */
497
        if ($post->post_type == $this->getPostType()) {
498
            if ($blocks = $this->getGutenbergBlocksForCustomPostType()) {
499
                return $blocks;
500
            }
501
        }
502
        return $allowedBlocks;
503
    }
504
505
    /**
506
     * Comment: this function below to remove block types doesn't work,
507
     * because some of the most basic ones, such as "core/paragraph",
508
     * are never registered using `register_block_types`, then they can't be obtained
509
     * from `\WP_Block_Type_Registry::get_instance()->get_all_registered()`,
510
     * and this information exists nowhere.
511
     *
512
     * As a consequence, I am currently disabling blocks by assigning them a category
513
     * (Eg: "Access Control for GraphiQL") which is not registered for other CPTs
514
     * Unluckily, this produces an error on JavaScript:
515
     * > The block "graphql-api/access-control" must have a registered category.
516
     * > The block "graphql-api/access-control-disable-access" must have a registered category.
517
     * > ...
518
     *
519
     * But at least it works
520
     */
521
    // /**
522
    //  * Restrict the Gutenberg blocks available for this Custom Post Type
523
    //  *
524
    //  * @param [type] $allowedBlocks
525
    //  * @param [type] $post
526
    //  * @return array
527
    //  */
528
    // public function allowGutenbergBlocksForCustomPostType($allowedBlocks, $post)
529
    // {
530
    //     if ($blocks = $this->getGutenbergBlocksForCustomPostType()) {
531
    //         /**
532
    //          * Check if it is this CPT
533
    //          */
534
    //         if ($post->post_type == $this->getPostType()) {
535
    //             return $blocks;
536
    //         } elseif ($this->removeGutenbergBlocksForOtherPostTypes($post)) {
537
    //             // Remove this CPT's blocks from other post types.
538
    //             // $allowedBlocks can be a boolean. In that case, retrieve all blocks types, and substract the blocks
539
    //             if (!is_array($allowedBlocks)) {
540
    //                 $blockTypes = \WP_Block_Type_Registry::get_instance()->get_all_registered();
541
    //                 $allowedBlocks = array_keys($blockTypes);
542
    //             }
543
    //             $allowedBlocks = array_values(array_diff(
544
    //                 $allowedBlocks,
545
    //                 $blocks
546
    //             ));
547
    //         }
548
    //     }
549
    //     return $allowedBlocks;
550
    // }
551
    // /**
552
    //  * Indicate if to not allow this CPT's blocks in other Custom Post Types
553
    //  *
554
    //  * @param [type] $post
555
    //  * @return boolean
556
    //  */
557
    // protected function removeGutenbergBlocksForOtherPostTypes($post): bool
558
    // {
559
    //     return true;
560
    // }
561
562
    /**
563
     * By default, if providing a template, then restrict the CPT to the blocks involved in the template
564
     *
565
     * @return string[] The list of block names
566
     */
567
    protected function getGutenbergBlocksForCustomPostType(): array
568
    {
569
        /**
570
         * If the CPT defined a template, then maybe restrict to those blocks
571
         */
572
        $template = $this->getGutenbergTemplate();
573
        if (!empty($template) && $this->enableOnlyGutenbergTemplateBlocks()) {
574
            // Get all the blocks involved in the template
575
            return array_values(array_unique(array_map(
576
                function (array $blockConfiguration) {
577
                    // The block is the first item from the $blockConfiguration
578
                    return $blockConfiguration[0];
579
                },
580
                $template
581
            )));
582
        }
583
        return [];
584
    }
585
586
    /**
587
     * Indicate if to restrict the blocks for the current post type to those involved in the template
588
     *
589
     * @return boolean `true` by default
590
     */
591
    protected function enableOnlyGutenbergTemplateBlocks(): bool
592
    {
593
        return true;
594
    }
595
596
    /**
597
     * Gutenberg templates to lock down the Custom Post Type to
598
     *
599
     * @return array<array> Every element is an array with template name in first pos, and attributes then
600
     */
601
    protected function getGutenbergTemplate(): array
602
    {
603
        return [];
604
    }
605
606
    /**
607
     * Indicates if to lock the Gutenberg templates
608
     *
609
     * @return boolean
610
     */
611
    protected function lockGutenbergTemplate(): bool
612
    {
613
        return false;
614
    }
615
}
616