Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 79 |
|
0.00% |
0 / 11 |
CRAP | |
0.00% |
0 / 1 |
BoardContentHandler | |
0.00% |
0 / 79 |
|
0.00% |
0 / 11 |
650 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getDiffEngineClass | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isSupportedFormat | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
serializeContent | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
unserializeContent | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
makeEmptyContent | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
canBeUsedOn | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getActionOverrides | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
30 | |||
fillParserOutput | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
30 | |||
generateHtml | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
2 | |||
getWorkflowLoader | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace Flow\Content; |
4 | |
5 | use Flow\Actions\FlowAction; |
6 | use Flow\Container; |
7 | use Flow\Diff\FlowBoardContentDiffView; |
8 | use Flow\FlowActions; |
9 | use Flow\LinksTableUpdater; |
10 | use Flow\Model\UUID; |
11 | use Flow\View; |
12 | use Flow\WorkflowLoaderFactory; |
13 | use InvalidArgumentException; |
14 | use MediaWiki\Content\Content; |
15 | use MediaWiki\Content\ContentHandler; |
16 | use MediaWiki\Content\Renderer\ContentParseParams; |
17 | use MediaWiki\Context\DerivativeContext; |
18 | use MediaWiki\Context\IContextSource; |
19 | use MediaWiki\Context\RequestContext; |
20 | use MediaWiki\Json\FormatJson; |
21 | use MediaWiki\MediaWikiServices; |
22 | use MediaWiki\Output\OutputPage; |
23 | use MediaWiki\Page\Article; |
24 | use MediaWiki\Parser\ParserOutput; |
25 | use MediaWiki\Request\FauxRequest; |
26 | use MediaWiki\Title\Title; |
27 | use MediaWiki\User\User; |
28 | |
29 | class BoardContentHandler extends ContentHandler { |
30 | public function __construct( $modelId ) { |
31 | if ( $modelId !== CONTENT_MODEL_FLOW_BOARD ) { |
32 | throw new InvalidArgumentException( __CLASS__ . " initialised for invalid content model" ); |
33 | } |
34 | |
35 | parent::__construct( CONTENT_MODEL_FLOW_BOARD, [ CONTENT_FORMAT_JSON ] ); |
36 | } |
37 | |
38 | protected function getDiffEngineClass() { |
39 | return FlowBoardContentDiffView::class; |
40 | } |
41 | |
42 | public function isSupportedFormat( $format ) { |
43 | // Necessary for backwards-compatability where |
44 | // the format "json" was used |
45 | if ( $format === 'json' ) { |
46 | $format = CONTENT_FORMAT_JSON; |
47 | } |
48 | |
49 | return parent::isSupportedFormat( $format ); |
50 | } |
51 | |
52 | /** |
53 | * Serializes a Content object of the type supported by this ContentHandler. |
54 | * |
55 | * @since 1.21 |
56 | * |
57 | * @param Content $content The Content object to serialize |
58 | * @param string|null $format The desired serialization format |
59 | * @return string Serialized form of the content |
60 | */ |
61 | public function serializeContent( Content $content, $format = null ) { |
62 | if ( !$content instanceof BoardContent ) { |
63 | throw new InvalidArgumentException( "Expected a BoardContent object, got a " . get_class( $content ) ); |
64 | } |
65 | |
66 | $info = []; |
67 | |
68 | if ( $content->getWorkflowId() ) { |
69 | $info['flow-workflow'] = $content->getWorkflowId()->getAlphadecimal(); |
70 | } |
71 | |
72 | return FormatJson::encode( $info ); |
73 | } |
74 | |
75 | /** |
76 | * Unserializes a Content object of the type supported by this ContentHandler. |
77 | * |
78 | * @since 1.21 |
79 | * |
80 | * @param string $blob Serialized form of the content |
81 | * @param string|null $format The format used for serialization |
82 | * |
83 | * @return BoardContent The Content object created by deserializing $blob |
84 | */ |
85 | public function unserializeContent( $blob, $format = null ) { |
86 | $info = FormatJson::decode( $blob, true ); |
87 | $uuid = null; |
88 | |
89 | if ( !$info ) { |
90 | // Temporary: Fix T167198 and instead throw an exception, to |
91 | // prevent corruption from software that does not understand |
92 | // Flow/content models. |
93 | |
94 | return $this->makeEmptyContent(); |
95 | } elseif ( isset( $info['flow-workflow'] ) ) { |
96 | $uuid = UUID::create( $info['flow-workflow'] ); |
97 | } |
98 | |
99 | return new BoardContent( CONTENT_MODEL_FLOW_BOARD, $uuid ); |
100 | } |
101 | |
102 | /** |
103 | * Creates an empty Content object of the type supported by this |
104 | * ContentHandler. |
105 | * |
106 | * @since 1.21 |
107 | * |
108 | * @return BoardContent |
109 | */ |
110 | public function makeEmptyContent() { |
111 | return new BoardContent; |
112 | } |
113 | |
114 | /** |
115 | * Don't let people turn random pages into Flow ones. They either need to be: |
116 | * * in a Flow-enabled namespace already (where content model is flow-board by |
117 | * default). In such a namespace, non-existent pages are created as Flow. |
118 | * * explicitly allowed for a user, requiring special permissions |
119 | * |
120 | * @param Title $title |
121 | * @return bool |
122 | */ |
123 | public function canBeUsedOn( Title $title ) { |
124 | /** @var \Flow\TalkpageManager $manager */ |
125 | $manager = Container::get( 'occupation_controller' ); |
126 | |
127 | /** @var User $user */ |
128 | $user = Container::get( 'user' ); |
129 | |
130 | return $manager->canBeUsedOn( $title, $user ); |
131 | } |
132 | |
133 | /** |
134 | * Returns overrides for action handlers. |
135 | * Classes listed here will be used instead of the default one when |
136 | * (and only when) $wgActions[$action] === true. This allows subclasses |
137 | * to override the default action handlers. |
138 | * |
139 | * @since 1.21 |
140 | * |
141 | * @return array Associative array mapping action names to handler callables |
142 | */ |
143 | public function getActionOverrides() { |
144 | /** @var FlowActions $actions */ |
145 | $actions = Container::get( 'flow_actions' ); |
146 | $output = []; |
147 | |
148 | foreach ( $actions->getActions() as $action ) { |
149 | $actionData = $actions->getValue( $action ); |
150 | if ( !is_array( $actionData ) ) { |
151 | continue; |
152 | } |
153 | |
154 | if ( !isset( $actionData['handler-class'] ) ) { |
155 | continue; |
156 | } |
157 | |
158 | if ( $actionData['handler-class'] === FlowAction::class ) { |
159 | $output[$action] = static function ( |
160 | Article $article, |
161 | IContextSource $source |
162 | ) use ( $action ) { |
163 | return new FlowAction( $article, $source, $action ); |
164 | }; |
165 | } else { |
166 | $output[$action] = $actionData['handler-class']; |
167 | } |
168 | } |
169 | |
170 | // Flow has its own handling for action=edit |
171 | $output['edit'] = \Flow\Actions\EditAction::class; |
172 | |
173 | return $output; |
174 | } |
175 | |
176 | /** |
177 | * @inheritDoc |
178 | */ |
179 | protected function fillParserOutput( |
180 | Content $content, |
181 | ContentParseParams $cpoParams, |
182 | ParserOutput &$output |
183 | ) { |
184 | '@phan-var BoardContent $content'; |
185 | $parserOptions = $cpoParams->getParserOptions(); |
186 | $revId = $cpoParams->getRevId(); |
187 | $title = Title::castFromPageReference( $cpoParams->getPage() ) |
188 | ?: Title::makeTitle( NS_MEDIAWIKI, 'BadTitle/Flow' ); |
189 | if ( $cpoParams->getGenerateHtml() ) { |
190 | try { |
191 | $user = MediaWikiServices::getInstance() |
192 | ->getUserFactory() |
193 | ->newFromUserIdentity( $parserOptions->getUserIdentity() ); |
194 | $this->generateHtml( $title, $user, $content, $output ); |
195 | } catch ( \Exception $e ) { |
196 | // Workflow does not yet exist (may be in the process of being created) |
197 | $output->setText( '' ); |
198 | } |
199 | } |
200 | |
201 | $output->updateCacheExpiry( 0 ); |
202 | |
203 | if ( $revId === null ) { |
204 | $wikiPage = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle( $title ); |
205 | $timestamp = $wikiPage->getTimestamp(); |
206 | } else { |
207 | $timestamp = MediaWikiServices::getInstance()->getRevisionLookup() |
208 | ->getTimestampFromId( $revId ); |
209 | } |
210 | |
211 | $output->setTimestamp( $timestamp ); |
212 | |
213 | /** @var LinksTableUpdater $updater */ |
214 | $updater = Container::get( 'reference.updater.links-tables' ); |
215 | $updater->mutateParserOutput( $title, $output ); |
216 | } |
217 | |
218 | /** |
219 | * @param Title $title |
220 | * @param User $user |
221 | * @param BoardContent $content |
222 | * @param ParserOutput $output |
223 | */ |
224 | protected function generateHtml( |
225 | Title $title, |
226 | User $user, |
227 | BoardContent $content, |
228 | ParserOutput $output |
229 | ) { |
230 | // Set up a derivative context (which inherits the current request) |
231 | // to hold the output modules + text |
232 | $childContext = new DerivativeContext( RequestContext::getMain() ); |
233 | $childContext->setOutput( new OutputPage( $childContext ) ); |
234 | $childContext->setRequest( new FauxRequest ); |
235 | $childContext->setUser( $user ); |
236 | |
237 | // Create a View set up to output to our derivative context |
238 | $view = new View( |
239 | Container::get( 'url_generator' ), |
240 | Container::get( 'lightncandy' ), |
241 | $childContext->getOutput(), |
242 | Container::get( 'flow_actions' ) |
243 | ); |
244 | |
245 | $loader = $this->getWorkflowLoader( $title, $content ); |
246 | $view->show( $loader, 'view' ); |
247 | |
248 | // Extract data from derivative context |
249 | $output->setText( $childContext->getOutput()->getHTML() ); |
250 | $output->addModules( $childContext->getOutput()->getModules() ); |
251 | $output->addModuleStyles( $childContext->getOutput()->getModuleStyles() ); |
252 | } |
253 | |
254 | /** |
255 | * @param Title $title |
256 | * @param BoardContent $content |
257 | * @return \Flow\WorkflowLoader |
258 | * @throws \Flow\Exception\CrossWikiException |
259 | * @throws \Flow\Exception\InvalidInputException |
260 | */ |
261 | protected function getWorkflowLoader( Title $title, BoardContent $content ) { |
262 | /** @var WorkflowLoaderFactory $factory */ |
263 | $factory = Container::get( 'factory.loader.workflow' ); |
264 | return $factory->createWorkflowLoader( $title, $content->getWorkflowId() ); |
265 | } |
266 | } |