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

src/ParserOutputHelper.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
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