Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.00% covered (success)
98.00%
98 / 100
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialListObjectsByType
98.00% covered (success)
98.00%
98 / 100
77.78% covered (warning)
77.78%
7 / 9
18
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDescription
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isListed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 userCanExecute
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getParameters
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
8
 execute
96.67% covered (success)
96.67%
29 / 30
0.00% covered (danger)
0.00%
0 / 1
2
 getHeaderTitle
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getHeaderForm
100.00% covered (success)
100.00%
42 / 42
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * WikiLambda Special:ListObjectsByType page
4 *
5 * @file
6 * @ingroup Extensions
7 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
8 * @license MIT
9 */
10
11namespace MediaWiki\Extension\WikiLambda\Special;
12
13use MediaWiki\Extension\WikiLambda\Fields\HTMLZTypeSelectField;
14use MediaWiki\Extension\WikiLambda\Pagers\BasicZObjectPager;
15use MediaWiki\Extension\WikiLambda\Registry\ZLangRegistry;
16use MediaWiki\Extension\WikiLambda\Registry\ZTypeRegistry;
17use MediaWiki\Extension\WikiLambda\WikiLambdaServices;
18use MediaWiki\Extension\WikiLambda\ZObjectStore;
19use MediaWiki\Extension\WikiLambda\ZObjectUtils;
20use MediaWiki\HTMLForm\HTMLForm;
21use MediaWiki\Language\LanguageFallback;
22use MediaWiki\SpecialPage\SpecialPage;
23use MediaWiki\User\User;
24
25class SpecialListObjectsByType extends SpecialPage {
26
27    private ZLangRegistry $langRegistry;
28
29    /**
30     * @param ZObjectStore $zObjectStore
31     * @param LanguageFallback $languageFallback
32     */
33    public function __construct(
34        private readonly ZObjectStore $zObjectStore,
35        private readonly LanguageFallback $languageFallback
36    ) {
37        parent::__construct( 'ListObjectsByType' );
38
39        // Non-injected items
40        $this->langRegistry = ZLangRegistry::singleton();
41    }
42
43    /**
44     * @inheritDoc
45     */
46    protected function getGroupName() {
47        // Triggers use of message specialpages-group-wikilambda
48        return 'wikilambda';
49    }
50
51    /**
52     * @inheritDoc
53     */
54    public function getDescription() {
55        return $this->msg( 'wikilambda-special-objectsbytype' );
56    }
57
58    /** @inheritDoc */
59    public function isListed() {
60        // No usage allowed on client-mode wikis.
61        return WikiLambdaServices::getMode()->isRepo();
62    }
63
64    /**
65     * @inheritDoc
66     *
67     * @param User $user
68     * @return bool
69     */
70    public function userCanExecute( User $user ) {
71        if ( !WikiLambdaServices::getMode()->isRepo() ) {
72            // No usage allowed on client-mode wikis.
73            return false;
74        }
75        return parent::userCanExecute( $user );
76    }
77
78    /**
79     * Get and validate SpecialPage parameters from subpage and url
80     *
81     * @param string $subpage
82     * @return array - type ZID, returnType ZID, orderby, bool excludePreDefined
83     */
84    private function getParameters( $subpage ) {
85        // Get type from subpage; overwrite with value from Request.
86        // * requestType can be empty string or valid/invalid string
87        // * subpage can be empty string or NULL or valid/invalid string
88        // Default: Z8/Function
89        $requestType = $this->getRequest()->getText( 'type' );
90        if ( $requestType && ZObjectUtils::isValidZObjectReference( $requestType ) ) {
91            $type = $requestType;
92        } else {
93            $type = $subpage && ZObjectUtils::isValidZObjectReference( $subpage ) ?
94                $subpage :
95                ZTypeRegistry::Z_FUNCTION;
96        }
97
98        // Get return type from Request.
99        // * user input can be empty string or valid/invalid string
100        // Default: Z1/ZObject, i.e. any ZObject
101        $requestReturnType = $this->getRequest()->getText( 'return_type' );
102        if ( $requestReturnType && ZObjectUtils::isValidZObjectReference( $requestReturnType ) ) {
103            $returnType = $requestReturnType;
104        } else {
105            $returnType = ZTypeRegistry::Z_OBJECT;
106        }
107
108        // Get orderby from Request.
109        // * user input can be empty string or valid/invalid string (name, latest, oldest)
110        // Default: 'name'
111        $requestOrderby = $this->getRequest()->getText( 'orderby' );
112        $orderby = in_array( $requestOrderby, BasicZObjectPager::ORDER_BY_OPTIONS ) ?
113            $requestOrderby :
114            BasicZObjectPager::ORDER_BY_NAME;
115
116        $excludePreDefined = $this->getRequest()->getBool( 'excludePreDefined' );
117
118        return [ $type, $returnType, $orderby, $excludePreDefined ];
119    }
120
121    /**
122     * @inheritDoc
123     */
124    public function execute( $subpage ) {
125        if ( !$this->userCanExecute( $this->getUser() ) ) {
126            $this->displayRestrictionError();
127        }
128
129        // Get and validate page parameters
130        [ $type, $returnType, $orderby, $excludePreDefined ] = $this->getParameters( $subpage );
131
132        // Set headers
133        $this->setHeaders();
134        $this->outputHeader( 'wikilambda-special-objectsbytype-summary' );
135
136        // Set output
137        $output = $this->getOutput();
138        $output->enableOOUI();
139        $output->addModuleStyles( [ 'mediawiki.special' ] );
140
141        $this->addHelpLink( 'Help:Wikifunctions/Objects by type' );
142
143        // Get list of fallback language Zids
144        $languageZids = $this->langRegistry->getListOfFallbackLanguageZids(
145            $this->languageFallback,
146            $this->getLanguage()->getCode()
147        );
148
149        // Build BasicZObjectPager for the given filters
150        $filters = [
151            'type' => $type,
152            'return_type' => $returnType
153        ];
154        $pager = new BasicZObjectPager(
155            $this->getContext(),
156            $this->zObjectStore,
157            $languageZids,
158            $orderby,
159            $excludePreDefined,
160            $filters
161        );
162
163        // Add the header form
164        $output->addHTML( $this->getHeaderForm( $type, $returnType, $orderby ) );
165
166        // Add the top pagination controls
167        $output->addHTML( $pager->getNavigationBar() );
168        // Add the item list body
169        $output->addWikiTextAsInterface( $pager->getBody() );
170        // Add the bottom pagination controls
171        $output->addHTML( $pager->getNavigationBar() );
172
173        // Add bottom pagination controls
174        $output->addWikiTextAsInterface( $pager->getBottomLinks() );
175    }
176
177    /**
178     * Render the header for listing ZObjects by a specific type.
179     *
180     * @param string $typeZid - The type Zid of ZObjects being listed.
181     * @return string - The text for the header.
182     */
183    private function getHeaderTitle( $typeZid ) {
184        $typeLabel = $this->zObjectStore->fetchZObjectLabel( $typeZid, $this->getLanguage()->getCode() );
185        return $this->msg( 'wikilambda-special-objectsbytype-listheader' )
186            ->plaintextParams( $typeLabel ?? $typeZid, $typeZid )
187            ->text();
188    }
189
190    /**
191     * Build the form to place at the head of the Special page,
192     * with a selector field for ZTypes and another for ZLanguages.
193     *
194     * @param string $typeZid
195     * @param string $returnType
196     * @param string $orderby
197     * @return string
198     */
199    public function getHeaderForm( string $typeZid, string $returnType, string $orderby ) {
200        $formHeader = $this->getHeaderTitle( $typeZid );
201        $formDescriptor = [
202            'type' => [
203                'label' => $this->msg( 'wikilambda-special-objectsbytype-form-type' )->text(),
204                'class' => HTMLZTypeSelectField::class,
205                'name' => 'type',
206                'default' => $typeZid
207            ],
208            'return_type' => [
209                'label' => $this->msg( 'wikilambda-special-objectsbytype-form-returntype' )->text(),
210                'class' => HTMLZTypeSelectField::class,
211                'name' => 'return_type',
212                'allowsZ1' => true,
213                'default' => $returnType,
214                'hide-if' => [ '!==', 'type', ZTypeRegistry::Z_FUNCTION ],
215            ],
216            'orderby' => [
217                'label' => $this->msg( 'wikilambda-special-objectsbytype-form-orderby' )->text(),
218                'type' => 'select',
219                'name' => 'orderby',
220                'options' => [
221                    $this->msg( 'wikilambda-special-objectsbytype-form-orderby-name' )
222                        ->escaped() => BasicZObjectPager::ORDER_BY_NAME,
223                    $this->msg( 'wikilambda-special-objectsbytype-form-orderby-latest' )
224                        ->escaped() => BasicZObjectPager::ORDER_BY_LATEST,
225                    $this->msg( 'wikilambda-special-objectsbytype-form-orderby-oldest' )
226                        ->escaped() => BasicZObjectPager::ORDER_BY_OLDEST
227                ],
228                'default' => $orderby
229            ],
230            'excludePreDefined' => [
231                'type' => 'check',
232                'label' => $this->msg( 'wikilambda-special-objectsbytype-form-excludepredefined' )->text(),
233                'name' => 'excludePreDefined',
234                'default' => false
235            ]
236        ];
237
238        $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
239            ->setWrapperLegend( $formHeader )
240            ->setCollapsibleOptions( false )
241            ->setMethod( 'get' );
242        return $htmlForm->prepareForm()->getHTML( false );
243    }
244}