Passed
Push — master ( 9c530b...98dfb9 )
by MusikAnimal
06:42
created

UserRepository::getUserConditions()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 5
nop 3
dl 0
loc 13
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file contains only the UserRepository class.
4
 */
5
6
declare(strict_types = 1);
7
8
namespace AppBundle\Repository;
9
10
use AppBundle\Model\Project;
11
use AppBundle\Model\User;
12
use Doctrine\DBAL\Driver\ResultStatement;
13
use Mediawiki\Api\SimpleRequest;
14
use Symfony\Component\DependencyInjection\ContainerInterface;
15
use Symfony\Component\HttpFoundation\Session\Session;
16
17
/**
18
 * This class provides data for the User class.
19
 * @codeCoverageIgnore
20
 */
21
class UserRepository extends Repository
22
{
23
    /**
24
     * Convenience method to get a new User object.
25
     * @param string $username The username.
26
     * @param ContainerInterface $container The DI container.
27
     * @return User
28
     */
29
    public static function getUser(string $username, ContainerInterface $container): User
30
    {
31
        $user = new User($username);
32
        $userRepo = new UserRepository();
33
        $userRepo->setContainer($container);
34
        $user->setRepository($userRepo);
35
        return $user;
36
    }
37
38
    /**
39
     * Get the user's ID and registration date.
40
     * @param string $databaseName The database to query.
41
     * @param string $username The username to find.
42
     * @return array|false With keys 'userId' and 'regDate'. false if user not found.
43
     */
44
    public function getIdAndRegistration(string $databaseName, string $username)
45
    {
46
        $cacheKey = $this->getCacheKey(func_get_args(), 'user_id_reg');
47
        if ($this->cache->hasItem($cacheKey)) {
48
            return $this->cache->getItem($cacheKey)->get();
49
        }
50
51
        $userTable = $this->getTableName($databaseName, 'user');
52
        $sql = "SELECT user_id AS userId, user_registration AS regDate
53
                FROM $userTable
54
                WHERE user_name = :username
55
                LIMIT 1";
56
        $resultQuery = $this->executeProjectsQuery($sql, ['username' => $username]);
57
58
        // Cache and return.
59
        return $this->setCache($cacheKey, $resultQuery->fetch());
60
    }
61
62
    /**
63
     * Get the user's registration date.
64
     * @param string $databaseName The database to query.
65
     * @param string $username The username to find.
66
     * @return string|null As returned by the database. Some very old accounts may return null.
67
     */
68
    public function getRegistrationDate(string $databaseName, string $username): ?string
69
    {
70
        $cacheKey = $this->getCacheKey(func_get_args(), 'user_registration');
71
        if ($this->cache->hasItem($cacheKey)) {
72
            return $this->cache->getItem($cacheKey)->get();
73
        }
74
75
        $userTable = $this->getTableName($databaseName, 'user');
76
        $sql = "SELECT user_registration FROM $userTable WHERE user_name = :username LIMIT 1";
77
        $resultQuery = $this->executeProjectsQuery($sql, ['username' => $username]);
78
        $registrationDate = $resultQuery->fetchColumn();
79
80
        // Cache and return.
81
        return $this->setCache($cacheKey, $registrationDate);
82
    }
83
84
    /**
85
     * Get the user's (system) edit count.
86
     * @param string $databaseName The database to query.
87
     * @param string $username The username to find.
88
     * @return string|null As returned by the database.
89
     */
90
    public function getEditCount(string $databaseName, string $username): ?string
91
    {
92
        // Quick cache of edit count, valid on for the same request.
93
        static $editCount = null;
94
        if (null !== $editCount) {
95
            return $editCount;
96
        }
97
98
        $userTable = $this->getTableName($databaseName, 'user');
99
        $sql = "SELECT user_editcount FROM $userTable WHERE user_name = :username LIMIT 1";
100
        $resultQuery = $this->executeProjectsQuery($sql, ['username' => $username]);
101
102
        $editCount = $resultQuery->fetchColumn();
103
        return $editCount;
104
    }
105
106
    /**
107
     * Search the ipblocks table to see if the user is currently blocked
108
     * and return the expiry if they are.
109
     * @param string $databaseName The database to query.
110
     * @param string $username The username of the user to search for.
111
     * @return bool|string Expiry of active block or false
112
     */
113
    public function getBlockExpiry(string $databaseName, string $username)
114
    {
115
        $ipblocksTable = $this->getTableName($databaseName, 'ipblocks', 'ipindex');
116
        $sql = "SELECT ipb_expiry
117
                FROM $ipblocksTable
118
                WHERE ipb_address = :username
119
                LIMIT 1";
120
        $resultQuery = $this->executeProjectsQuery($sql, ['username' => $username]);
121
        return $resultQuery->fetchColumn();
122
    }
123
124
    /**
125
     * Get edit count within given timeframe and namespace.
126
     * @param Project $project
127
     * @param User $user
128
     * @param int|string $namespace Namespace ID or 'all' for all namespaces
129
     * @param string $start Start date in a format accepted by strtotime()
130
     * @param string $end End date in a format accepted by strtotime()
131
     * @return int
132
     */
133
    public function countEdits(Project $project, User $user, $namespace = 'all', $start = '', $end = ''): int
134
    {
135
        $cacheKey = $this->getCacheKey(func_get_args(), 'user_editcount');
136
        if ($this->cache->hasItem($cacheKey)) {
137
            return (int)$this->cache->getItem($cacheKey)->get();
138
        }
139
140
        [$condBegin, $condEnd] = $this->getRevTimestampConditions($start, $end);
141
        [$pageJoin, $condNamespace] = $this->getPageAndNamespaceSql($project, $namespace);
142
        $revisionTable = $project->getTableName('revision');
143
144
        $sql = "SELECT COUNT(rev_id)
145
                FROM $revisionTable
146
                $pageJoin
147
                WHERE rev_user_text = :username
148
                $condNamespace
149
                $condBegin
150
                $condEnd";
151
152
        $resultQuery = $this->executeQuery($sql, $user, $namespace, $start, $end);
153
        $result = (int)$resultQuery->fetchColumn();
154
155
        // Cache and return.
156
        return $this->setCache($cacheKey, $result);
157
    }
158
159
    /**
160
     * Get information about the currently-logged in user.
161
     * @return array|object|null null if not logged in.
162
     */
163
    public function getXtoolsUserInfo()
164
    {
165
        /** @var Session $session */
166
        $session = $this->container->get('session');
167
        return $session->get('logged_in_user');
168
    }
169
170
    /**
171
     * Maximum number of edits to process, based on configuration.
172
     * @return int
173
     */
174
    public function maxEdits(): int
175
    {
176
        return (int)$this->container->getParameter('app.max_user_edits');
177
    }
178
179
    /**
180
     * Get SQL clauses for joining on `page` and restricting to a namespace.
181
     * @param Project $project
182
     * @param int|string $namespace Namespace ID or 'all' for all namespaces.
183
     * @return array [page join clause, page namespace clause]
184
     */
185
    protected function getPageAndNamespaceSql(Project $project, $namespace): array
186
    {
187
        if ('all' === $namespace) {
188
            return [null, null];
189
        }
190
191
        $pageTable = $project->getTableName('page');
192
        $pageJoin = 'all' !== $namespace ? "LEFT JOIN $pageTable ON rev_page = page_id" : null;
193
        $condNamespace = 'AND page_namespace = :namespace';
194
195
        return [$pageJoin, $condNamespace];
196
    }
197
198
    /**
199
     * Get SQL clauses for rev_timestamp, based on whether values for the given start and end parameters exist.
200
     * @param string $start
201
     * @param string $end
202
     * @param string $tableAlias Alias of table FOLLOWED BY DOT.
203
     * @todo FIXME: merge with Repository::getDateConditions
204
     * @return string[] Clauses for start and end timestamps.
205
     */
206
    protected function getRevTimestampConditions(string $start, string $end, string $tableAlias = ''): array
207
    {
208
        $condBegin = '';
209
        $condEnd = '';
210
211
        if (!empty($start)) {
212
            $condBegin = "AND {$tableAlias}rev_timestamp >= :start ";
213
        }
214
        if (!empty($end)) {
215
            $condEnd = "AND {$tableAlias}rev_timestamp <= :end ";
216
        }
217
218
        return [$condBegin, $condEnd];
219
    }
220
221
    /**
222
     * Prepare the given SQL, bind the given parameters, and execute the Doctrine Statement.
223
     * @param string $sql
224
     * @param User $user
225
     * @param int|string $namespace Namespace ID or 'all' for all namespaces.
226
     * @param string $start
227
     * @param string $end
228
     * @param array $extraParams Will get merged in the params array used for binding values.
229
     * @return ResultStatement
230
     */
231
    protected function executeQuery(
232
        string $sql,
233
        User $user,
234
        $namespace = 'all',
235
        string $start = '',
236
        string $end = '',
237
        array $extraParams = []
238
    ): ResultStatement {
239
        $params = [
240
            'username' => $user->getUsername(),
241
        ];
242
243
        if (!empty($start)) {
244
            $params['start'] = date('Ymd000000', strtotime($start));
245
        }
246
        if (!empty($end)) {
247
            $params['end'] = date('Ymd235959', strtotime($end));
248
        }
249
        if ('all' !== $namespace) {
250
            $params['namespace'] = $namespace;
251
        }
252
253
        return $this->executeProjectsQuery($sql, array_merge($params, $extraParams));
254
    }
255
256
    /**
257
     * Get a user's local user rights on the given Project.
258
     * @param Project $project
259
     * @param User $user
260
     * @return string[]
261
     */
262
    public function getUserRights(Project $project, User $user): array
263
    {
264
        $cacheKey = $this->getCacheKey(func_get_args(), 'user_rights');
265
        if ($this->cache->hasItem($cacheKey)) {
266
            return $this->cache->getItem($cacheKey)->get();
267
        }
268
269
        $userGroupsTable = $project->getTableName('user_groups');
270
        $userTable = $project->getTableName('user');
271
272
        $sql = "SELECT ug_group
273
                FROM $userGroupsTable
274
                JOIN $userTable ON user_id = ug_user
275
                WHERE user_name = :username";
276
277
        $ret = $this->executeProjectsQuery($sql, [
278
            'username' => $user->getUsername(),
279
        ])->fetchAll(\PDO::FETCH_COLUMN);
280
281
        // Cache and return.
282
        return $this->setCache($cacheKey, $ret);
283
    }
284
285
    /**
286
     * Get a user's global group membership (starting at XTools' default project if none is
287
     * provided). This requires the CentralAuth extension to be installed.
288
     * @link https://www.mediawiki.org/wiki/Extension:CentralAuth
289
     * @param string $username The username.
290
     * @param Project $project The project to query.
291
     * @return string[]
292
     */
293
    public function getGlobalUserRights(string $username, ?Project $project = null): array
294
    {
295
        $cacheKey = $this->getCacheKey(func_get_args(), 'user_global_groups');
296
        if ($this->cache->hasItem($cacheKey)) {
297
            return $this->cache->getItem($cacheKey)->get();
298
        }
299
300
        // Get the default project if not provided.
301
        if (!$project instanceof Project) {
302
            $project = ProjectRepository::getDefaultProject($this->container);
303
        }
304
305
        // Create the API query.
306
        $api = $this->getMediawikiApi($project);
307
        $params = [
308
            'meta' => 'globaluserinfo',
309
            'guiuser' => $username,
310
            'guiprop' => 'groups',
311
        ];
312
        $query = new SimpleRequest('query', $params);
313
314
        // Get the result.
315
        $res = $api->getRequest($query);
316
        $result = [];
317
        if (isset($res['batchcomplete']) && isset($res['query']['globaluserinfo']['groups'])) {
318
            $result = $res['query']['globaluserinfo']['groups'];
319
        }
320
321
        // Cache and return.
322
        return $this->setCache($cacheKey, $result);
323
    }
324
}
325