Completed
Branch master (ecb46d)
by Tobias
01:39
created

ParserOutputHelper::renderErrorMessage()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.0123

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 8
cts 9
cp 0.8889
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 2
nop 1
crap 3.0123
1
<?php
2
/**
3
 * Contains the class augmenting the parser output.
4
 *
5
 * @copyright (C) 2018, Tobias Oetterer, Paderborn University
6
 * @license       https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License, version 3 (or later)
7
 *
8
 * This file is part of the MediaWiki extension BootstrapComponents.
9
 * The BootstrapComponents extension is free software: you can redistribute it
10
 * and/or modify it under the terms of the GNU General Public License as published
11
 * by the Free Software Foundation, either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * The BootstrapComponents extension is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21
 *
22
 * @file
23
 * @ingroup       BootstrapComponents
24
 * @author        Tobias Oetterer
25
 */
26
27
namespace BootstrapComponents;
28
29
use \Html;
30
use \ParserOutput;
31
use \RequestContext;
32
use \Title;
33
34
/**
35
 * Class ParserOutputHelper
36
 *
37
 * Performs all the adaptions on the ParserOutput
38
 *
39
 * @since 1.0
40
 */
41
class ParserOutputHelper {
42
43
	/**
44
	 * @var string
45
	 */
46
	const INJECTION_PREFIX = '<!-- injected by Extension:BootstrapComponents -->';
47
48
	/**
49
	 * @var string
50
	 */
51
	const INJECTION_SUFFIX = '<!-- /injected by Extension:BootstrapComponents -->';
52
53
	/**
54
	 * To make sure, we only add the tracking category once.
55
	 *
56
	 * @var bool $articleTracked
57
	 */
58
	private $articleTracked;
59
60
	/**
61
	 * To make sure, we only add the error tracking category once.
62
	 *
63
	 * @var bool $articleTrackedOnError
64
	 */
65
	private $articleTrackedOnError;
66
67
	/**
68
	 * Here, components can store html to be added to the page at a later time.
69
	 *
70
	 * @var string $contentForLaterInjection
71
	 */
72
	private $contentForLaterInjection;
73
74
	/**
75
	 * Holds the name of the skin we use (or false, if there is no skin).
76
	 *
77
	 * @var string $nameOfActiveSkin
78
	 */
79
	private $nameOfActiveSkin;
80
81
	/**
82
	 * @var \Parser $parser
83
	 */
84
	private $parser;
85
86
87
	/**
88
	 * ParserOutputHelper constructor.
89
	 *
90
	 * Do not instantiate directly, but use {@see ApplicationFactory::getParserOutputHelper} instead.
91
	 *
92
	 * @param \Parser $parser
93
	 *
94
	 * @see ApplicationFactory::getParserOutputHelper
95
	 */
96 11
	public function __construct( $parser ) {
97 11
		$this->articleTracked = false;
98 11
		$this->articleTrackedOnError = false;
99 11
		$this->parser = $parser;
100 11
		$this->nameOfActiveSkin = $this->detectSkinInUse();
101 11
		$this->contentForLaterInjection = '';
102 11
	}
103
104
	/**
105
	 * Adds the error tracking category to the current page if not done already.
106
	 */
107 11
	public function addErrorTrackingCategory() {
108 11
		if ( $this->articleTrackedOnError ) {
109 11
			return;
110
		}
111 1
		$this->placeTrackingCategory( 'bootstrap-components-error-tracking-category' );
112 1
		$this->articleTrackedOnError = true;
113 1
	}
114
115
	/**
116
	 * Adds the supplied modules to the parser output.
117
	 *
118
	 * @param array $modulesToAdd
119
	 */
120 20
	public function addModules( $modulesToAdd ) {
121 20
		$parserOutput = $this->getParser()->getOutput();
122 20
		if ( is_a( $parserOutput, ParserOutput::class ) ) {
123
			// Q: when do we expect \Parser->getOutput() no to be a \ParserOutput? A:During tests.
124 20
			$parserOutput->addModules( $modulesToAdd );
125 20
		}
126 20
	}
127
128
	/**
129
	 * Adds the tracking category to the current page if not done already.
130
	 */
131 18
	public function addTrackingCategory() {
132 18
		if ( $this->articleTracked ) {
133 18
			return;
134
		}
135 1
		$this->placeTrackingCategory( 'bootstrap-components-tracking-category' );
136 1
		$this->articleTracked = true;
137 1
	}
138
139
	/**
140
	 * Unless I find a solution for the integration test problem, I cannot use an instance of
141
	 * ParserOutputHelper in ImageModal to ascertain this. In integration tests, "we" use a
142
	 * different parser than the InternalParseBeforeLinks-Hook. At least, after I added
143
	 * Scribunto _unit_ tests. All messes up, I'm afraid. ImageModal better use global parser, and
144
	 * for the time being this method will be
145
	 * @deprecated
146
	 *
147
	 * @return bool|null
148
	 */
149
	public function areImageModalsSuppressed() {
150
		return $this->getParser()->getOutput()->getExtensionData( 'bsc_no_image_modal' );
151
	}
152
153
	/**
154
	 * Returns the raw html that is be inserted at the end of the page.
155
	 *
156
	 * @return string
157
	 */
158 24
	public function getContentForLaterInjection() {
159 24
		if ( $this->contentForLaterInjection == '' ) {
160 23
			return '';
161
		}
162 7
		$ret = self::INJECTION_PREFIX . $this->contentForLaterInjection . self::INJECTION_SUFFIX;
163
		// clear the stored injection content, so that integration tests can run correctly
164 7
		$this->contentForLaterInjection = '';
165 7
		return $ret;
166
	}
167
168
	/**
169
	 * @return string
170
	 */
171 22
	public function getNameOfActiveSkin() {
172 22
		return $this->nameOfActiveSkin;
173
	}
174
175
	/**
176
	 * Allows to store html that will be added to the page at a later time.
177
	 *
178
	 * @param string $rawHtml
179
	 *
180
	 * @return ParserOutputHelper $this (fluid)
181
	 */
182 10
	public function injectLater( $rawHtml ) {
183 10
		if ( !empty( $rawHtml ) ) {
184 7
			$this->contentForLaterInjection .= $rawHtml;
185 7
		}
186 10
		return $this;
187
	}
188
189
	/**
190
	 * Adds the bootstrap modules and styles to the page, if not done already
191
	 */
192 20
	public function loadBootstrapModules() {
193 20
		$parserOutput = $this->getParser()->getOutput();
194 20
		if ( is_a( $parserOutput, ParserOutput::class ) ) {
195
			// Q: when do we expect \Parser->getOutput() no to be a \ParserOutput? A:During tests.
196 20
			$parserOutput->addModuleStyles( 'ext.bootstrap.styles' );
197 20
			$parserOutput->addModuleScripts( 'ext.bootstrap.scripts' );
198 20
			if ( $this->vectorSkinInUse() ) {
199 20
				$parserOutput->addModules( 'ext.bootstrapComponents.vector-fix' );
200 20
			}
201 20
		}
202 20
	}
203
204
	/**
205
	 * Formats a text as error text so it can be added to the output.
206
	 *
207
	 * @param string $errorMessageName
208
	 *
209
	 * @return string
210
	 */
211 10
	public function renderErrorMessage( $errorMessageName ) {
212 10
		if ( !$errorMessageName || !trim( $errorMessageName ) ) {
213
			return '';
214
		}
215 10
		$this->addErrorTrackingCategory();
216 10
		return Html::rawElement(
217 10
			'span',
218 10
			[ 'class' => 'error' ],
219 10
			wfMessage( trim( $errorMessageName ) )->inContentLanguage()->title( $this->parser->getTitle() )->parse()
220 10
		);
221
	}
222
223
	/**
224
	 * Returns true, if active skin is vector
225
	 *
226
	 * @return bool
227
	 */
228 21
	public function vectorSkinInUse() {
229 21
		return strtolower( $this->getNameOfActiveSkin() ) == 'vector';
230
	}
231
232
	/**
233
	 * @return \Parser
234
	 */
235 21
	protected function getParser() {
236 21
		return $this->parser;
237
	}
238
239
	/**
240
	 * @return string
241
	 */
242 11
	private function detectSkinInUse() {
243 11
		$skin = RequestContext::getMain()->getSkin();
244 11
		return ($skin && is_a( $skin, 'Skin' ) ? $skin->getSkinName() : 'unknown');
245
	}
246
247
	/**
248
	 * Adds current page to the indicated tracking category, if not done already.
249
	 *
250
	 * @param String $trackingCategoryMessageName name of the message, containing the tracking category
251
	 */
252 2
	private function placeTrackingCategory( $trackingCategoryMessageName ) {
253 2
		$categoryMessage = wfMessage( $trackingCategoryMessageName )->inContentLanguage();
254 2
		$parserOutput = $this->parser->getOutput();
255 2
		if ( !$categoryMessage->isDisabled() && is_a( $parserOutput, ParserOutput::class ) ) {
256
			// Q: when do we expect \Parser->getOutput() no to be a \ParserOutput? A:During tests.
257
			$cat = Title::makeTitleSafe( NS_CATEGORY, $categoryMessage->text() );
258
			if ( $cat ) {
259
				$sort = (string) $parserOutput->getProperty( 'defaultsort' );
260
				$parserOutput->addCategory( $cat->getDBkey(), $sort );
261
			} else {
262
				wfDebug( __METHOD__ . ": [[MediaWiki:{$trackingCategoryMessageName}]] is not a valid title!\n" );
263
			}
264
		}
265 2
	}
266
}
267