Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 76 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
NewcomerTasksChangeTagsManager | |
0.00% |
0 / 76 |
|
0.00% |
0 / 5 |
306 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
apply | |
0.00% |
0 / 37 |
|
0.00% |
0 / 1 |
30 | |||
checkExistingTags | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
checkUserAccess | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
42 | |||
getTags | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | namespace GrowthExperiments\NewcomerTasks; |
4 | |
5 | use ChangeTags; |
6 | use GrowthExperiments\HomepageModules\SuggestedEdits; |
7 | use GrowthExperiments\NewcomerTasks\ConfigurationLoader\ConfigurationLoader; |
8 | use GrowthExperiments\NewcomerTasks\TaskType\TaskType; |
9 | use GrowthExperiments\NewcomerTasks\TaskType\TaskTypeHandler; |
10 | use GrowthExperiments\NewcomerTasks\TaskType\TaskTypeHandlerRegistry; |
11 | use MediaWiki\Config\Config; |
12 | use MediaWiki\Logger\LoggerFactory; |
13 | use MediaWiki\Revision\RevisionLookup; |
14 | use MediaWiki\User\Options\UserOptionsLookup; |
15 | use MediaWiki\User\UserIdentity; |
16 | use MediaWiki\User\UserIdentityUtils; |
17 | use PrefixingStatsdDataFactoryProxy; |
18 | use RequestContext; |
19 | use StatusValue; |
20 | use Wikimedia\Rdbms\IConnectionProvider; |
21 | |
22 | class NewcomerTasksChangeTagsManager { |
23 | |
24 | /** @var ConfigurationLoader */ |
25 | private $configurationLoader; |
26 | /** @var RevisionLookup */ |
27 | private $revisionLookup; |
28 | /** @var TaskTypeHandlerRegistry */ |
29 | private $taskTypeHandlerRegistry; |
30 | /** @var UserOptionsLookup */ |
31 | private $userOptionsLookup; |
32 | /** @var PrefixingStatsdDataFactoryProxy */ |
33 | private $perDbNameStatsdDataFactory; |
34 | /** @var IConnectionProvider */ |
35 | private $connectionProvider; |
36 | /** @var UserIdentityUtils */ |
37 | private $userIdentityUtils; |
38 | /** @var Config|null */ |
39 | private $config; |
40 | /** @var UserIdentity|null */ |
41 | private $user; |
42 | |
43 | /** |
44 | * @param UserOptionsLookup $userOptionsLookup |
45 | * @param TaskTypeHandlerRegistry $taskTypeHandlerRegistry |
46 | * @param ConfigurationLoader $configurationLoader |
47 | * @param PrefixingStatsdDataFactoryProxy $perDbNameStatsdDataFactory |
48 | * @param RevisionLookup $revisionLookup |
49 | * @param IConnectionProvider $connectionProvider |
50 | * @param UserIdentityUtils $userIdentityUtils |
51 | * @param Config|null $config |
52 | * @param UserIdentity|null $user |
53 | * FIXME $config and $user should be mandatory and injected by a factory |
54 | */ |
55 | public function __construct( |
56 | UserOptionsLookup $userOptionsLookup, |
57 | TaskTypeHandlerRegistry $taskTypeHandlerRegistry, |
58 | ConfigurationLoader $configurationLoader, |
59 | PrefixingStatsdDataFactoryProxy $perDbNameStatsdDataFactory, |
60 | RevisionLookup $revisionLookup, |
61 | IConnectionProvider $connectionProvider, |
62 | UserIdentityUtils $userIdentityUtils, |
63 | Config $config = null, |
64 | UserIdentity $user = null |
65 | ) { |
66 | $this->configurationLoader = $configurationLoader; |
67 | $this->revisionLookup = $revisionLookup; |
68 | $this->taskTypeHandlerRegistry = $taskTypeHandlerRegistry; |
69 | $this->userOptionsLookup = $userOptionsLookup; |
70 | $this->perDbNameStatsdDataFactory = $perDbNameStatsdDataFactory; |
71 | $this->connectionProvider = $connectionProvider; |
72 | $this->userIdentityUtils = $userIdentityUtils; |
73 | $this->config = $config; |
74 | $this->user = $user; |
75 | } |
76 | |
77 | /** |
78 | * Apply change tags to a newcomer task. |
79 | * |
80 | * Note that this should only be used with non-VisualEditor based edits. VE edits are handled via |
81 | * the onVisualEditorApiVisualEditorEditPreSave hook, which also allows for displaying the change |
82 | * tags in the RecentChanges feed. |
83 | * |
84 | * Also note that using this method will set the tags for display in article history but it will |
85 | * not appear in RecentChanges (T24509). |
86 | * |
87 | * @param string $taskTypeId |
88 | * @param int $revisionId |
89 | * @param UserIdentity $userIdentity |
90 | * @return StatusValue |
91 | */ |
92 | public function apply( string $taskTypeId, int $revisionId, UserIdentity $userIdentity ): StatusValue { |
93 | $result = $this->getTags( $taskTypeId, $userIdentity ); |
94 | |
95 | if ( !$result->isGood() ) { |
96 | return $result; |
97 | } |
98 | $tags = $result->getValue(); |
99 | |
100 | $revision = $this->revisionLookup->getRevisionById( $revisionId ); |
101 | if ( !$revision ) { |
102 | return StatusValue::newFatal( $revisionId . ' is not a valid revision ID.' ); |
103 | } |
104 | $revisionUserId = $revision->getUser()->getId(); |
105 | $authorityUserId = $userIdentity->getId(); |
106 | if ( $revisionUserId !== $authorityUserId ) { |
107 | return StatusValue::newFatal( |
108 | sprintf( |
109 | 'User ID %d on revision does not match logged-in user ID %d.', $revisionUserId, $authorityUserId |
110 | ) |
111 | ); |
112 | } |
113 | |
114 | $result = $this->checkExistingTags( $revisionId ); |
115 | if ( !$result->isGood() ) { |
116 | return $result; |
117 | } |
118 | |
119 | $rc_id = null; |
120 | $log_id = null; |
121 | $result = ChangeTags::updateTags( |
122 | $tags, |
123 | null, |
124 | $rc_id, |
125 | $revisionId, |
126 | $log_id, |
127 | null, |
128 | null, |
129 | $userIdentity |
130 | ); |
131 | LoggerFactory::getInstance( 'GrowthExperiments' )->debug( |
132 | 'ChangeTags::updateTags result in NewcomerTaskCompleteHandler: ' . json_encode( $result ) |
133 | ); |
134 | // This is needed for non-VE edits. |
135 | // VE edits are incremented in the post-save VisualEditor hook. |
136 | $this->perDbNameStatsdDataFactory->increment( |
137 | 'GrowthExperiments.NewcomerTask.' . $taskTypeId . '.Save' |
138 | ); |
139 | return StatusValue::newGood( $result ); |
140 | } |
141 | |
142 | /** |
143 | * @param int $revId |
144 | * @return StatusValue |
145 | */ |
146 | private function checkExistingTags( int $revId ): StatusValue { |
147 | $rc_id = null; |
148 | $log_id = null; |
149 | $existingTags = ChangeTags::getTags( |
150 | $this->connectionProvider->getReplicaDatabase(), |
151 | $rc_id, |
152 | $revId, |
153 | $log_id |
154 | ); |
155 | |
156 | // Guard against duplicate submissions, or re-tagging older revisions. |
157 | if ( in_array( TaskTypeHandler::NEWCOMER_TASK_TAG, $existingTags ) ) { |
158 | return StatusValue::newFatal( 'Revision already has newcomer task tag.' ); |
159 | } |
160 | return StatusValue::newGood(); |
161 | } |
162 | |
163 | /** |
164 | * @param UserIdentity $userIdentity |
165 | * @return StatusValue |
166 | */ |
167 | private function checkUserAccess( UserIdentity $userIdentity ): StatusValue { |
168 | if ( !$this->userIdentityUtils->isNamed( $userIdentity ) ) { |
169 | return StatusValue::newFatal( 'You must be logged-in' ); |
170 | } |
171 | if ( !$this->config || !$this->user ) { |
172 | $ctx = RequestContext::getMain(); |
173 | $this->config = $ctx->getConfig(); |
174 | $this->user = $ctx->getUser(); |
175 | } |
176 | if ( !SuggestedEdits::isEnabled( $this->config ) || |
177 | !SuggestedEdits::isActivated( $this->user, $this->userOptionsLookup ) |
178 | ) { |
179 | return StatusValue::newFatal( 'Suggested edits are not enabled or activated for your user.' ); |
180 | } |
181 | return StatusValue::newGood(); |
182 | } |
183 | |
184 | /** |
185 | * @param string $taskTypeId |
186 | * @param UserIdentity $userIdentity |
187 | * @return StatusValue |
188 | */ |
189 | public function getTags( string $taskTypeId, UserIdentity $userIdentity ): StatusValue { |
190 | $result = $this->checkUserAccess( $userIdentity ); |
191 | if ( !$result->isGood() ) { |
192 | return $result; |
193 | } |
194 | $taskType = $this->configurationLoader->getTaskTypes()[$taskTypeId] ?? null; |
195 | if ( !$taskType instanceof TaskType ) { |
196 | return StatusValue::newFatal( 'Invalid task type ID: ' . $taskTypeId ); |
197 | } |
198 | $taskTypeHandler = $this->taskTypeHandlerRegistry->getByTaskType( $taskType ); |
199 | $tags = $taskTypeHandler->getChangeTags( $taskType->getId() ); |
200 | return StatusValue::newGood( $tags ); |
201 | } |
202 | |
203 | } |