Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
41.46% covered (danger)
41.46%
34 / 82
30.77% covered (danger)
30.77%
4 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
CollaborationListContentHandler
41.46% covered (danger)
41.46%
34 / 82
30.77% covered (danger)
30.77%
4 / 13
82.99
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 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 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
 makeMemberList
100.00% covered (success)
100.00%
20 / 20
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
 supportsDirectApiEditing
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
 preSaveTransform
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
 postMemberList
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3use MediaWiki\Content\Transform\PreSaveTransformParams;
4use MediaWiki\MediaWikiServices;
5
6/**
7 * Content handler for CollaborationListContent.
8 *
9 * We extend TextContentHandler instead of JsonContentHandler since
10 * we do not display this as JSON code except upon request.
11 *
12 * @file
13 */
14class CollaborationListContentHandler extends TextContentHandler {
15    const FORMAT_WIKI = 'text/x-collabkit';
16
17    /**
18     * @param string $modelId
19     * @param string[] $formats
20     */
21    public function __construct(
22        $modelId = 'CollaborationListContent',
23        $formats = [ CONTENT_FORMAT_JSON, CONTENT_FORMAT_TEXT, self::FORMAT_WIKI ]
24    ) {
25        // text/x-collabkit is a format for lists similar to <gallery>.
26        // CONTENT_FORMAT_TEXT is for back-compat with old revs. Could be removed.
27        // @todo Ideally, we'd have the preferred format for editing be
28        // self::FORMAT_WIKI and the preferred format for db be
29        // CONTENT_FORMAT_JSON. Unclear if that's possible.
30        parent::__construct( $modelId, $formats );
31    }
32
33    /**
34     * Can this content handler be used on a given page?
35     *
36     * @param Title $title Page to check
37     * @return bool
38     */
39    public function canBeUsedOn( Title $title ) {
40        global $wgCollaborationListAllowedNamespaces;
41
42        $namespace = $title->getNamespace();
43        return isset( $wgCollaborationListAllowedNamespaces[$namespace] ) &&
44            $wgCollaborationListAllowedNamespaces[$namespace];
45    }
46
47    /**
48     * Takes JSON string and creates a new CollaborationListContent object.
49     *
50     * Validation is intentionally not done at this step, as it is done later.
51     *
52     * @param string $text
53     * @param string|null $format
54     * @return CollaborationListContent
55     * @throws MWContentSerializationException
56     */
57    public function unserializeContent( $text, $format = null ) {
58        $this->checkFormat( $format );
59        if ( $format === self::FORMAT_WIKI ) {
60            $data = CollaborationListContent::convertFromHumanEditable( $text );
61            $text = FormatJson::encode( $data );
62        }
63        $content = new CollaborationListContent( $text );
64        return $content;
65    }
66
67    /**
68     * Serializes the CollaborationListContent object.
69     *
70     * @param Content|CollaborationListContent $content
71     * @param string|null $format
72     * @return string
73     */
74    public function serializeContent( Content $content, $format = null ) {
75        if ( $format === self::FORMAT_WIKI ) {
76            return $content->convertToHumanEditable();
77        }
78        return parent::serializeContent( $content, $format );
79    }
80
81    /**
82     * @return CollaborationListContent
83     */
84    public function makeEmptyContent() {
85        $empty = <<<JSON
86            {
87                "displaymode": "normal",
88                "columns": [
89                    { "items": [] }
90                ],
91                "options": {},
92                "description": ""
93            }
94JSON;
95        return new CollaborationListContent( $empty );
96    }
97
98    /**
99     * Spawns a new "members" list, using the project creator as initial member.
100     *
101     * @param string $username Username without "User:" prefix
102     * @param string $initialDescription The initial description of the list
103     * @return CollaborationListContent
104     */
105    public static function makeMemberList( $username, $initialDescription ) {
106        $linkToUserpage = Title::makeTitleSafe( NS_USER, $username )
107            ->getPrefixedText();
108        $newMemberList = [
109            'displaymode' => 'members',
110            'columns' => [ [
111                'items' => [ [
112                    'title' => $linkToUserpage
113                ] ]
114            ] ],
115            'options' => [
116                'mode' => 'normal'
117            ],
118            'description' => $initialDescription
119        ];
120        $newMemberListJson = FormatJson::encode(
121            $newMemberList,
122            "\t",
123            FormatJson::ALL_OK
124        );
125        return new CollaborationListContent( $newMemberListJson );
126    }
127
128    /**
129     * @return string
130     */
131    protected function getContentClass() {
132        return 'CollaborationListContent';
133    }
134
135    /**
136     * FIXME is this really true?
137     * @return bool
138     */
139    public function isParserCacheSupported() {
140        return true;
141    }
142
143    /**
144     * @return bool
145     */
146    public function supportsDirectApiEditing() {
147        return true;
148    }
149
150    /**
151     * @return bool
152     */
153    public function supportsRedirects() {
154        return true;
155    }
156
157    /**
158     * Turns CollaborationListContent page into redirect
159     *
160     * Note that wikitext redirects are created, as generally, this content model
161     * is used in namespaces that support wikitext, and wikitext redirects are
162     * expected.
163     *
164     * @param Title $destination The page to redirect to
165     * @param string $text Text to include in the redirect.
166     * @return Content
167     */
168    public function makeRedirectContent( Title $destination, $text = '' ) {
169        $handler = MediaWikiServices::getInstance()
170            ->getContentHandlerFactory()
171            ->getContentHandler( CONTENT_MODEL_WIKITEXT );
172        return $handler->makeRedirectContent( $destination, $text );
173    }
174
175    /**
176     * Beautifies JSON and does subst: prior to save.
177     *
178     * @param Content $content
179     * @param PreSaveTransformParams $pstParams
180     * @return CollaborationListContent
181     */
182    public function preSaveTransform( Content $content, PreSaveTransformParams $pstParams ): Content {
183        '@phan-var CollaborationListContent $content';
184        $parser = MediaWikiServices::getInstance()->getParser();
185        // WikiPage::doEditContent invokes PST before validation. As such,
186        // native data may be invalid (though PST result is discarded later in
187        // that case).
188        $text = $content->getText();
189        // pst will hopefully not make json invalid. Def should not.
190        $pst = $parser->preSaveTransform(
191            $text,
192            $pstParams->getPage(),
193            $pstParams->getUser(),
194            $pstParams->getParserOptions()
195        );
196
197        $pstContent = new CollaborationListContent( $pst );
198
199        if ( !$pstContent->isValid() ) {
200            return $content;
201        }
202
203        return new CollaborationListContent( $pstContent->beautifyJSON() );
204    }
205
206    /**
207     * Posts the newly created "members" list on-wiki.
208     *
209     * @param Title $title
210     * @param string $summary
211     * @param IContextSource $context
212     * @todo rework this to use a generic CollaborationList editor function once
213     *  it exists
214     * @return Status
215     */
216    public static function postMemberList( Title $title, $summary,
217        IContextSource $context
218    ) {
219        $username = $context->getUser()->getName();
220        $collabList = self::makeMemberList(
221            $username,
222            $context->msg( 'collaborationkit-hub-members-description' )->text()
223        );
224        // Ensure that a valid context is provided to the API in unit tests
225        $der = new DerivativeContext( $context );
226        $request = new DerivativeRequest(
227            $context->getRequest(),
228            [
229                'action' => 'edit',
230                'title' => $title->getFullText(),
231                'contentmodel' => 'CollaborationListContent',
232                'contentformat' => 'application/json',
233                'text' => $collabList->serialize(),
234                'summary' => $summary,
235                'token' => $context->getUser()->getEditToken(),
236            ],
237            true // Treat data as POSTed
238        );
239        $der->setRequest( $request );
240        try {
241            $api = new ApiMain( $der, true );
242            $api->execute();
243        } catch ( ApiUsageException $e ) {
244            return Status::newFatal(
245                $context->msg( 'collaborationkit-hub-edit-apierror',
246                $e->getMessageObject() )
247            );
248        }
249        return Status::newGood();
250    }
251}