Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 62 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
Hooks | |
0.00% |
0 / 62 |
|
0.00% |
0 / 9 |
812 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
onGetPreferences | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
onBeforePageDisplay | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
20 | |||
onSkinTemplateNavigation__Universal | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
skinConfigViewsLinks | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
30 | |||
showIcon | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
getUserTalkPage | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
110 | |||
onListDefinedTags | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onChangeTagsListActive | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | // phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName |
4 | |
5 | namespace MediaWiki\Extension\WikiLove; |
6 | |
7 | use MediaWiki\Api\ApiMessage; |
8 | use MediaWiki\Api\IApiMessage; |
9 | use MediaWiki\ChangeTags\Hook\ChangeTagsListActiveHook; |
10 | use MediaWiki\ChangeTags\Hook\ListDefinedTagsHook; |
11 | use MediaWiki\Config\Config; |
12 | use MediaWiki\Hook\SkinTemplateNavigation__UniversalHook; |
13 | use MediaWiki\Output\Hook\BeforePageDisplayHook; |
14 | use MediaWiki\Output\OutputPage; |
15 | use MediaWiki\Permissions\PermissionManager; |
16 | use MediaWiki\Preferences\Hook\GetPreferencesHook; |
17 | use MediaWiki\Title\Title; |
18 | use MediaWiki\User\Options\UserOptionsLookup; |
19 | use MediaWiki\User\User; |
20 | use Skin; |
21 | use SkinTemplate; |
22 | |
23 | /** |
24 | * Hooks for WikiLove extension |
25 | * |
26 | * @file |
27 | * @ingroup Extensions |
28 | */ |
29 | |
30 | class Hooks implements |
31 | GetPreferencesHook, |
32 | SkinTemplateNavigation__UniversalHook, |
33 | BeforePageDisplayHook, |
34 | ListDefinedTagsHook, |
35 | ChangeTagsListActiveHook |
36 | { |
37 | private Config $config; |
38 | private PermissionManager $permissionManager; |
39 | private UserOptionsLookup $userOptionsLookup; |
40 | |
41 | public function __construct( |
42 | Config $config, |
43 | PermissionManager $permissionManager, |
44 | UserOptionsLookup $userOptionsLookup |
45 | ) { |
46 | $this->config = $config; |
47 | $this->permissionManager = $permissionManager; |
48 | $this->userOptionsLookup = $userOptionsLookup; |
49 | } |
50 | |
51 | /** |
52 | * Add the preference in the user preferences with the GetPreferences hook. |
53 | * |
54 | * @param User $user |
55 | * @param array &$preferences |
56 | */ |
57 | public function onGetPreferences( $user, &$preferences ) { |
58 | if ( !$this->config->get( 'WikiLoveGlobal' ) ) { |
59 | $preferences['wikilove-enabled'] = [ |
60 | 'type' => 'check', |
61 | 'section' => 'editing/advancedediting', |
62 | 'label-message' => 'wikilove-enable-preference', |
63 | ]; |
64 | } |
65 | } |
66 | |
67 | /** |
68 | * Adds the required module if we are on a user (talk) page. |
69 | * |
70 | * @param OutputPage $out |
71 | * @param Skin $skin |
72 | */ |
73 | public function onBeforePageDisplay( $out, $skin ): void { |
74 | if ( |
75 | !$this->config->get( 'WikiLoveGlobal' ) && |
76 | !$this->userOptionsLookup->getOption( $out->getUser(), 'wikilove-enabled' ) |
77 | ) { |
78 | return; |
79 | } |
80 | |
81 | $title = self::getUserTalkPage( |
82 | $this->permissionManager, |
83 | $skin->getTitle(), |
84 | $skin->getUser() |
85 | ); |
86 | // getUserTalkPage() returns an ApiMessage on error |
87 | if ( !$title instanceof ApiMessage ) { |
88 | $recipient = $title->getBaseText(); |
89 | |
90 | $out->addJsConfigVars( [ 'wikilove-recipient' => $recipient ] ); |
91 | |
92 | $out->addModules( 'ext.wikiLove.init' ); |
93 | $out->addModuleStyles( 'ext.wikiLove.icon' ); |
94 | } |
95 | } |
96 | |
97 | /** |
98 | * Add a tab or an icon the new way (MediaWiki 1.18+) |
99 | * |
100 | * @param SkinTemplate $skin |
101 | * @param array &$links Navigation links |
102 | */ |
103 | public function onSkinTemplateNavigation__Universal( $skin, &$links ): void { |
104 | if ( $this->showIcon( $skin ) ) { |
105 | $this->skinConfigViewsLinks( $skin, $links['views'] ); |
106 | } else { |
107 | $this->skinConfigViewsLinks( $skin, $links['actions'] ); |
108 | } |
109 | } |
110 | |
111 | /** |
112 | * Configure views links. |
113 | * |
114 | * Helper function for SkinTemplateNavigation hooks |
115 | * to configure views links. |
116 | */ |
117 | private function skinConfigViewsLinks( Skin $skin, array &$views ): void { |
118 | // If WikiLove is turned off for this user, don't display tab. |
119 | if ( |
120 | !$this->config->get( 'WikiLoveGlobal' ) && |
121 | !$this->userOptionsLookup->getOption( $skin->getUser(), 'wikilove-enabled' ) |
122 | ) { |
123 | return; |
124 | } |
125 | |
126 | // getUserTalkPage() returns an ApiMessage on error |
127 | if ( !self::getUserTalkPage( |
128 | $this->permissionManager, |
129 | $skin->getTitle(), |
130 | $skin->getUser() |
131 | ) instanceof ApiMessage |
132 | ) { |
133 | $views['wikilove'] = [ |
134 | 'text' => $skin->msg( 'wikilove-tab-text' )->text(), |
135 | 'href' => '#', |
136 | ]; |
137 | if ( $this->showIcon( $skin ) ) { |
138 | $views['wikilove']['icon'] = 'heart'; |
139 | $views['wikilove']['button'] = true; |
140 | $views['wikilove']['primary'] = true; |
141 | } |
142 | } |
143 | } |
144 | |
145 | /** |
146 | * Only show an icon when the global preference is enabled and the current skin isn't CologneBlue. |
147 | */ |
148 | private function showIcon( Skin $skin ): bool { |
149 | return $this->config->get( 'WikiLoveTabIcon' ) && |
150 | $skin->getSkinName() !== 'cologneblue'; |
151 | } |
152 | |
153 | /** |
154 | * Find the editable talk page of the user we want to send WikiLove to. This |
155 | * function also does some sense-checking to make sure we will actually |
156 | * be able to send WikiLove to the target. |
157 | * |
158 | * Phan false positives are suppressed |
159 | * |
160 | * @param PermissionManager $permissionManager |
161 | * @param Title $title The title of a user page or user talk page |
162 | * @param User $user the current user |
163 | * @return Title|IApiMessage Returns either the Title object for the talk page or an error message |
164 | * @suppress PhanPossiblyUndeclaredVariable,PhanTypeMismatchReturnNullable,PhanTypeMismatchArgumentNullable |
165 | */ |
166 | public static function getUserTalkPage( PermissionManager $permissionManager, Title $title, User $user ) { |
167 | // Exit early if the sending user isn't logged in |
168 | if ( !$user->isRegistered() || $user->isTemp() ) { |
169 | return ApiMessage::create( 'wikilove-err-not-logged-in', 'notloggedin' ); |
170 | } |
171 | |
172 | // Exit early if the page is in the wrong namespace |
173 | $ns = $title->getNamespace(); |
174 | if ( $ns !== NS_USER && $ns !== NS_USER_TALK ) { |
175 | return ApiMessage::create( 'wikilove-err-invalid-username', 'invalidusername' ); |
176 | } |
177 | |
178 | // If we're on a subpage, get the root page title |
179 | $baseTitle = $title->getRootTitle(); |
180 | |
181 | // Users can't send WikiLove to themselves |
182 | if ( $user->getName() === $baseTitle->getText() ) { |
183 | return ApiMessage::create( 'wikilove-err-no-self-wikilove', 'no-self-wikilove' ); |
184 | } |
185 | |
186 | // Get the user talk page |
187 | if ( $ns === NS_USER_TALK ) { |
188 | // We're already on the user talk page |
189 | $talkTitle = $baseTitle; |
190 | } elseif ( $ns === NS_USER ) { |
191 | // We're on the user page, so retrieve the user talk page instead |
192 | $talkTitle = $baseTitle->getTalkPage(); |
193 | } |
194 | |
195 | // If it's a redirect, exit. We don't follow redirects since it might confuse the user or |
196 | // lead to an endless loop (like if the talk page redirects to the user page or a subpage). |
197 | // This means that the WikiLove tab will not appear on user pages or user talk pages if |
198 | // the user talk page is a redirect. |
199 | if ( $talkTitle->isRedirect() ) { |
200 | return ApiMessage::create( 'wikilove-err-redirect', 'isredirect' ); |
201 | } |
202 | |
203 | // Make sure we can edit the page |
204 | if ( !$permissionManager->quickUserCan( 'edit', $user, $talkTitle ) ) { |
205 | return ApiMessage::create( 'wikilove-err-cannot-edit', 'cannotedit' ); |
206 | } |
207 | |
208 | return $talkTitle; |
209 | } |
210 | |
211 | /** |
212 | * ListDefinedTags hook handler |
213 | * |
214 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/ListDefinedTags |
215 | * @param array &$tags |
216 | */ |
217 | public function onListDefinedTags( &$tags ) { |
218 | $tags[] = 'wikilove'; |
219 | } |
220 | |
221 | /** |
222 | * ChangeTagsListActive hook handler |
223 | * |
224 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/ChangeTagsListActive |
225 | * @param array &$tags |
226 | */ |
227 | public function onChangeTagsListActive( &$tags ) { |
228 | $tags[] = 'wikilove'; |
229 | } |
230 | |
231 | } |