Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
4.72% covered (danger)
4.72%
5 / 106
16.67% covered (danger)
16.67%
1 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbuseFilterView
4.72% covered (danger)
4.72%
5 / 106
16.67% covered (danger)
16.67%
1 / 6
267.00
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 show
n/a
0 / 0
n/a
0 / 0
0
 buildFilterLoader
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
2
 buildTestConditions
0.00% covered (danger)
0.00%
0 / 60
0.00% covered (danger)
0.00%
0 / 1
90
 buildVisibilityConditions
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 getLinkToLatestDiff
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\AbuseFilter\View;
4
5use ContextSource;
6use Flow\Data\Listener\RecentChangesListener;
7use IContextSource;
8use MediaWiki\Extension\AbuseFilter\AbuseFilterPermissionManager;
9use MediaWiki\Linker\LinkRenderer;
10use MediaWiki\Permissions\Authority;
11use MediaWiki\Revision\RevisionRecord;
12use MediaWiki\SpecialPage\SpecialPage;
13use MediaWiki\Title\Title;
14use OOUI;
15use RecentChange;
16use UnexpectedValueException;
17use Wikimedia\Rdbms\Platform\ISQLPlatform;
18use Xml;
19
20abstract class AbuseFilterView extends ContextSource {
21
22    /**
23     * @var AbuseFilterPermissionManager
24     */
25    protected $afPermManager;
26
27    /**
28     * @var array The parameters of the current request
29     */
30    protected $mParams;
31
32    /**
33     * @var LinkRenderer
34     */
35    protected $linkRenderer;
36
37    /** @var string */
38    protected $basePageName;
39
40    /**
41     * @param AbuseFilterPermissionManager $afPermManager
42     * @param IContextSource $context
43     * @param LinkRenderer $linkRenderer
44     * @param string $basePageName
45     * @param array $params
46     */
47    public function __construct(
48        AbuseFilterPermissionManager $afPermManager,
49        IContextSource $context,
50        LinkRenderer $linkRenderer,
51        string $basePageName,
52        array $params
53    ) {
54        $this->mParams = $params;
55        $this->setContext( $context );
56        $this->linkRenderer = $linkRenderer;
57        $this->basePageName = $basePageName;
58        $this->afPermManager = $afPermManager;
59    }
60
61    /**
62     * @param string|int $subpage
63     * @return Title
64     */
65    public function getTitle( $subpage = '' ) {
66        return SpecialPage::getTitleFor( $this->basePageName, $subpage );
67    }
68
69    /**
70     * Function to show the page
71     */
72    abstract public function show();
73
74    /**
75     * Build input and button for loading a filter
76     *
77     * @return string
78     */
79    public function buildFilterLoader() {
80        $loadText =
81            new OOUI\TextInputWidget(
82                [
83                    'type' => 'number',
84                    'name' => 'wpInsertFilter',
85                    'id' => 'mw-abusefilter-load-filter'
86                ]
87            );
88        $loadButton =
89            new OOUI\ButtonWidget(
90                [
91                    'label' => $this->msg( 'abusefilter-test-load' )->text(),
92                    'id' => 'mw-abusefilter-load'
93                ]
94            );
95        $loadGroup =
96            new OOUI\ActionFieldLayout(
97                $loadText,
98                $loadButton,
99                [
100                    'label' => $this->msg( 'abusefilter-test-load-filter' )->text()
101                ]
102            );
103        // CSS class for reducing default input field width
104        return Xml::tags(
105            'div',
106            [ 'class' => 'mw-abusefilter-load-filter-id' ],
107            $loadGroup
108        );
109    }
110
111    /**
112     * @param ISQLPlatform $db
113     * @param string|false $action 'edit', 'move', 'createaccount', 'delete' or false for all
114     * @return string
115     */
116    public function buildTestConditions( ISQLPlatform $db, $action = false ) {
117        $editSources = [
118            RecentChange::SRC_EDIT,
119            RecentChange::SRC_NEW,
120        ];
121        if ( in_array( 'flow', $this->getConfig()->get( 'AbuseFilterValidGroups' ), true ) ) {
122            // TODO Should this be separated somehow? Also, this case should be handled via a hook, not
123            // by special-casing Flow here.
124            // @phan-suppress-next-line PhanUndeclaredClassConstant Temporary solution
125            $editSources[] = RecentChangesListener::SRC_FLOW;
126        }
127        // If one of these is true, we're abusefilter compatible.
128        switch ( $action ) {
129            case 'edit':
130                return $db->makeList( [
131                    // Actually, this is only one condition, but this way we get it as string
132                    'rc_source' => $editSources
133                ], LIST_AND );
134            case 'move':
135                return $db->makeList( [
136                    'rc_source' => RecentChange::SRC_LOG,
137                    'rc_log_type' => 'move',
138                    'rc_log_action' => 'move'
139                ], LIST_AND );
140            case 'createaccount':
141                return $db->makeList( [
142                    'rc_source' => RecentChange::SRC_LOG,
143                    'rc_log_type' => 'newusers',
144                    'rc_log_action' => [ 'create', 'autocreate' ]
145                ], LIST_AND );
146            case 'delete':
147                return $db->makeList( [
148                    'rc_source' => RecentChange::SRC_LOG,
149                    'rc_log_type' => 'delete',
150                    'rc_log_action' => 'delete'
151                ], LIST_AND );
152            case 'upload':
153                return $db->makeList( [
154                    'rc_source' => RecentChange::SRC_LOG,
155                    'rc_log_type' => 'upload',
156                    'rc_log_action' => [ 'upload', 'overwrite', 'revert' ]
157                ], LIST_AND );
158            case false:
159                // Done later
160                break;
161            default:
162                throw new UnexpectedValueException( __METHOD__ . ' called with invalid action: ' . $action );
163        }
164
165        return $db->makeList( [
166            'rc_source' => $editSources,
167            $db->makeList( [
168                'rc_source' => RecentChange::SRC_LOG,
169                $db->makeList( [
170                    $db->makeList( [
171                        'rc_log_type' => 'move',
172                        'rc_log_action' => 'move'
173                    ], LIST_AND ),
174                    $db->makeList( [
175                        'rc_log_type' => 'newusers',
176                        'rc_log_action' => [ 'create', 'autocreate' ]
177                    ], LIST_AND ),
178                    $db->makeList( [
179                        'rc_log_type' => 'delete',
180                        'rc_log_action' => 'delete'
181                    ], LIST_AND ),
182                    $db->makeList( [
183                        'rc_log_type' => 'upload',
184                        'rc_log_action' => [ 'upload', 'overwrite', 'revert' ]
185                    ], LIST_AND ),
186                ], LIST_OR ),
187            ], LIST_AND ),
188        ], LIST_OR );
189    }
190
191    /**
192     * @todo Core should provide a method for this (T233222)
193     * @param ISQLPlatform $db
194     * @param Authority $authority
195     * @return array
196     */
197    public function buildVisibilityConditions( ISQLPlatform $db, Authority $authority ): array {
198        if ( !$authority->isAllowed( 'deletedhistory' ) ) {
199            $bitmask = RevisionRecord::DELETED_USER;
200        } elseif ( !$authority->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
201            $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
202        } else {
203            $bitmask = 0;
204        }
205        return $bitmask
206            ? [ $db->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask" ]
207            : [];
208    }
209
210    /**
211     * @param string|int $id
212     * @param string|null $text
213     * @return string HTML
214     */
215    public function getLinkToLatestDiff( $id, $text = null ) {
216        return $this->linkRenderer->makeKnownLink(
217            $this->getTitle( "history/$id/diff/prev/cur" ),
218            $text
219        );
220    }
221
222}