Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 127
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialMobileEditWatchlist
0.00% covered (danger)
0.00%
0 / 127
0.00% covered (danger)
0.00%
0 / 10
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 outputSubtitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLineHtml
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
20
 execute
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getPageOffset
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 getPagesToDisplay
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getNextPage
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 executeViewEditWatchlist
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 1
42
 getViewHtml
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getAssociatedNavigationLinks
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3use MediaWiki\Html\Html;
4use MediaWiki\MediaWikiServices;
5use MediaWiki\SpecialPage\SpecialPage;
6use MediaWiki\Specials\SpecialEditWatchlist;
7use MediaWiki\Title\Title;
8use MobileFrontend\Hooks\HookRunner;
9use MobileFrontend\Models\MobileCollection;
10use 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 */
16class 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}