Merge "Selenium: replace UserLoginPage with BlankPage where possible"
[lhc/web/wiklou.git] / includes / logging / BlockLogFormatter.php
1 <?php
2 /**
3 * Formatter for block log entries.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @license GPL-2.0-or-later
22 * @since 1.25
23 */
24
25 /**
26 * This class formats block log entries.
27 *
28 * @since 1.25
29 */
30 class BlockLogFormatter extends LogFormatter {
31 protected function getMessageParameters() {
32 $params = parent::getMessageParameters();
33
34 $title = $this->entry->getTarget();
35 if ( substr( $title->getText(), 0, 1 ) === '#' ) {
36 // autoblock - no user link possible
37 $params[2] = $title->getText();
38 $params[3] = ''; // no user name for gender use
39 } else {
40 // Create a user link for the blocked
41 $username = $title->getText();
42 // @todo Store the user identifier in the parameters
43 // to make this faster for future log entries
44 $targetUser = User::newFromName( $username, false );
45 $params[2] = Message::rawParam( $this->makeUserLink( $targetUser, Linker::TOOL_LINKS_NOBLOCK ) );
46 $params[3] = $username; // plain user name for gender use
47 }
48
49 $subtype = $this->entry->getSubtype();
50 if ( $subtype === 'block' || $subtype === 'reblock' ) {
51 if ( !isset( $params[4] ) ) {
52 // Very old log entry without duration: means infinite
53 $params[4] = 'infinite';
54 }
55 // Localize the duration, and add a tooltip
56 // in English to help visitors from other wikis.
57 // The lrm is needed to make sure that the number
58 // is shown on the correct side of the tooltip text.
59 $durationTooltip = '&lrm;' . htmlspecialchars( $params[4] );
60 $blockExpiry = $this->context->getLanguage()->translateBlockExpiry(
61 $params[4],
62 $this->context->getUser(),
63 wfTimestamp( TS_UNIX, $this->entry->getTimestamp() )
64 );
65 if ( $this->plaintext ) {
66 $params[4] = Message::rawParam( $blockExpiry );
67 } else {
68 $params[4] = Message::rawParam(
69 "<span class=\"blockExpiry\" title=\"$durationTooltip\">" .
70 $blockExpiry .
71 '</span>'
72 );
73 }
74 $params[5] = isset( $params[5] ) ?
75 self::formatBlockFlags( $params[5], $this->context->getLanguage() ) : '';
76
77 // block restrictions
78 if ( isset( $params[6] ) ) {
79 $pages = $params[6]['pages'] ?? [];
80 $pages = array_map( function ( $page ) {
81 return $this->makePageLink( Title::newFromText( $page ) );
82 }, $pages );
83
84 $namespaces = $params[6]['namespaces'] ?? [];
85 $namespaces = array_map( function ( $ns ) {
86 $text = (int)$ns === NS_MAIN
87 ? $this->msg( 'blanknamespace' )->text()
88 : $this->context->getLanguage()->getFormattedNsText( $ns );
89 $params = [ 'namespace' => $ns ];
90
91 return $this->makePageLink( SpecialPage::getTitleFor( 'Allpages' ), $params, $text );
92 }, $namespaces );
93
94 $restrictions = [];
95 if ( $pages ) {
96 $restrictions[] = $this->msg( 'logentry-partialblock-block-page' )
97 ->numParams( count( $pages ) )
98 ->rawParams( $this->context->getLanguage()->listToText( $pages ) )->text();
99 }
100
101 if ( $namespaces ) {
102 $restrictions[] = $this->msg( 'logentry-partialblock-block-ns' )
103 ->numParams( count( $namespaces ) )
104 ->rawParams( $this->context->getLanguage()->listToText( $namespaces ) )->text();
105 }
106
107 $params[6] = Message::rawParam( $this->context->getLanguage()->listToText( $restrictions ) );
108 }
109 }
110
111 return $params;
112 }
113
114 protected function extractParameters() {
115 $params = parent::extractParameters();
116 // Legacy log params returning the params in index 3 and 4, moved to 4 and 5
117 if ( $this->entry->isLegacy() && isset( $params[3] ) ) {
118 if ( isset( $params[4] ) ) {
119 $params[5] = $params[4];
120 }
121 $params[4] = $params[3];
122 $params[3] = '';
123 }
124 return $params;
125 }
126
127 public function getPreloadTitles() {
128 $title = $this->entry->getTarget();
129 // Preload user page for non-autoblocks
130 if ( substr( $title->getText(), 0, 1 ) !== '#' && $title->isValid() ) {
131 return [ $title->getTalkPage() ];
132 }
133 return [];
134 }
135
136 public function getActionLinks() {
137 $subtype = $this->entry->getSubtype();
138 $linkRenderer = $this->getLinkRenderer();
139 if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) // Action is hidden
140 || !( $subtype === 'block' || $subtype === 'reblock' )
141 || !$this->context->getUser()->isAllowed( 'block' )
142 ) {
143 return '';
144 }
145
146 // Show unblock/change block link
147 $title = $this->entry->getTarget();
148 $links = [
149 $linkRenderer->makeKnownLink(
150 SpecialPage::getTitleFor( 'Unblock', $title->getDBkey() ),
151 $this->msg( 'unblocklink' )->text()
152 ),
153 $linkRenderer->makeKnownLink(
154 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
155 $this->msg( 'change-blocklink' )->text()
156 )
157 ];
158
159 return $this->msg( 'parentheses' )->rawParams(
160 $this->context->getLanguage()->pipeList( $links ) )->escaped();
161 }
162
163 /**
164 * Convert a comma-delimited list of block log flags
165 * into a more readable (and translated) form
166 *
167 * @param string $flags Flags to format
168 * @param Language $lang
169 * @return string
170 */
171 public static function formatBlockFlags( $flags, Language $lang ) {
172 $flags = trim( $flags );
173 if ( $flags === '' ) {
174 return ''; // nothing to do
175 }
176 $flags = explode( ',', $flags );
177 $flagsCount = count( $flags );
178
179 for ( $i = 0; $i < $flagsCount; $i++ ) {
180 $flags[$i] = self::formatBlockFlag( $flags[$i], $lang );
181 }
182
183 return wfMessage( 'parentheses' )->inLanguage( $lang )
184 ->rawParams( $lang->commaList( $flags ) )->escaped();
185 }
186
187 /**
188 * Translate a block log flag if possible
189 *
190 * @param int $flag Flag to translate
191 * @param Language $lang Language object to use
192 * @return string
193 */
194 public static function formatBlockFlag( $flag, Language $lang ) {
195 static $messages = [];
196
197 if ( !isset( $messages[$flag] ) ) {
198 $messages[$flag] = htmlspecialchars( $flag ); // Fallback
199
200 // For grepping. The following core messages can be used here:
201 // * block-log-flags-angry-autoblock
202 // * block-log-flags-anononly
203 // * block-log-flags-hiddenname
204 // * block-log-flags-noautoblock
205 // * block-log-flags-nocreate
206 // * block-log-flags-noemail
207 // * block-log-flags-nousertalk
208 $msg = wfMessage( 'block-log-flags-' . $flag )->inLanguage( $lang );
209
210 if ( $msg->exists() ) {
211 $messages[$flag] = $msg->escaped();
212 }
213 }
214
215 return $messages[$flag];
216 }
217
218 protected function getParametersForApi() {
219 $entry = $this->entry;
220 $params = $entry->getParameters();
221
222 static $map = [
223 // While this looks wrong to be starting at 5 rather than 4, it's
224 // because getMessageParameters uses $4 for its own purposes.
225 '5::duration',
226 '6:array:flags',
227 '6::flags' => '6:array:flags',
228 ];
229
230 foreach ( $map as $index => $key ) {
231 if ( isset( $params[$index] ) ) {
232 $params[$key] = $params[$index];
233 unset( $params[$index] );
234 }
235 }
236
237 ksort( $params );
238
239 $subtype = $entry->getSubtype();
240 if ( $subtype === 'block' || $subtype === 'reblock' ) {
241 // Defaults for old log entries missing some fields
242 $params += [
243 '5::duration' => 'infinite',
244 '6:array:flags' => [],
245 ];
246
247 if ( !is_array( $params['6:array:flags'] ) ) {
248 $params['6:array:flags'] = $params['6:array:flags'] === ''
249 ? []
250 : explode( ',', $params['6:array:flags'] );
251 }
252
253 if ( !wfIsInfinity( $params['5::duration'] ) ) {
254 $ts = wfTimestamp( TS_UNIX, $entry->getTimestamp() );
255 $expiry = strtotime( $params['5::duration'], $ts );
256 if ( $expiry !== false && $expiry > 0 ) {
257 $params[':timestamp:expiry'] = $expiry;
258 }
259 }
260 }
261
262 return $params;
263 }
264
265 public function formatParametersForApi() {
266 $ret = parent::formatParametersForApi();
267 if ( isset( $ret['flags'] ) ) {
268 ApiResult::setIndexedTagName( $ret['flags'], 'f' );
269 }
270
271 if ( isset( $ret['restrictions']['pages'] ) ) {
272 $ret['restrictions']['pages'] = array_map( function ( $title ) {
273 return $this->formatParameterValueForApi( 'page', 'title-link', $title );
274 }, $ret['restrictions']['pages'] );
275 ApiResult::setIndexedTagName( $ret['restrictions']['pages'], 'p' );
276 }
277
278 if ( isset( $ret['restrictions']['namespaces'] ) ) {
279 ApiResult::setIndexedTagName( $ret['restrictions']['namespaces'], 'ns' );
280 }
281
282 return $ret;
283 }
284
285 protected function getMessageKey() {
286 $type = $this->entry->getType();
287 $subtype = $this->entry->getSubtype();
288 $sitewide = $this->entry->getParameters()['sitewide'] ?? true;
289
290 $key = "logentry-$type-$subtype";
291 if ( ( $subtype === 'block' || $subtype === 'reblock' ) && !$sitewide ) {
292 // $this->getMessageParameters is doing too much. We just need
293 // to check the presence of restrictions ($param[6]) and calling
294 // on parent gives us that
295 $params = parent::getMessageParameters();
296
297 // message changes depending on whether there are editing restrictions or not
298 if ( isset( $params[6] ) ) {
299 $key = "logentry-partial$type-$subtype";
300 } else {
301 $key = "logentry-non-editing-$type-$subtype";
302 }
303 }
304
305 return $key;
306 }
307 }