Completed
Push — master ( 292636...4a8b61 )
by Tobias
09:54
created

ParserOutputHelper::detectSkinInUse()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5.0187

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 10
cts 11
cp 0.9091
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 11
nc 6
nop 1
crap 5.0187
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 \ConfigException;
30
use \Html;
31
use \MediaWiki\MediaWikiServices;
32
use \ParserOutput;
33
use \RequestContext;
34
use \Title;
35
36
/**
37
 * Class ParserOutputHelper
38
 *
39
 * Performs all the adaptions on the ParserOutput
40
 *
41
 * @since 1.0
42
 */
43
class ParserOutputHelper {
44
45
	/**
46
	 * @var string
47
	 */
48
	const INJECTION_PREFIX = '<!-- injected by Extension:BootstrapComponents -->';
49
50
	/**
51
	 * @var string
52
	 */
53
	const INJECTION_SUFFIX = '<!-- /injected by Extension:BootstrapComponents -->';
54
55
	/**
56
	 * To make sure, we only add the tracking category once.
57
	 *
58
	 * @var bool $articleTracked
59
	 */
60
	private $articleTracked;
61
62
	/**
63
	 * To make sure, we only add the error tracking category once.
64
	 *
65
	 * @var bool $articleTrackedOnError
66
	 */
67
	private $articleTrackedOnError;
68
69
	/**
70
	 * Here, components can store html to be added to the page at a later time.
71
	 *
72
	 * @var string $contentForLaterInjection
73
	 */
74
	private $contentForLaterInjection;
75
76
	/**
77
	 * Holds the name of the skin we use (or false, if there is no skin).
78
	 *
79
	 * @var string $nameOfActiveSkin
80
	 */
81
	private $nameOfActiveSkin;
82
83
	/**
84
	 * @var \Parser $parser
85
	 */
86
	private $parser;
87
88
89
	/**
90
	 * ParserOutputHelper constructor.
91
	 *
92
	 * Do not instantiate directly, but use {@see ApplicationFactory::getParserOutputHelper} instead.
93
	 *
94
	 * @param \Parser $parser
95
	 *
96
	 * @see ApplicationFactory::getParserOutputHelper
97
	 */
98 18
	public function __construct( $parser ) {
99 18
		$this->articleTracked = false;
100 18
		$this->articleTrackedOnError = false;
101 18
		$this->parser = $parser;
102 18
		$this->nameOfActiveSkin = $this->detectSkinInUse(
103 18
			defined( 'MW_NO_SESSION' )
104 18
		);
105 18
		$this->contentForLaterInjection = '';
106 18
	}
107
108
	/**
109
	 * Adds the error tracking category to the current page if not done already.
110
	 */
111 13
	public function addErrorTrackingCategory() {
112 13
		if ( $this->articleTrackedOnError ) {
113 11
			return;
114
		}
115 3
		$this->placeTrackingCategory( 'bootstrap-components-error-tracking-category' );
116 3
		$this->articleTrackedOnError = true;
117 3
	}
118
119
	/**
120
	 * Adds the supplied modules to the parser output.
121
	 *
122
	 * @param array $modulesToAdd
123
	 */
124 20
	public function addModules( $modulesToAdd ) {
125 20
		$parserOutput = $this->getParser()->getOutput();
126 20
		if ( is_a( $parserOutput, ParserOutput::class ) ) {
127
			// Q: when do we expect \Parser->getOutput() no to be a \ParserOutput? A:During tests.
128 20
			$parserOutput->addModules( $modulesToAdd );
129 20
		}
130 20
	}
131
132
	/**
133
	 * Adds the tracking category to the current page if not done already.
134
	 */
135 18
	public function addTrackingCategory() {
136 18
		if ( $this->articleTracked ) {
137 18
			return;
138
		}
139 1
		$this->placeTrackingCategory( 'bootstrap-components-tracking-category' );
140 1
		$this->articleTracked = true;
141 1
	}
142
143
	/**
144
	 * Unless I find a solution for the integration test problem, I cannot use an instance of
145
	 * ParserOutputHelper in ImageModal to ascertain this. In integration tests, "we" use a
146
	 * different parser than the InternalParseBeforeLinks-Hook. At least, after I added
147
	 * Scribunto _unit_ tests. All messes up, I'm afraid. ImageModal better use global parser, and
148
	 * for the time being this method will be
149
	 * @deprecated
150
	 *
151
	 * @return bool|null
152
	 */
153
	public function areImageModalsSuppressed() {
154
		return $this->getParser()->getOutput()->getExtensionData( 'bsc_no_image_modal' );
155
	}
156
157
	/**
158
	 * Returns the raw html that is be inserted at the end of the page.
159
	 *
160
	 * @return string
161
	 */
162 24
	public function getContentForLaterInjection() {
163 24
		if ( $this->contentForLaterInjection == '' ) {
164 23
			return '';
165
		}
166 7
		$ret = self::INJECTION_PREFIX . $this->contentForLaterInjection . self::INJECTION_SUFFIX;
167
		// clear the stored injection content, so that integration tests can run correctly
168 7
		$this->contentForLaterInjection = '';
169 7
		return $ret;
170
	}
171
172
	/**
173
	 * @return string
174
	 */
175 22
	public function getNameOfActiveSkin() {
176 22
		return $this->nameOfActiveSkin;
177
	}
178
179
	/**
180
	 * Allows to store html that will be added to the page at a later time.
181
	 *
182
	 * @param string $rawHtml
183
	 *
184
	 * @return ParserOutputHelper $this (fluid)
185
	 */
186 10
	public function injectLater( $rawHtml ) {
187 10
		if ( !empty( $rawHtml ) ) {
188 7
			$this->contentForLaterInjection .= $rawHtml;
189 7
		}
190 10
		return $this;
191
	}
192
193
	/**
194
	 * Adds the bootstrap modules and styles to the page, if not done already
195
	 */
196 20
	public function loadBootstrapModules() {
197 20
		$parserOutput = $this->getParser()->getOutput();
198 20
		if ( is_a( $parserOutput, ParserOutput::class ) ) {
199
			// Q: when do we expect \Parser->getOutput() no to be a \ParserOutput? A:During tests.
200 20
			$parserOutput->addModuleStyles( 'ext.bootstrap.styles' );
201 20
			$parserOutput->addModuleScripts( 'ext.bootstrap.scripts' );
202 20
			if ( $this->vectorSkinInUse() ) {
203 20
				$parserOutput->addModules( 'ext.bootstrapComponents.vector-fix' );
204 20
			}
205 20
		}
206 20
	}
207
208
	/**
209
	 * Formats a text as error text so it can be added to the output.
210
	 *
211
	 * @param string $errorMessageName
212
	 *
213
	 * @return string
214
	 */
215 16
	public function renderErrorMessage( $errorMessageName ) {
216 16
		if ( !$errorMessageName || !trim( $errorMessageName ) ) {
217 4
			return '';
218
		}
219 12
		$this->addErrorTrackingCategory();
220 12
		return Html::rawElement(
221 12
			'span',
222 12
			[ 'class' => 'error' ],
223 12
			wfMessage( trim( $errorMessageName ) )->inContentLanguage()->title( $this->parser->getTitle() )->parse()
224 12
		);
225
	}
226
227
	/**
228
	 * Returns true, if active skin is vector
229
	 *
230
	 * @return bool
231
	 */
232 21
	public function vectorSkinInUse() {
233 21
		return strtolower( $this->getNameOfActiveSkin() ) == 'vector';
234
	}
235
236
	/**
237
	 * @return \Parser
238
	 */
239 21
	protected function getParser() {
240 21
		return $this->parser;
241
	}
242
243
	/**
244
	 * @param bool $useConfig   set to true, if we can't rely on {@see \RequestContext::getSkin}
245
	 *
246
	 * @return string
247
	 */
248 18
	private function detectSkinInUse( $useConfig = false ) {
249 18
		if ( $useConfig ) {
250 1
			$skin = RequestContext::getMain()->getSkin();
251 1
		}
252 18
		if ( !empty( $skin ) && is_a( $skin, 'Skin' ) ) {
253 1
			return $skin->getSkinName();
254
		}
255 18
		$mainConfig = MediaWikiServices::getInstance()->getMainConfig();
256
		try {
257 18
			$defaultSkin = $mainConfig->get( 'DefaultSkin' );
258 18
		} catch ( ConfigException $e ) {
0 ignored issues
show
Bug introduced by
The class ConfigException does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
259
			$defaultSkin = 'unknown';
260
		}
261 18
		return $defaultSkin;
262
	}
263
264
	/**
265
	 * Adds current page to the indicated tracking category, if not done already.
266
	 *
267
	 * @param String $trackingCategoryMessageName name of the message, containing the tracking category
268
	 */
269 4
	private function placeTrackingCategory( $trackingCategoryMessageName ) {
270 4
		$categoryMessage = wfMessage( $trackingCategoryMessageName )->inContentLanguage();
271 4
		$parserOutput = $this->parser->getOutput();
272 4
		if ( !$categoryMessage->isDisabled() && is_a( $parserOutput, ParserOutput::class ) ) {
273
			// Q: when do we expect \Parser->getOutput() no to be a \ParserOutput? A:During tests.
274 2
			$cat = Title::makeTitleSafe( NS_CATEGORY, $categoryMessage->text() );
275 2
			if ( $cat ) {
276 2
				$sort = (string) $parserOutput->getProperty( 'defaultsort' );
277 2
				$parserOutput->addCategory( $cat->getDBkey(), $sort );
278 2
			} else {
279
				wfDebug( __METHOD__ . ": [[MediaWiki:{$trackingCategoryMessageName}]] is not a valid title!\n" );
280
			}
281 2
		}
282 4
	}
283
}
284