Completed
Push — master ( eadb3e...25e675 )
by
unknown
10:12
created

ParserOutputHelper::detectSkinInUse()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.027

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 10
cts 11
cp 0.9091
rs 9.2222
c 0
b 0
f 0
cc 6
nc 10
nop 1
crap 6.027
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 EXTENSION_DATA_DEFERRED_CONTENT_KEY = 'bsc_deferredContent';
49
50
	/**
51
	 * @var string
52
	 */
53
	const INJECTION_PREFIX = '<!-- injected by Extension:BootstrapComponents -->';
54
55
	/**
56
	 * @var string
57
	 */
58
	const INJECTION_SUFFIX = '<!-- /injected by Extension:BootstrapComponents -->';
59
60
	/**
61
	 * To make sure, we only add the tracking category once.
62
	 *
63
	 * @var bool $articleTracked
64
	 */
65
	private $articleTracked;
66
67
	/**
68
	 * To make sure, we only add the error tracking category once.
69
	 *
70
	 * @var bool $articleTrackedOnError
71
	 */
72
	private $articleTrackedOnError;
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 38
	public function __construct( $parser ) {
97 38
		$this->articleTracked = false;
98 38
		$this->articleTrackedOnError = false;
99 38
		$this->parser = $parser;
100 38
		$this->nameOfActiveSkin = $this->detectSkinInUse(
101 38
			defined( 'MW_NO_SESSION' )
102 38
		);
103 38
	}
104
105
	/**
106
	 * Adds the error tracking category to the current page if not done already.
107
	 */
108 13
	public function addErrorTrackingCategory() {
109 13
		if ( $this->articleTrackedOnError ) {
110 11
			return;
111
		}
112 13
		$this->placeTrackingCategory( 'bootstrap-components-error-tracking-category' );
113 13
		$this->articleTrackedOnError = true;
114 13
	}
115
116
	/**
117
	 * Adds the supplied modules to the parser output.
118
	 *
119
	 * @param array $modulesToAdd
120
	 */
121 20
	public function addModules( $modulesToAdd ) {
122 20
		$parserOutput = $this->getParser()->getOutput();
123 20
		if ( is_a( $parserOutput, ParserOutput::class ) ) {
124
			// Q: when do we expect \Parser->getOutput() no to be a \ParserOutput? A:During tests.
125 20
			$parserOutput->addModules( $modulesToAdd );
126 20
		}
127 20
	}
128
129
	/**
130
	 * Adds the tracking category to the current page if not done already.
131
	 */
132 18
	public function addTrackingCategory() {
133 18
		if ( $this->articleTracked ) {
134 18
			return;
135
		}
136 18
		$this->placeTrackingCategory( 'bootstrap-components-tracking-category' );
137 18
		$this->articleTracked = true;
138 18
	}
139
140
	/**
141
	 * Unless I find a solution for the integration test problem, I cannot use an instance of
142
	 * ParserOutputHelper in ImageModal to ascertain this. In integration tests, "we" use a
143
	 * different parser than the InternalParseBeforeLinks-Hook. At least, after I added
144
	 * Scribunto _unit_ tests. All messes up, I'm afraid. ImageModal better use global parser, and
145
	 * for the time being this method will be
146
	 * @deprecated
147
	 *
148
	 * @return bool|null
149
	 */
150
	public function areImageModalsSuppressed() {
151
		return $this->getParser()->getOutput()->getExtensionData( 'bsc_no_image_modal' );
152
	}
153
154
	/**
155
	 * Returns the raw html that is be inserted at the end of the page.
156
	 *
157
	 * @param \ParserOutput $parserOutput
158
	 *
159
	 * @return string
160
	 */
161 24
	public function getContentForLaterInjection( \ParserOutput $parserOutput ) {
162 24
		$deferredContent = $parserOutput->getExtensionData( self::EXTENSION_DATA_DEFERRED_CONTENT_KEY );
163
164 24
		if ( empty( $deferredContent ) || !is_array( $deferredContent ) ) {
165 23
			return '';
166
		}
167
168
		// clearing extension data for unit and integration tests to work
169 7
		$parserOutput->setExtensionData( self::EXTENSION_DATA_DEFERRED_CONTENT_KEY, null );
170 7
		return self::INJECTION_PREFIX . implode( array_values( $deferredContent ) ) . self::INJECTION_SUFFIX;
171
	}
172
173
	/**
174
	 * @return string
175
	 */
176 22
	public function getNameOfActiveSkin() {
177 22
		return $this->nameOfActiveSkin;
178
	}
179
180
	/**
181
	 * Allows for html fragments for a given container id to be stored, so that it can be added to the page at a later time.
182
	 *
183
	 * @param string $id
184
	 * @param string $rawHtml
185
	 *
186
	 * @return ParserOutputHelper $this (fluid)
187
	 */
