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