Issues (55)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

Plugin.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace GinoPane\BlogTaxonomy;
4
5
use Event;
6
use Input;
7
use Backend;
8
use Exception;
9
use Validator;
10
use System\Models\File;
11
use Backend\Widgets\Form;
12
use Backend\Widgets\Lists;
13
use Backend\Widgets\Filter;
14
use System\Classes\PluginBase;
15
use Backend\Classes\Controller;
16
use GinoPane\BlogTaxonomy\Models\Tag;
17
use GinoPane\BlogTaxonomy\Models\Series;
18
use Backend\Behaviors\RelationController;
19
use RainLab\Blog\Models\Post as PostModel;
20
use GinoPane\BlogTaxonomy\Models\Settings;
21
use GinoPane\BlogTaxonomy\Models\PostType;
22
use GinoPane\BlogTaxonomy\Components\TagList;
23
use GinoPane\BlogTaxonomy\Components\TagPosts;
24
use GinoPane\BlogTaxonomy\Components\SeriesList;
25
use GinoPane\BlogTaxonomy\Components\SeriesPosts;
26
use RainLab\Blog\Models\Category as CategoryModel;
27
use GinoPane\BlogTaxonomy\Components\RelatedPosts;
28
use GinoPane\BlogTaxonomy\Components\RelatedSeries;
29
use GinoPane\BlogTaxonomy\Console\MigrateFromPlugin;
30
use RainLab\Blog\Controllers\Posts as PostsController;
31
use GinoPane\BlogTaxonomy\Components\SeriesNavigation;
32
use RainLab\Blog\Controllers\Categories as CategoriesController;
33
34
/**
35
 * Class Plugin
36
 *
37
 * @package GinoPane\BlogTaxonomy
38
 */
