Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
TopicSubscriptionsPager
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 10
420
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 preprocessResults
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getFieldNames
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 formatValue
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
72
 getCellAttrs
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getQueryInfo
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDefaultSort
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDefaultDirections
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getIndexField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isFieldSortable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace MediaWiki\Extension\DiscussionTools;
4
5use IContextSource;
6use MediaWiki\Cache\LinkBatchFactory;
7use MediaWiki\Linker\LinkRenderer;
8use MWException;
9use OOUI;
10use TablePager;
11use Title;
12
13class TopicSubscriptionsPager extends TablePager {
14
15    /**
16     * Map of our field names (see ::getFieldNames()) to the column names actually used for
17     * pagination. This is needed to ensure that the values are unique, and that pagination
18     * won't get "stuck" when e.g. 50 subscriptions are all created within a second.
19     */
20    private const INDEX_FIELDS = [
21        // The auto-increment ID will almost always have the same order as sub_created
22        // and the field already has an index.
23        '_topic' => [ 'sub_id' ],
24        'sub_created' => [ 'sub_id' ],
25        // TODO Add indexes that cover these fields to enable sorting by them
26        // 'sub_state' => [ 'sub_state', 'sub_item' ],
27        // 'sub_created' => [ 'sub_created', 'sub_item' ],
28        // 'sub_notified' => [ 'sub_notified', 'sub_item' ],
29    ];
30
31    private LinkBatchFactory $linkBatchFactory;
32
33    public function __construct(
34        IContextSource $context,
35        LinkRenderer $linkRenderer,
36        LinkBatchFactory $linkBatchFactory
37    ) {
38        parent::__construct( $context, $linkRenderer );
39        $this->linkBatchFactory = $linkBatchFactory;
40    }
41
42    /**
43     * @inheritDoc
44     */
45    public function preprocessResults( $result ) {
46        $lb = $this->linkBatchFactory->newLinkBatch();
47        foreach ( $result as $row ) {
48            $lb->add( $row->sub_namespace, $row->sub_title );
49        }
50        $lb->execute();
51    }
52
53    /**
54     * @inheritDoc
55     */
56    protected function getFieldNames() {
57        return [
58            '_topic' => $this->msg( 'discussiontools-topicsubscription-pager-topic' )->text(),
59            '_page' => $this->msg( 'discussiontools-topicsubscription-pager-page' )->text(),
60            'sub_created' => $this->msg( 'discussiontools-topicsubscription-pager-created' )->text(),
61            'sub_notified' => $this->msg( 'discussiontools-topicsubscription-pager-notified' )->text(),
62            '_unsubscribe' => $this->msg( 'discussiontools-topicsubscription-pager-actions' )->text(),
63        ];
64    }
65
66    /**
67     * @inheritDoc
68     */
69    public function formatValue( $field, $value ) {
70        /** @var stdClass $row */
71        $row = $this->mCurrentRow;
72        $linkRenderer = $this->getLinkRenderer();
73
74        switch ( $field ) {
75            case '_topic':
76                $titleSection = Title::makeTitleSafe( $row->sub_namespace, $row->sub_title, $row->sub_section );
77                return $linkRenderer->makeLink( $titleSection, $row->sub_section );
78
79            case '_page':
80                $title = Title::makeTitleSafe( $row->sub_namespace, $row->sub_title );
81                return $linkRenderer->makeLink( $title, $title->getPrefixedText() );
82
83            case 'sub_created':
84                return htmlspecialchars( $this->getLanguage()->userTimeAndDate( $value, $this->getUser() ) );
85
86            case 'sub_notified':
87                return $value ?
88                    htmlspecialchars( $this->getLanguage()->userTimeAndDate( $value, $this->getUser() ) ) :
89                    $this->msg( 'discussiontools-topicsubscription-pager-notified-never' )->escaped();
90
91            case '_unsubscribe':
92                $title = Title::makeTitleSafe( $row->sub_namespace, $row->sub_title );
93                return (string)new OOUI\ButtonWidget( [
94                    'label' => $this->msg( 'discussiontools-topicsubscription-pager-unsubscribe-button' )->text(),
95                    'framed' => false,
96                    'flags' => [ 'destructive' ],
97                    'href' => $title->getLinkURL( [
98                        'action' => 'dtunsubscribe',
99                        'commentname' => $row->sub_item,
100                    ] ),
101                ] );
102
103            default:
104                throw new MWException( "Unknown field '$field'" );
105        }
106    }
107
108    /**
109     * @inheritDoc
110     */
111    protected function getCellAttrs( $field, $value ) {
112        $attrs = parent::getCellAttrs( $field, $value );
113        if ( $field === '_unsubscribe' ) {
114            $attrs['style'] = 'text-align: center;';
115        }
116        return $attrs;
117    }
118
119    /**
120     * @inheritDoc
121     */
122    public function getQueryInfo() {
123        return [
124            'tables' => [
125                'discussiontools_subscription',
126            ],
127            'fields' => [
128                'sub_id',
129                'sub_item',
130                'sub_namespace',
131                'sub_title',
132                'sub_section',
133                'sub_created',
134                'sub_notified',
135            ],
136            'conds' => [
137                'sub_user' => $this->getUser()->getId(),
138                'sub_state != ' . SubscriptionStore::STATE_UNSUBSCRIBED,
139            ],
140        ];
141    }
142
143    /**
144     * @inheritDoc
145     */
146    public function getDefaultSort() {
147        return 'sub_created';
148    }
149
150    /**
151     * @inheritDoc
152     */
153    public function getDefaultDirections() {
154        return static::DIR_DESCENDING;
155    }
156
157    /**
158     * @inheritDoc
159     */
160    public function getIndexField() {
161        return [ static::INDEX_FIELDS[$this->mSort] ];
162    }
163
164    /**
165     * @inheritDoc
166     */
167    protected function isFieldSortable( $field ) {
168        // Hide the sort button for "Topic" as it is more accurately shown as "Created"
169        return isset( static::INDEX_FIELDS[$field] ) && $field !== '_topic';
170    }
171}