Completed
Push — master ( cd44b6...7febf5 )
by MusikAnimal
03:41 queued 01:27
created

AdminStatsRepository::getStats()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 57
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 57
rs 9.6818
c 0
b 0
f 0
cc 2
eloc 26
nc 2
nop 3

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file contains only the AdminStatsRepository class.
4
 */
5
6
namespace Xtools;
7
8
use DateInterval;
9
use Mediawiki\Api\MediawikiApi;
10
use Mediawiki\Api\SimpleRequest;
11
12
/**
13
 * AdminStatsRepository is responsible for retrieving data from the database
14
 * about users with administrative rights on a given wiki.
15
 */
16
class AdminStatsRepository extends Repository
17
{
18
    const ADMIN_PERMISSIONS = [
19
        'block',
20
        'delete',
21
        'deletedhistory',
22
        'deletedtext',
23
        'deletelogentry',
24
        'deleterevision',
25
        'editinterface',
26
        'globalblock',
27
        'hideuser',
28
        'protect',
29
        'suppressionlog',
30
        'suppressrevision',
31
        'undelete',
32
        'userrights',
33
    ];
34
35
    /**
36
     * Core function to get statistics about users who have admin-like permissions.
37
     * @param  Project $project
38
     * @param  string  $start SQL-ready format.
39
     * @param  string  $end
40
     * @return string[] with keys 'user_name', 'user_id', 'delete', 'restore', 'block',
41
     *   'unblock', 'protect', 'unprotect', 'rights', 'import', and 'total'.
42
     */
43
    public function getStats(Project $project, $start, $end)
44
    {
45
        $cacheKey = 'adminstats.'.$project->getDatabaseName().$start.$end;
46
        if ($this->cache->hasItem($cacheKey)) {
47
            return $this->cache->getItem($cacheKey)->get();
48
        }
49
50
        $userTable = $project->getTableName('user');
51
        $loggingTable = $project->getTableName('logging', 'userindex');
52
        $userGroupsTable = $project->getTableName('user_groups');
53
        $ufgTable = $project->getTableName('user_former_groups');
54
55
        $adminGroups = join(array_map(function ($group) {
56
            return "'$group'";
57
        }, $this->getAdminGroups($project)), ',');
58
59
        $sql = "SELECT user_name, user_id,
60
                    SUM(IF( (log_type = 'delete'  AND log_action != 'restore'),1,0)) AS 'delete',
61
                    SUM(IF( (log_type = 'delete'  AND log_action  = 'restore'),1,0)) AS 'restore',
62
                    SUM(IF( (log_type = 'block'   AND log_action != 'unblock'),1,0)) AS 'block',
63
                    SUM(IF( (log_type = 'block'   AND log_action  = 'unblock'),1,0)) AS 'unblock',
64
                    SUM(IF( (log_type = 'protect' AND log_action != 'unprotect'),1,0)) AS 'protect',
65
                    SUM(IF( (log_type = 'protect' AND log_action  = 'unprotect'),1,0)) AS 'unprotect',
66
                    SUM(IF( log_type  = 'rights',1,0)) AS 'rights',
67
                    SUM(IF( log_type  = 'import',1,0)) AS 'import',
68
                    SUM(IF(log_type  != '',1,0)) AS 'total'
69
                FROM $loggingTable
70
                JOIN $userTable ON user_id = log_user
71
                WHERE log_timestamp > '$start' AND log_timestamp <= '$end'
72
                  AND log_type IS NOT NULL
73
                  AND log_action IS NOT NULL
74
                  AND log_type IN ('block', 'delete', 'protect', 'import', 'rights')
75
                GROUP BY user_name
76
                HAVING 'delete' > 0 OR user_id IN (
77
                    -- Make sure they were at some point were in a qualifying user group.
78
                    -- This also ensures we account for users who were inactive within the time period.
79
                    SELECT ug_user
80
                    FROM $userGroupsTable
81
                    WHERE ug_group IN ($adminGroups)
82
                    UNION
83
                    SELECT ufg_user
84
                    FROM $ufgTable
85
                    WHERE ufg_group IN ($adminGroups)
86
                )
87
                ORDER BY 'total' DESC";
88
89
        $results = $this->getProjectsConnection()->query($sql)->fetchAll();
90
91
        // Cache for 10 minutes.
92
        $cacheItem = $this->cache
93
            ->getItem($cacheKey)
94
            ->set($results)
95
            ->expiresAfter(new DateInterval('PT10M'));
96
        $this->cache->save($cacheItem);
97
98
        return $results;
99
    }
100
101
    /**
102
     * Get all user groups with admin-like permissions.
103
     * @param  Project $project
104
     * @return array Each entry contains 'name' (user group) and 'rights' (the permissions).
105
     */
106
    public function getAdminGroups(Project $project)
107
    {
108
        $cacheKey = 'admingroups.'.$project->getDatabaseName();
109
        if ($this->cache->hasItem($cacheKey)) {
110
            return $this->cache->getItem($cacheKey)->get();
111
        }
112
113
        $userGroups = [];
114
115
        $params = [
116
            'meta' => 'siteinfo',
117
            'siprop' => 'usergroups',
118
        ];
119
        $api = $this->getMediawikiApi($project);
120
        $query = new SimpleRequest('query', $params);
121
        $res = $api->getRequest($query);
122
123
        // If there isn't a usergroups hash than let it error out...
124
        // Something else must have gone horribly wrong.
125
        foreach ($res['query']['usergroups'] as $userGroup) {
126
            // If they are able to add and remove user groups,
127
            // we'll treat them as having the 'userrights' permission.
128
            if (isset($userGroup['add']) || isset($userGroup['remove'])) {
129
                array_push($userGroup['rights'], 'userrights');
130
            }
131
132
            if (count(array_intersect($userGroup['rights'], self::ADMIN_PERMISSIONS)) > 0) {
133
                $userGroups[] = $userGroup['name'];
134
            }
135
        }
136
137
        // Cache for a week.
138
        $cacheItem = $this->cache
139
            ->getItem($cacheKey)
140
            ->set($userGroups)
141
            ->expiresAfter(new DateInterval('P7D'));
142
        $this->cache->save($cacheItem);
143
144
        return $userGroups;
145
    }
146
}
147