Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
82.09% covered (warning)
82.09%
55 / 67
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialAbuseFilter
82.09% covered (warning)
82.09%
55 / 67
60.00% covered (warning)
60.00%
3 / 5
32.50
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
 doesWrites
n/a
0 / 0
n/a
0 / 0
1
 getGroupName
n/a
0 / 0
n/a
0 / 0
1
 execute
50.00% covered (danger)
50.00%
11 / 22
0.00% covered (danger)
0.00%
0 / 1
2.50
 instantiateView
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getViewClassAndPageType
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
1 / 1
21
 getTitleForSubpage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\AbuseFilter\Special;
4
5use MediaWiki\Extension\AbuseFilter\AbuseFilterPermissionManager;
6use MediaWiki\Extension\AbuseFilter\CentralDBManager;
7use MediaWiki\Extension\AbuseFilter\Consequences\ConsequencesFactory;
8use MediaWiki\Extension\AbuseFilter\Consequences\ConsequencesRegistry;
9use MediaWiki\Extension\AbuseFilter\EditBox\EditBoxBuilderFactory;
10use MediaWiki\Extension\AbuseFilter\FilterImporter;
11use MediaWiki\Extension\AbuseFilter\FilterLookup;
12use MediaWiki\Extension\AbuseFilter\FilterProfiler;
13use MediaWiki\Extension\AbuseFilter\FilterStore;
14use MediaWiki\Extension\AbuseFilter\Parser\RuleCheckerFactory;
15use MediaWiki\Extension\AbuseFilter\SpecsFormatter;
16use MediaWiki\Extension\AbuseFilter\VariableGenerator\VariableGeneratorFactory;
17use MediaWiki\Extension\AbuseFilter\Variables\VariablesBlobStore;
18use MediaWiki\Extension\AbuseFilter\Variables\VariablesFormatter;
19use MediaWiki\Extension\AbuseFilter\Variables\VariablesManager;
20use MediaWiki\Extension\AbuseFilter\View\AbuseFilterView;
21use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewDiff;
22use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewEdit;
23use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewExamine;
24use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewHistory;
25use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewImport;
26use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewList;
27use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewRevert;
28use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewTestBatch;
29use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewTools;
30use MediaWiki\Html\Html;
31use MediaWiki\Title\Title;
32use Wikimedia\ObjectFactory\ObjectFactory;
33
34class SpecialAbuseFilter extends AbuseFilterSpecialPage {
35
36    private const PAGE_NAME = 'AbuseFilter';
37
38    /**
39     * @var ObjectFactory
40     */
41    private $objectFactory;
42
43    private const SERVICES_PER_VIEW = [
44        AbuseFilterViewDiff::class => [
45            AbuseFilterPermissionManager::SERVICE_NAME,
46            SpecsFormatter::SERVICE_NAME,
47            FilterLookup::SERVICE_NAME,
48        ],
49        AbuseFilterViewEdit::class => [
50            'DBLoadBalancerFactory',
51            'PermissionManager',
52            AbuseFilterPermissionManager::SERVICE_NAME,
53            FilterProfiler::SERVICE_NAME,
54            FilterLookup::SERVICE_NAME,
55            FilterImporter::SERVICE_NAME,
56            FilterStore::SERVICE_NAME,
57            EditBoxBuilderFactory::SERVICE_NAME,
58            ConsequencesRegistry::SERVICE_NAME,
59            SpecsFormatter::SERVICE_NAME,
60        ],
61        AbuseFilterViewExamine::class => [
62            'DBLoadBalancerFactory',
63            AbuseFilterPermissionManager::SERVICE_NAME,
64            FilterLookup::SERVICE_NAME,
65            EditBoxBuilderFactory::SERVICE_NAME,
66            VariablesBlobStore::SERVICE_NAME,
67            VariablesFormatter::SERVICE_NAME,
68            VariablesManager::SERVICE_NAME,
69            VariableGeneratorFactory::SERVICE_NAME,
70        ],
71        AbuseFilterViewHistory::class => [
72            'UserNameUtils',
73            'LinkBatchFactory',
74            AbuseFilterPermissionManager::SERVICE_NAME,
75            FilterLookup::SERVICE_NAME,
76            SpecsFormatter::SERVICE_NAME,
77        ],
78        AbuseFilterViewImport::class => [
79            AbuseFilterPermissionManager::SERVICE_NAME,
80        ],
81        AbuseFilterViewList::class => [
82            'LinkBatchFactory',
83            'ConnectionProvider',
84            AbuseFilterPermissionManager::SERVICE_NAME,
85            FilterProfiler::SERVICE_NAME,
86            SpecsFormatter::SERVICE_NAME,
87            CentralDBManager::SERVICE_NAME,
88        ],
89        AbuseFilterViewRevert::class => [
90            'DBLoadBalancerFactory',
91            'UserFactory',
92            AbuseFilterPermissionManager::SERVICE_NAME,
93            FilterLookup::SERVICE_NAME,
94            ConsequencesFactory::SERVICE_NAME,
95            VariablesBlobStore::SERVICE_NAME,
96            SpecsFormatter::SERVICE_NAME,
97        ],
98        AbuseFilterViewTestBatch::class => [
99            'DBLoadBalancerFactory',
100            AbuseFilterPermissionManager::SERVICE_NAME,
101            EditBoxBuilderFactory::SERVICE_NAME,
102            RuleCheckerFactory::SERVICE_NAME,
103            VariableGeneratorFactory::SERVICE_NAME,
104        ],
105        AbuseFilterViewTools::class => [
106            AbuseFilterPermissionManager::SERVICE_NAME,
107            EditBoxBuilderFactory::SERVICE_NAME,
108        ],
109    ];
110
111    /**
112     * @param AbuseFilterPermissionManager $afPermissionManager
113     * @param ObjectFactory $objectFactory
114     */
115    public function __construct(
116        AbuseFilterPermissionManager $afPermissionManager,
117        ObjectFactory $objectFactory
118    ) {
119        parent::__construct( self::PAGE_NAME, 'abusefilter-view', $afPermissionManager );
120        $this->objectFactory = $objectFactory;
121    }
122
123    /**
124     * @codeCoverageIgnore Merely declarative
125     * @inheritDoc
126     */
127    public function doesWrites() {
128        return true;
129    }
130
131    /**
132     * @codeCoverageIgnore Merely declarative
133     * @inheritDoc
134     */
135    protected function getGroupName() {
136        return 'wiki';
137    }
138
139    /**
140     * @param string|null $subpage
141     */
142    public function execute( $subpage ) {
143        $out = $this->getOutput();
144        $request = $this->getRequest();
145
146        $out->addModuleStyles( 'ext.abuseFilter' );
147
148        $this->setHeaders();
149        $this->addHelpLink( 'Extension:AbuseFilter' );
150
151        $this->checkPermissions();
152
153        if ( $request->getVal( 'result' ) === 'success' ) {
154            $out->setSubtitle( $this->msg( 'abusefilter-edit-done-subtitle' ) );
155            $changedFilter = intval( $request->getVal( 'changedfilter' ) );
156            $changeId = intval( $request->getVal( 'changeid' ) );
157            $out->addHTML( Html::successBox(
158                $this->msg(
159                    'abusefilter-edit-done',
160                    $changedFilter,
161                    $changeId,
162                    $this->getLanguage()->formatNum( $changedFilter )
163                )->parse()
164            ) );
165        }
166
167        [ $view, $pageType, $params ] = $this->getViewClassAndPageType( $subpage );
168
169        // Links at the top
170        $this->addNavigationLinks( $pageType );
171
172        $view = $this->instantiateView( $view, $params );
173        $view->show();
174    }
175
176    /**
177     * Instantiate the view class
178     *
179     * @suppress PhanTypeInvalidCallableArraySize
180     *
181     * @param class-string<AbuseFilterView> $viewClass
182     * @param array $params
183     * @return AbuseFilterView
184     */
185    public function instantiateView( string $viewClass, array $params ): AbuseFilterView {
186        return $this->objectFactory->createObject( [
187            'class' => $viewClass,
188            'services' => self::SERVICES_PER_VIEW[$viewClass],
189            'args' => [ $this->getContext(), $this->getLinkRenderer(), self::PAGE_NAME, $params ]
190        ] );
191    }
192
193    /**
194     * Determine the view class to instantiate
195     *
196     * @param string|null $subpage
197     * @return array A tuple of three elements:
198     *      - a subclass of AbuseFilterView
199     *      - type of page for addNavigationLinks
200     *      - array of parameters for the class
201     * @phan-return array{0:class-string,1:string,2:array}
202     */
203    public function getViewClassAndPageType( $subpage ): array {
204        // Filter by removing blanks.
205        $params = array_values( array_filter(
206            explode( '/', $subpage ?: '' ),
207            static function ( $value ) {
208                return $value !== '';
209            }
210        ) );
211
212        if ( $subpage === 'tools' ) {
213            return [ AbuseFilterViewTools::class, 'tools', [] ];
214        }
215
216        if ( $subpage === 'import' ) {
217            return [ AbuseFilterViewImport::class, 'import', [] ];
218        }
219
220        if ( is_numeric( $subpage ) || $subpage === 'new' ) {
221            return [
222                AbuseFilterViewEdit::class,
223                'edit',
224                [ 'filter' => is_numeric( $subpage ) ? (int)$subpage : null ]
225            ];
226        }
227
228        if ( $params ) {
229            if ( count( $params ) === 2 && $params[0] === 'revert' && is_numeric( $params[1] ) ) {
230                $params[1] = (int)$params[1];
231                return [ AbuseFilterViewRevert::class, 'revert', $params ];
232            }
233
234            if ( $params[0] === 'test' ) {
235                return [ AbuseFilterViewTestBatch::class, 'test', $params ];
236            }
237
238            if ( $params[0] === 'examine' ) {
239                return [ AbuseFilterViewExamine::class, 'examine', $params ];
240            }
241
242            if ( $params[0] === 'history' || $params[0] === 'log' ) {
243                if ( count( $params ) <= 2 ) {
244                    $params = isset( $params[1] ) ? [ 'filter' => (int)$params[1] ] : [];
245                    return [ AbuseFilterViewHistory::class, 'recentchanges', $params ];
246                }
247                if ( count( $params ) === 4 && $params[2] === 'item' ) {
248                    return [
249                        AbuseFilterViewEdit::class,
250                        '',
251                        [ 'filter' => (int)$params[1], 'history' => (int)$params[3] ]
252                    ];
253                }
254                if ( count( $params ) === 5 && $params[2] === 'diff' ) {
255                    // Special:AbuseFilter/history/<filter>/diff/<oldid>/<newid>
256                    return [ AbuseFilterViewDiff::class, '', $params ];
257                }
258            }
259        }
260
261        return [ AbuseFilterViewList::class, 'home', [] ];
262    }
263
264    /**
265     * Static variant to get the associated Title.
266     *
267     * @param string|int $subpage
268     * @return Title
269     */
270    public static function getTitleForSubpage( $subpage ): Title {
271        return self::getTitleFor( self::PAGE_NAME, $subpage );
272    }
273}