Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.85% covered (success)
97.85%
91 / 93
75.00% covered (warning)
75.00%
6 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
GlobalRenameRequestStore
97.85% covered (success)
97.85%
91 / 93
75.00% covered (warning)
75.00%
6 / 8
12
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
 save
100.00% covered (success)
100.00%
38 / 38
100.00% covered (success)
100.00%
1 / 1
2
 newBlankRequest
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newForUser
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 newFromId
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 nameHasPendingRequest
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
2.00
 fetchRowFromDB
95.45% covered (success)
95.45%
21 / 22
0.00% covered (danger)
0.00%
0 / 1
2
 newFromRow
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\Extension\CentralAuth\GlobalRename;
22
23use IDBAccessObject;
24use MediaWiki\Extension\CentralAuth\CentralAuthDatabaseManager;
25use MediaWiki\User\UserNameUtils;
26use stdClass;
27
28/**
29 * Stores and loads GlobalRenameRequest objects in a database.
30 *
31 * @author Taavi "Majavah" Väänänen <hi@taavi.wtf>
32 */
33class GlobalRenameRequestStore {
34    /** @var CentralAuthDatabaseManager */
35    private $dbManager;
36
37    /** @var UserNameUtils */
38    private $userNameUtils;
39
40    /**
41     * @param CentralAuthDatabaseManager $dbManager
42     * @param UserNameUtils $userNameUtils
43     */
44    public function __construct(
45        CentralAuthDatabaseManager $dbManager,
46        UserNameUtils $userNameUtils
47    ) {
48        $this->dbManager = $dbManager;
49        $this->userNameUtils = $userNameUtils;
50    }
51
52    /**
53     * Persists the given global rename request to the central database.
54     * @param GlobalRenameRequest $request
55     * @return bool
56     */
57    public function save( GlobalRenameRequest $request ): bool {
58        $dbw = $this->dbManager->getCentralPrimaryDB();
59        if ( $request->getId() === null ) {
60            $request
61                ->setRequested( wfTimestampNow() )
62                ->setStatus( GlobalRenameRequest::PENDING );
63
64            $dbw->newInsertQueryBuilder()
65                ->insertInto( 'renameuser_queue' )
66                ->row( [
67                    'rq_name'         => $request->getName(),
68                    'rq_wiki'         => $request->getWiki(),
69                    'rq_newname'      => $request->getNewName(),
70                    'rq_reason'       => $request->getReason(),
71                    'rq_requested_ts' => $dbw->timestamp( $request->getRequested() ),
72                    'rq_status'       => $request->getStatus(),
73                ] )
74                ->caller( __METHOD__ )
75                ->execute();
76
77            $request->setId( $dbw->insertId() );
78        } else {
79            $dbw->newUpdateQueryBuilder()
80                ->update( 'renameuser_queue' )
81                ->set( [
82                    'rq_name'         => $request->getName(),
83                    'rq_wiki'         => $request->getWiki(),
84                    'rq_newname'      => $request->getNewName(),
85                    'rq_reason'       => $request->getReason(),
86                    'rq_requested_ts' => $dbw->timestamp( $request->getRequested() ),
87                    'rq_status'       => $request->getStatus(),
88                    'rq_completed_ts' => $dbw->timestamp( $request->getCompleted() ),
89                    'rq_deleted'      => $request->getDeleted(),
90                    'rq_performer'    => $request->getPerformer(),
91                    'rq_comments'     => $request->getComments(),
92                ] )
93                ->where( [
94                    'rq_id' => $request->getId()
95                ] )
96                ->caller( __METHOD__ )
97                ->execute();
98        }
99
100        return $dbw->affectedRows() === 1;
101    }
102
103    /**
104     * Creates a new GlobalRenameRequest object without any filled data.
105     * @return GlobalRenameRequest
106     */
107    public function newBlankRequest(): GlobalRenameRequest {
108        return new GlobalRenameRequest( $this->userNameUtils );
109    }
110
111    /**
112     * Get the pending rename request for the given user and wiki.
113     *
114     * @param string $username
115     * @param string|null $wiki
116     * @param int $flags One of the IDBAccessObject::READ_* constants
117     * @return GlobalRenameRequest
118     */
119    public function newForUser( string $username, $wiki, int $flags = IDBAccessObject::READ_NORMAL ) {
120        return $this->newFromRow(
121            $this->fetchRowFromDB( [
122                'rq_name'   => $username,
123                'rq_wiki'   => $wiki,
124                'rq_status' => GlobalRenameRequest::PENDING,
125            ], $flags )
126        );
127    }
128
129    /**
130     * Get a request record.
131     *
132     * @param int $id Request id
133     * @param int $flags One of the IDBAccessObject::READ_* constants
134     * @return GlobalRenameRequest
135     */
136    public function newFromId( int $id, int $flags = IDBAccessObject::READ_NORMAL ) {
137        return $this->newFromRow(
138            $this->fetchRowFromDB( [
139                'rq_id' => $id,
140            ], $flags )
141        );
142    }
143
144    /**
145     * Check to see if there is a pending rename request to the given name.
146     *
147     * @param string $newname
148     * @param int $flags One of the IDBAccessObject::READ_* constants
149     * @return bool
150     */
151    public function nameHasPendingRequest( string $newname, int $flags = IDBAccessObject::READ_NORMAL ) {
152        if ( ( $flags & IDBAccessObject::READ_LATEST ) == IDBAccessObject::READ_LATEST ) {
153            $dbr = $this->dbManager->getCentralPrimaryDB();
154        } else {
155            $dbr = $this->dbManager->getCentralReplicaDB();
156        }
157
158        $res = $dbr->newSelectQueryBuilder()
159            ->select( 'rq_id' )
160            ->from( 'renameuser_queue' )
161            ->where( [
162                'rq_newname' => $newname,
163                'rq_status'  => GlobalRenameRequest::PENDING,
164            ] )
165            ->recency( $flags )
166            ->caller( __METHOD__ )
167            ->fetchField();
168
169        return $res !== false;
170    }
171
172    /**
173     * Fetch a single request from the database.
174     *
175     * @param array $where Where clause criteria
176     * @param int $flags One of the IDBAccessObject::READ_* constants
177     * @return stdClass|false Row as object or false if not found
178     */
179    protected function fetchRowFromDB( array $where, int $flags = IDBAccessObject::READ_NORMAL ) {
180        if ( ( $flags & IDBAccessObject::READ_LATEST ) == IDBAccessObject::READ_LATEST ) {
181            $dbr = $this->dbManager->getCentralPrimaryDB();
182        } else {
183            $dbr = $this->dbManager->getCentralReplicaDB();
184        }
185
186        return $dbr->newSelectQueryBuilder()
187            ->select( [
188                'id'        => 'rq_id',
189                'name'      => 'rq_name',
190                'wiki'      => 'rq_wiki',
191                'newname'   => 'rq_newname',
192                'reason'    => 'rq_reason',
193                'requested' => 'rq_requested_ts',
194                'status'    => 'rq_status',
195                'completed' => 'rq_completed_ts',
196                'deleted'   => 'rq_deleted',
197                'performer' => 'rq_performer',
198                'comments'  => 'rq_comments',
199            ] )
200            ->from( 'renameuser_queue' )
201            ->where( $where )
202            ->recency( $flags )
203            ->caller( __METHOD__ )
204            ->fetchRow();
205    }
206
207    /**
208     * Creates a new GlobalRenameRequest object from a database row.
209     *
210     * @param stdClass|false $row Database result
211     * @return GlobalRenameRequest
212     */
213    protected function newFromRow( $row ): GlobalRenameRequest {
214        $request = $this->newBlankRequest();
215
216        if ( $row ) {
217            $request->importRow( $row );
218        }
219
220        return $request;
221    }
222}