Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
4.27% |
5 / 117 |
|
12.50% |
1 / 8 |
CRAP | |
0.00% |
0 / 1 |
Hooks | |
4.27% |
5 / 117 |
|
12.50% |
1 / 8 |
1742.25 | |
0.00% |
0 / 1 |
getConfig | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isConfiguredCorrectly | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
onBeforePageDisplay | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
156 | |||
loadForUser | |
40.00% |
4 / 10 |
|
0.00% |
0 / 1 |
7.46 | |||
onResourceLoaderRegisterModules | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
12 | |||
onEditPage__showEditForm_initial | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
132 | |||
makeCentralLink | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
20 | |||
onGetPreferences | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
56 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
16 | * |
17 | * @file |
18 | * @author Szymon Ćwierkosz |
19 | * @author Kunal Mehta |
20 | */ |
21 | |
22 | namespace MediaWiki\GlobalCssJs; |
23 | |
24 | use MediaWiki\Config\Config; |
25 | use MediaWiki\EditPage\EditPage; |
26 | use MediaWiki\Hook\BeforePageDisplayHook; |
27 | use MediaWiki\Hook\EditPage__showEditForm_initialHook; |
28 | use MediaWiki\Linker\Linker; |
29 | use MediaWiki\MediaWikiServices; |
30 | use MediaWiki\Output\OutputPage; |
31 | use MediaWiki\Preferences\Hook\GetPreferencesHook; |
32 | use MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook; |
33 | use MediaWiki\ResourceLoader\ResourceLoader; |
34 | use MediaWiki\Title\Title; |
35 | use MediaWiki\User\User; |
36 | use MediaWiki\User\UserIdentity; |
37 | use MediaWiki\WikiMap\WikiMap; |
38 | use RequestContext; |
39 | use Skin; |
40 | |
41 | //phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName |
42 | class Hooks implements |
43 | BeforePageDisplayHook, |
44 | ResourceLoaderRegisterModulesHook, |
45 | EditPage__showEditForm_initialHook, |
46 | GetPreferencesHook |
47 | { |
48 | |
49 | /** |
50 | * @return Config |
51 | */ |
52 | private static function getConfig(): Config { |
53 | return MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'globalcssjs' ); |
54 | } |
55 | |
56 | /** |
57 | * Helper function for checking whether the extension has been configured correctly. |
58 | * |
59 | * @param array $config |
60 | * @return bool |
61 | */ |
62 | private static function isConfiguredCorrectly( $config ) { |
63 | return !( $config['wiki'] === false || $config['source'] === false ); |
64 | } |
65 | |
66 | /** |
67 | * Handler for BeforePageDisplay hook. |
68 | * |
69 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/BeforePageDisplay |
70 | * @param OutputPage $out |
71 | * @param Skin $skin |
72 | */ |
73 | public function onBeforePageDisplay( $out, $skin ): void { |
74 | $config = self::getConfig(); |
75 | $useSiteCssJs = $config->get( 'UseGlobalSiteCssJs' ); |
76 | $globalCssJsConfig = $config->get( 'GlobalCssJsConfig' ); |
77 | |
78 | if ( !self::isConfiguredCorrectly( $globalCssJsConfig ) ) { |
79 | // Not configured yet, don't register any modules. |
80 | return; |
81 | } |
82 | |
83 | $out->addModuleStyles( [ 'ext.globalCssJs.user.styles' ] ); |
84 | $out->addModules( [ 'ext.globalCssJs.user' ] ); |
85 | if ( $useSiteCssJs ) { |
86 | $out->addModuleStyles( [ 'ext.globalCssJs.site.styles' ] ); |
87 | $out->addModules( [ 'ext.globalCssJs.site' ] ); |
88 | } |
89 | |
90 | // Add help link |
91 | $rlConfig = $config->get( 'GlobalCssJsConfig' ); |
92 | if ( $rlConfig['wiki'] === WikiMap::getCurrentWikiId() ) { |
93 | $title = $out->getTitle(); |
94 | $user = $out->getUser(); |
95 | $name = $user->getName(); |
96 | if ( $useSiteCssJs && $title->inNamespace( NS_MEDIAWIKI ) |
97 | && ( $title->getText() === 'Global.css' || $title->getText() === 'Global.js' ) |
98 | ) { |
99 | $out->addHelpLink( 'Help:Extension:GlobalCssJs' ); |
100 | } elseif ( $user->isRegistered() && $title->inNamespace( NS_USER ) |
101 | && ( $title->getText() === "$name/global.js" || $title->getText() === "$name/global.css" ) |
102 | ) { |
103 | $out->addHelpLink( 'Help:Extension:GlobalCssJs' ); |
104 | } |
105 | } |
106 | } |
107 | |
108 | /** |
109 | * Given a user, should we load scripts for them? |
110 | * |
111 | * @param UserIdentity $user |
112 | * @return bool |
113 | */ |
114 | public static function loadForUser( UserIdentity $user ): bool { |
115 | $config = self::getConfig()->get( 'GlobalCssJsConfig' ); |
116 | $wiki = $config['wiki']; |
117 | if ( $wiki === WikiMap::getCurrentWikiId() ) { |
118 | return true; |
119 | } |
120 | if ( $wiki === false ) { |
121 | // Not configured, don't load anything |
122 | return false; |
123 | } |
124 | |
125 | $lookup = MediaWikiServices::getInstance() |
126 | ->getCentralIdLookupFactory() |
127 | ->getLookup(); |
128 | return $lookup->isAttached( $user ) && $lookup->isAttached( $user, $wiki ); |
129 | } |
130 | |
131 | /** |
132 | * Handler for ResourceLoaderRegisterModules hook. |
133 | * |
134 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderRegisterModules |
135 | * @param ResourceLoader $resourceLoader |
136 | */ |
137 | public function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ): void { |
138 | $config = self::getConfig()->get( 'GlobalCssJsConfig' ); |
139 | |
140 | if ( !self::isConfiguredCorrectly( $config ) ) { |
141 | // Not configured yet, don't register any modules. |
142 | return; |
143 | } |
144 | |
145 | $userJs = [ |
146 | 'class' => ResourceLoaderGlobalUserModule::class, |
147 | 'type' => 'script', |
148 | ] + $config; |
149 | $resourceLoader->register( 'ext.globalCssJs.user', $userJs ); |
150 | |
151 | $userCss = [ |
152 | 'class' => ResourceLoaderGlobalUserModule::class, |
153 | 'type' => 'style', |
154 | ] + $config; |
155 | $resourceLoader->register( 'ext.globalCssJs.user.styles', $userCss ); |
156 | |
157 | if ( self::getConfig()->get( 'UseGlobalSiteCssJs' ) ) { |
158 | $siteJs = [ |
159 | 'class' => ResourceLoaderGlobalSiteModule::class, |
160 | 'type' => 'script', |
161 | ] + $config; |
162 | $resourceLoader->register( 'ext.globalCssJs.site', $siteJs ); |
163 | |
164 | $siteCss = [ |
165 | 'class' => ResourceLoaderGlobalSiteModule::class, |
166 | 'type' => 'style', |
167 | ] + $config; |
168 | $resourceLoader->register( 'ext.globalCssJs.site.styles', $siteCss ); |
169 | } |
170 | } |
171 | |
172 | /** |
173 | * Handler for 'EditPage::showEditForm:initial' hook. |
174 | * |
175 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/EditPage::showEditForm:initial |
176 | * @param EditPage $editPage |
177 | * @param OutputPage $output |
178 | */ |
179 | public function onEditPage__showEditForm_initial( $editPage, $output ) { |
180 | $gcssjsConfig = self::getConfig()->get( 'GlobalCssJsConfig' ); |
181 | $config = $output->getConfig(); |
182 | $user = $output->getUser(); |
183 | $title = $editPage->getTitle(); |
184 | if ( $gcssjsConfig['wiki'] === WikiMap::getCurrentWikiId() && $user->isRegistered() |
185 | && $editPage->formtype == 'initial' && $title->isUserConfigPage() |
186 | ) { |
187 | $title = $editPage->getTitle(); |
188 | $name = $user->getName(); |
189 | if ( $config->get( 'AllowUserJs' ) && $title->isUserJsConfigPage() && |
190 | $title->getText() == $name . '/global.js' |
191 | ) { |
192 | $msg = 'globalcssjs-warning-js'; |
193 | } elseif ( $config->get( 'AllowUserCss' ) && $title->isUserCssConfigPage() && |
194 | $title->getText() == $name . '/global.css' |
195 | ) { |
196 | $msg = 'globalcssjs-warning-css'; |
197 | } else { |
198 | // CSS or JS page, but not a global one |
199 | return; |
200 | } |
201 | $output->wrapWikiMsg( "<div id='mw-$msg'>\n$1\n</div>", [ $msg ] ); |
202 | } |
203 | } |
204 | |
205 | /** |
206 | * Convenince function to make a link to page that might be on another site. |
207 | * |
208 | * @param Title $title |
209 | * @param string $msg message key |
210 | * @return string HTMl link |
211 | * @suppress SecurityCheck-DoubleEscaped phan false positive |
212 | */ |
213 | protected static function makeCentralLink( Title $title, string $msg ): string { |
214 | $config = self::getConfig()->get( 'GlobalCssJsConfig' ); |
215 | $message = wfMessage( $msg ); |
216 | if ( $config['wiki'] === WikiMap::getCurrentWikiId() ) { |
217 | return MediaWikiServices::getInstance()->getLinkRenderer()->makeLink( $title, $message->text() ); |
218 | } elseif ( isset( $config['baseurl'] ) && $config['baseurl'] !== false ) { |
219 | return Linker::makeExternalLink( |
220 | // bug 66873, don't use localized namespace |
221 | $config['baseurl'] . '/User:' . |
222 | htmlspecialchars( $title->getText(), ENT_QUOTES ), |
223 | $message->escaped() |
224 | ); |
225 | } else { |
226 | return WikiMap::makeForeignLink( |
227 | $config['wiki'], |
228 | 'User:' . $title->getText(), // bug 66873, don't use localized namespace |
229 | $message->escaped() |
230 | ); |
231 | } |
232 | } |
233 | |
234 | /** |
235 | * Handler for GetPreferences hook. |
236 | * |
237 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/GetPreferences |
238 | * @param User $user |
239 | * @param array &$prefs |
240 | */ |
241 | public function onGetPreferences( $user, &$prefs ) { |
242 | $ctx = RequestContext::getMain(); |
243 | $allowUserCss = $ctx->getConfig()->get( 'AllowUserCss' ); |
244 | $allowUserJs = $ctx->getConfig()->get( 'AllowUserJs' ); |
245 | |
246 | if ( !$allowUserCss && !$allowUserJs ) { |
247 | // No user CSS or JS allowed |
248 | return; |
249 | } |
250 | |
251 | $safeMode = MediaWikiServices::getInstance()->getUserOptionsLookup()->getOption( $user, 'forcesafemode' ); |
252 | if ( $safeMode ) { |
253 | // Safe mode is enabled |
254 | return; |
255 | } |
256 | |
257 | if ( !self::loadForUser( $user ) ) { |
258 | // No global scripts for this user :( |
259 | return; |
260 | } |
261 | $userName = $user->getName(); |
262 | $linkTools = []; |
263 | if ( $allowUserCss ) { |
264 | $cssPage = Title::makeTitleSafe( NS_USER, $userName . '/global.css' ); |
265 | $linkTools[] = self::makeCentralLink( $cssPage, 'globalcssjs-custom-css' ); |
266 | } |
267 | if ( $allowUserJs ) { |
268 | $jsPage = Title::makeTitleSafe( NS_USER, $userName . '/global.js' ); |
269 | $linkTools[] = self::makeCentralLink( $jsPage, 'globalcssjs-custom-js' ); |
270 | } |
271 | |
272 | $prefs = wfArrayInsertAfter( |
273 | $prefs, |
274 | [ 'globalcssjs' => [ |
275 | 'type' => 'info', |
276 | 'raw' => 'true', |
277 | 'default' => $ctx->getLanguage()->pipeList( $linkTools ), |
278 | 'label-message' => 'globalcssjs-custom-css-js', |
279 | 'section' => 'rendering/skin', |
280 | ] ], |
281 | 'commoncssjs' |
282 | ); |
283 | } |
284 | } |