Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
SchemaChangesHandler
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
2 / 2
10
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 newFromGlobalState
n/a
0 / 0
n/a
0 / 0
1
 onLoadExtensionSchemaUpdates
n/a
0 / 0
n/a
0 / 0
5
 createAbuseFilterUser
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
4
5use MediaWiki\Context\RequestContext;
6use MediaWiki\Extension\AbuseFilter\Maintenance\MigrateActorsAF;
7use MediaWiki\Extension\AbuseFilter\Maintenance\UpdateVarDumps;
8use MediaWiki\Installer\DatabaseUpdater;
9use MediaWiki\Installer\Hook\LoadExtensionSchemaUpdatesHook;
10use MediaWiki\MediaWikiServices;
11use MediaWiki\User\User;
12use MediaWiki\User\UserFactory;
13use MediaWiki\User\UserGroupManager;
14use MessageLocalizer;
15
16class SchemaChangesHandler implements LoadExtensionSchemaUpdatesHook {
17    /** @var MessageLocalizer */
18    private $messageLocalizer;
19    /** @var UserGroupManager */
20    private $userGroupManager;
21    /** @var UserFactory */
22    private $userFactory;
23
24    /**
25     * @param MessageLocalizer $messageLocalizer
26     * @param UserGroupManager $userGroupManager
27     * @param UserFactory $userFactory
28     */
29    public function __construct(
30        MessageLocalizer $messageLocalizer,
31        UserGroupManager $userGroupManager,
32        UserFactory $userFactory
33    ) {
34        $this->messageLocalizer = $messageLocalizer;
35        $this->userGroupManager = $userGroupManager;
36        $this->userFactory = $userFactory;
37    }
38
39    /**
40     * @note The hook doesn't allow injecting services!
41     * @codeCoverageIgnore
42     * @return self
43     */
44    public static function newFromGlobalState(): self {
45        return new self(
46            // @todo Use a proper MessageLocalizer once available (T247127)
47            RequestContext::getMain(),
48            MediaWikiServices::getInstance()->getUserGroupManager(),
49            MediaWikiServices::getInstance()->getUserFactory()
50        );
51    }
52
53    /**
54     * @codeCoverageIgnore This is tested by installing or updating MediaWiki
55     * @param DatabaseUpdater $updater
56     */
57    public function onLoadExtensionSchemaUpdates( $updater ) {
58        $dbType = $updater->getDB()->getType();
59        $dir = __DIR__ . "/../../../db_patches";
60
61        $updater->addExtensionTable(
62            'abuse_filter',
63            "$dir/$dbType/tables-generated.sql"
64        );
65
66        if ( $dbType === 'mysql' || $dbType === 'sqlite' ) {
67            if ( $dbType === 'mysql' ) {
68                // 1.37
69                $updater->renameExtensionIndex(
70                    'abuse_filter_log',
71                    'ip_timestamp',
72                    'afl_ip_timestamp',
73                    "$dir/mysql/patch-rename-indexes.sql",
74                    true
75                );
76
77                // 1.38
78                // This one has its own files because apparently, sometimes this particular index can already
79                // have the correct name (T291725)
80                $updater->renameExtensionIndex(
81                    'abuse_filter_log',
82                    'wiki_timestamp',
83                    'afl_wiki_timestamp',
84                    "$dir/mysql/patch-rename-wiki-timestamp-index.sql",
85                    true
86                );
87
88                // 1.38
89                // This one is also separate to avoid interferences with the afl_filter field removal below.
90                $updater->renameExtensionIndex(
91                    'abuse_filter_log',
92                    'filter_timestamp',
93                    'afl_filter_timestamp',
94                    "$dir/mysql/patch-rename-filter_timestamp-index.sql",
95                    true
96                );
97            }
98            // 1.38
99            $updater->dropExtensionField(
100                'abuse_filter_log',
101                'afl_filter',
102                "$dir/$dbType/patch-remove-afl_filter.sql"
103            );
104        } elseif ( $dbType === 'postgres' ) {
105            $updater->addExtensionUpdate( [
106                'dropPgIndex', 'abuse_filter_log', 'abuse_filter_log_timestamp'
107            ] );
108            $updater->addExtensionUpdate( [
109                'dropPgField', 'abuse_filter_log', 'afl_filter'
110            ] );
111            $updater->addExtensionUpdate( [
112                'dropDefault', 'abuse_filter_log', 'afl_filter_id'
113            ] );
114            $updater->addExtensionUpdate( [
115                'dropDefault', 'abuse_filter_log', 'afl_global'
116            ] );
117            $updater->addExtensionUpdate( [
118                'renameIndex', 'abuse_filter', 'abuse_filter_user', 'af_user'
119            ] );
120            $updater->addExtensionUpdate( [
121                'renameIndex', 'abuse_filter', 'abuse_filter_group_enabled_id', 'af_group_enabled'
122            ] );
123            $updater->addExtensionUpdate( [
124                'renameIndex', 'abuse_filter_action', 'abuse_filter_action_consequence', 'afa_consequence'
125            ] );
126            $updater->addExtensionUpdate( [
127                'renameIndex', 'abuse_filter_log', 'abuse_filter_log_filter_timestamp_full', 'afl_filter_timestamp_full'
128            ] );
129            $updater->addExtensionUpdate( [
130                'renameIndex', 'abuse_filter_log', 'abuse_filter_log_user_timestamp', 'afl_user_timestamp'
131            ] );
132            $updater->addExtensionUpdate( [
133                'renameIndex', 'abuse_filter_log', 'abuse_filter_log_timestamp', 'afl_timestamp'
134            ] );
135            $updater->addExtensionUpdate( [
136                'renameIndex', 'abuse_filter_log', 'abuse_filter_log_page_timestamp', 'afl_page_timestamp'
137            ] );
138            $updater->addExtensionUpdate( [
139                'renameIndex', 'abuse_filter_log', 'abuse_filter_log_ip_timestamp', 'afl_ip_timestamp'
140            ] );
141            $updater->addExtensionUpdate( [
142                'renameIndex', 'abuse_filter_log', 'abuse_filter_log_rev_id', 'afl_rev_id'
143            ] );
144            $updater->addExtensionUpdate( [
145                'renameIndex', 'abuse_filter_log', 'abuse_filter_log_wiki_timestamp', 'afl_wiki_timestamp'
146            ] );
147            $updater->addExtensionUpdate( [
148                'renameIndex', 'abuse_filter_history', 'abuse_filter_history_filter', 'afh_filter'
149            ] );
150            $updater->addExtensionUpdate( [
151                'renameIndex', 'abuse_filter_history', 'abuse_filter_history_user', 'afh_user'
152            ] );
153            $updater->addExtensionUpdate( [
154                'renameIndex', 'abuse_filter_history', 'abuse_filter_history_user_text', 'afh_user_text'
155            ] );
156            $updater->addExtensionUpdate( [
157                'renameIndex', 'abuse_filter_history', 'abuse_filter_history_timestamp', 'afh_timestamp'
158            ] );
159            $updater->addExtensionUpdate( [
160                'changeNullableField', ' abuse_filter_history', 'afh_public_comments', 'NULL', true
161            ] );
162            $updater->addExtensionUpdate( [
163                'changeNullableField', ' abuse_filter_history', 'afh_actions', 'NULL', true
164            ] );
165        }
166
167        // 1.41
168        $updater->addExtensionUpdate( [
169            'addField', 'abuse_filter', 'af_actor',
170            "$dir/$dbType/patch-add-af_actor.sql", true
171        ] );
172
173        // 1.41
174        $updater->addExtensionUpdate( [
175            'addField', 'abuse_filter_history', 'afh_actor',
176            "$dir/$dbType/patch-add-afh_actor.sql", true
177        ] );
178
179        // 1.43
180        $updater->addExtensionUpdate( [
181            'runMaintenance',
182            MigrateActorsAF::class,
183        ] );
184
185        // 1.43
186        $updater->addExtensionUpdate( [
187            'dropField', 'abuse_filter', 'af_user',
188            "$dir/$dbType/patch-drop-af_user.sql", true
189        ] );
190
191        // 1.43
192        $updater->addExtensionUpdate( [
193            'dropField', 'abuse_filter_history', 'afh_user',
194            "$dir/$dbType/patch-drop-afh_user.sql", true
195        ] );
196
197        $updater->addExtensionUpdate( [ [ $this, 'createAbuseFilterUser' ] ] );
198        // 1.35
199        $updater->addPostDatabaseUpdateMaintenance( UpdateVarDumps::class );
200    }
201
202    /**
203     * Updater callback to create the AbuseFilter user after the user tables have been updated.
204     * @param DatabaseUpdater $updater
205     * @return bool
206     */
207    public function createAbuseFilterUser( DatabaseUpdater $updater ): bool {
208        $username = $this->messageLocalizer->msg( 'abusefilter-blocker' )->inContentLanguage()->text();
209        $user = $this->userFactory->newFromName( $username );
210
211        if ( $user && !$updater->updateRowExists( 'create abusefilter-blocker-user' ) ) {
212            $user = User::newSystemUser( $username, [ 'steal' => true ] );
213            $updater->insertUpdateRow( 'create abusefilter-blocker-user' );
214            // Promote user so it doesn't look too crazy.
215            $this->userGroupManager->addUserToGroup( $user, 'sysop' );
216            return true;
217        }
218        return false;
219    }
220}