Passed
Pull Request — master (#158)
by MusikAnimal
04:58
created

TopEditsRepository::getContainer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file contains only the TopEditsRepository class.
4
 */
5
6
namespace Xtools;
7
8
use Symfony\Component\DependencyInjection\Container;
9
10
/**
11
 * TopEditsRepository is responsible for retrieving data from the database
12
 * about the top-edited pages of a user. It doesn't do any post-processing
13
 * of that information.
14
 * @codeCoverageIgnore
15
 */
16
class TopEditsRepository extends Repository
17
{
18
    /**
19
     * Expose the container to the TopEdits class.
20
     * @return Container
21
     */
22
    public function getContainer()
23
    {
24
        return $this->container;
25
    }
26
27
    /**
28
     * Get the top edits by a user in a single namespace.
29
     * @param Project $project
30
     * @param User $user
31
     * @param int $namespace Namespace ID.
32
     * @param int $limit Number of edits to fetch.
33
     * @return string[] page_namespace, page_title, page_is_redirect,
34
     *   count (number of edits), assessment (page assessment).
35
     */
36
    public function getTopEditsNamespace(Project $project, User $user, $namespace = 0, $limit = 100)
37
    {
38
        // Set up cache.
39
        $cacheKey = $this->getCacheKey(func_get_args(), 'topedits_ns');
40
        if ($this->cache->hasItem($cacheKey)) {
41
            return $this->cache->getItem($cacheKey)->get();
42
        }
43
44
        $pageTable = $this->getTableName($project->getDatabaseName(), 'page');
45
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
46
47
        $hasPageAssessments = $this->isLabs() && $project->hasPageAssessments() && $namespace === 0;
48
        $paTable = $this->getTableName($project->getDatabaseName(), 'page_assessments');
49
        $paSelect = $hasPageAssessments
50
            ?  ", (
51
                    SELECT pa_class
52
                    FROM $paTable
53
                    WHERE pa_page_id = page_id
54
                    AND pa_class != 'Unknown'
55
                    LIMIT 1
56
                ) AS pa_class"
57
            : '';
58
59
        $sql = "SELECT page_namespace, page_title, page_is_redirect, COUNT(page_title) AS count
60
                    $paSelect
61
                FROM $pageTable JOIN $revisionTable ON page_id = rev_page
62
                WHERE rev_user_text = :username
63
                AND page_namespace = :namespace
64
                GROUP BY page_namespace, page_title
65
                ORDER BY count DESC
66
                LIMIT $limit";
67
        $resultQuery = $this->getProjectsConnection()->prepare($sql);
68
        $username = $user->getUsername();
69
        $resultQuery->bindParam('username', $username);
70
        $resultQuery->bindParam('namespace', $namespace);
71
        $resultQuery->execute();
72
        $results = $resultQuery->fetchAll();
73
74
        // Cache for 10 minutes, and return.
75
        $this->setCache($cacheKey, $results);
76
77
        return $results;
78
    }
79
80
    /**
81
     * Get the top edits by a user across all namespaces.
82
     * @param Project $project
83
     * @param User $user
84
     * @param int $limit Number of edits to fetch.
85
     * @return string[] page_namespace, page_title, page_is_redirect,
86
     *   count (number of edits), assessment (page assessment).
87
     */
88
    public function getTopEditsAllNamespaces(Project $project, User $user, $limit = 10)
89
    {
90
        // Set up cache.
91
        $cacheKey = $this->getCacheKey(func_get_args(), 'topedits_all');
92
        if ($this->cache->hasItem($cacheKey)) {
93
            return $this->cache->getItem($cacheKey)->get();
94
        }
95
96
        $pageTable = $this->getTableName($project->getDatabaseName(), 'page');
97
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
98
        $hasPageAssessments = $this->isLabs() && $project->hasPageAssessments();
99
        $pageAssessmentsTable = $this->getTableName($project->getDatabaseName(), 'page_assessments');
100
        $paSelect = $hasPageAssessments
101
            ?  ", (
102
                    SELECT pa_class
103
                    FROM $pageAssessmentsTable
104
                    WHERE pa_page_id = e.page_id
105
                    LIMIT 1
106
                ) AS pa_class"
107
            : ', NULL as pa_class';
108
109
        $sql = "SELECT c.page_namespace, e.page_title, c.page_is_redirect, c.count $paSelect
110
                FROM
111
                (
112
                    SELECT b.page_namespace, b.page_is_redirect, b.rev_page, b.count
113
                        ,@rn := if(@ns = b.page_namespace, @rn + 1, 1) AS row_number
114
                        ,@ns := b.page_namespace AS dummy
115
                    FROM
116
                    (
117
                        SELECT page_namespace, page_is_redirect, rev_page, count(rev_page) AS count
118
                        FROM $revisionTable
119
                        JOIN $pageTable ON page_id = rev_page
120
                        WHERE rev_user_text = :username
121
                        GROUP BY page_namespace, rev_page
122
                    ) AS b
123
                    JOIN (SELECT @ns := NULL, @rn := 0) AS vars
124
                    ORDER BY b.page_namespace ASC, b.count DESC
125
                ) AS c
126
                JOIN $pageTable e ON e.page_id = c.rev_page
127
                WHERE c.row_number < $limit";
128
        $resultQuery = $this->getProjectsConnection()->prepare($sql);
129
        $username = $user->getUsername();
130
        $resultQuery->bindParam('username', $username);
131
        $resultQuery->execute();
132
        $results = $resultQuery->fetchAll();
133
134
        // Cache for 10 minutes, and return.
135
        $this->setCache($cacheKey, $results);
136
137
        return $results;
138
    }
