Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 97 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 1 |
ToolbarBuilder | |
0.00% |
0 / 97 |
|
0.00% |
0 / 8 |
702 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
getGroup | |
0.00% |
0 / 36 |
|
0.00% |
0 / 1 |
210 | |||
createContributionsPageAction | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
createEditPageAction | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
6 | |||
createWatchPageAction | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
12 | |||
getHistoryPageAction | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
getHistoryUrl | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
getLoginUrl | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
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 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | namespace MediaWiki\Minerva\Menu\PageActions; |
22 | |
23 | use ExtensionRegistry; |
24 | use MediaWiki\Config\ServiceOptions; |
25 | use MediaWiki\Context\IContextSource; |
26 | use MediaWiki\Minerva\LanguagesHelper; |
27 | use MediaWiki\Minerva\Menu\Entries\IMenuEntry; |
28 | use MediaWiki\Minerva\Menu\Entries\LanguageSelectorEntry; |
29 | use MediaWiki\Minerva\Menu\Entries\SingleMenuEntry; |
30 | use MediaWiki\Minerva\Menu\Group; |
31 | use MediaWiki\Minerva\Permissions\IMinervaPagePermissions; |
32 | use MediaWiki\Minerva\SkinOptions; |
33 | use MediaWiki\Minerva\Skins\SkinUserPageHelper; |
34 | use MediaWiki\SpecialPage\SpecialPage; |
35 | use MediaWiki\Title\Title; |
36 | use MediaWiki\User\User; |
37 | use MediaWiki\User\UserIdentity; |
38 | use MediaWiki\Watchlist\WatchlistManager; |
39 | use SpecialMobileHistory; |
40 | |
41 | class ToolbarBuilder { |
42 | |
43 | /** @var Title Article title user is currently browsing */ |
44 | private Title $title; |
45 | /** @var User Currently logged in user */ |
46 | private User $user; |
47 | private IContextSource $context; |
48 | private IMinervaPagePermissions $permissions; |
49 | private SkinOptions $skinOptions; |
50 | private SkinUserPageHelper $relevantUserPageHelper; |
51 | private LanguagesHelper $languagesHelper; |
52 | /** @var bool Correlates to $wgWatchlistExpiry feature flag. */ |
53 | private bool $watchlistExpiryEnabled; |
54 | private WatchlistManager $watchlistManager; |
55 | |
56 | /** |
57 | * ServiceOptions needed. |
58 | */ |
59 | public const CONSTRUCTOR_OPTIONS = [ |
60 | 'WatchlistExpiry', |
61 | ]; |
62 | |
63 | /** |
64 | * Build Group containing icons for toolbar |
65 | * @param Title $title Article title user is currently browsing |
66 | * @param User $user Currently logged in user |
67 | * @param IContextSource $context |
68 | * @param IMinervaPagePermissions $permissions Minerva permissions system |
69 | * @param SkinOptions $skinOptions |
70 | * @param SkinUserPageHelper $relevantUserPageHelper User Page helper. The |
71 | * UserPageHelper passed should always be specific to the user page Title. If on a |
72 | * user talk page, UserPageHelper should be instantiated with the user page |
73 | * Title and NOT with the user talk page Title. |
74 | * @param LanguagesHelper $languagesHelper Helper to check title languages/variants |
75 | * @param ServiceOptions $options |
76 | * @param WatchlistManager $watchlistManager |
77 | */ |
78 | public function __construct( |
79 | Title $title, |
80 | User $user, |
81 | IContextSource $context, |
82 | IMinervaPagePermissions $permissions, |
83 | SkinOptions $skinOptions, |
84 | SkinUserPageHelper $relevantUserPageHelper, |
85 | LanguagesHelper $languagesHelper, |
86 | ServiceOptions $options, |
87 | WatchlistManager $watchlistManager |
88 | ) { |
89 | $this->title = $title; |
90 | $this->user = $user; |
91 | $this->context = $context; |
92 | $this->permissions = $permissions; |
93 | $this->skinOptions = $skinOptions; |
94 | $this->relevantUserPageHelper = $relevantUserPageHelper; |
95 | $this->languagesHelper = $languagesHelper; |
96 | $this->watchlistExpiryEnabled = $options->get( 'WatchlistExpiry' ); |
97 | $this->watchlistManager = $watchlistManager; |
98 | } |
99 | |
100 | /** |
101 | * @param array $actions |
102 | * @param array $views |
103 | * @return Group |
104 | */ |
105 | public function getGroup( array $actions, array $views ): Group { |
106 | $group = new Group( 'p-views' ); |
107 | $permissions = $this->permissions; |
108 | $userPageOrUserTalkPageWithOverflowMode = $this->skinOptions->get( SkinOptions::TOOLBAR_SUBMENU ) |
109 | && $this->relevantUserPageHelper->isUserPage(); |
110 | |
111 | if ( !$userPageOrUserTalkPageWithOverflowMode && $permissions->isAllowed( |
112 | IMinervaPagePermissions::SWITCH_LANGUAGE ) ) { |
113 | $group->insertEntry( new LanguageSelectorEntry( |
114 | $this->title, |
115 | $this->languagesHelper->doesTitleHasLanguagesOrVariants( |
116 | $this->context->getOutput(), |
117 | $this->title |
118 | ), |
119 | $this->context, |
120 | true |
121 | ) ); |
122 | } |
123 | |
124 | $watchKey = $key = isset( $actions['unwatch'] ) ? 'unwatch' : 'watch'; |
125 | // The watchstar is typically not shown to anonymous users but it is in Minerva. |
126 | $watchData = $actions[ $watchKey ] ?? [ |
127 | 'icon' => 'star', |
128 | 'class' => '', |
129 | 'href' => $this->getLoginUrl( [ 'returnto' => $this->title ] ), |
130 | 'text' => $this->context->msg( 'watch' ), |
131 | ]; |
132 | if ( $permissions->isAllowed( IMinervaPagePermissions::WATCHABLE ) && $watchData ) { |
133 | $group->insertEntry( $this->createWatchPageAction( $watchKey, $watchData ) ); |
134 | } |
135 | |
136 | $historyView = $views[ 'history'] ?? []; |
137 | if ( $historyView && $permissions->isAllowed( IMinervaPagePermissions::HISTORY ) ) { |
138 | $group->insertEntry( $this->getHistoryPageAction( $historyView ) ); |
139 | } |
140 | |
141 | $user = $this->relevantUserPageHelper->getPageUser(); |
142 | $isUserPageAccessible = $this->relevantUserPageHelper->isUserPageAccessibleToCurrentUser(); |
143 | if ( $user && $isUserPageAccessible ) { |
144 | // T235681: Contributions icon should be added to toolbar on user pages |
145 | // and user talk pages for all users |
146 | $group->insertEntry( $this->createContributionsPageAction( $user ) ); |
147 | } |
148 | |
149 | // We want the edit icon/action(s) always to be the last element on the toolbar list |
150 | if ( $permissions->isAllowed( IMinervaPagePermissions::CONTENT_EDIT ) ) { |
151 | foreach ( $views as $key => $viewData ) { |
152 | if ( in_array( $key, [ 've-edit', 'viewsource', 'edit' ] ) ) { |
153 | $group->insertEntry( $this->createEditPageAction( $key, $viewData ) ); |
154 | } |
155 | } |
156 | } |
157 | return $group; |
158 | } |
159 | |
160 | /** |
161 | * Create Contributions page action visible on user pages or user talk pages |
162 | * for given $user |
163 | * |
164 | * @param UserIdentity $user Determines what the contribution page action will link to |
165 | * @return IMenuEntry |
166 | */ |
167 | protected function createContributionsPageAction( UserIdentity $user ): IMenuEntry { |
168 | $label = $this->context->msg( 'mobile-frontend-user-page-contributions' ); |
169 | |
170 | $entry = new SingleMenuEntry( |
171 | 'page-actions-contributions', |
172 | $label->escaped(), |
173 | SpecialPage::getTitleFor( 'Contributions', $user->getName() )->getLocalURL() ); |
174 | $entry->setTitle( $label ) |
175 | ->trackClicks( 'contributions' ) |
176 | ->setIcon( 'userContributions' ); |
177 | |
178 | return $entry; |
179 | } |
180 | |
181 | /** |
182 | * Creates the "edit" page action: the well-known pencil icon that, when tapped, will open an |
183 | * editor with the lead section loaded. |
184 | * |
185 | * @param string $key |
186 | * @param array $editAction |
187 | * @return IMenuEntry An edit page actions menu entry |
188 | */ |
189 | protected function createEditPageAction( string $key, array $editAction ): IMenuEntry { |
190 | $title = $this->title; |
191 | |
192 | $id = $editAction['single-id'] ?? 'ca-edit'; |
193 | $entry = new SingleMenuEntry( |
194 | 'page-actions-' . $key, |
195 | $editAction['text'], |
196 | $editAction['href'], |
197 | 'edit-page' |
198 | ); |
199 | $iconFallback = $key === 'viewsource' ? 'editLock' : 'edit'; |
200 | $icon = $editAction['icon'] ?? $iconFallback; |
201 | $entry->setIcon( $icon . '-base20' ) |
202 | ->trackClicks( $key ) |
203 | ->setTitle( $this->context->msg( 'tooltip-' . $id ) ) |
204 | ->setNodeID( $id ); |
205 | return $entry; |
206 | } |
207 | |
208 | /** |
209 | * Creates the "watch" or "unwatch" action: the well-known star icon that, when tapped, will |
210 | * add the page to or remove the page from the user's watchlist; or, if the user is logged out, |
211 | * will direct the user's UA to Special:Login. |
212 | * |
213 | * @param string $watchKey either watch or unwatch |
214 | * @param array $watchData |
215 | * @return IMenuEntry An watch/unwatch page actions menu entry |
216 | */ |
217 | protected function createWatchPageAction( string $watchKey, array $watchData ): IMenuEntry { |
218 | $entry = new SingleMenuEntry( |
219 | 'page-actions-watch', |
220 | $watchData['text'], |
221 | $watchData['href'], |
222 | $watchData[ 'class' ], |
223 | $this->permissions->isAllowed( IMinervaPagePermissions::WATCH ) |
224 | ); |
225 | $icon = $watchData['icon'] ?? ''; |
226 | if ( $icon ) { |
227 | $icon .= $watchKey === 'unwatch' ? '-progressive' : '-base20'; |
228 | } |
229 | return $entry->trackClicks( $watchKey ) |
230 | ->setIcon( $icon ) |
231 | ->setTitle( $this->context->msg( $watchKey ) ) |
232 | ->setNodeID( 'ca-watch' ); |
233 | } |
234 | |
235 | /** |
236 | * Creates a history action: An icon that links to the mobile history page. |
237 | * |
238 | * @param array $historyAction |
239 | * @return IMenuEntry A menu entry object that represents a map of HTML attributes |
240 | * and a 'text' property to be used with the pageActionMenu.mustache template. |
241 | */ |
242 | protected function getHistoryPageAction( array $historyAction ): IMenuEntry { |
243 | $entry = new SingleMenuEntry( |
244 | 'page-actions-history', |
245 | $historyAction['text'], |
246 | $historyAction['href'], |
247 | ); |
248 | $icon = $historyAction['icon'] ?? 'history'; |
249 | $entry->setIcon( $icon . '-base20' ) |
250 | ->trackClicks( 'history' ); |
251 | return $entry; |
252 | } |
253 | |
254 | /** |
255 | * Get the URL for the history page for the given title using Special:History |
256 | * when available. |
257 | * FIXME: temporary duplicated code, same as SkinMinerva::getHistoryUrl() |
258 | * @param Title $title The Title object of the page being viewed |
259 | * @return string |
260 | */ |
261 | protected function getHistoryUrl( Title $title ): string { |
262 | return ExtensionRegistry::getInstance()->isLoaded( 'MobileFrontend' ) && |
263 | SpecialMobileHistory::shouldUseSpecialHistory( $title, $this->user ) ? |
264 | SpecialPage::getTitleFor( 'History', $title )->getLocalURL() : |
265 | $title->getLocalURL( [ 'action' => 'history' ] ); |
266 | } |
267 | |
268 | /** |
269 | * Prepares a url to the Special:UserLogin with query parameters |
270 | * @param array $query |
271 | * @return string |
272 | */ |
273 | private function getLoginUrl( $query ): string { |
274 | return SpecialPage::getTitleFor( 'Userlogin' )->getLocalURL( $query ); |
275 | } |
276 | } |