Completed
Push — master ( 7aacb2...2b39b6 )
by
unknown
06:55
created

HookRegistry::getCompleteHookDefinitionList()   B

Complexity

Conditions 3
Paths 1

Size

Total Lines 101

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 101
rs 8
c 0
b 0
f 0
cc 3
nc 1
nop 3

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Contains the class registering the needed/wanted hooks.
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 BootstrapComponents\Hooks\OutputPageParserOutput;
30
use BootstrapComponents\Hooks\ParserFirstCallInit;
31
use Bootstrap\BootstrapManager;
32
use Hooks;
33
use MagicWord;
34
use MediaWiki\MediaWikiServices;
35
use Parser;
36
37
/**
38
 * Class HookRegistry
39
 *
40
 * Registers all hooks and components for Extension BootstrapComponents.
41
 *
42
 * Information on how to add an additional hook
43
 *  1. add it to {@see HookRegistry::AVAILABLE_HOOKS}.
44
 *  2. add an appropriate entry in the array inside {@see HookRegistry::getCompleteHookDefinitionList}
45
 *     with the hook as array key and the callback as value.
46
 *  3. have {@see HookRegistry::compileRequestedHooksListFor} add the hook to its result array. Based on
47
 *     a certain condition, if necessary.
48
 *  4. add appropriate tests to {@see \BootstrapComponents\Tests\Unit\HookRegistryTest}.
49
 *
50
 * @since 1.0
51
 */
