Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 63 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
CampaignHooks | |
0.00% |
0 / 63 |
|
0.00% |
0 / 7 |
306 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onContentModelCanBeUsedOn | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
onPageSaveComplete | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
20 | |||
onLinksUpdateComplete | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
onArticleDeleteComplete | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
onPageMoveComplete | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
onEditFilterMergedContent | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\UploadWizard; |
4 | |
5 | use Content; |
6 | use IContextSource; |
7 | use JsonSchemaException; |
8 | use ManualLogEntry; |
9 | use MediaWiki\Content\Hook\ContentModelCanBeUsedOnHook; |
10 | use MediaWiki\Deferred\LinksUpdate\LinksUpdate; |
11 | use MediaWiki\EditPage\EditPage; |
12 | use MediaWiki\Hook\EditFilterMergedContentHook; |
13 | use MediaWiki\Hook\LinksUpdateCompleteHook; |
14 | use MediaWiki\Hook\PageMoveCompleteHook; |
15 | use MediaWiki\Linker\LinkTarget; |
16 | use MediaWiki\Page\Hook\ArticleDeleteCompleteHook; |
17 | use MediaWiki\Revision\RevisionRecord; |
18 | use MediaWiki\Status\Status; |
19 | use MediaWiki\Storage\EditResult; |
20 | use MediaWiki\Storage\Hook\PageSaveCompleteHook; |
21 | use MediaWiki\Title\Title; |
22 | use MediaWiki\User\User; |
23 | use MediaWiki\User\UserIdentity; |
24 | use Wikimedia\Rdbms\IConnectionProvider; |
25 | use WikiPage; |
26 | |
27 | /** |
28 | * Hooks for managing JSON Schema namespace and content model. |
29 | * |
30 | * @file |
31 | * @ingroup Extensions |
32 | * @ingroup UploadWizard |
33 | * |
34 | * @author Ori Livneh <ori@wikimedia.org> |
35 | */ |
36 | |
37 | class CampaignHooks implements |
38 | ContentModelCanBeUsedOnHook, |
39 | EditFilterMergedContentHook, |
40 | PageSaveCompleteHook, |
41 | ArticleDeleteCompleteHook, |
42 | PageMoveCompleteHook, |
43 | LinksUpdateCompleteHook |
44 | { |
45 | |
46 | /** @var IConnectionProvider */ |
47 | private $dbLoadBalancerFactory; |
48 | |
49 | /** |
50 | * @param IConnectionProvider $dbLoadBalancerFactory |
51 | */ |
52 | public function __construct( IConnectionProvider $dbLoadBalancerFactory ) { |
53 | $this->dbLoadBalancerFactory = $dbLoadBalancerFactory; |
54 | } |
55 | |
56 | /** |
57 | * 'Campaign' content model must be used in, and only in, the 'Campaign' namespace. |
58 | * |
59 | * @param string $contentModel |
60 | * @param Title $title |
61 | * @param bool &$ok |
62 | * @return bool |
63 | */ |
64 | public function onContentModelCanBeUsedOn( $contentModel, $title, &$ok ) { |
65 | $isCampaignModel = $contentModel === 'Campaign'; |
66 | $isCampaignNamespace = $title->inNamespace( NS_CAMPAIGN ); |
67 | if ( $isCampaignModel !== $isCampaignNamespace ) { |
68 | $ok = false; |
69 | return false; |
70 | } |
71 | return true; |
72 | } |
73 | |
74 | /** |
75 | * FIXME: This should be done as a DataUpdate |
76 | * |
77 | * Sets up appropriate entries in the uc_campaigns table for each Campaign |
78 | * Acts everytime a page in the NS_CAMPAIGN namespace is saved |
79 | * |
80 | * @param WikiPage $wikiPage |
81 | * @param UserIdentity $userIdentity |
82 | * @param string $summary |
83 | * @param int $flags |
84 | * @param RevisionRecord $revisionRecord |
85 | * @param EditResult $editResult |
86 | */ |
87 | public function onPageSaveComplete( |
88 | $wikiPage, |
89 | $userIdentity, |
90 | $summary, |
91 | $flags, |
92 | $revisionRecord, |
93 | $editResult |
94 | ) { |
95 | $content = $wikiPage->getContent(); |
96 | if ( !$content instanceof CampaignContent ) { |
97 | return; |
98 | } |
99 | |
100 | $dbw = $this->dbLoadBalancerFactory->getPrimaryDatabase(); |
101 | |
102 | $campaignData = $content->getJsonData(); |
103 | $insertData = [ |
104 | 'campaign_enabled' => $campaignData !== null && $campaignData['enabled'] ? 1 : 0 |
105 | ]; |
106 | $dbw->newInsertQueryBuilder() |
107 | ->insertInto( 'uw_campaigns' ) |
108 | ->row( array_merge( |
109 | [ 'campaign_name' => $wikiPage->getTitle()->getDBkey() ], |
110 | $insertData |
111 | ) ) |
112 | ->onDuplicateKeyUpdate() |
113 | ->uniqueIndexFields( 'campaign_name' ) |
114 | ->set( $insertData ) |
115 | ->caller( __METHOD__ ) |
116 | ->execute(); |
117 | |
118 | $campaign = new Campaign( $wikiPage->getTitle(), $content->getJsonData() ); |
119 | $dbw->onTransactionPreCommitOrIdle( static function () use ( $campaign ) { |
120 | $campaign->invalidateCache(); |
121 | }, __METHOD__ ); |
122 | } |
123 | |
124 | /** |
125 | * Invalidates the cache for a campaign when any of its dependents are edited. The 'dependents' |
126 | * are tracked by entries in the templatelinks table, which are inserted by using the |
127 | * PageContentSaveComplete hook. |
128 | * |
129 | * This is usually run via the Job Queue mechanism. |
130 | * @param LinksUpdate $linksupdate |
131 | * @param mixed $ticket |
132 | */ |
133 | public function onLinksUpdateComplete( $linksupdate, $ticket ) { |
134 | if ( !$linksupdate->getTitle()->inNamespace( NS_CAMPAIGN ) ) { |
135 | return; |
136 | } |
137 | |
138 | $campaign = new Campaign( $linksupdate->getTitle() ); |
139 | $campaign->invalidateCache(); |
140 | } |
141 | |
142 | /** |
143 | * Deletes entries from uc_campaigns table when a Campaign is deleted |
144 | * @param WikiPage $article |
145 | * @param User $user |
146 | * @param string $reason |
147 | * @param int $id |
148 | * @param Content $content |
149 | * @param ManualLogEntry $logEntry |
150 | * @param int $archivedRevisionCount |
151 | */ |
152 | public function onArticleDeleteComplete( |
153 | $article, $user, $reason, $id, $content, $logEntry, $archivedRevisionCount |
154 | ) { |
155 | if ( !$article->getTitle()->inNamespace( NS_CAMPAIGN ) ) { |
156 | return; |
157 | } |
158 | |
159 | $fname = __METHOD__; |
160 | $dbw = $this->dbLoadBalancerFactory->getPrimaryDatabase(); |
161 | $dbw->onTransactionPreCommitOrIdle( static function () use ( $dbw, $article, $fname ) { |
162 | $dbw->newDeleteQueryBuilder() |
163 | ->deleteFrom( 'uw_campaigns' ) |
164 | ->where( [ 'campaign_name' => $article->getTitle()->getDBkey() ] ) |
165 | ->caller( $fname ) |
166 | ->execute(); |
167 | }, $fname ); |
168 | } |
169 | |
170 | /** |
171 | * Update campaign names when the Campaign page moves |
172 | * @param LinkTarget $oldTitle |
173 | * @param LinkTarget $newTitle |
174 | * @param UserIdentity $user |
175 | * @param int $pageid |
176 | * @param int $redirid |
177 | * @param string $reason |
178 | * @param RevisionRecord $revisionRecord |
179 | */ |
180 | public function onPageMoveComplete( |
181 | $oldTitle, |
182 | $newTitle, |
183 | $user, |
184 | $pageid, |
185 | $redirid, |
186 | $reason, |
187 | $revisionRecord |
188 | ): void { |
189 | if ( !$oldTitle->inNamespace( NS_CAMPAIGN ) ) { |
190 | return; |
191 | } |
192 | |
193 | $dbw = $this->dbLoadBalancerFactory->getPrimaryDatabase(); |
194 | $dbw->newUpdateQueryBuilder() |
195 | ->update( 'uw_campaigns' ) |
196 | ->set( [ 'campaign_name' => $newTitle->getDBkey() ] ) |
197 | ->where( [ 'campaign_name' => $oldTitle->getDBkey() ] ) |
198 | ->caller( __METHOD__ ) |
199 | ->execute(); |
200 | } |
201 | |
202 | /** |
203 | * Validates that the revised contents are valid JSON. |
204 | * If not valid, rejects edit with error message. |
205 | * @param IContextSource $context |
206 | * @param Content $content |
207 | * @param Status $status |
208 | * @param string $summary |
209 | * @param User $user |
210 | * @param bool $minoredit |
211 | * @return bool |
212 | */ |
213 | public function onEditFilterMergedContent( IContextSource $context, Content $content, Status $status, $summary, |
214 | User $user, $minoredit |
215 | ) { |
216 | if ( !$context->getTitle()->inNamespace( NS_CAMPAIGN ) |
217 | || !$content instanceof CampaignContent |
218 | ) { |
219 | return true; |
220 | } |
221 | |
222 | try { |
223 | $content->validate(); |
224 | } catch ( JsonSchemaException $e ) { |
225 | $status->fatal( $context->msg( $e->getCode(), $e->args ) ); |
226 | // @todo Remove this line after this extension do not support mediawiki version 1.36 and before |
227 | $status->value = EditPage::AS_HOOK_ERROR_EXPECTED; |
228 | return false; |
229 | } |
230 | |
231 | return true; |
232 | } |
233 | } |