Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
1.80% |
2 / 111 |
|
18.18% |
2 / 11 |
CRAP | |
0.00% |
0 / 1 |
CollaborationHubContentHandler | |
1.80% |
2 / 111 |
|
18.18% |
2 / 11 |
616.82 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
canBeUsedOn | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
unserializeContent | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
serializeContent | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
makeEmptyContent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getContentClass | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isParserCacheSupported | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
supportsRedirects | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
makeRedirectContent | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
edit | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
12 | |||
onParserOutputPostCacheTransform | |
0.00% |
0 / 56 |
|
0.00% |
0 / 1 |
90 |
1 | <?php |
2 | |
3 | /** |
4 | * Content handler for CollaborationHubContent. |
5 | * |
6 | * We extend TextContentHandler instead of JsonContentHandler since |
7 | * we do not display this as JSON code except upon request. |
8 | * |
9 | * @file |
10 | */ |
11 | |
12 | use MediaWiki\MediaWikiServices; |
13 | |
14 | class CollaborationHubContentHandler extends TextContentHandler { |
15 | |
16 | const FORMAT_WIKI = 'text/x-collabkit'; |
17 | |
18 | /** |
19 | * @param string $modelId |
20 | * @param string[] $formats |
21 | */ |
22 | public function __construct( |
23 | $modelId = 'CollaborationHubContent', |
24 | $formats = [ CONTENT_FORMAT_JSON, CONTENT_FORMAT_TEXT, self::FORMAT_WIKI ] |
25 | ) { |
26 | // text/x-collabkit is a format for lists similar to <gallery>. |
27 | // CONTENT_FORMAT_TEXT is for back-compat with old revs. Could be removed. |
28 | |
29 | // @todo Ideally, we'd have the preferred format for editing be |
30 | // self::FORMAT_WIKI and the preferred format for db be |
31 | // CONTENT_FORMAT_JSON. Unclear if that's possible. |
32 | parent::__construct( $modelId, $formats ); |
33 | } |
34 | |
35 | /** |
36 | * @param Title $title Page to check |
37 | * @return bool |
38 | */ |
39 | public function canBeUsedOn( Title $title ) { |
40 | global $wgCollaborationHubAllowedNamespaces; |
41 | |
42 | $namespace = $title->getNamespace(); |
43 | $namespaceInfo = MediaWikiServices::getInstance()->getNamespaceInfo(); |
44 | return isset( $wgCollaborationHubAllowedNamespaces[$namespace] ) && |
45 | $wgCollaborationHubAllowedNamespaces[$namespace] && |
46 | $namespaceInfo->hasSubpages( $namespace ); |
47 | } |
48 | |
49 | /** |
50 | * Constructs a CollaborationHubContent object. Does not perform any |
51 | * validation, as that is done at a later step (to allow for outputting of |
52 | * invalid content for debugging purposes.) |
53 | * |
54 | * @param string $text |
55 | * @param string|null $format |
56 | * @return CollaborationHubContent |
57 | */ |
58 | public function unserializeContent( $text, $format = null ) { |
59 | $this->checkFormat( $format ); |
60 | if ( $format === self::FORMAT_WIKI ) { |
61 | $data = CollaborationHubContent::convertFromHumanEditable( $text ); |
62 | $text = FormatJson::encode( $data ); |
63 | } |
64 | $content = new CollaborationHubContent( $text ); |
65 | // Deliberately not validating at this step; validation is done later. |
66 | return $content; |
67 | } |
68 | |
69 | /** |
70 | * Serializes the CollaborationHubContent object. |
71 | * |
72 | * @param Content|CollaborationHubContent $content |
73 | * @param string|null $format |
74 | * @return mixed |
75 | */ |
76 | public function serializeContent( Content $content, $format = null ) { |
77 | if ( $format === self::FORMAT_WIKI ) { |
78 | return $content->convertToHumanEditable(); |
79 | } |
80 | return parent::serializeContent( $content, $format ); |
81 | } |
82 | |
83 | /** |
84 | * @return CollaborationHubContent |
85 | */ |
86 | public function makeEmptyContent() { |
87 | $empty = <<<JSON |
88 | { |
89 | "display_name": "", |
90 | "introduction": "", |
91 | "footer": "", |
92 | "content": [] |
93 | } |
94 | JSON; |
95 | return new CollaborationHubContent( $empty ); |
96 | } |
97 | |
98 | /** |
99 | * @return string |
100 | */ |
101 | protected function getContentClass() { |
102 | return 'CollaborationHubContent'; |
103 | } |
104 | |
105 | /** |
106 | * @return bool |
107 | */ |
108 | public function isParserCacheSupported() { |
109 | return true; |
110 | } |
111 | |
112 | /** |
113 | * @return bool |
114 | */ |
115 | public function supportsRedirects() { |
116 | return true; |
117 | } |
118 | |
119 | /** |
120 | * Turns CollaborationHubContent page into redirect |
121 | * |
122 | * Note that wikitext redirects are created, as generally, this content model |
123 | * is used in namespaces that support wikitext, and wikitext redirects are |
124 | * expected. |
125 | * |
126 | * @param Title $destination The page to redirect to |
127 | * @param string $text Text to include in the redirect. |
128 | * @return Content |
129 | */ |
130 | public function makeRedirectContent( Title $destination, $text = '' ) { |
131 | $handler = MediaWikiServices::getInstance() |
132 | ->getContentHandlerFactory() |
133 | ->getContentHandler( CONTENT_MODEL_WIKITEXT ); |
134 | return $handler->makeRedirectContent( $destination, $text ); |
135 | } |
136 | |
137 | /** |
138 | * Edit a Collaboration Hub via the edit API |
139 | * @param Title $title |
140 | * @param string $displayName |
141 | * @param string $image |
142 | * @param string $colour |
143 | * @param string $introduction |
144 | * @param string $footer |
145 | * @param array $content |
146 | * @param string $summary Message key for edit summary |
147 | * @param IContextSource $context The calling context |
148 | * @return Status |
149 | */ |
150 | public static function edit( Title $title, $displayName, $image, $colour, |
151 | $introduction, $footer, $content, $summary, IContextSource $context |
152 | ) { |
153 | $contentBlock = [ |
154 | 'display_name' => $displayName, |
155 | 'introduction' => $introduction, |
156 | 'footer' => $footer, |
157 | 'image' => $image, |
158 | 'colour' => $colour, |
159 | 'content' => $content |
160 | ]; |
161 | |
162 | // TODO Do content |
163 | |
164 | $jsonText = FormatJson::encode( $contentBlock ); |
165 | if ( $jsonText === null ) { |
166 | return Status::newFatal( 'collaborationkit-hub-edit-tojsonerror' ); |
167 | } |
168 | |
169 | // Ensure that a valid context is provided to the API in unit tests |
170 | $der = new DerivativeContext( $context ); |
171 | $request = new DerivativeRequest( |
172 | $context->getRequest(), |
173 | [ |
174 | 'action' => 'edit', |
175 | 'title' => $title->getFullText(), |
176 | 'contentmodel' => 'CollaborationHubContent', |
177 | 'text' => $jsonText, |
178 | 'summary' => $summary, |
179 | 'token' => $context->getUser()->getEditToken(), |
180 | ], |
181 | true // Treat data as POSTed |
182 | ); |
183 | $der->setRequest( $request ); |
184 | |
185 | try { |
186 | $api = new ApiMain( $der, true ); |
187 | $api->execute(); |
188 | } catch ( ApiUsageException $e ) { |
189 | return Status::newFatal( |
190 | $context->msg( 'collaborationkit-hub-edit-apierror', |
191 | $e->getMessageObject() ) ); |
192 | } |
193 | return Status::newGood(); |
194 | } |
195 | |
196 | /** |
197 | * Post-parse out our button markers for uncachable permissions-dependent actions and stuff |
198 | * |
199 | * @param ParserOutput $parserOutput |
200 | * @param string &$text The text being transformed, before core transformations are done |
201 | * @param array &$options The options array being used for the transformation. |
202 | * |
203 | * @return bool |
204 | */ |
205 | public static function onParserOutputPostCacheTransform( $parserOutput, &$text, &$options ) { |
206 | // Maybe not blindly do this on every getText ever |
207 | if ( !$parserOutput->getExtensionData( 'ck-editmarkers' ) ) { |
208 | return true; |
209 | } |
210 | |
211 | // This is a tad dumb, but if it doesn't follow this format exactly it didn't come |
212 | // from here anyway. Or we broke it. Either or. |
213 | $regex = '#<ext:ck:editmarker page="(.*?)"target="(.*?)"message="(.*?)"link="(.*?)"' |
214 | . 'classes="(.*?)"icon="(.*?)"framed="(.*?)"primary="(.*?)"' |
215 | . '(.*?)/>#s'; |
216 | $text = preg_replace_callback( |
217 | $regex, |
218 | static function ( $m ) { |
219 | $user = RequestContext::getMain()->getUser(); |
220 | $permissionManager = MediaWiki\MediaWikiServices::getInstance()->getPermissionManager(); |
221 | |
222 | $currentPage = Title::newFromText( htmlspecialchars_decode( $m[1] ) ); |
223 | $targetPage = Title::newFromText( htmlspecialchars_decode( $m[2] ) ); |
224 | |
225 | if ( $permissionManager->userCan( 'edit', $user, $targetPage ) ) { |
226 | $message = htmlspecialchars_decode( $m[3] ); |
227 | |
228 | // more checks for various combinations of pages we need to |
229 | // edit/create |
230 | // in particular we need to be able to both create a new |
231 | // subpage AND edit currentpage to flat-out add a new |
232 | // feature... |
233 | // create right check covered throught edit right check |
234 | if ( $message === 'collaborationkit-hub-addpage' && |
235 | !$permissionManager->userCan( 'edit', $user, $currentPage ) |
236 | ) { |
237 | return ''; |
238 | } |
239 | |
240 | $link = $m[4]; |
241 | $classes = $m[5]; |
242 | |
243 | $icon = null; |
244 | if ( $m[6] !== '0' ) { |
245 | $icon = htmlspecialchars_decode( $m[6] ); |
246 | } |
247 | |
248 | $framed = false; |
249 | if ( $m[7] === '1' ) { |
250 | $framed = true; |
251 | } |
252 | |
253 | $flags = [ 'progressive' ]; |
254 | if ( $m[8] === '1' ) { |
255 | $flags[] = 'primary'; |
256 | } |
257 | |
258 | return new OOUI\ButtonWidget( [ |
259 | 'label' => wfMessage( $message )->inContentLanguage()->text(), |
260 | 'href' => $link, |
261 | 'framed' => $framed, |
262 | 'icon' => $icon, |
263 | 'flags' => $flags, |
264 | 'classes' => [ $classes ] |
265 | ] ); |
266 | } |
267 | |
268 | return ''; |
269 | }, |
270 | $text |
271 | ); |
272 | |
273 | // missing page message |
274 | $text = preg_replace_callback( |
275 | '#<ext:ck:missingfeature-note target="(.*?)"(.*?)/>#s', |
276 | static function ( $m ) { |
277 | $user = RequestContext::getMain()->getUser(); |
278 | $permissionManager = MediaWiki\MediaWikiServices::getInstance()->getPermissionManager(); |
279 | $targetPage = Title::newFromText( htmlspecialchars_decode( $m[1] ) ); |
280 | |
281 | if ( $permissionManager->userCan( 'create', $user, $targetPage ) ) { |
282 | return wfMessage( 'collaborationkit-hub-missingpage-note' ) |
283 | ->inContentLanguage() |
284 | ->parse(); |
285 | } else { |
286 | return wfMessage( 'collaborationkit-hub-missingpage-protected-note' ) |
287 | ->inContentLanguage() |
288 | ->parse(); |
289 | } |
290 | }, |
291 | $text |
292 | ); |
293 | } |
294 | } |