Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
53.66% |
66 / 123 |
|
37.50% |
3 / 8 |
CRAP | |
0.00% |
0 / 1 |
PlayerHooks | |
53.66% |
66 / 123 |
|
37.50% |
3 / 8 |
75.65 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
onBeforePageDisplay | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
3 | |||
shouldWikispeechRun | |
97.06% |
33 / 34 |
|
0.00% |
0 / 1 |
10 | |||
onResourceLoaderGetConfigVars | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
onGetPreferences | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
2 | |||
addVoicePreferences | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
20 | |||
addSpeechRatePreferences | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
2 | |||
onSkinTemplateNavigation__Universal | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Wikispeech\Hooks; |
4 | |
5 | use Action; |
6 | use Config; |
7 | use ConfigFactory; |
8 | use Exception; |
9 | use MediaWiki\Hook\BeforePageDisplayHook; |
10 | use MediaWiki\Hook\SkinTemplateNavigation__UniversalHook; |
11 | use MediaWiki\Http\HttpRequestFactory; |
12 | use MediaWiki\Languages\LanguageFactory; |
13 | use MediaWiki\Logger\LoggerFactory; |
14 | use MediaWiki\Permissions\PermissionManager; |
15 | use MediaWiki\Preferences\Hook\GetPreferencesHook; |
16 | use MediaWiki\User\UserOptionsLookup; |
17 | use MediaWiki\Wikispeech\ConfigurationValidator; |
18 | use MediaWiki\Wikispeech\SpeechoidConnector; |
19 | use MediaWiki\Wikispeech\VoiceHandler; |
20 | use OutputPage; |
21 | use Psr\Log\LoggerInterface; |
22 | use Skin; |
23 | use SkinTemplate; |
24 | use User; |
25 | use WANObjectCache; |
26 | |
27 | /** |
28 | * @file |
29 | * @ingroup Extensions |
30 | * @license GPL-2.0-or-later |
31 | * @since 0.1.11 |
32 | */ |
33 | class PlayerHooks implements |
34 | BeforePageDisplayHook, |
35 | GetPreferencesHook, |
36 | SkinTemplateNavigation__UniversalHook |
37 | { |
38 | /** @var Config */ |
39 | private $config; |
40 | |
41 | /** @var ConfigurationValidator */ |
42 | private $configValidator; |
43 | |
44 | /** @var UserOptionsLookup */ |
45 | private $userOptionsLookup; |
46 | |
47 | /** @var LoggerInterface */ |
48 | private $logger; |
49 | |
50 | /** @var WANObjectCache */ |
51 | private $mainWANObjectCache; |
52 | |
53 | /** @var LanguageFactory */ |
54 | private $languageFactory; |
55 | |
56 | /** @var PermissionManager */ |
57 | private $permissionManager; |
58 | |
59 | /** @var HttpRequestFactory */ |
60 | private $requestFactory; |
61 | |
62 | /** |
63 | * @since 0.1.11 |
64 | * @param ConfigFactory $configFactory |
65 | * @param UserOptionsLookup $userOptionsLookup |
66 | * @param WANObjectCache $mainWANObjectCache |
67 | * @param LanguageFactory $languageFactory |
68 | * @param PermissionManager $permissionManager |
69 | * @param HttpRequestFactory $requestFactory |
70 | */ |
71 | public function __construct( |
72 | ConfigFactory $configFactory, |
73 | UserOptionsLookup $userOptionsLookup, |
74 | WANObjectCache $mainWANObjectCache, |
75 | LanguageFactory $languageFactory, |
76 | PermissionManager $permissionManager, |
77 | HttpRequestFactory $requestFactory |
78 | ) { |
79 | $this->logger = LoggerFactory::getInstance( 'Wikispeech' ); |
80 | $this->config = $configFactory->makeConfig( 'wikispeech' ); |
81 | $this->configValidator = new ConfigurationValidator( $this->config, $this->logger ); |
82 | $this->userOptionsLookup = $userOptionsLookup; |
83 | $this->mainWANObjectCache = $mainWANObjectCache; |
84 | $this->languageFactory = $languageFactory; |
85 | $this->permissionManager = $permissionManager; |
86 | $this->requestFactory = $requestFactory; |
87 | } |
88 | |
89 | /** |
90 | * Hook for BeforePageDisplay. |
91 | * |
92 | * Enables JavaScript. |
93 | * |
94 | * @since 0.1.11 |
95 | * @param OutputPage $out The OutputPage object. |
96 | * @param Skin $skin Skin object that will be used to generate the page, |
97 | * added in MediaWiki 1.13. |
98 | */ |
99 | public function onBeforePageDisplay( $out, $skin ): void { |
100 | if ( !$this->shouldWikispeechRun( $out ) ) { |
101 | return; |
102 | } |
103 | $showPlayer = $this->userOptionsLookup->getOption( |
104 | $out->getUser(), 'wikispeechShowPlayer' |
105 | ); |
106 | if ( $showPlayer ) { |
107 | $this->logger->info( __METHOD__ . ': Loading player.' ); |
108 | $out->addModules( [ 'ext.wikispeech' ] ); |
109 | } else { |
110 | $this->logger->info( __METHOD__ . ': Adding option to load player.' ); |
111 | $out->addModules( [ 'ext.wikispeech.loader' ] ); |
112 | } |
113 | $out->addJsConfigVars( [ |
114 | 'wgWikispeechKeyboardShortcuts' => $this->config->get( 'WikispeechKeyboardShortcuts' ), |
115 | 'wgWikispeechContentSelector' => $this->config->get( 'WikispeechContentSelector' ), |
116 | 'wgWikispeechSkipBackRewindsThreshold' => |
117 | $this->config->get( 'WikispeechSkipBackRewindsThreshold' ), |
118 | 'wgWikispeechHelpPage' => $this->config->get( 'WikispeechHelpPage' ), |
119 | 'wgWikispeechFeedbackPage' => $this->config->get( 'WikispeechFeedbackPage' ) |
120 | ] ); |
121 | } |
122 | |
123 | /** |
124 | * Checks if Wikispeech should run. |
125 | * |
126 | * Returns true if all of the following are true: |
127 | * * User has enabled Wikispeech in the settings |
128 | * * User is allowed to listen to pages |
129 | * * Wikispeech configuration is valid |
130 | * * Wikispeech is enabled for the page's namespace |
131 | * * Revision is current |
132 | * * Page's language is enabled for Wikispeech |
133 | * * The action is "view" |
134 | * |
135 | * @since 0.1.11 |
136 | * @param OutputPage $out |
137 | * @return bool |
138 | */ |
139 | private function shouldWikispeechRun( OutputPage $out ) { |
140 | $wikispeechEnabled = $this->userOptionsLookup |
141 | ->getOption( $out->getUser(), 'wikispeechEnable' ); |
142 | if ( !$wikispeechEnabled ) { |
143 | $this->logger->info( __METHOD__ . ': Not loading Wikispeech: disabled by user.' ); |
144 | return false; |
145 | } |
146 | |
147 | $userIsAllowed = $this->permissionManager |
148 | ->userHasRight( $out->getUser(), 'wikispeech-listen' ); |
149 | if ( !$userIsAllowed ) { |
150 | $this->logger->info( __METHOD__ . |
151 | ': Not loading Wikispeech: user lacks right "wikispeech-listen".' ); |
152 | return false; |
153 | } |
154 | |
155 | if ( !$this->configValidator->validateConfiguration() ) { |
156 | $this->logger->info( __METHOD__ . ': Not loading Wikispeech: config invalid.' ); |
157 | return false; |
158 | } |
159 | |
160 | $namespace = $out->getTitle()->getNamespace(); |
161 | $validNamespaces = $this->config->get( 'WikispeechNamespaces' ); |
162 | if ( !in_array( $namespace, $validNamespaces ) ) { |
163 | $this->logger->info( __METHOD__ . ': Not loading Wikispeech: unsupported namespace.' ); |
164 | return false; |
165 | } |
166 | |
167 | if ( !$out->isRevisionCurrent() ) { |
168 | $this->logger->info( __METHOD__ . ': Not loading Wikispeech: non-current revision.' ); |
169 | return false; |
170 | } |
171 | |
172 | if ( $namespace == NS_MEDIA || $namespace < 0 ) { |
173 | // cannot get pageContentLanguage of e.g. a Special page or a |
174 | // virtual page. These should all use the interface language. |
175 | $pageContentLanguage = $out->getLanguage(); |
176 | } else { |
177 | $pageContentLanguage = $out->getTitle()->getPageLanguage(); |
178 | } |
179 | $validLanguages = array_keys( $this->config->get( 'WikispeechVoices' ) ); |
180 | if ( !in_array( $pageContentLanguage->getCode(), $validLanguages ) ) { |
181 | $this->logger->info( __METHOD__ . ': Not loading Wikispeech: unsupported language.' ); |
182 | return false; |
183 | } |
184 | |
185 | $actionName = Action::getActionName( $out ); |
186 | if ( $actionName !== 'view' ) { |
187 | $this->logger->info( __METHOD__ . ': Not loading Wikispeech: unsupported action.' ); |
188 | return false; |
189 | } |
190 | |
191 | return true; |
192 | } |
193 | |
194 | /** |
195 | * Conditionally register static configuration variables for the |
196 | * ext.wikispeech module only if that module is loaded. |
197 | * |
198 | * @since 0.1.11 |
199 | * @param array &$vars The array of static configuration variables. |
200 | * @param string $skin |
201 | * @param Config $config |
202 | */ |
203 | public function onResourceLoaderGetConfigVars( array &$vars, $skin, Config $config ): void { |
204 | $vars['wgWikispeechSpeechoidUrl'] = $config->get( 'WikispeechSpeechoidUrl' ); |
205 | $vars['wgWikispeechNamespaces'] = $config->get( 'WikispeechNamespaces' ); |
206 | } |
207 | |
208 | /** |
209 | * Add Wikispeech options to Special:Preferences. |
210 | * |
211 | * @since 0.1.11 |
212 | * @param User $user current User object. |
213 | * @param array &$preferences Preferences array. |
214 | */ |
215 | public function onGetPreferences( $user, &$preferences ) { |
216 | $speechoidConnector = new SpeechoidConnector( |
217 | $this->config, |
218 | $this->requestFactory |
219 | ); |
220 | $voiceHandler = new VoiceHandler( |
221 | $this->logger, |
222 | $this->config, |
223 | $speechoidConnector, |
224 | $this->mainWANObjectCache |
225 | ); |
226 | $preferences['wikispeechEnable'] = [ |
227 | 'type' => 'toggle', |
228 | 'label-message' => 'prefs-wikispeech-enable', |
229 | 'section' => 'wikispeech' |
230 | ]; |
231 | $preferences['wikispeechShowPlayer'] = [ |
232 | 'type' => 'toggle', |
233 | 'label-message' => 'prefs-wikispeech-show-player', |
234 | 'section' => 'wikispeech' |
235 | ]; |
236 | $this->addVoicePreferences( $preferences, $voiceHandler ); |
237 | $this->addSpeechRatePreferences( $preferences ); |
238 | } |
239 | |
240 | /** |
241 | * Add preferences for selecting voices per language. |
242 | * |
243 | * @since 0.1.11 |
244 | * @param array &$preferences Preferences array. |
245 | * @param VoiceHandler $voiceHandler |
246 | */ |
247 | private function addVoicePreferences( &$preferences, $voiceHandler ) { |
248 | $wikispeechVoices = $this->config->get( 'WikispeechVoices' ); |
249 | foreach ( $wikispeechVoices as $language => $voices ) { |
250 | $languageKey = 'wikispeechVoice' . ucfirst( $language ); |
251 | $mwLanguage = $this->languageFactory->getLanguage( 'en' ); |
252 | $languageName = $mwLanguage->getVariantname( $language ); |
253 | $options = []; |
254 | try { |
255 | $defaultVoice = $voiceHandler->getDefaultVoice( $language ); |
256 | $options["Default ($defaultVoice)"] = ''; |
257 | } catch ( Exception $e ) { |
258 | $options["Default"] = ''; |
259 | } |
260 | foreach ( $voices as $voice ) { |
261 | $options[$voice] = $voice; |
262 | } |
263 | $preferences[$languageKey] = [ |
264 | 'type' => 'select', |
265 | 'label' => $languageName, |
266 | 'section' => 'wikispeech/wikispeech-voice', |
267 | 'options' => $options |
268 | ]; |
269 | } |
270 | } |
271 | |
272 | /** |
273 | * Add preferences for selecting speech rate. |
274 | * |
275 | * @since 0.1.11 |
276 | * @param array &$preferences Preferences array. |
277 | */ |
278 | private function addSpeechRatePreferences( &$preferences ) { |
279 | $options = [ |
280 | '400%' => 4.0, |
281 | '200%' => 2.0, |
282 | '150%' => 1.5, |
283 | '100%' => 1.0, |
284 | '75%' => 0.75, |
285 | '50%' => 0.5 |
286 | ]; |
287 | $preferences['wikispeechSpeechRate'] = [ |
288 | 'type' => 'select', |
289 | 'label-message' => 'prefs-wikispeech-speech-rate', |
290 | 'section' => 'wikispeech/wikispeech-voice', |
291 | 'options' => $options |
292 | ]; |
293 | } |
294 | |
295 | /** |
296 | * Add tab for activating Wikispeech player. |
297 | * |
298 | * @since 0.1.11 |
299 | * @param SkinTemplate $skinTemplate The skin template on which |
300 | * the UI is built. |
301 | * @param array &$links Navigation links. |
302 | */ |
303 | public function onSkinTemplateNavigation__Universal( $skinTemplate, &$links ): void { // phpcs:ignore MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName, Generic.Files.LineLength.TooLong |
304 | $out = $skinTemplate->getOutput(); |
305 | if ( $this->shouldWikispeechRun( $out ) ) { |
306 | $links['actions']['listen'] = [ |
307 | 'class' => 'ext-wikispeech-listen', |
308 | 'text' => $skinTemplate->msg( 'wikispeech-listen' )->text(), |
309 | 'href' => 'javascript:void(0)' |
310 | ]; |
311 | } |
312 | } |
313 | } |