Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
97.10% |
67 / 69 |
|
71.43% |
5 / 7 |
CRAP | |
0.00% |
0 / 1 |
Hooks | |
97.10% |
67 / 69 |
|
71.43% |
5 / 7 |
18 | |
0.00% |
0 / 1 |
onSpecialPageBeforeExecute | |
95.65% |
22 / 23 |
|
0.00% |
0 / 1 |
5 | |||
getJsConfigVars | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
2 | |||
redirectToNamespacedRequest | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
getDefaultNamespaces | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
isNamespacedSearch | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
4.07 | |||
onSpecialSearchResultsPrepend | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
onGetPreferences | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace AdvancedSearch; |
4 | |
5 | use ExtensionRegistry; |
6 | use MediaWiki\Config\Config; |
7 | use MediaWiki\Hook\SpecialSearchResultsPrependHook; |
8 | use MediaWiki\Html\Html; |
9 | use MediaWiki\MediaWikiServices; |
10 | use MediaWiki\Output\OutputPage; |
11 | use MediaWiki\Preferences\Hook\GetPreferencesHook; |
12 | use MediaWiki\Request\WebRequest; |
13 | use MediaWiki\SpecialPage\Hook\SpecialPageBeforeExecuteHook; |
14 | use MediaWiki\SpecialPage\SpecialPage; |
15 | use MediaWiki\Specials\SpecialSearch; |
16 | use MediaWiki\User\User; |
17 | use MediaWiki\User\UserIdentity; |
18 | use MessageLocalizer; |
19 | |
20 | /** |
21 | * @license GPL-2.0-or-later |
22 | */ |
23 | class Hooks implements |
24 | SpecialPageBeforeExecuteHook, |
25 | GetPreferencesHook, |
26 | SpecialSearchResultsPrependHook |
27 | { |
28 | |
29 | /** |
30 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/SpecialPageBeforeExecute |
31 | * |
32 | * @param SpecialPage $special |
33 | * @param string|null $subpage |
34 | * @return false|void false to abort the execution of the special page, "void" otherwise |
35 | */ |
36 | public function onSpecialPageBeforeExecute( $special, $subpage ) { |
37 | if ( $special->getName() !== 'Search' ) { |
38 | return; |
39 | } |
40 | |
41 | $services = MediaWikiServices::getInstance(); |
42 | $user = $special->getUser(); |
43 | $outputPage = $special->getOutput(); |
44 | |
45 | /** |
46 | * If the user is logged in and has explicitly requested to disable the extension, don't load. |
47 | * Ensure namespaces are always part of search URLs |
48 | */ |
49 | if ( $user->isNamed() && |
50 | $services->getUserOptionsLookup()->getBoolOption( $user, 'advancedsearch-disable' ) |
51 | ) { |
52 | return; |
53 | } |
54 | |
55 | /** |
56 | * Ensure the current URL is specifying the namespaces which are to be used |
57 | */ |
58 | $redirect = self::redirectToNamespacedRequest( $special ); |
59 | if ( $redirect !== null ) { |
60 | $outputPage->redirect( $redirect ); |
61 | // Abort execution of the SpecialPage by returning false since we are redirecting |
62 | return false; |
63 | } |
64 | |
65 | $outputPage->addModules( [ |
66 | 'ext.advancedSearch.init', |
67 | 'ext.advancedSearch.searchtoken', |
68 | ] ); |
69 | |
70 | $outputPage->addModuleStyles( 'ext.advancedSearch.initialstyles' ); |
71 | |
72 | $outputPage->addJsConfigVars( $this->getJsConfigVars( |
73 | $special->getContext(), |
74 | $special->getConfig(), |
75 | ExtensionRegistry::getInstance(), |
76 | $services |
77 | ) ); |
78 | } |
79 | |
80 | /** |
81 | * @param MessageLocalizer $context |
82 | * @param Config $config |
83 | * @param ExtensionRegistry $extensionRegistry |
84 | * @param MediaWikiServices $services |
85 | * @return array |
86 | */ |
87 | private function getJsConfigVars( |
88 | MessageLocalizer $context, |
89 | Config $config, |
90 | ExtensionRegistry $extensionRegistry, |
91 | MediaWikiServices $services |
92 | ): array { |
93 | $vars = [ |
94 | 'advancedSearch.mimeTypes' => |
95 | ( new MimeTypeConfigurator( $services->getMimeAnalyzer() ) )->getMimeTypes( |
96 | $config->get( 'FileExtensions' ) |
97 | ), |
98 | 'advancedSearch.tooltips' => ( new TooltipGenerator( $context ) )->generateTooltips(), |
99 | 'advancedSearch.namespacePresets' => $config->get( 'AdvancedSearchNamespacePresets' ), |
100 | 'advancedSearch.deepcategoryEnabled' => $config->get( 'AdvancedSearchDeepcatEnabled' ), |
101 | 'advancedSearch.searchableNamespaces' => |
102 | SearchableNamespaceListBuilder::getCuratedNamespaces( |
103 | $services->getSearchEngineConfig()->searchableNamespaces() |
104 | ), |
105 | ]; |
106 | |
107 | if ( $extensionRegistry->isLoaded( 'Translate' ) ) { |
108 | $vars += [ 'advancedSearch.languages' => |
109 | $services->getLanguageNameUtils()->getLanguageNames() |
110 | ]; |
111 | } |
112 | |
113 | return $vars; |
114 | } |
115 | |
116 | /** |
117 | * If the request does not contain any namespaces, redirect to URL with user default namespaces |
118 | * @param SpecialPage $special |
119 | * @return string|null the URL to redirect to or null if not needed |
120 | */ |
121 | private static function redirectToNamespacedRequest( SpecialPage $special ): ?string { |
122 | if ( !self::isNamespacedSearch( $special->getRequest() ) ) { |
123 | $namespacedSearchUrl = $special->getRequest()->getFullRequestURL(); |
124 | $queryParts = []; |
125 | foreach ( self::getDefaultNamespaces( $special->getUser() ) as $ns ) { |
126 | $queryParts['ns' . $ns] = '1'; |
127 | } |
128 | return wfAppendQuery( $namespacedSearchUrl, $queryParts ); |
129 | } |
130 | return null; |
131 | } |
132 | |
133 | /** |
134 | * Retrieves the default namespaces for the current user |
135 | * |
136 | * @param UserIdentity $user The user to lookup default namespaces for |
137 | * @return int[] List of namespaces to be searched by default |
138 | */ |
139 | public static function getDefaultNamespaces( UserIdentity $user ): array { |
140 | $searchConfig = MediaWikiServices::getInstance()->getSearchEngineConfig(); |
141 | return $searchConfig->userNamespaces( $user ) ?: $searchConfig->defaultNamespaces(); |
142 | } |
143 | |
144 | /** |
145 | * Checks if there is a search request, and it already specifies namespaces. |
146 | * @param WebRequest $request |
147 | * @return bool |
148 | */ |
149 | private static function isNamespacedSearch( WebRequest $request ): bool { |
150 | if ( $request->getRawVal( 'search', '' ) === '' ) { |
151 | return true; |
152 | } |
153 | |
154 | foreach ( $request->getValueNames() as $requestKey ) { |
155 | if ( preg_match( '/^ns\d+$/', $requestKey ) ) { |
156 | return true; |
157 | } |
158 | } |
159 | return false; |
160 | } |
161 | |
162 | /** |
163 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/SpecialSearchResultsPrepend |
164 | * |
165 | * @param SpecialSearch $specialSearch |
166 | * @param OutputPage $output |
167 | * @param string $term |
168 | */ |
169 | public function onSpecialSearchResultsPrepend( $specialSearch, $output, $term ) { |
170 | $output->addHTML( |
171 | Html::rawElement( |
172 | 'div', |
173 | [ 'class' => 'mw-search-spinner' ], |
174 | Html::element( 'div', [ 'class' => 'mw-search-spinner-bounce' ] ) |
175 | ) |
176 | ); |
177 | } |
178 | |
179 | /** |
180 | * @param User $user |
181 | * @param array[] &$preferences |
182 | */ |
183 | public function onGetPreferences( $user, &$preferences ) { |
184 | $preferences['advancedsearch-disable'] = [ |
185 | 'type' => 'toggle', |
186 | 'label-message' => 'advancedsearch-preference-disable', |
187 | 'section' => 'searchoptions/advancedsearch', |
188 | 'help-message' => 'advancedsearch-preference-help', |
189 | ]; |
190 | } |
191 | } |