Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 127 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 1 |
SpecialMobileEditWatchlist | |
0.00% |
0 / 127 |
|
0.00% |
0 / 10 |
506 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
outputSubtitle | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getLineHtml | |
0.00% |
0 / 46 |
|
0.00% |
0 / 1 |
20 | |||
execute | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getPageOffset | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
getPagesToDisplay | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getNextPage | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
executeViewEditWatchlist | |
0.00% |
0 / 47 |
|
0.00% |
0 / 1 |
42 | |||
getViewHtml | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
getAssociatedNavigationLinks | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | use MediaWiki\Html\Html; |
4 | use MediaWiki\MediaWikiServices; |
5 | use MediaWiki\SpecialPage\SpecialPage; |
6 | use MediaWiki\Specials\SpecialEditWatchlist; |
7 | use MediaWiki\Title\Title; |
8 | use MobileFrontend\Hooks\HookRunner; |
9 | use MobileFrontend\Models\MobileCollection; |
10 | use MobileFrontend\Models\MobilePage; |
11 | |
12 | /** |
13 | * The mobile version of the watchlist editing page. |
14 | * @deprecated in future this should be the core SpecialEditWatchlist page (T109277) |
15 | */ |
16 | class SpecialMobileEditWatchlist extends SpecialEditWatchlist { |
17 | /** @var string The name of the title to begin listing the watchlist from */ |
18 | protected $offsetTitle; |
19 | |
20 | public function __construct() { |
21 | $req = $this->getRequest(); |
22 | $this->offsetTitle = $req->getVal( 'from', '' ); |
23 | $watchStoreItem = MediaWikiServices::getInstance()->getWatchedItemStore(); |
24 | parent::__construct( $watchStoreItem ); |
25 | } |
26 | |
27 | /** |
28 | * Renders the subheader. |
29 | */ |
30 | protected function outputSubtitle() { |
31 | $user = $this->getUser(); |
32 | } |
33 | |
34 | /** |
35 | * Gets the HTML fragment for a watched page. The client uses a very different |
36 | * structure for client-rendered items in PageListItem.hogan. |
37 | * |
38 | * @param MobilePage $mp a definition of the page to be rendered. |
39 | * @return string |
40 | */ |
41 | protected function getLineHtml( MobilePage $mp ) { |
42 | $thumb = $mp->getSmallThumbnailHtml( true ); |
43 | $title = $mp->getTitle(); |
44 | if ( !$thumb ) { |
45 | $thumb = Html::rawElement( 'div', [ |
46 | 'class' => 'list-thumb list-thumb-placeholder' |
47 | ], Html::element( 'span', [ |
48 | 'class' => 'mf-icon-image' |
49 | ] ) |
50 | ); |
51 | } |
52 | $timestamp = $mp->getLatestTimestamp(); |
53 | $user = $this->getUser(); |
54 | $titleText = $title->getPrefixedText(); |
55 | if ( $timestamp ) { |
56 | $lastModified = $this->msg( |
57 | 'mobile-frontend-last-modified-date', |
58 | $this->getLanguage()->userDate( $timestamp, $user ), |
59 | $this->getLanguage()->userTime( $timestamp, $user ) |
60 | )->text(); |
61 | $edit = $mp->getLatestEdit(); |
62 | $dataAttrs = [ |
63 | 'data-timestamp' => $edit['timestamp'], |
64 | 'data-user-name' => $edit['name'], |
65 | 'data-user-gender' => $edit['gender'], |
66 | ]; |
67 | $className = 'title'; |
68 | } else { |
69 | $className = 'title new'; |
70 | $lastModified = ''; |
71 | $dataAttrs = []; |
72 | } |
73 | |
74 | $html = |
75 | Html::openElement( 'li', [ |
76 | 'class' => 'page-summary', |
77 | 'title' => $titleText, |
78 | 'data-id' => $title->getArticleID() |
79 | ] ) . |
80 | Html::openElement( 'a', [ 'href' => $title->getLocalURL(), 'class' => $className ] ); |
81 | $html .= $thumb; |
82 | $html .= |
83 | Html::element( 'h3', [], $titleText ); |
84 | |
85 | if ( $lastModified ) { |
86 | $html .= Html::openElement( 'div', [ 'class' => 'info' ] ) . |
87 | Html::element( 'span', array_merge( $dataAttrs, [ 'class' => 'modified-enhancement' ] ), |
88 | $lastModified ) . |
89 | Html::closeElement( 'div' ); |
90 | } |
91 | $html .= Html::closeElement( 'a' ) . |
92 | Html::closeElement( 'li' ); |
93 | |
94 | return $html; |
95 | } |
96 | |
97 | /** |
98 | * The main entry point for the page. |
99 | * |
100 | * @param string|null $mode Whether the user is viewing, editing, or clearing their |
101 | * watchlist |
102 | */ |
103 | public function execute( $mode ) { |
104 | // Anons don't get a watchlist edit |
105 | $this->requireLogin( 'mobile-frontend-watchlist-purpose' ); |
106 | |
107 | $out = $this->getOutput(); |
108 | $out->addBodyClasses( 'mw-mf-special-page' ); |
109 | parent::execute( $mode ); |
110 | $out->setPageTitleMsg( $this->msg( 'watchlist' ) ); |
111 | } |
112 | |
113 | /** |
114 | * Finds the offset of the page given in this->offsetTitle |
115 | * If doesn't exist returns 0 to show from beginning of array of pages. |
116 | * |
117 | * @param array $pages |
118 | * @return int where the index of the next page to be shown. |
119 | */ |
120 | private function getPageOffset( $pages ) { |
121 | // Deal with messiness of mediawiki |
122 | $pages = array_keys( $pages ); |
123 | |
124 | if ( $this->offsetTitle ) { |
125 | // PHP is stupid. strict test to avoid issues when page '0' is watched. |
126 | $offset = array_search( $this->offsetTitle, $pages, true ); |
127 | // Deal with cases where invalid title given |
128 | if ( $offset === false ) { |
129 | $offset = 0; |
130 | } |
131 | } else { |
132 | $offset = 0; |
133 | } |
134 | return $offset; |
135 | } |
136 | |
137 | /** |
138 | * Create paginated view of entire watchlist |
139 | * |
140 | * @param array $pages |
141 | * @return array of pages that should be displayed in current view |
142 | */ |
143 | private function getPagesToDisplay( $pages ) { |
144 | $offset = $this->getPageOffset( $pages ); |
145 | // Get the slice we are going to display and display it |
146 | return array_slice( $pages, $offset, SpecialMobileWatchlist::LIMIT, true ); |
147 | } |
148 | |
149 | /** |
150 | * Identify the next page to be shown |
151 | * |
152 | * @param array $pages |
153 | * @return string|bool representing title of next page to show or |
154 | * false if there isn't another page to show. |
155 | */ |
156 | private function getNextPage( $pages ) { |
157 | $total = count( $pages ); |
158 | $offset = $this->getPageOffset( $pages ); |
159 | $limit = SpecialMobileWatchlist::LIMIT; |
160 | |
161 | // Work out if we need a more button and where to start from |
162 | if ( $total > $offset + $limit ) { |
163 | $pageKeys = array_keys( $pages ); |
164 | $from = $pageKeys[$offset + $limit]; |
165 | } else { |
166 | $from = false; |
167 | } |
168 | return $from; |
169 | } |
170 | |
171 | /** |
172 | * Renders the view/edit (normal) mode of the watchlist. |
173 | */ |
174 | protected function executeViewEditWatchlist() { |
175 | $ns = NS_MAIN; |
176 | $images = []; |
177 | |
178 | $watchlist = $this->getWatchlistInfo(); |
179 | |
180 | if ( isset( $watchlist[$ns] ) ) { |
181 | $allPages = $watchlist[$ns]; |
182 | $from = $this->getNextPage( $allPages ); |
183 | $allPages = $this->getPagesToDisplay( $allPages ); |
184 | } else { |
185 | $allPages = []; |
186 | $from = false; |
187 | } |
188 | |
189 | // Begin rendering of watchlist. |
190 | $watchlist = [ $ns => $allPages ]; |
191 | $services = MediaWikiServices::getInstance(); |
192 | ( new HookRunner( $services->getHookContainer() ) ) |
193 | ->onSpecialMobileEditWatchlist__images( |
194 | $this->getContext(), $watchlist, $images |
195 | ); |
196 | |
197 | // create list of pages |
198 | $mobilePages = new MobileCollection(); |
199 | $pageKeys = array_keys( $watchlist[$ns] ); |
200 | $repoGroup = $services->getRepoGroup(); |
201 | foreach ( $pageKeys as $dbkey ) { |
202 | if ( isset( $images[$ns][$dbkey] ) ) { |
203 | $page = new MobilePage( |
204 | Title::makeTitleSafe( $ns, $dbkey ), |
205 | $repoGroup->findFile( $images[$ns][$dbkey] ) |
206 | ); |
207 | } else { |
208 | $page = new MobilePage( Title::makeTitleSafe( $ns, $dbkey ) ); |
209 | } |
210 | $mobilePages->add( $page ); |
211 | } |
212 | |
213 | if ( $mobilePages->isEmpty() ) { |
214 | $html = SpecialMobileWatchlist::getEmptyListHtml( false, $this->getLanguage() ); |
215 | } else { |
216 | $html = $this->getViewHtml( $mobilePages ); |
217 | } |
218 | if ( $from ) { |
219 | // show more link if there are more items to show |
220 | $qs = [ 'from' => $from ]; |
221 | $html .= Html::element( 'a', |
222 | [ |
223 | 'class' => 'mw-mf-watchlist-more', |
224 | 'href' => SpecialPage::getTitleFor( 'EditWatchlist' )->getLocalURL( $qs ), |
225 | ], |
226 | $this->msg( 'mobile-frontend-watchlist-more' )->text() ); |
227 | } |
228 | $out = $this->getOutput(); |
229 | $out->addHTML( $html ); |
230 | $out->addModules( 'mobile.special.watchlist.scripts' ); |
231 | $out->addModuleStyles( |
232 | [ |
233 | 'mobile.pagelist.styles', |
234 | 'mobile.pagesummary.styles', |
235 | // FIXME: This module should be removed when the following tickets are resolved: |
236 | // * T305113 |
237 | // * T109277 |
238 | // * T117279 |
239 | 'mobile.special.pagefeed.styles' |
240 | ] |
241 | ); |
242 | } |
243 | |
244 | /** |
245 | * @param MobileCollection $collection Collection of pages to get view for |
246 | * @return string html representation of collection in watchlist view |
247 | */ |
248 | protected function getViewHtml( MobileCollection $collection ) { |
249 | $html = Html::openElement( 'ul', [ 'class' => 'content-unstyled mw-mf-page-list thumbs' |
250 | . ' page-summary-list mw-mf-watchlist-page-list' ] ); |
251 | foreach ( $collection as $mobilePage ) { |
252 | $html .= $this->getLineHtml( $mobilePage ); |
253 | } |
254 | $html .= Html::closeElement( 'ul' ); |
255 | return $html; |
256 | } |
257 | |
258 | /** |
259 | * @inheritDoc |
260 | */ |
261 | public function getAssociatedNavigationLinks() { |
262 | return SpecialMobileWatchlist::WATCHLIST_TAB_PATHS; |
263 | } |
264 | } |