52
class HookRegistry {
53
54
	/**
55
	 * @var array
56
	 */
57
	const AVAILABLE_HOOKS = [
58
		'GalleryGetModes', 'ImageBeforeProduceHTML', 'InternalParseBeforeLinks', 'OutputPageParserOutput',
59
		'ParserAfterParse', 'ParserFirstCallInit', 'ScribuntoExternalLibraries', 'SetupAfterCache',
60
	];
61
	// dev note: for modals, please see \BootstrapComponents\ModalBuilder for a list of tested hooks
62
63
	/**
64
	 * @var ComponentLibrary $componentLibrary
65
	 */
66
	private $componentLibrary;
67
68
	/**
69
	 * @var \Config $myConfig
70
	 */
71
	private $myConfig;
72
73
	/**
74
	 * @var NestingController $nestingController
75
	 */
76
	private $nestingController;
77
78
	/**
79
	 * HookRegistry constructor.
80
	 *
81
	 * @throws \ConfigException cascading {@see \BootstrapComponents\HookRegistry::getHooksToRegister}
82
	 * @throws \MWException cascading {@see \BootstrapComponents\HookRegistry::getHooksToRegister}
83
	 *
84
	 */
85
	public function __construct() {
86
87
		$this->myConfig = $this->registerMyConfiguration();
88
89
		list ( $this->componentLibrary, $this->nestingController ) = $this->initializeApplications( $this->myConfig );
90
	}
91
92
	/**
93
	 * @param array $hooksToRegister
94
	 *
95
	 * @return array
96
	 */
97
	public function buildHookCallbackListFor( $hooksToRegister ) {
98
		$hookCallbackList = [];
99
		$completeHookDefinitionList = $this->getCompleteHookDefinitionList(
100
			$this->myConfig, $this->componentLibrary, $this->nestingController
101
		);
102
		foreach ( $hooksToRegister as $requestedHook ) {
103
			if ( isset( $completeHookDefinitionList[$requestedHook] ) ) {
104
				$hookCallbackList[$requestedHook] = $completeHookDefinitionList[$requestedHook];
105
			}
106
		}
107
		return $hookCallbackList;
108
	}
109
110
	/**
111
	 * @throws \MWException cascading {@see \Hooks::clear}
112
	 */
113
	public function clear() {
114
		foreach ( self::AVAILABLE_HOOKS as $name ) {
115
			Hooks::clear( $name );
116
		}
117
	}
118
119
	/**
120
	 * @param \Config $myConfig
121
	 *
122
	 * @throws \ConfigException cascading {@see \Config::get}
123
	 *
124
	 * @return string[]
125
	 */
126
	public function compileRequestedHooksListFor( $myConfig ) {
127
		$requestedHookList = [
128
			'OutputPageParserOutput', 'ParserAfterParse', 'ParserFirstCallInit',
129
			'ScribuntoExternalLibraries', 'SetupAfterCache',
130
		];
131
		if ( $myConfig->has( 'BootstrapComponentsEnableCarouselGalleryMode' )
132
			&& $myConfig->get( 'BootstrapComponentsEnableCarouselGalleryMode' )
133
		) {
134
			$requestedHookList[] = 'GalleryGetModes';
135
		}
136
		if ( $myConfig->has( 'BootstrapComponentsModalReplaceImageTag' )
137
			&& $myConfig->get( 'BootstrapComponentsModalReplaceImageTag' )
138
		) {
139
			$requestedHookList[] = 'ImageBeforeProduceHTML';
140
			$requestedHookList[] = 'InternalParseBeforeLinks';
141
		}
142
		return $requestedHookList;
143
	}
144
145
	/**
146
	 * @param \Config           $myConfig
147
	 * @param ComponentLibrary  $componentLibrary
148
	 * @param NestingController $nestingController
149
	 *
150
	 * @return \Closure[]
151
	 */
152
	public function getCompleteHookDefinitionList( $myConfig, $componentLibrary, $nestingController ) {
153
		return [
154
			/**
155
			 * Hook: GalleryGetModes
156
			 *
157
			 * Allows extensions to add classes that can render different modes of a gallery.
158
			 *
159
			 * @see https://www.mediawiki.org/wiki/Manual:Hooks/GalleryGetModes
160
			 */
161
			'GalleryGetModes'            => function( &$modeArray ) {
162
				$modeArray['carousel'] = 'BootstrapComponents\\CarouselGallery';
163
				return true;
164
			},
165
166
			/**
167
			 * Hook: ImageBeforeProduceHTML
168
			 *
169
			 * Called before producing the HTML created by a wiki image insertion
170
			 *
171
			 * @see https://www.mediawiki.org/wiki/Manual:Hooks/ImageBeforeProduceHTML
172
			 */
173
			'ImageBeforeProduceHTML'     => $this->createImageBeforeProduceHTMLCallback( $nestingController, $myConfig ),
174
175
			/**
176
			 * Hook: InternalParseBeforeLinks
177
			 *
178
			 * Used to process the expanded wiki code after <nowiki>, HTML-comments, and templates have been treated.
179
			 *
180
			 * @see https://www.mediawiki.org/wiki/Manual:Hooks/InternalParseBeforeLinks
181
			 */
182
			'InternalParseBeforeLinks'   => $this->createInternalParseBeforeLinksCallback(),
183
184
			/**
185
			 * Hook: OutputPageParserOutput
186
			 *
187
			 * Called after parse, before the HTML is added to the output.
188
			 *
189
			 * @see https://www.mediawiki.org/wiki/Manual:Hooks/OutputPageParserOutput
190
			 */
191
			'OutputPageParserOutput'     => function( \OutputPage &$outputPage, \ParserOutput $parserOutput, ParserOutputHelper &$parserOutputHelper = null ) {
192
				// @todo check, if we need to omit execution on actions edit, submit, or history
193
				// $action = $outputPage->parserOptions()->getUser()->getRequest()->getVal( "action" );
194
				$hook = new OutputPageParserOutput( $outputPage, $parserOutput, $parserOutputHelper );
195
				return $hook->process();
196
			},
197
198
			/**
199
			 * Hook: ParserAfterParse
200
			 *
201
			 * Called from Parser::parse() just after the call to Parser::internalParse() returns.
202
			 *
203
			 * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserAfterParse
204
			 */
205
			'ParserAfterParse'           => function( Parser &$parser, &$text, \StripState &$stripState ) {
0 ignored issues
show
Unused Code introduced by
The parameter $text is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $stripState is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
206
				if ( $parser->getOutput()->getExtensionData( 'bsc_load_modules' ) ) {
207
					$parser->getOutput()->addModules( 'ext.bootstrap.styles' );
208
					$parser->getOutput()->addModules( 'ext.bootstrap.scripts' );
209
				}
210
				return true;
211
			},
212
213
			/**
214
			 * Hook: ParserFirstCallInit
215
			 *
216
			 * Called when the parser initializes for the first time.
217
			 *
218
			 * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserFirstCallInit
219
			 */
220
			'ParserFirstCallInit'        => function( Parser $parser ) use ( $componentLibrary, $nestingController ) {
221
				$hook = new ParserFirstCallInit( $parser, $componentLibrary, $nestingController );
222
				return $hook->process();
223
			},
224
225
			/**
226
			 * Hook: ScribuntoExternalLibraries
227
			 *
228
			 * Allow extensions to add Scribunto libraries
229
			 *
230
			 * @see https://www.mediawiki.org/wiki/Manual:Hooks/ScribuntoExternalLibraries
231
			 */
232
			'ScribuntoExternalLibraries' => function( $engine, &$extraLibraries ) {
233
				if ( $engine == 'lua' ) {
234
					$extraLibraries['mw.bootstrap'] = 'BootstrapComponents\\LuaLibrary';
235
				}
236
				return true;
237
			},
238
239
			/**
240
			 * Hook: SetupAfterCache
241
			 *
242
			 * Called in Setup.php, after cache objects are set
243
			 *
244
			 * @see https://www.mediawiki.org/wiki/Manual:Hooks/SetupAfterCache
245
			 */
246
			'SetupAfterCache'            => function() {
247
				// @todo change 'adding all bootstrap modules' to 'only add used modules' during parse.
248
				BootstrapManager::getInstance()->addAllBootstrapModules();
249
				return true;
250
			},
251
		];
252
	}
253
254
	/**
255
	 * @param \Config $myConfig
256
	 *
257
	 * @throws \MWException cascading {@see \BootstrapComponents\ApplicationFactory} calls
258
	 * @throws \ConfigException cascading {@see \Config::get}
259
	 *
260
	 * @return array
261
	 */
262
	public function initializeApplications( $myConfig ) {
263
		$applicationFactory = ApplicationFactory::getInstance();
264
		$componentLibrary = $applicationFactory->getComponentLibrary(
265
			$myConfig->get( 'BootstrapComponentsWhitelist' )
266
		);
267
		$nestingController = $applicationFactory->getNestingController();
268
		return [ $componentLibrary, $nestingController ];
269
	}
270
271
	/**
272
	 * @param string $hook
273
	 *
274
	 * @return boolean
275
	 */
276
	public function isRegistered( $hook ) {
277
		return Hooks::isRegistered( $hook );
278
	}
279
280
	/**
281
	 * Registers all supplied hooks.
282
	 *
283
	 * @param array $hookList $hook => $callback
284
	 *
285
	 * @return int  number of registered hooks
286
	 */
287
	public function register( $hookList ) {
288
		foreach ( $hookList as $hook => $callback ) {
289
			Hooks::register( $hook, $callback );
290
		}
291
		return count( $hookList );
292
	}
293
294
	/**
295
	 * Executes the setup process.
296
	 *
297
	 * @throws \ConfigException
298
	 *
299
	 * @return int
300
	 */
301
	public function run() {
302
		$requestedHooks = $this->compileRequestedHooksListFor(
303
			$this->myConfig
304
		);
305
		$hookCallbackList = $this->buildHookCallbackListFor(
306
			$requestedHooks
307
		);
308
309
		return $this->register( $hookCallbackList );
310
	}
311
312
	/**
313
	 * Callback for Hook: ImageBeforeProduceHTML
314
	 *
315
	 * Called before producing the HTML created by a wiki image insertion
316
	 *
317
	 * @param NestingController $nestingController
318
	 * @param \Config           $myConfig
319
	 *
320
	 * @return \Closure
321
	 * @see https://www.mediawiki.org/wiki/Manual:Hooks/ImageBeforeProduceHTML
322
	 *
323
	 */
324
	private function createImageBeforeProduceHTMLCallback( $nestingController, $myConfig ) {
325
326
		return function( &$dummy, &$title, &$file, &$frameParams, &$handlerParams, &$time, &$res
327
		) use ( $nestingController, $myConfig ) {
328
329
			$imageModal = new ImageModal( $dummy, $title, $file, $nestingController );
330
331
			if ( $myConfig->has( 'BootstrapComponentsDisableSourceLinkOnImageModal' )
332
				&& $myConfig->get( 'BootstrapComponentsDisableSourceLinkOnImageModal' )
333
			) {
334
				$imageModal->disableSourceLink();
335
			}
336
337
			return $imageModal->parse( $frameParams, $handlerParams, $time, $res );
338
		};
339
	}
340
341
	/**
342
	 * Callback for Hook: InternalParseBeforeLinks
343
	 *
344
	 * Used to process the expanded wiki code after <nowiki>, HTML-comments, and templates have been treated.
345
	 *
346
	 * @see https://www.mediawiki.org/wiki/Manual:Hooks/InternalParseBeforeLinks
347
	 *
348
	 * @return \Closure
349
	 */
350
	private function createInternalParseBeforeLinksCallback() {
351
		return function( Parser &$parser, &$text ) {
352
			if ( class_exists( '\MediaWiki\MediaWikiServices' )
353
				&& method_exists( '\MediaWiki\MediaWikiServices', 'getMagicWordFactory' )
354
			) {
355
				$mw = MediaWikiServices::getInstance()->getMagicWordFactory()->get( 'BSC_NO_IMAGE_MODAL' );;
356
			} else {
357
				$mw = MagicWord::get( 'BSC_NO_IMAGE_MODAL' );
358
			}
359
			// we do not use our ParserOutputHelper class here, for we would need to reset it in integration tests.
360
			// resetting our factory build classes is unfortunately a little skittish
361
			$parser->getOutput()->setExtensionData(
362
				'bsc_no_image_modal',
363
				$mw->matchAndRemove( $text )
364
			);
365
			return true;
366
		};
367
	}
368
369
	/**
370
	 * Registers and returns my own configuration, so that it is present during pre-init onExtensionLoad(). See phabricator issue T184837
371
	 *
372
	 * @see https://phabricator.wikimedia.org/T184837
373
	 *
374
	 * @return \Config
375
	 */
376
	private function registerMyConfiguration() {
377
		$configFactory = MediaWikiServices::getInstance()->getConfigFactory();
378
		$configFactory->register( 'BootstrapComponents', 'GlobalVarConfig::newInstance' );
379
		return $configFactory->makeConfig( 'BootstrapComponents' );
380
	}
381
}
382