39
class Plugin extends PluginBase
40
{
41
    const LOCALIZATION_KEY = 'ginopane.blogtaxonomy::lang.';
42
43
    const DIRECTORY_KEY = 'ginopane/blogtaxonomy';
44
45
    const REQUIRED_PLUGIN_RAINLAB_BLOG = 'RainLab.Blog';
46
47
    const DEFAULT_ICON = 'icon-sitemap';
48
49
    /**
50
     * @var array   Require the RainLab.Blog plugin
51
     */
52
    public $require = [
53
        'RainLab.Blog'
54
    ];
55
56
    /**
57
     * @var Settings
58
     */
59
    private $settings;
60
61
    /**
62
     * Returns information about this plugin
63
     *
64
     * @return  array
65
     */
66
    public function pluginDetails(): array
67
    {
68
        return [
69
            'name'        => self::LOCALIZATION_KEY . 'plugin.name',
70
            'description' => self::LOCALIZATION_KEY . 'plugin.description',
71
            'author'      => 'Siarhei <Gino Pane> Karavai',
72
            'icon'        => self::DEFAULT_ICON,
73
            'homepage'    => 'https://github.com/GinoPane/oc-blogtaxonomy-plugin'
74
        ];
75
    }
76
77
    /**
78
     * Register components
79
     *
80
     * @return  array
81
     */
82
    public function registerComponents(): array
83
    {
84
        return [
85
            TagList::class          => TagList::NAME,
86
            TagPosts::class         => TagPosts::NAME,
87
            RelatedPosts::class     => RelatedPosts::NAME,
88
            SeriesList::class       => SeriesList::NAME,
89
            SeriesPosts::class      => SeriesPosts::NAME,
90
            SeriesNavigation::class => SeriesNavigation::NAME,
91
            RelatedSeries::class    => RelatedSeries::NAME
92
        ];
93
    }
94
95
    public function register()
96
    {
97
        $this->registerConsoleCommand(MigrateFromPlugin::NAME, MigrateFromPlugin::class);
98
    }
99
100
    /**
101
     * Boot method, called right before the request route
102
     */
103
    public function boot(): void
104
    {
105
        $this->extendValidator();
106
107
        $this->extendPostModel();
108
109
        $this->extendPostListColumns();
110
111
        $this->extendPostFilterScopes();
112
113
        $this->extendPostsController();
114
115
        $this->extendCategoriesModel();
116
117
        $this->extendCategoriesController();
118
119
        $this->extendCategoriesFormFields();
120
    }
121
122
    private function getSettings(): Settings
123
    {
124
        return $this->settings ?: $this->settings = Settings::instance();
125
    }
126
127
    /**
128
     * Register plugin navigation
129
     * - add tags and series menu items
130
     *
131
     * @return void
132
     */
133
    public function registerNavigation(): void
134
    {
135
        // Extend the navigation
136
        Event::listen('backend.menu.extendItems', function ($manager) {
137
            $manager->addSideMenuItems(self::REQUIRED_PLUGIN_RAINLAB_BLOG, 'blog', [
138
                'series' => [
139
                    'label' => self::LOCALIZATION_KEY . 'navigation.sidebar.series',
140
                    'icon' => 'icon-list-alt',
141
                    'code' => 'series',
142
                    'owner' => self::REQUIRED_PLUGIN_RAINLAB_BLOG,
143
                    'url' => Backend::url(self::DIRECTORY_KEY . '/series')
144
                ],
145
146
                'tags' => [
147
                    'label' => self::LOCALIZATION_KEY . 'navigation.sidebar.tags',
148
                    'icon'  => 'icon-tags',
149
                    'code'  => 'tags',
150
                    'owner' => self::REQUIRED_PLUGIN_RAINLAB_BLOG,
151
                    'url'   => Backend::url(self::DIRECTORY_KEY . '/tags')
152
                ]
153
            ]);
154
155
            if ($this->getSettings()->postTypesEnabled()) {
156
                $manager->addSideMenuItems(self::REQUIRED_PLUGIN_RAINLAB_BLOG, 'blog', [
157
                    'post_types' => [
158
                        'label' => self::LOCALIZATION_KEY . 'navigation.sidebar.post_types',
159
                        'icon'  => 'icon-cog',
160
                        'code'  => 'post_types',
161
                        'owner' => self::REQUIRED_PLUGIN_RAINLAB_BLOG,
162
                        'url'   => Backend::url(self::DIRECTORY_KEY . '/posttypes')
163
                    ]
164
                ]);
165
            }
166
        });
167
    }
168
169
    /**
170
     * Register plugin settings
171
     *
172
     * @return array
173
     */
174
    public function registerSettings(): array
175
    {
176
        return [
177
            'settings' => [
178
                'label'       => self::LOCALIZATION_KEY . 'plugin.name',
179
                'description' => self::LOCALIZATION_KEY . 'plugin.description',
180
                'icon'        => self::DEFAULT_ICON,
181
                'class'       => Settings::class,
182
                'order'       => 100
183
            ]
184
        ];
185
    }
186
187
    /**
188
     * Extend RainLab Post model
189
     * - add tags relation
190
     * - add series relation
191
     * - add post type relation
192
     *
193
     * @return void
194
     */
195
    private function extendPostModel(): void
196
    {
197
        PostModel::extend(function ($model) {
198
            $model->morphToMany = [
199
                'tags' => [Tag::class, 'name' => Tag::PIVOT_COLUMN]
200
            ];
201
202
            $model->belongsTo['series'] = [
203
                Series::class,
204
                'key' => Series::TABLE_NAME . "_id"
205
            ];
206
207
            if ($this->getSettings()->postTypesEnabled()) {
208
                $model->belongsTo['post_type'] = [
209
                    PostType::class,
210
                    'key' => PostType::TABLE_NAME . "_id"
211
                ];
212
213
                $model->addJsonable(PostType::TABLE_NAME. '_attributes');
214
215
                $model->addDynamicMethod('typeAttributes', function () use ($model) {
216
                    if (!empty($model->post_type->id)) {
217
                        $rawFields = $model->{PostType::TABLE_NAME. '_attributes'}[0] ?? [];
218
                        $prefix = $model->post_type->id.'.';
219
                        $fields = [];
220
221
                        foreach ($rawFields as $code => $value) {
222
                            if (strpos($code, $prefix) === 0) {
223
                                $fields[str_replace($prefix, '', $code)] = $value;
224
                            }
225
                        }
226
227
                        return $fields;
228
                    }
229
230
                    return [];
231
                });
232
233
                $model->addDynamicMethod('typeAttribute', function (string $code) use ($model) {
234
                    if (!empty($model->post_type->id)) {
235
                        $attributeKey = sprintf('%s.%s', $model->post_type->id, $code);
236
237
                        return $model->{PostType::TABLE_NAME. '_attributes'}[0][$attributeKey] ?? null;
238
                    }
239
240
                    return $model->post_type->id;
241
                });
242
243
                $model->addDynamicMethod('scopeFilterPostTypes', function ($query, array $types) {
244
                    return $query->whereHas('post_type', function ($query) use ($types) {
245
                        $query->whereIn('id', $types);
246
                    });
247
                });
248
            }
249
        });
250
    }
251
252
    /**
253
     * Extends post controller functionality
254
     * - transform categories into taglist and move then into taxonomy tab
255
     * - add tags and series properties
256
     *
257
     * @throws Exception
258
     *
259
     * @return void
260
     */
261
    private function extendPostsController(): void
262
    {
263
        PostsController::extendFormFields(function (Form $form, $model) {
264
            if (!$model instanceof PostModel) {
0 ignored issues
show
The class RainLab\Blog\Models\Post does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
265
                return;
266
            }
267
268
            /*
269
             * When extending the form, you should check to see if $formWidget->isNested === false
270
             * as the Repeater FormWidget includes nested Form widgets which can cause your changes
271
             * to be made in unexpected places.
272
             *
273
             * @link https://octobercms.com/docs/plugin/extending#extending-backend-form
274
             */
275
            if (!empty($form->isNested)) {
276
                return;
277
            }
278
279
            $tab = self::LOCALIZATION_KEY . 'navigation.tab.taxonomy';
280
281
            $categoriesConfig = $this->transformPostCategoriesIntoTaglist($form, $tab);
282
283
            $form->addSecondaryTabFields([
284
                'categories' => $categoriesConfig,
285
                'tags' => [
286
                    'label' => self::LOCALIZATION_KEY . 'form.tags.label',
287
                    'comment' => self::LOCALIZATION_KEY . 'form.tags.comment_post',
288
                    'mode' => 'relation',
289
                    'tab' => $tab,
290
                    'type' => 'taglist',
291
                    'placeholder' => self::LOCALIZATION_KEY . 'placeholders.tags',
292
                ],
293
                'series' => [
294
                    'label' => self::LOCALIZATION_KEY . 'form.series.label',
295
                    'tab' => $tab,
296
                    'type' => 'relation',
297
                    'nameFrom' => 'title',
298
                    'comment' => self::LOCALIZATION_KEY . 'form.series.comment',
299
                    'placeholder' => self::LOCALIZATION_KEY . 'placeholders.series'
300
                ],
301
            ]);
302
303
            if ($this->getSettings()->postTypesEnabled()) {
304
                $this->addPostTypeAttributes($form, $model);
305
            }
306
        });
307
    }
308
309
    /**
310
     * Extends categories controller functionality
311
     */
312
    private function extendCategoriesController(): void
313
    {
314
        CategoriesController::extend(function (Controller $controller) {
315
            $controller->implement[] = RelationController::class;
316
            $relationConfig = '$/' . self::DIRECTORY_KEY . '/controllers/category/config_relation.yaml';
317
318
            if (property_exists($controller, 'relationConfig')) {
319
                $controller->relationConfig = $controller->mergeConfig(
320
                    $controller->relationConfig,
321
                    $relationConfig
322
                );
323
            } else {
324
                $controller->addDynamicProperty('relationConfig', $relationConfig);
325
            }
326
327
            $formConfig = '$/' . self::DIRECTORY_KEY . '/controllers/category/config_form.yaml';
328
329
            if (property_exists($controller, 'formConfig')) {
330
                $controller->formConfig = $controller->mergeConfig(
331
                    $controller->formConfig,
332
                    $formConfig
333
                );
334
            } else {
335
                $controller->addDynamicProperty('formConfig', $formConfig);
336
            }
337
        });
338
    }
339
340
    private function extendCategoriesModel(): void
341
    {
342
        CategoryModel::extend(function ($model) {
343
            if ($this->getSettings()->postCategoriesCoverImageEnabled()) {
344
                $model->attachOne['cover_image'] = [
345
                    File::class, 'delete' => true
346
                ];
347
            }
348
349
            if ($this->getSettings()->postCategoriesFeaturedImagesEnabled()) {
350
                $model->attachMany['featured_images'] = [
351
                    File::class, 'order' => 'sort_order', 'delete' => true
352
                ];
353
            }
354
        });
355
    }
356
357
    private function extendCategoriesFormFields(): void
358
    {
359
        CategoriesController::extendFormFields(function ($form, $model) {
360
            if (!$model instanceof CategoryModel) {
0 ignored issues
show
The class RainLab\Blog\Models\Category does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
361
                return;
362
            }
363
364
            if ($this->getSettings()->postCategoriesCoverImageEnabled() ||
365
                $this->getSettings()->postCategoriesFeaturedImagesEnabled()
366
            ) {
367
                $form->addFields([
368
                    'images_section' => [
369
                        'label' => self::LOCALIZATION_KEY . 'form.categories.images_section',
370
                        'type' => 'section',
371
                        'comment' => self::LOCALIZATION_KEY . 'form.categories.images_section_comment'
372
                    ]
373
                ]);
374
            }
375
376 View Code Duplication
            if ($this->getSettings()->postCategoriesCoverImageEnabled()) {
377
                $form->addFields([
378
                    'cover_image' => [
379
                        'label'     => self::LOCALIZATION_KEY . 'form.fields.cover_image',
380
                        'type'      => 'fileupload',
381
                        'mode'      => 'image',
382
                        'tab'       => 'Images',
383
                        'span'      => 'left'
384
                    ]
385
                ]);
386
            }
387
388 View Code Duplication
            if ($this->getSettings()->postCategoriesFeaturedImagesEnabled()) {
389
                $form->addFields([
390
                    'featured_images' => [
391
                        'label'     => self::LOCALIZATION_KEY . 'form.fields.featured_images',
392
                        'type'      => 'fileupload',
393
                        'mode'      => 'image',
394
                        'tab'       => 'Images'
395
                    ]
396
                ]);
397
            }
398
        });
399
    }
400
401
    private function extendValidator(): void
402
    {
403
        if ($this->getSettings()->postTypesEnabled()) {
404
            Validator::extend('unique_in_repeater', function ($attribute, $value, $parameters, $validator) {
405
                $attributeNameParts = explode('.', $attribute);
406
407
                $repeaterName = reset($attributeNameParts);
408
                $fieldName = end($attributeNameParts);
409
410
                $repeaterData = isset($validator->getData()[$repeaterName])
411
                    ? (array) $validator->getData()[$repeaterName]
412
                    : [];
413
414
                $fieldData = array_column($repeaterData, $fieldName);
415
416
                if (count(array_unique($fieldData)) !== count($fieldData)) {
417
                    return false;
418
                }
419
420
                return true;
421
            });
422
        }
423
    }
424
425
    private function transformPostCategoriesIntoTaglist(Form $form, string $tab)
426
    {
427
        $categoriesConfig = $form->getField('categories')->config;
428
        $categoriesConfig['tab'] = $tab;
429
        $categoriesConfig['mode'] = 'relation';
430
        $categoriesConfig['type'] = 'taglist';
431
        $categoriesConfig['label'] = 'rainlab.blog::lang.post.tab_categories';
432
        $categoriesConfig['comment'] = "rainlab.blog::lang.post.categories_comment";
433
        $categoriesConfig['placeholder'] = self::LOCALIZATION_KEY . 'placeholders.categories';
434
        unset($categoriesConfig['commentAbove']);
435
436
        $form->removeField('categories');
437
        return $categoriesConfig;
438
    }
439
440
    private function addPostTypeAttributes(Form $form, PostModel $model): void
441
    {
442
        $tab = self::LOCALIZATION_KEY . 'navigation.tab.type';
443
444
        $form->addSecondaryTabFields([
445
            'post_type' => [
446
                'label' => self::LOCALIZATION_KEY . 'form.post_types.label',
447
                'tab' => $tab,
448
                'type' => 'relation',
449
                'nameFrom' => 'name',
450
                'comment' => self::LOCALIZATION_KEY . 'form.post_types.comment',
451
                'placeholder' => self::LOCALIZATION_KEY . 'placeholders.post_types'
452
            ],
453
        ]);
454
455
        $condition = implode(
456
            array_map(
457
                static function ($value) {
458
                    return "[$value]";
459
                },
460
                PostType::all()->pluck('id')->toArray()
461
            )
462
        );
463
464
        $typeAttributes = [
465
            'label' => self::LOCALIZATION_KEY . 'form.post_types.type_attributes',
466
            'commentAbove' => self::LOCALIZATION_KEY . 'form.post_types.type_attributes_comment',
467
            'type' => 'repeater',
468
            'minItems' => 1,
469
            // there's October bug related to maxItems option when you can add more than one record
470
            // though only one is expected. The backend won't allow to save this anyway
471
            // https://github.com/octobercms/october/issues/5533
472
            'maxItems' => 1,
473
            'dependsOn' => 'post_type',
474
            'trigger' => [
475
                'action' => 'show',
476
                'field' => 'post_type',
477
                'condition' => "value$condition"
478
            ],
479
            'sortable' => false,
480
            'style' => 'accordion',
481
            'tab' => $tab,
482
            'form' => [
483
                'fields' => []
484
            ]
485
        ];
486
487
        if ((($postTypeId = Input::get('Post.post_type')) !== null &&
488
                $postType = PostType::find($postTypeId))
489
            ||
490
            (!empty($model->id) && !empty($model->post_type->id) && $postType = $model->post_type)
491
        ) {
492
            if (!empty($postType->type_attributes)) {
493
                $fields = [];
494
495
                foreach ($postType->type_attributes as $typeAttribute) {
496
                    if (empty($typeAttribute['code'])) {
497
                        continue;
498
                    }
499
500
                    $field = [];
501
502
                    $type = $typeAttribute['type'] ?? 'text';
503
504
                    switch ($type) {
505
                        case 'file':
506
                        case 'image':
507
                            $field['type'] = 'mediafinder';
508
                            $field['mode'] = $type;
509
                            $field['imageWidth'] = 200;
510
                            break;
511
                        case 'dropdown':
512
                            $field['type'] = $type;
513
514
                            $options = array_map(static function ($value) {
515
                                return trim($value);
516
                            }, explode(',', $typeAttribute['dropdown_options'] ?? ''));
517
518
                            $field['options'] = $options;
519
520
                            break;
521
                        case 'text':
522
                        case 'textarea':
523
                            $field['type'] = $type;
524
                            break;
525
                        case 'datepicker':
526
                            $field['type'] = $type;
527
                            $field['mode'] = $typeAttribute['datepicker_mode'] ?? 'date';
528
529
                            break;
530
                    }
531
532
                    $field['label'] = $typeAttribute['name'] ?? '';
533
534
                    $fields[sprintf("%s.%s", $postType->id, $typeAttribute['code'])] = $field;
535
                }
536
537
                $typeAttributes['form']['fields'] = $fields;
538
            }
539
        }
540
541
        $form->addSecondaryTabFields([
542
            PostType::TABLE_NAME . '_attributes' => $typeAttributes
543
        ]);
544
    }
545
546
    private function extendPostListColumns(): void
547
    {
548
        Event::listen('backend.list.extendColumns', function (Lists $listWidget) {
549
            // Only for the Posts controller
550
            if (!$listWidget->getController() instanceof PostsController) {
0 ignored issues
show
The class RainLab\Blog\Controllers\Posts does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
551
                return;
552
            }
553
554
            // Only for the Post model
555
            if (!$listWidget->model instanceof PostModel) {
0 ignored issues
show
The class RainLab\Blog\Models\Post does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
556
                return;
557
            }
558
559
            if ($this->getSettings()->postTypesEnabled()) {
560
                $listWidget->addColumns([
561
                    'type' => [
562
                        'label' => self::LOCALIZATION_KEY . 'form.post_types.post_list_column',
563
                        'relation' => 'post_type',
564
                        'select' => 'name',
565
                        'searchable' => 'true',
566
                        'sortable' => true
567
                    ]
568
                ]);
569
            }
570
        });
571
    }
572
573
    private function extendPostFilterScopes(): void
574
    {
575
        Event::listen('backend.filter.extendScopes', function (Filter $filterWidget) {
576
            if ($this->getSettings()->postTypesEnabled()) {
577
                $filterWidget->addScopes([
578
                    'type' => [
579
                        'label' => self::LOCALIZATION_KEY . 'form.post_types.post_list_filter_scope',
580
                        'modelClass' => PostType::class,
581
                        'nameFrom' => 'name',
582
                        'scope' => 'filterPostTypes'
583
                    ]
584
                ]);
585
            }
586
        });
587
    }
588
}
589