Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.73% covered (warning)
86.73%
85 / 98
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
RevisionFromEditCompleteHookHandler
86.73% covered (warning)
86.73%
85 / 98
33.33% covered (danger)
33.33%
1 / 3
17.67
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 handle
87.27% covered (warning)
87.27%
48 / 55
0.00% covered (danger)
0.00%
0 / 1
12.30
 insertAutoModeratorSendRevertTalkPageMsgJob
82.35% covered (warning)
82.35%
28 / 34
0.00% covered (danger)
0.00%
0 / 1
4.09
1<?php
2/**
3 * Copyright (C) 2016 Brad Jorsch <bjorsch@wikimedia.org>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19namespace AutoModerator\Hooks;
20
21use AutoModerator\RevisionCheck;
22use AutoModerator\Services\AutoModeratorFetchRevScoreJob;
23use AutoModerator\Services\AutoModeratorSendRevertTalkPageMsgJob;
24use AutoModerator\Util;
25use Exception;
26use JobQueueGroup;
27use MediaWiki\Config\Config;
28use MediaWiki\Content\ContentHandlerFactory;
29use MediaWiki\Logger\LoggerFactory;
30use MediaWiki\MainConfigNames;
31use MediaWiki\Page\WikiPageFactory;
32use MediaWiki\Permissions\RestrictionStore;
33use MediaWiki\Revision\RevisionRecord;
34use MediaWiki\Revision\RevisionStore;
35use MediaWiki\Title\Title;
36use MediaWiki\Title\TitleFactory;
37use MediaWiki\User\User;
38use MediaWiki\User\UserGroupManager;
39use MediaWiki\User\UserIdentity;
40use Psr\Log\LoggerInterface;
41use WikiPage;
42
43class RevisionFromEditCompleteHookHandler {
44
45    private Config $wikiConfig;
46
47    private UserGroupManager $userGroupManager;
48
49    private Config $config;
50
51    private WikiPageFactory $wikiPageFactory;
52
53    private RevisionStore $revisionStore;
54
55    private ContentHandlerFactory $contentHandlerFactory;
56
57    private RestrictionStore $restrictionStore;
58
59    private JobQueueGroup $jobQueueGroup;
60
61    private TitleFactory $titleFactory;
62
63    /**
64     * @param Config $wikiConfig
65     * @param UserGroupManager $userGroupManager
66     * @param Config $config
67     * @param WikiPageFactory $wikiPageFactory
68     * @param RevisionStore $revisionStore
69     * @param ContentHandlerFactory $contentHandlerFactory
70     * @param RestrictionStore $restrictionStore
71     * @param JobQueueGroup $jobQueueGroup
72     * @param TitleFactory $titleFactory
73     */
74    public function __construct( Config $wikiConfig, UserGroupManager $userGroupManager, Config $config,
75        WikiPageFactory $wikiPageFactory, RevisionStore $revisionStore, ContentHandlerFactory $contentHandlerFactory,
76        RestrictionStore $restrictionStore, JobQueueGroup $jobQueueGroup, TitleFactory $titleFactory ) {
77            $this->wikiConfig = $wikiConfig;
78            $this->userGroupManager = $userGroupManager;
79            $this->config = $config;
80            $this->wikiPageFactory = $wikiPageFactory;
81            $this->revisionStore = $revisionStore;
82            $this->contentHandlerFactory = $contentHandlerFactory;
83            $this->restrictionStore = $restrictionStore;
84            $this->titleFactory = $titleFactory;
85            $this->jobQueueGroup = $jobQueueGroup;
86    }
87
88    /**
89     * @param WikiPage|null $wikiPage
90     * @param RevisionRecord|null $rev
91     * @param int|false $originalRevId
92     * @param UserIdentity|null $user
93     * @param string[] &$tags
94     */
95    public function handle(
96        $wikiPage, $rev, $originalRevId, $user, &$tags
97    ) {
98        if ( !$wikiPage || !$rev || !$user ) {
99            return;
100        }
101        if ( !$this->wikiConfig->get( 'AutoModeratorEnableRevisionCheck' ) ) {
102            return;
103        }
104        $autoModeratorUser = Util::getAutoModeratorUser( $this->config, $this->userGroupManager );
105        $userId = $user->getId();
106        $logger = LoggerFactory::getInstance( 'AutoModerator' );
107        $title = $wikiPage->getTitle();
108        $wikiPageId = $wikiPage->getId();
109        $revId = $rev->getId();
110        if ( $autoModeratorUser->getId() === $userId && in_array( 'mw-undo', $tags ) ) {
111            if ( $this->wikiConfig->get( 'AutoModeratorRevertTalkPageMessageEnabled' ) ) {
112                $this->insertAutoModeratorSendRevertTalkPageMsgJob(
113                    $title,
114                    $wikiPageId,
115                    $revId,
116                    $autoModeratorUser,
117                    $logger );
118            }
119            return;
120        }
121        if ( !RevisionCheck::revertPreCheck(
122            $user,
123            $autoModeratorUser,
124            $logger,
125            $this->revisionStore,
126            $tags,
127            $this->restrictionStore,
128            $this->wikiPageFactory,
129            $this->userGroupManager,
130            $this->wikiConfig,
131            $revId,
132            $wikiPageId ) ) {
133            return;
134        }
135        $undoSummaryMessageKey = ( !$user->isRegistered() && $this->config->get( MainConfigNames::DisableAnonTalk ) )
136            ? 'automoderator-wiki-undo-summary-anon' : 'automoderator-wiki-undo-summary';
137        $job = new AutoModeratorFetchRevScoreJob( $title,
138            [
139                'wikiPageId' => $wikiPageId,
140                'revId' => $revId,
141                'originalRevId' => $originalRevId,
142                // The test/production environments do not work when you pass the entire User object.
143                // To get around this, we have split the required parameters from the User object
144                // into individual parameters so that the test/production Job constructor will accept them.
145                'userId' => $userId,
146                'userName' => $user->getName(),
147                'tags' => $tags,
148                'undoSummary' => wfMessage( $undoSummaryMessageKey )->rawParams( $revId, $user->getName() )->plain()
149            ]
150        );
151        try {
152            $this->jobQueueGroup->lazyPush( $job );
153            $logger->debug( 'Job pushed for {rev}', [
154                'rev' => $revId,
155            ] );
156        } catch ( Exception $e ) {
157            $msg = $e->getMessage();
158            $logger->error( 'Job push failed for {rev}: {msg}', [
159                'rev' => $revId,
160                'msg' => $msg
161            ] );
162        }
163    }
164
165    /**
166     * @param Title $title
167     * @param int $wikiPageId
168     * @param int|null $revId
169     * @param User $autoModeratorUser
170     * @param LoggerInterface $logger
171     * @return void
172     */
173    public function insertAutoModeratorSendRevertTalkPageMsgJob(
174        Title $title,
175        int $wikiPageId,
176        ?int $revId,
177        User $autoModeratorUser,
178        LoggerInterface $logger ): void {
179        try {
180            $rev = $this->revisionStore->getRevisionById( $revId );
181            if ( $rev === null ) {
182                $logger->debug( "AutoModerator skip rev" . __METHOD__ . " - new page creation" );
183                return;
184            }
185            $parentRevId = $rev->getParentId();
186            if ( $parentRevId === null ) {
187                $logger->debug( "AutoModerator skip rev" . __METHOD__ . " - new page creation" );
188                return;
189            }
190            $userTalkPageJob = new AutoModeratorSendRevertTalkPageMsgJob(
191                $title,
192                [
193                    'wikiPageId' => $wikiPageId,
194                    'revId' => $revId,
195                    'parentRevId' => $parentRevId,
196                    // The test/production environments do not work when you pass the entire User object.
197                    // To get around this, we have split the required parameters from the User object
198                    // into individual parameters so that the test/production Job constructor will accept them.
199                    'autoModeratorUserId' => $autoModeratorUser->getId(),
200                    'autoModeratorUserName' => $autoModeratorUser->getName(),
201                    'talkPageMessageHeader' => wfMessage( 'automoderator-wiki-revert-message-header' )
202                        ->params( $autoModeratorUser->getName() )->plain(),
203                    'talkPageMessageEditSummary' => wfMessage( 'automoderator-wiki-revert-edit-summary' )
204                        ->params( $title )->plain(),
205                    'falsePositiveReportPageId' => $this->wikiConfig->get( "AutoModeratorFalsePositivePageTitle" ),
206                    'wikiId' => Util::getWikiID( $this->config ),
207                ]
208            );
209            $this->jobQueueGroup->push( $userTalkPageJob );
210            $logger->debug( 'AutoModeratorSendRevertTalkPageMsgJob pushed for {rev}', [
211                'rev' => $revId,
212            ] );
213        } catch ( Exception $e ) {
214            $msg = $e->getMessage();
215            $logger->error( 'AutoModeratorSendRevertTalkPageMsgJob push failed for {rev}: {msg}', [
216                'rev' => $revId,
217                'msg' => $msg
218            ] );
219        }
220    }
221}