Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hooks
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 8
380
0.00% covered (danger)
0.00%
0 / 1
 onPageSaveComplete
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
12
 onRollbackComplete
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
30
 onRegisterTags
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onListDefinedTags
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onChangeTagsListActive
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 countersOnEditSuccess
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 countersOnUndo
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 getCounters
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 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 *
17 * @file
18 */
19
20namespace MediaWiki\Extension\WikimediaEditorTasks;
21
22use IDBAccessObject;
23use MediaWiki\ChangeTags\Hook\ChangeTagsListActiveHook;
24use MediaWiki\ChangeTags\Hook\ListDefinedTagsHook;
25use MediaWiki\Deferred\AutoCommitUpdate;
26use MediaWiki\Deferred\DeferredUpdates;
27use MediaWiki\MediaWikiServices;
28use MediaWiki\Page\Hook\RollbackCompleteHook;
29use MediaWiki\Revision\RevisionRecord;
30use MediaWiki\Storage\EditResult;
31use MediaWiki\Storage\Hook\PageSaveCompleteHook;
32use MediaWiki\Title\Title;
33use MediaWiki\User\UserIdentity;
34use RequestContext;
35use WikiPage;
36
37/**
38 * Hooks for WikimediaEditorTasks extension
39 */
40class Hooks implements
41    RollbackCompleteHook,
42    PageSaveCompleteHook,
43    ListDefinedTagsHook,
44    ChangeTagsListActiveHook
45{
46
47    /**
48     * Handler for PageSaveComplete hook
49     * @see https://www.mediawiki.org/wiki/Manual:Hooks/PageSaveComplete
50     *
51     * @param WikiPage $wikiPage
52     * @param UserIdentity $userIdentity
53     * @param string $summary
54     * @param int $flags
55     * @param RevisionRecord $revisionRecord
56     * @param EditResult $editResult
57     */
58    public function onPageSaveComplete(
59        $wikiPage,
60        $userIdentity,
61        $summary,
62        $flags,
63        $revisionRecord,
64        $editResult
65    ) {
66        $undidRevId = $editResult->getUndidRevId();
67
68        $cb = function () use ( $revisionRecord, $userIdentity, $undidRevId, $wikiPage ) {
69            if ( $userIdentity->isRegistered() ) {
70                self::countersOnEditSuccess( $userIdentity, $revisionRecord );
71            }
72
73            if ( $undidRevId ) {
74                self::countersOnUndo( $undidRevId, $wikiPage );
75            }
76        };
77
78        $services = MediaWikiServices::getInstance();
79        DeferredUpdates::addUpdate(
80            new AutoCommitUpdate(
81                Utils::getUserCountsDB( DB_PRIMARY, $services ),
82                __METHOD__,
83                $cb,
84                [ $services->getConnectionProvider()->getPrimaryDatabase() ]
85            ),
86            DeferredUpdates::POSTSEND
87        );
88    }
89
90    /**
91     * Handler for RollbackComplete hook.
92     * @see https://www.mediawiki.org/wiki/Manual:Hooks/RollbackComplete
93     *
94     * @param WikiPage $wikiPage The article that was edited
95     * @param UserIdentity $agent The user who did the rollback
96     * @param RevisionRecord $newRev The revision the page was reverted back to
97     * @param RevisionRecord $oldRev The revision of the top edit that was reverted
98     */
99    public function onRollbackComplete( $wikiPage, $agent, $newRev, $oldRev ) {
100        $cb = function () use ( $wikiPage, $oldRev, $newRev ) {
101            $victim = $oldRev->getUser();
102
103            // Ignore anonymous users and null rollbacks
104            if ( $victim && $victim->isRegistered() && !$oldRev->hasSameContent( $newRev ) ) {
105                $victimCentralId = Utils::getCentralId( $victim );
106                $oldId = $oldRev->getId();
107                foreach ( self::getCounters() as $counter ) {
108                    $counter->onRevert(
109                        $victimCentralId,
110                        $oldId,
111                        $oldRev
112                    );
113                }
114            }
115        };
116
117        $services = MediaWikiServices::getInstance();
118        DeferredUpdates::addUpdate(
119            new AutoCommitUpdate(
120                Utils::getUserCountsDB( DB_PRIMARY, $services ),
121                __METHOD__,
122                $cb,
123                [ $services->getConnectionProvider()->getPrimaryDatabase() ]
124            ),
125            DeferredUpdates::POSTSEND
126        );
127    }
128
129    /**
130     * @param array &$tags
131     */
132    private static function onRegisterTags( array &$tags ) {
133        $tags[] = 'apps-suggested-edits';
134    }
135
136    /**
137     * @param array &$tags
138     * @see https://www.mediawiki.org/wiki/Manual:Hooks/ListDefinedTags
139     */
140    public function onListDefinedTags( &$tags ) {
141        self::onRegisterTags( $tags );
142    }
143
144    /**
145     * @param array &$tags
146     */
147    public function onChangeTagsListActive( &$tags ) {
148        self::onRegisterTags( $tags );
149    }
150
151    /**
152     * @param UserIdentity $user user who succeeded in editing
153     * @param RevisionRecord $revision revision representing the successful edit
154     */
155    private static function countersOnEditSuccess( UserIdentity $user, RevisionRecord $revision ) {
156        $centralId = Utils::getCentralId( $user );
157
158        // We need to check the underlying request headers to determine if this is an app edit
159        $request = RequestContext::getMain()->getRequest();
160
161        foreach ( self::getCounters() as $counter ) {
162            $counter->onEditSuccess( $centralId, $request, $revision );
163        }
164    }
165
166    /**
167     * @param int $undidRevId revision that was undone
168     * @param WikiPage $wikiPage wiki page that was just edited
169     */
170    private static function countersOnUndo( $undidRevId, $wikiPage ) {
171        $undidRev = MediaWikiServices::getInstance()->getRevisionStore()
172            ->getRevisionById( $undidRevId, IDBAccessObject::READ_LATEST_IMMUTABLE );
173
174        if ( !$undidRev ) {
175            return;
176        }
177
178        $undidUserIdentity = $undidRev->getUser( RevisionRecord::FOR_PUBLIC );
179        if ( !$undidUserIdentity ) {
180            return;
181        }
182
183        // Check that this isn't a spoofed revert (T59474)
184        $undidTitle = Title::newFromLinkTarget( $undidRev->getPageAsLinkTarget() );
185        if ( !$undidTitle->equals( $wikiPage->getTitle() ) ) {
186            return;
187        }
188
189        $undidUserCentralId = Utils::getCentralId( $undidUserIdentity );
190        foreach ( self::getCounters() as $counter ) {
191            $counter->onRevert( $undidUserCentralId, $undidRevId, $undidRev );
192        }
193    }
194
195    /**
196     * @return Counter[]
197     */
198    private static function getCounters() {
199        $counterFactory = WikimediaEditorTasksServices::getInstance()->getCounterFactory();
200        return $counterFactory->createAll( Utils::getEnabledCounters() );
201    }
202
203}