139
140
    /**
141
     * Get the top edits by a user to a single page.
142
     * @param Page $page
143
     * @param User $user
144
     * @return string[] Each row with keys 'id', 'timestamp', 'minor', 'length',
145
     *   'length_change', 'reverted', 'user_id', 'username', 'comment', 'parent_comment'
146
     */
147
    public function getTopEditsPage(Page $page, User $user)
148
    {
149
        // Set up cache.
150
        $cacheKey = $this->getCacheKey(func_get_args(), 'topedits_page');
151
        if ($this->cache->hasItem($cacheKey)) {
152
            return $this->cache->getItem($cacheKey)->get();
153
        }
154
155
        $results = $this->queryTopEditsPage($page, $user, true);
156
157
        // Now we need to get the most recent revision, since the childrevs stuff excludes it.
158
        $lastRev = $this->queryTopEditsPage($page, $user, false);
159
        if ($lastRev[0]['id'] !== end($results)['id']) {
160
            $results = array_merge($lastRev, $results);
161
        }
162
163
        // Cache for 10 minutes, and return.
164
        $this->setCache($cacheKey, $results);
165
166
        return $results;
167
    }
168
169
    /**
170
     * The actual query to get the top edits by the user to the page.
171
     * Because of the way the main query works, we aren't given the most recent revision,
172
     * so we have to call this twice, once with $childRevs set to true and once with false.
173
     * @param  Page    $page
174
     * @param  User    $user
175
     * @param  boolean $childRevs Whether to include child revisions.
176
     * @return string[] Each row with keys 'id', 'timestamp', 'minor', 'length',
177
     *   'length_change', 'reverted', 'user_id', 'username', 'comment', 'parent_comment'
178
     */
179
    private function queryTopEditsPage(Page $page, User $user, $childRevs = false)
180
    {
181
        $revTable = $this->getTableName($page->getProject()->getDatabaseName(), 'revision');
182
183
        if ($childRevs) {
184
            $childSelect = ', (CASE WHEN childrevs.rev_sha1 = parentrevs.rev_sha1 THEN 1 ELSE 0 END) AS reverted,
185
                    childrevs.rev_comment AS parent_comment';
186
            $childJoin = "LEFT JOIN $revTable AS childrevs ON (revs.rev_id = childrevs.rev_parent_id)";
187
            $childWhere = 'AND childrevs.rev_page = :pageid';
188
            $childLimit = '';
189
        } else {
190
            $childSelect = ', "" AS parent_comment, 0 AS reverted';
191
            $childJoin = '';
192
            $childWhere = '';
193
            $childLimit = 'LIMIT 1';
194
        }
195
196
        $sql = "SELECT
197
                    revs.rev_id AS id,
198
                    revs.rev_timestamp AS timestamp,
199
                    revs.rev_minor_edit AS minor,
200
                    revs.rev_len AS length,
201
                    (CAST(revs.rev_len AS SIGNED) - IFNULL(parentrevs.rev_len, 0)) AS length_change,
202
                    revs.rev_user AS user_id,
203
                    revs.rev_user_text AS username,
204
                    revs.rev_comment AS comment
205
                    $childSelect
206
                FROM $revTable AS revs
207
                LEFT JOIN $revTable AS parentrevs ON (revs.rev_parent_id = parentrevs.rev_id)
208
                $childJoin
209
                WHERE revs.rev_user_text = :username
210
                AND revs.rev_page = :pageid
211
                $childWhere
212
                ORDER BY revs.rev_timestamp DESC
213
                $childLimit";
214
215
        $conn = $this->getProjectsConnection();
216
        $resultQuery = $conn->executeQuery($sql, [
217
            'pageid' => $page->getId(),
218
            'username' => $user->getUsername(),
219
        ]);
220
221
        return $resultQuery->fetchAll();
222
    }
223
224
    /**
225
     * Get the display titles of the given pages.
226
     * @param Project $project
227
     * @param  string[] $titles List of page titles.
228
     * @return string[] Keys are the original supplied titles, and values are the display titles.
229
     */
230
    public function getDisplayTitles(Project $project, $titles)
231
    {
232
        /** @var ApiHelper $apiHelper */
233
        $apiHelper = $this->container->get('app.api_helper');
234
235
        return $apiHelper->displayTitles($project->getDomain(), $titles);
236
    }
237
}
238