Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 144
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
CentralNoticePageLogPager
0.00% covered (danger)
0.00%
0 / 144
0.00% covered (danger)
0.00%
0 / 6
272
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
 getIndexField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getQueryInfo
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
6
 formatRow
0.00% covered (danger)
0.00%
0 / 86
0.00% covered (danger)
0.00%
0 / 1
90
 getStartBody
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
6
 getEndBody
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;
5
6/**
7 * This class generates a paginated log of recent changes to banner messages (the parts that get
8 * translated). We use the rencentchanges table since it is lightweight, however, this means that
9 * the log only goes back 30 days.
10 */
11class CentralNoticePageLogPager extends ReverseChronologicalPager {
12    /** @var Title */
13    public $viewPage;
14    /** @var SpecialPage */
15    public $special;
16    /** @var string */
17    public $logType;
18
19    /**
20     * Construct instance of class.
21     * @param SpecialPage $special object calling object
22     * @param string $type type of log - 'bannercontent' or 'bannermessages' (optional)
23     */
24    public function __construct( $special, $type = 'bannercontent' ) {
25        $this->special = $special;
26        parent::__construct();
27
28        $this->viewPage = SpecialPage::getTitleFor( 'NoticeTemplate', 'view' );
29        $this->logType = $type;
30    }
31
32    /**
33     * Sort the log list by timestamp
34     * @return string
35     */
36    public function getIndexField() {
37        return 'rc_timestamp';
38    }
39
40    /**
41     * Pull log entries from the database
42     * @return array[]
43     */
44    public function getQueryInfo() {
45        $conds = [
46            'rc_bot' => 1, // include bot edits (all edits made by CentralNotice are bot edits)
47            'rc_namespace' => 8, // only MediaWiki pages
48        ];
49        $db = CNDatabase::getDb();
50        if ( $this->logType == 'bannercontent' ) {
51            // Add query contitions for banner content log
52            $conds += [
53                // get banner content
54                'rc_title' . $db->buildLike( 'Centralnotice-template-', $db->anyString() ),
55            ];
56        } else {
57            // Add query contitions for banner messages log
58            $conds += [
59                // get banner messages
60                'rc_title' . $db->buildLike( 'Centralnotice-', $db->anyString() ),
61                // exclude normal banner content
62                'rc_title NOT' . $db->buildLike( 'Centralnotice-template-', $db->anyString() ),
63            ];
64        }
65
66        $rcQuery = RecentChange::getQueryInfo();
67        $ret = [
68            'tables' => $rcQuery['tables'],
69            'fields' => $rcQuery['fields'],
70            'conds' => $conds, // WHERE conditions
71            'join_conds' => $rcQuery['joins'],
72        ];
73
74        return $ret;
75    }
76
77    /**
78     * Generate the content of each table row (1 row = 1 log entry)
79     * @param stdClass $row
80     * @return string HTML
81     */
82    public function formatRow( $row ) {
83        // Create a user object so we can pull the name, user page, etc.
84        $loggedUser = User::newFromId( $row->rc_user );
85        // Create the user page link
86        $userLink = $this->special->getLinkRenderer()->makeKnownLink(
87            $loggedUser->getUserPage(),
88            $loggedUser->getName()
89        );
90        $userTalkLink = $this->special->getLinkRenderer()->makeKnownLink(
91            $loggedUser->getTalkPage(),
92            $this->msg( 'centralnotice-talk-link' )->text()
93        );
94
95        $language = 'en'; // English is the default for CentralNotice messages
96
97        if ( $this->logType == 'bannercontent' ) {
98            // Extract the banner name from the title
99            $pattern = '/Centralnotice-template-(.*)/';
100            preg_match( $pattern, $row->rc_title, $matches );
101            $banner = $matches[1];
102        } elseif ( $this->logType == 'bannermessages' ) {
103            // Split the title into banner, message, and language
104            $titlePieces = explode( "/", $row->rc_title, 2 );
105            $titleBase = $titlePieces[0];
106            if ( array_key_exists( 1, $titlePieces ) ) {
107                $language = $titlePieces[1];
108            }
109            $pattern = '/Centralnotice-([^-]*)-(.*)/';
110            preg_match( $pattern, $titleBase, $matches );
111            $banner = $matches[1];
112            $message = $matches[2];
113        } else {
114            throw new LogicException( "Unknown type {$this->logType}" );
115        }
116
117        // Create banner link
118        $bannerLink = $this->special->getLinkRenderer()->makeKnownLink(
119            $this->viewPage,
120            $banner,
121            [],
122            [ 'template' => $banner ]
123        );
124
125        // Create title object
126        $title = Title::newFromText( "MediaWiki:{$row->rc_title}" );
127
128        if ( $this->logType == 'bannercontent' ) {
129            // If the banner was just created, show a link to the banner. If the banner was
130            // edited, show a link to the banner and a link to the diff.
131            if ( $row->rc_source === RecentChange::SRC_NEW ) {
132                $bannerCell = $bannerLink;
133            } else {
134                $querydiff = [
135                    'curid' => $row->rc_cur_id,
136                    'diff' => $row->rc_this_oldid,
137                    'oldid' => $row->rc_last_oldid
138                ];
139                $diffUrl = htmlspecialchars( $title->getLinkUrl( $querydiff ) );
140                // Should "diff" be localised? It appears not to be elsewhere in the interface.
141                // See ChangesList->preCacheMessages() for example.
142                $bannerCell = $bannerLink . "&nbsp;(<a href=\"$diffUrl\">diff</a>)";
143            }
144        } elseif ( $this->logType == 'bannermessages' ) {
145            $bannerCell = $bannerLink;
146
147            // Create the message link
148            // @phan-suppress-next-line PhanPossiblyUndeclaredVariable
149            $messageLink = $this->special->getLinkRenderer()->makeKnownLink( $title, $message );
150
151            // If the message was just created, show a link to the message. If the message was
152            // edited, show a link to the message and a link to the diff.
153            if ( $row->rc_source === RecentChange::SRC_NEW ) {
154                $messageCell = $messageLink;
155            } else {
156                $querydiff = [
157                    'curid' => $row->rc_cur_id,
158                    'diff' => $row->rc_this_oldid,
159                    'oldid' => $row->rc_last_oldid
160                ];
161                $diffUrl = htmlspecialchars( $title->getLinkUrl( $querydiff ) );
162                // Should "diff" be localised? It appears not to be elsewhere in the interface.
163                // See ChangesList->preCacheMessages() for example.
164                $messageCell = $messageLink . "&nbsp;(<a href=\"$diffUrl\">diff</a>)";
165            }
166        } else {
167            throw new LogicException( "Unknown type {$this->logType}" );
168        }
169
170        // Begin log entry primary row
171        $lang = $this->getLanguage();
172        $htmlOut = Html::openElement( 'tr' );
173
174        $htmlOut .= Html::element( 'td', [ 'valign' => 'top' ] );
175        $htmlOut .= Xml::element( 'td', [ 'valign' => 'top', 'class' => 'primary' ],
176            $lang->date( $row->rc_timestamp ) . ' ' . $lang->time( $row->rc_timestamp )
177        );
178        $htmlOut .= Html::rawElement( 'td', [ 'valign' => 'top', 'class' => 'primary' ],
179            $this->msg( 'centralnotice-user-links' )
180                ->rawParams( $userLink, $userTalkLink )
181                ->escaped()
182        );
183        $htmlOut .= Html::rawElement( 'td', [ 'valign' => 'top', 'class' => 'primary' ],
184            $bannerCell
185        );
186        if ( $this->logType == 'bannermessages' ) {
187            // @phan-suppress-next-next-line PhanPossiblyUndeclaredVariable,PhanTypeMismatchArgumentNullable
188            $htmlOut .= Html::rawElement( 'td', [ 'valign' => 'top', 'class' => 'primary' ],
189                $messageCell
190            );
191            $htmlOut .= Html::rawElement( 'td', [ 'valign' => 'top', 'class' => 'primary' ],
192                $language
193            );
194        }
195
196        $htmlOut .= Html::rawElement( 'td',
197            [ 'valign' => 'top', 'class' => 'primary-summary' ],
198            htmlspecialchars(
199                MediaWikiServices::getInstance()->getCommentStore()->getComment( 'rc_comment', $row )->text
200            )
201        );
202        $htmlOut .= Html::rawElement( 'td', [],
203            '&nbsp;'
204        );
205
206        // End log entry primary row
207        $htmlOut .= Html::closeElement( 'tr' );
208
209        return $htmlOut;
210    }
211
212    /**
213     * @return string
214     */
215    public function getStartBody() {
216        $htmlOut = '';
217        $htmlOut .= Html::openElement( 'table', [ 'id' => 'cn-campaign-logs', 'cellpadding' => 3 ] );
218        $htmlOut .= Html::openElement( 'tr' );
219        $htmlOut .= Xml::element( 'th', [ 'style' => 'width: 20px;' ] );
220        $htmlOut .= Xml::element( 'th', [ 'align' => 'left', 'style' => 'width: 130px;' ],
221            $this->msg( 'centralnotice-timestamp' )->text()
222        );
223        $htmlOut .= Xml::element( 'th', [ 'align' => 'left', 'style' => 'width: 160px;' ],
224            $this->msg( 'centralnotice-user' )->text()
225        );
226        $htmlOut .= Xml::element( 'th', [ 'align' => 'left', 'style' => 'width: 160px;' ],
227            $this->msg( 'centralnotice-banner' )->text()
228        );
229        if ( $this->logType == 'bannermessages' ) {
230            $htmlOut .= Xml::element( 'th', [ 'align' => 'left', 'style' => 'width: 160px;' ],
231                $this->msg( 'centralnotice-message' )->text()
232            );
233            $htmlOut .= Xml::element( 'th', [ 'align' => 'left', 'style' => 'width: 100px;' ],
234                $this->msg( 'centralnotice-language' )->text()
235            );
236
237            $commentWidth = '120px';
238
239        } else {
240            $commentWidth = '250px';
241        }
242
243        $htmlOut .= Xml::element( 'th',
244            [ 'align' => 'left', 'style' => "width: {$commentWidth};" ],
245            $this->msg( 'centralnotice-change-summary-heading' )->text()
246        );
247
248        $htmlOut .= Html::rawElement( 'td', [],
249            '&nbsp;'
250        );
251        $htmlOut .= Html::closeElement( 'tr' );
252        return $htmlOut;
253    }
254
255    /**
256     * Close table
257     * @return string
258     */
259    public function getEndBody() {
260        return Html::closeElement( 'table' );
261    }
262}