Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 172 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
SpecialCreateHubFeature | |
0.00% |
0 / 172 |
|
0.00% |
0 / 7 |
930 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
doesWrites | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
6 | |||
getFormFields | |
0.00% |
0 / 48 |
|
0.00% |
0 / 1 |
30 | |||
onSubmit | |
0.00% |
0 / 107 |
|
0.00% |
0 / 1 |
380 | |||
onSuccess | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDisplayFormat | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | /** |
4 | * Form to create features for Collaboration Hubs |
5 | * Based on code from MassMessage |
6 | * |
7 | * @file |
8 | */ |
9 | |
10 | use MediaWiki\MediaWikiServices; |
11 | |
12 | class SpecialCreateHubFeature extends FormSpecialPage { |
13 | |
14 | public function __construct() { |
15 | parent::__construct( 'CreateHubFeature' ); |
16 | } |
17 | |
18 | public function doesWrites() { |
19 | return true; |
20 | } |
21 | |
22 | /** |
23 | * @param string $par |
24 | */ |
25 | public function execute( $par ) { |
26 | if ( !$this->getUser()->isAllowed( 'createpage' ) ) { |
27 | throw new PermissionsError( 'createpage' ); |
28 | } |
29 | |
30 | $output = $this->getContext()->getOutput(); |
31 | $output->addModules( 'ext.CollaborationKit.iconbrowser' ); |
32 | $output->addModuleStyles( [ |
33 | 'ext.CollaborationKit.createhubfeature.styles', |
34 | 'ext.CollaborationKit.edit.styles' |
35 | ] ); |
36 | $output->addJsConfigVars( |
37 | 'wgCollaborationKitIconList', |
38 | CollaborationKitImage::getCannedIcons() |
39 | ); |
40 | parent::execute( $par ); |
41 | } |
42 | |
43 | /** |
44 | * @return array |
45 | */ |
46 | protected function getFormFields() { |
47 | // Allow collaboration hub to be passed via parameter (full page title) ?collaborationhub= |
48 | // Allow feature name to be passed via parameter (subpage title) ?feature= |
49 | if ( $this->getRequest()->getVal( 'collaborationhub' ) ) { |
50 | $defaultCollabHub = $this->getRequest()->getVal( 'collaborationhub' ); |
51 | } else { |
52 | $defaultCollabHub = ''; |
53 | } |
54 | |
55 | if ( $this->getRequest()->getVal( 'feature' ) ) { |
56 | $defaultFeatureName = $this->getRequest()->getVal( 'feature' ); |
57 | } else { |
58 | $defaultFeatureName = ''; |
59 | } |
60 | |
61 | $icons = CollaborationKitImage::getCannedIcons(); |
62 | $iconChoices = array_combine( $icons, $icons ); |
63 | |
64 | $fields = [ |
65 | 'collaborationhub' => [ |
66 | 'type' => 'title', |
67 | 'cssclass' => 'mw-ck-fulltitle-input', |
68 | 'label-message' => 'collaborationkit-createhubfeature-collaborationhub', |
69 | 'default' => $defaultCollabHub |
70 | ], |
71 | 'featurename' => [ |
72 | 'type' => 'text', |
73 | 'cssclass' => 'mw-ck-display-input', |
74 | 'label-message' => 'collaborationkit-createhubfeature-featurename', |
75 | 'default' => $defaultFeatureName |
76 | ], |
77 | // TODO replace with subclassed image selector |
78 | 'icon' => [ |
79 | 'type' => 'combobox', |
80 | 'cssclass' => 'mw-ck-icon-input', |
81 | 'label-message' => 'collaborationkit-createhubfeature-icon', |
82 | 'help-message' => 'collaborationkit-createhubfeature-icon-help', |
83 | 'options' => $iconChoices, |
84 | 'default' => 'circlestar' |
85 | ], |
86 | 'contenttype' => [ |
87 | 'type' => 'radio', |
88 | 'cssclass' => 'mw-ck-content-type-input', |
89 | 'label-message' => 'collaborationkit-createhubfeature-contenttype', |
90 | 'options' => [ |
91 | $this |
92 | ->msg( 'collaborationkit-createhubfeature-freetext' ) |
93 | ->escaped() => 'wikitext', |
94 | $this |
95 | ->msg( 'collaborationkit-createhubfeature-articlelist' ) |
96 | ->escaped() => 'CollaborationListContent' |
97 | ] |
98 | ] |
99 | ]; |
100 | |
101 | // If either of these fields is set, that means the user came to the |
102 | // special page by way of a special workflow, meaning that the name of |
103 | // the hub and/or the feature is already known. Changing it would cause |
104 | // problems (e.g. the hub saying the feature is called one thing and |
105 | // then the user changes their mind) so we disable further edits in the |
106 | // middle of the workflow. |
107 | if ( $defaultCollabHub != '' ) { |
108 | $fields['collaborationhub']['readonly'] = true; |
109 | } |
110 | if ( $defaultFeatureName != '' ) { |
111 | $fields['featurename']['readonly'] = true; |
112 | } |
113 | |
114 | return $fields; |
115 | } |
116 | |
117 | /** |
118 | * The result of submitting the form |
119 | * |
120 | * @param array $data |
121 | * @return Status |
122 | */ |
123 | public function onSubmit( array $data ) { |
124 | $collaborationHub = $data['collaborationhub']; |
125 | $hubTitleObject = Title::newFromText( $collaborationHub ); |
126 | $permissionManager = MediaWiki\MediaWikiServices::getInstance()->getPermissionManager(); |
127 | $user = $this->getUser(); |
128 | |
129 | // This special page can only be used to create subpages of Collaboration |
130 | // Hubs. This checks if the parent page is one. |
131 | |
132 | if ( !$hubTitleObject->exists() ) { |
133 | return Status::newFatal( 'collaborationkit-createhubfeature-hubdoesnotexist' ); |
134 | } |
135 | |
136 | if ( $hubTitleObject->getContentModel() != 'CollaborationHubContent' ) { |
137 | return Status::newFatal( 'collaborationkit-createhubfeature-hubisnotahub' ); |
138 | } |
139 | |
140 | $featureName = $data['featurename']; |
141 | $titleText = $collaborationHub . '/' . $featureName; |
142 | $title = Title::newFromText( $titleText ); |
143 | $contentModel = $data['contenttype']; |
144 | |
145 | if ( !$title ) { |
146 | return Status::newFatal( 'collaborationkit-createhubfeature-invalidtitle' ); |
147 | } elseif ( $title->exists() ) { |
148 | return Status::newFatal( 'collaborationkit-createhubfeature-exists' ); |
149 | } elseif ( |
150 | !$permissionManager->userCan( 'edit', $user, $title ) |
151 | ) { |
152 | return Status::newFatal( 'collaborationkit-createhubfeature-nopermission' ); |
153 | } elseif ( $contentModel == 'CollaborationListContent' && |
154 | !$permissionManager->userCan( 'editcontentmodel', $user, $title ) |
155 | ) { |
156 | return Status::newFatal( 'collaborationkit-createhubfeature-nopermission-contentmodel' ); |
157 | } |
158 | |
159 | // Create feature |
160 | if ( $contentModel != 'wikitext' |
161 | && $contentModel != 'CollaborationListContent' ) { |
162 | return Status::newFatal( 'collaborationkit-createhubfeature-invalidcontenttype' ); |
163 | } |
164 | |
165 | if ( $contentModel == 'wikitext' ) { |
166 | $contentFormat = 'text/x-wiki'; |
167 | } elseif ( $contentModel == 'CollaborationListContent' ) { |
168 | $contentFormat = 'application/json'; |
169 | } else { |
170 | return Status::newFatal( 'collaborationkit-createhubfeature-invalidcontenttype' ); |
171 | } |
172 | |
173 | // Create empty page by default; exception is if there needs to be |
174 | // something such as JSON. |
175 | $initialContent = ''; |
176 | if ( $contentModel == 'CollaborationListContent' ) { |
177 | $contentModelObj = ContentHandler::getForModelID( $contentModel ); |
178 | // ^ Roan recommends renaming $contentModel to $contentModelID |
179 | $initialContent = $contentModelObj->serializeContent( |
180 | $contentModelObj->makeEmptyContent() |
181 | ); |
182 | } |
183 | |
184 | $summary = $this |
185 | ->msg( 'collaborationkit-createhubfeature-editsummary' ) |
186 | ->plain(); |
187 | |
188 | $context = $this->getContext(); |
189 | $der = new DerivativeContext( $context ); |
190 | $request = new DerivativeRequest( |
191 | $context->getRequest(), |
192 | [ |
193 | 'action' => 'edit', |
194 | 'title' => $title->getFullText(), |
195 | 'contentmodel' => $contentModel, |
196 | 'contentformat' => $contentFormat, |
197 | 'text' => $initialContent, |
198 | 'summary' => $summary, |
199 | 'token' => $context->getUser()->getEditToken(), |
200 | ], |
201 | true // Treat data as POSTed |
202 | ); |
203 | $der->setRequest( $request ); |
204 | try { |
205 | $api = new ApiMain( $der, true ); |
206 | $api->execute(); |
207 | } catch ( ApiUsageException $e ) { |
208 | return Status::newFatal( |
209 | $context->msg( |
210 | 'collaborationkit-hub-edit-apierror', |
211 | $e->getMessageObject() |
212 | ) |
213 | ); |
214 | } |
215 | |
216 | // Update hub with link to new feature |
217 | $newFeature = [ 'title' => $titleText, 'display_title' => $featureName ]; |
218 | |
219 | if ( $data[ 'icon' ] ) { |
220 | $newFeature['image'] = $data[ 'icon' ]; |
221 | } |
222 | |
223 | $hubWikiPageObject = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle( $hubTitleObject ); |
224 | $hubRawContent = $hubWikiPageObject->getContent(); |
225 | $hubContent = json_decode( $hubRawContent->serialize(), true ); |
226 | |
227 | // Don't actually update the hub if the hub includes the feature. |
228 | $found = false; |
229 | // @phan-suppress-next-line PhanTypeArraySuspiciousNullable |
230 | foreach ( $hubContent['content'] as $c ) { |
231 | if ( $c['title'] === $titleText ) { |
232 | $found = true; |
233 | break; |
234 | } |
235 | } |
236 | |
237 | if ( !$found ) { |
238 | $hubContent['content'][] = $newFeature; |
239 | $newHubRawContent = json_encode( $hubContent ); |
240 | $editHubSummary = $this |
241 | ->msg( 'collaborationkit-createhubfeature-hubeditsummary', $featureName ) |
242 | ->plain(); |
243 | |
244 | $context = $this->getContext(); |
245 | $der = new DerivativeContext( $context ); |
246 | $request = new DerivativeRequest( |
247 | $context->getRequest(), |
248 | [ |
249 | 'action' => 'edit', |
250 | 'title' => $hubTitleObject->getFullText(), |
251 | 'contentmodel' => 'CollaborationHubContent', |
252 | 'text' => $newHubRawContent, |
253 | 'summary' => $editHubSummary, |
254 | 'token' => $context->getUser()->getEditToken(), |
255 | ], |
256 | true // Treat data as POSTed |
257 | ); |
258 | $der->setRequest( $request ); |
259 | try { |
260 | $api = new ApiMain( $der, true ); |
261 | $api->execute(); |
262 | } catch ( ApiUsageException $e ) { |
263 | return Status::newFatal( |
264 | $context->msg( |
265 | 'collaborationkit-hub-edit-apierror', |
266 | $e->getMessageObject() |
267 | ) |
268 | ); |
269 | } |
270 | } |
271 | |
272 | // Purge the hub's cache so that it doesn't say "feature does not exist" |
273 | $hubTitleObject->invalidateCache(); |
274 | |
275 | $this->getOutput()->redirect( $title->getFullURL() ); |
276 | |
277 | return Status::newGood(); |
278 | } |
279 | |
280 | public function onSuccess() { |
281 | // No-op: We have already redirected. |
282 | } |
283 | |
284 | /** @inheritDoc */ |
285 | protected function getDisplayFormat() { |
286 | return 'ooui'; |
287 | } |
288 | } |