Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / logging / BlockLogFormatter.php
blob05d53665c1aaf2f7b6abedb21667b3e3e24be62e
1 <?php
2 /**
3 * Formatter for block log entries.
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.
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.
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
20 * @file
21 * @license GPL-2.0-or-later
22 * @since 1.25
25 use MediaWiki\Api\ApiResult;
26 use MediaWiki\Language\Language;
27 use MediaWiki\Linker\Linker;
28 use MediaWiki\MainConfigNames;
29 use MediaWiki\Message\Message;
30 use MediaWiki\SpecialPage\SpecialPage;
31 use MediaWiki\Title\Title;
32 use MediaWiki\User\User;
34 /**
35 * This class formats block log entries.
37 * @since 1.25
39 class BlockLogFormatter extends LogFormatter {
40 protected function getMessageParameters() {
41 $params = parent::getMessageParameters();
43 $title = $this->entry->getTarget();
44 if ( substr( $title->getText(), 0, 1 ) === '#' ) {
45 // autoblock - no user link possible
46 $params[2] = $title->getText();
47 $params[3] = ''; // no user name for gender use
48 } else {
49 // Create a user link for the blocked
50 $username = $title->getText();
51 // @todo Store the user identifier in the parameters
52 // to make this faster for future log entries
53 $targetUser = User::newFromName( $username, false );
54 $params[2] = Message::rawParam( $this->makeUserLink( $targetUser, Linker::TOOL_LINKS_NOBLOCK ) );
55 $params[3] = $username; // plain user name for gender use
58 $subtype = $this->entry->getSubtype();
59 if ( $subtype === 'block' || $subtype === 'reblock' ) {
60 if ( !isset( $params[4] ) ) {
61 // Very old log entry without duration: means infinity
62 $params[4] = 'infinity';
64 // Localize the duration, and add a tooltip
65 // in English to help visitors from other wikis.
66 // The lrm is needed to make sure that the number
67 // is shown on the correct side of the tooltip text.
68 // @phan-suppress-next-line SecurityCheck-DoubleEscaped
69 $durationTooltip = '&lrm;' . htmlspecialchars( $params[4] );
70 $blockExpiry = $this->context->getLanguage()->translateBlockExpiry(
71 $params[4],
72 $this->context->getUser(),
73 (int)wfTimestamp( TS_UNIX, $this->entry->getTimestamp() )
75 if ( $this->plaintext ) {
76 // @phan-suppress-next-line SecurityCheck-XSS Unlikely positive, only if language format is bad
77 $params[4] = Message::rawParam( $blockExpiry );
78 } else {
79 $params[4] = Message::rawParam(
80 "<span class=\"blockExpiry\" title=\"$durationTooltip\">" .
81 // @phan-suppress-next-line SecurityCheck-DoubleEscaped language class does not escape
82 htmlspecialchars( $blockExpiry ) .
83 '</span>'
86 $params[5] = isset( $params[5] ) ?
87 self::formatBlockFlags( $params[5], $this->context->getLanguage() ) : '';
89 // block restrictions
90 if ( isset( $params[6] ) ) {
91 $pages = $params[6]['pages'] ?? [];
92 $pageLinks = [];
93 foreach ( $pages as $page ) {
94 $pageLinks[] = $this->makePageLink( Title::newFromText( $page ) );
97 $rawNamespaces = $params[6]['namespaces'] ?? [];
98 $namespaces = [];
99 foreach ( $rawNamespaces as $ns ) {
100 $text = (int)$ns === NS_MAIN
101 ? $this->msg( 'blanknamespace' )->escaped()
102 : htmlspecialchars( $this->context->getLanguage()->getFormattedNsText( $ns ) );
103 if ( $this->plaintext ) {
104 // Because the plaintext cannot link to the Special:AllPages
105 // link that is linked to in non-plaintext mode, just return
106 // the name of the namespace.
107 $namespaces[] = $text;
108 } else {
109 $namespaces[] = $this->makePageLink(
110 SpecialPage::getTitleFor( 'Allpages' ),
111 [ 'namespace' => $ns ],
112 $text
117 $rawActions = $params[6]['actions'] ?? [];
118 $actions = [];
119 foreach ( $rawActions as $action ) {
120 $actions[] = $this->msg( 'ipb-action-' . $action )->escaped();
123 $restrictions = [];
124 if ( $pageLinks ) {
125 $restrictions[] = $this->msg( 'logentry-partialblock-block-page' )
126 ->numParams( count( $pageLinks ) )
127 ->rawParams( $this->context->getLanguage()->listToText( $pageLinks ) )->escaped();
130 if ( $namespaces ) {
131 $restrictions[] = $this->msg( 'logentry-partialblock-block-ns' )
132 ->numParams( count( $namespaces ) )
133 ->rawParams( $this->context->getLanguage()->listToText( $namespaces ) )->escaped();
135 $enablePartialActionBlocks = $this->context->getConfig()
136 ->get( MainConfigNames::EnablePartialActionBlocks );
137 if ( $actions && $enablePartialActionBlocks ) {
138 $restrictions[] = $this->msg( 'logentry-partialblock-block-action' )
139 ->numParams( count( $actions ) )
140 ->rawParams( $this->context->getLanguage()->listToText( $actions ) )->escaped();
143 $params[6] = Message::rawParam( $this->context->getLanguage()->listToText( $restrictions ) );
147 return $params;
150 protected function extractParameters() {
151 $params = parent::extractParameters();
152 // Legacy log params returning the params in index 3 and 4, moved to 4 and 5
153 if ( $this->entry->isLegacy() && isset( $params[3] ) ) {
154 if ( isset( $params[4] ) ) {
155 $params[5] = $params[4];
157 $params[4] = $params[3];
158 $params[3] = '';
160 return $params;
163 public function getPreloadTitles() {
164 $title = $this->entry->getTarget();
165 $preload = [];
166 // Preload user page for non-autoblocks
167 if ( substr( $title->getText(), 0, 1 ) !== '#' && $title->canExist() ) {
168 $preload[] = $title->getTalkPage();
170 // Preload page restriction
171 $params = $this->extractParameters();
172 if ( isset( $params[6]['pages'] ) ) {
173 foreach ( $params[6]['pages'] as $page ) {
174 $preload[] = Title::newFromText( $page );
177 return $preload;
180 public function getActionLinks() {
181 $subtype = $this->entry->getSubtype();
182 $linkRenderer = $this->getLinkRenderer();
183 if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) // Action is hidden
184 || !( $subtype === 'block' || $subtype === 'reblock' )
185 || !$this->context->getAuthority()->isAllowed( 'block' )
187 return '';
190 // Show unblock/change block link
191 $title = $this->entry->getTarget();
192 $links = [
193 $linkRenderer->makeKnownLink(
194 SpecialPage::getTitleFor( 'Unblock', $title->getDBkey() ),
195 $this->msg( 'unblocklink' )->text()
197 $linkRenderer->makeKnownLink(
198 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
199 $this->msg( 'change-blocklink' )->text()
203 return $this->msg( 'parentheses' )->rawParams(
204 $this->context->getLanguage()->pipeList( $links ) )->escaped();
208 * Convert a comma-delimited list of block log flags
209 * into a more readable (and translated) form
211 * @param string $flags Flags to format
212 * @param Language $lang
213 * @return string
215 public static function formatBlockFlags( $flags, Language $lang ) {
216 $flags = trim( $flags );
217 if ( $flags === '' ) {
218 return ''; // nothing to do
220 $flags = explode( ',', $flags );
221 $flagsCount = count( $flags );
223 for ( $i = 0; $i < $flagsCount; $i++ ) {
224 $flags[$i] = self::formatBlockFlag( $flags[$i], $lang );
227 return wfMessage( 'parentheses' )->inLanguage( $lang )
228 ->rawParams( $lang->commaList( $flags ) )->escaped();
232 * Translate a block log flag if possible
234 * @param string $flag Flag to translate
235 * @param Language $lang Language object to use
236 * @return string
238 public static function formatBlockFlag( $flag, Language $lang ) {
239 static $messages = [];
241 if ( !isset( $messages[$flag] ) ) {
242 $messages[$flag] = htmlspecialchars( $flag ); // Fallback
244 // For grepping. The following core messages can be used here:
245 // * block-log-flags-angry-autoblock
246 // * block-log-flags-anononly
247 // * block-log-flags-hiddenname
248 // * block-log-flags-noautoblock
249 // * block-log-flags-nocreate
250 // * block-log-flags-noemail
251 // * block-log-flags-nousertalk
252 $msg = wfMessage( 'block-log-flags-' . $flag )->inLanguage( $lang );
254 if ( $msg->exists() ) {
255 $messages[$flag] = $msg->escaped();
259 return $messages[$flag];
262 protected function getParametersForApi() {
263 $entry = $this->entry;
264 $params = $entry->getParameters();
266 static $map = [
267 // While this looks wrong to be starting at 5 rather than 4, it's
268 // because getMessageParameters uses $4 for its own purposes.
269 '5::duration',
270 '6:array:flags',
271 '6::flags' => '6:array:flags',
274 foreach ( $map as $index => $key ) {
275 if ( isset( $params[$index] ) ) {
276 $params[$key] = $params[$index];
277 unset( $params[$index] );
281 ksort( $params );
283 $subtype = $entry->getSubtype();
284 if ( $subtype === 'block' || $subtype === 'reblock' ) {
285 // Defaults for old log entries missing some fields
286 $params += [
287 '5::duration' => 'infinity',
288 '6:array:flags' => [],
291 if ( !is_array( $params['6:array:flags'] ) ) {
292 // @phan-suppress-next-line PhanSuspiciousValueComparison
293 $params['6:array:flags'] = $params['6:array:flags'] === ''
294 ? []
295 : explode( ',', $params['6:array:flags'] );
298 if ( wfIsInfinity( $params['5::duration'] ) ) {
299 // Normalize all possible values to one for pre-T241709 rows
300 $params['5::duration'] = 'infinity';
301 } else {
302 $ts = (int)wfTimestamp( TS_UNIX, $entry->getTimestamp() );
303 $expiry = strtotime( $params['5::duration'], $ts );
304 if ( $expiry !== false && $expiry > 0 ) {
305 $params[':timestamp:expiry'] = $expiry;
310 return $params;
314 * @inheritDoc
315 * @suppress PhanTypeInvalidDimOffset
317 public function formatParametersForApi() {
318 $ret = parent::formatParametersForApi();
319 if ( isset( $ret['flags'] ) ) {
320 ApiResult::setIndexedTagName( $ret['flags'], 'f' );
323 if ( isset( $ret['restrictions']['pages'] ) ) {
324 $ret['restrictions']['pages'] = array_map( function ( $title ) {
325 return $this->formatParameterValueForApi( 'page', 'title-link', $title );
326 }, $ret['restrictions']['pages'] );
327 ApiResult::setIndexedTagName( $ret['restrictions']['pages'], 'p' );
330 if ( isset( $ret['restrictions']['namespaces'] ) ) {
331 // @phan-suppress-next-line PhanTypeMismatchArgument False positive
332 ApiResult::setIndexedTagName( $ret['restrictions']['namespaces'], 'ns' );
335 return $ret;
338 protected function getMessageKey() {
339 $type = $this->entry->getType();
340 $subtype = $this->entry->getSubtype();
341 $sitewide = $this->entry->getParameters()['sitewide'] ?? true;
343 $key = "logentry-$type-$subtype";
344 if ( ( $subtype === 'block' || $subtype === 'reblock' ) && !$sitewide ) {
345 // $this->getMessageParameters is doing too much. We just need
346 // to check the presence of restrictions ($param[6]) and calling
347 // on parent gives us that
348 $params = parent::getMessageParameters();
350 // message changes depending on whether there are editing restrictions or not
351 if ( isset( $params[6] ) ) {
352 $key = "logentry-partial$type-$subtype";
353 } else {
354 $key = "logentry-non-editing-$type-$subtype";
358 return $key;