188 10
	public function injectLater( $id, $rawHtml ) {
189 10
		if ( !empty( $rawHtml ) ) {
190 7
			$deferredContent = $this->getParser()->getOutput()->getExtensionData( self::EXTENSION_DATA_DEFERRED_CONTENT_KEY );
191 7
			if ( empty( $deferredContent ) ) {
192 7
				$deferredContent = [];
193 7
			}
194 7
			$deferredContent[$id] = $rawHtml;
195 7
			$this->getParser()->getOutput()->setExtensionData( self::EXTENSION_DATA_DEFERRED_CONTENT_KEY, $deferredContent );
196 7
		}
197 10
		return $this;
198
	}
199
200
	/**
201
	 * Adds the bootstrap modules and styles to the page, if not done already
202
	 */
203 20
	public function loadBootstrapModules() {
204 20
		$parserOutput = $this->getParser()->getOutput();
205 20
		if ( is_a( $parserOutput, ParserOutput::class ) ) {
206
			// Q: when do we expect \Parser->getOutput() no to be a \ParserOutput? A: During tests.
207 20
			$parserOutput->addModuleStyles( 'ext.bootstrap.styles' );
208 20
			$parserOutput->addModuleScripts( 'ext.bootstrap.scripts' );
209 20
			if ( $this->vectorSkinInUse() ) {
210 20
				$parserOutput->addModules( 'ext.bootstrapComponents.vector-fix' );
211 20
			}
212 20
		}
213 20
	}
214
215
	/**
216
	 * Formats a text as error text so it can be added to the output.
217
	 *
218
	 * @param string $errorMessageName
219
	 *
220
	 * @return string
221
	 */
222 16
	public function renderErrorMessage( $errorMessageName ) {
223 16
		if ( !$errorMessageName || !trim( $errorMessageName ) ) {
224 4
			return '';
225
		}
226 12
		$this->addErrorTrackingCategory();
227 12
		return Html::rawElement(
228 12
			'span',
229 12
			[ 'class' => 'error' ],
230 12
			wfMessage( trim( $errorMessageName ) )->inContentLanguage()->title( $this->parser->getTitle() )->parse()
231 12
		);
232
	}
233
234
	/**
235
	 * Returns true, if active skin is vector
236
	 *
237
	 * @return bool
238
	 */
239 21
	public function vectorSkinInUse() {
240 21
		return strtolower( $this->getNameOfActiveSkin() ) == 'vector';
241
	}
242
243
	/**
244
	 * @return \Parser
245
	 */
246 22
	protected function getParser() {
247 22
		return $this->parser;
248
	}
249
250
	/**
251
	 * @param bool $useConfig   set to true, if we can't rely on {@see \RequestContext::getSkin}
252
	 *
253
	 * @return string
254
	 */
255 38
	private function detectSkinInUse( $useConfig = false ) {
256 38
		if ( !$useConfig ) {
257 38
			$skin = RequestContext::getMain()->getSkin();
258 38
		}
259 38
		if ( !empty( $skin ) && is_a( $skin, 'Skin' ) ) {
260 38
			return $skin->getSkinName();
261
		}
262 1
		$mainConfig = MediaWikiServices::getInstance()->getMainConfig();
263
		try {
264 1
			$defaultSkin = $mainConfig->get( 'DefaultSkin' );
265 1
		} 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...
266
			$defaultSkin = 'unknown';
267
		}
268 1
		return empty( $defaultSkin ) ? 'unknown' : $defaultSkin;
269
	}
270
271
	/**
272
	 * Adds current page to the indicated tracking category, if not done already.
273
	 *
274
	 * @param String $trackingCategoryMessageName name of the message, containing the tracking category
275
	 */
276 21
	private function placeTrackingCategory( $trackingCategoryMessageName ) {
277 21
		$categoryMessage = wfMessage( $trackingCategoryMessageName )->inContentLanguage();
278 21
		$parserOutput = $this->parser->getOutput();
279 21
		if ( !$categoryMessage->isDisabled() && is_a( $parserOutput, ParserOutput::class ) ) {
280
			// Q: when do we expect \Parser->getOutput() no to be a \ParserOutput? A:During tests.
281 12
			$cat = Title::makeTitleSafe( NS_CATEGORY, $categoryMessage->text() );
282 12
			if ( $cat ) {
283 12
				$sort = (string) $parserOutput->getProperty( 'defaultsort' );
284 12
				$parserOutput->addCategory( $cat->getDBkey(), $sort );
285 12
			} else {
286
				wfDebug( __METHOD__ . ": [[MediaWiki:{$trackingCategoryMessageName}]] is not a valid title!\n" );
287
			}
288 12
		}
289 21
	}
290
}
291