Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
1.80% covered (danger)
1.80%
2 / 111
18.18% covered (danger)
18.18%
2 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
CollaborationHubContentHandler
1.80% covered (danger)
1.80%
2 / 111
18.18% covered (danger)
18.18%
2 / 11
616.82
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 canBeUsedOn
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 unserializeContent
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 serializeContent
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 makeEmptyContent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getContentClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isParserCacheSupported
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 supportsRedirects
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 makeRedirectContent
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 edit
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
12
 onParserOutputPostCacheTransform
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
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
12use MediaWiki\MediaWikiServices;
13
14class 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            }
94JSON;
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}