MediaWiki master
BlockLogFormatter.php
Go to the documentation of this file.
1<?php
12
24use Wikimedia\Timestamp\TimestampFormat as TS;
25
32 private TitleParser $titleParser;
33 private NamespaceInfo $namespaceInfo;
34
35 public function __construct(
37 TitleParser $titleParser,
38 NamespaceInfo $namespaceInfo
39 ) {
40 parent::__construct( $entry );
41 $this->titleParser = $titleParser;
42 $this->namespaceInfo = $namespaceInfo;
43 }
44
46 protected function getMessageParameters() {
47 $params = parent::getMessageParameters();
48
49 $title = $this->entry->getTarget();
50 if ( str_starts_with( $title->getText(), '#' ) ) {
51 // autoblock - no user link possible
52 $params[2] = $title->getText();
53 $params[3] = ''; // no user name for gender use
54 } else {
55 // Create a user link for the blocked
56 $username = $title->getText();
57 // @todo Store the user identifier in the parameters
58 // to make this faster for future log entries
59 $targetUser = User::newFromName( $username, false );
60 $params[2] = Message::rawParam( $this->makeUserLink( $targetUser, Linker::TOOL_LINKS_NOBLOCK ) );
61 $params[3] = $username; // plain user name for gender use
62 }
63
64 $subtype = $this->entry->getSubtype();
65 if ( $subtype === 'block' || $subtype === 'reblock' ) {
66 if ( !isset( $params[4] ) ) {
67 // Very old log entry without duration: means infinity
68 $params[4] = 'infinity';
69 }
70 // Localize the duration, and add a tooltip
71 // in English to help visitors from other wikis.
72 // The lrm is needed to make sure that the number
73 // is shown on the correct side of the tooltip text.
74 // @phan-suppress-next-line SecurityCheck-DoubleEscaped
75 $durationTooltip = '&lrm;' . htmlspecialchars( $params[4] );
76 $blockExpiry = $this->context->getLanguage()->translateBlockExpiry(
77 $params[4],
78 $this->context->getUser(),
79 (int)wfTimestamp( TS::UNIX, $this->entry->getTimestamp() )
80 );
81 if ( $this->plaintext ) {
82 // @phan-suppress-next-line SecurityCheck-XSS Unlikely positive, only if language format is bad
83 $params[4] = Message::rawParam( $blockExpiry );
84 } else {
85 $params[4] = Message::rawParam(
86 "<span class=\"blockExpiry\" title=\"$durationTooltip\">" .
87 // @phan-suppress-next-line SecurityCheck-DoubleEscaped language class does not escape
88 htmlspecialchars( $blockExpiry ) .
89 '</span>'
90 );
91 }
92 $params[5] = isset( $params[5] ) ?
93 self::formatBlockFlags( $params[5], $this->context->getLanguage() ) : '';
94
95 // block restrictions
96 if ( isset( $params[6] ) ) {
97 $pages = $params[6]['pages'] ?? [];
98 $pageLinks = [];
99 foreach ( $pages as $page ) {
100 $pageLinks[] = $this->makePageLink( Title::newFromText( $page ) );
101 }
102
103 $rawNamespaces = $params[6]['namespaces'] ?? [];
104 $namespaces = [];
105 foreach ( $rawNamespaces as $ns ) {
106 $text = (int)$ns === NS_MAIN
107 ? $this->msg( 'blanknamespace' )->escaped()
108 : htmlspecialchars( $this->context->getLanguage()->getFormattedNsText( $ns ) );
109 if ( $this->plaintext ) {
110 // Because the plaintext cannot link to the Special:AllPages
111 // link that is linked to in non-plaintext mode, just return
112 // the name of the namespace.
113 $namespaces[] = $text;
114 } else {
115 $namespaces[] = $this->makePageLink(
116 SpecialPage::getTitleFor( 'Allpages' ),
117 [ 'namespace' => $ns ],
118 $text
119 );
120 }
121 }
122
123 $rawActions = $params[6]['actions'] ?? [];
124 $actions = [];
125 foreach ( $rawActions as $action ) {
126 $actions[] = $this->msg( 'ipb-action-' . $action )->escaped();
127 }
128
129 $restrictions = [];
130 if ( $pageLinks ) {
131 $restrictions[] = $this->msg( 'logentry-partialblock-block-page' )
132 ->numParams( count( $pageLinks ) )
133 ->rawParams( $this->context->getLanguage()->listToText( $pageLinks ) )->escaped();
134 }
135
136 if ( $namespaces ) {
137 $restrictions[] = $this->msg( 'logentry-partialblock-block-ns' )
138 ->numParams( count( $namespaces ) )
139 ->rawParams( $this->context->getLanguage()->listToText( $namespaces ) )->escaped();
140 }
141 if ( $actions ) {
142 $restrictions[] = $this->msg( 'logentry-partialblock-block-action' )
143 ->numParams( count( $actions ) )
144 ->rawParams( $this->context->getLanguage()->listToText( $actions ) )->escaped();
145 }
146
147 $params[6] = Message::rawParam( $this->context->getLanguage()->listToText( $restrictions ) );
148 }
149 }
150
151 return $params;
152 }
153
155 protected function extractParameters() {
156 $params = parent::extractParameters();
157 // Legacy log params returning the params in index 3 and 4, moved to 4 and 5
158 if ( $this->entry->isLegacy() && isset( $params[3] ) ) {
159 if ( isset( $params[4] ) ) {
160 $params[5] = $params[4];
161 }
162 $params[4] = $params[3];
163 $params[3] = '';
164 }
165 return $params;
166 }
167
169 public function getPreloadTitles() {
170 $title = $this->entry->getTarget();
171 $preload = [];
172 // Preload user page for non-autoblocks
173 if ( substr( $title->getText(), 0, 1 ) !== '#' && $title->canExist() ) {
174 $preload[] = $this->namespaceInfo->getTalkPage( $title );
175 }
176 // Preload page restriction
177 $params = $this->extractParameters();
178 if ( isset( $params[6]['pages'] ) ) {
179 foreach ( $params[6]['pages'] as $page ) {
180 try {
181 $preload[] = $this->titleParser->parseTitle( $page );
182 } catch ( MalformedTitleException ) {
183 }
184 }
185 }
186 return $preload;
187 }
188
190 public function getActionLinks() {
191 $subtype = $this->entry->getSubtype();
192 $linkRenderer = $this->getLinkRenderer();
193
194 // Don't show anything if the action is hidden
195 if ( $this->entry->isDeleted( LogPage::DELETED_ACTION )
196 || !( $subtype === 'block' || $subtype === 'reblock' )
197 || !$this->context->getAuthority()->isAllowed( 'block' )
198 ) {
199 return '';
200 }
201
202 $title = $this->entry->getTarget();
203 if ( $this->context->getConfig()->get( MainConfigNames::UseCodexSpecialBlock ) ) {
204 $params = $this->entry->getParameters();
205 if ( isset( $params['blockId'] ) ) {
206 // If we have a block ID, show remove/change links
207 $query = isset( $params['blockId'] ) ? [ 'id' => $params['blockId'] ] : [];
208 $links = [
209 $linkRenderer->makeKnownLink(
210 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
211 $this->msg( 'remove-blocklink' )->text(),
212 [],
213 $query + [ 'remove' => '1' ]
214 ),
215 $linkRenderer->makeKnownLink(
216 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
217 $this->msg( 'change-blocklink' )->text(),
218 [],
219 $query
220 )
221 ];
222 } else {
223 // For legacy log entries, just show "manage blocks" since the
224 // Codex block page doesn't have an "unblock by target" mode
225 $links = [
226 $linkRenderer->makeKnownLink(
227 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
228 $this->msg( 'manage-blocklink' )->text(),
229 ),
230 ];
231 }
232 } else {
233 // Show unblock/change links
234 $links = [
235 $linkRenderer->makeKnownLink(
236 SpecialPage::getTitleFor( 'Unblock', $title->getDBkey() ),
237 $this->msg( 'unblocklink' )->text()
238 ),
239 $linkRenderer->makeKnownLink(
240 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
241 $this->msg( 'change-blocklink' )->text()
242 )
243 ];
244 }
245
246 return $this->msg( 'parentheses' )->rawParams(
247 $this->context->getLanguage()->pipeList( $links ) )->escaped();
248 }
249
258 public static function formatBlockFlags( $flags, Language $lang ) {
259 $flags = trim( $flags );
260 if ( $flags === '' ) {
261 return ''; // nothing to do
262 }
263 $flags = explode( ',', $flags );
264 $flagsCount = count( $flags );
265
266 for ( $i = 0; $i < $flagsCount; $i++ ) {
267 $flags[$i] = self::formatBlockFlag( $flags[$i], $lang );
268 }
269
270 return wfMessage( 'parentheses' )->inLanguage( $lang )
271 ->rawParams( $lang->commaList( $flags ) )->escaped();
272 }
273
281 public static function formatBlockFlag( $flag, Language $lang ) {
282 static $messages = [];
283
284 if ( !isset( $messages[$flag] ) ) {
285 $messages[$flag] = htmlspecialchars( $flag ); // Fallback
286
287 // For grepping. The following core messages can be used here:
288 // * block-log-flags-angry-autoblock
289 // * block-log-flags-anononly
290 // * block-log-flags-hiddenname
291 // * block-log-flags-noautoblock
292 // * block-log-flags-nocreate
293 // * block-log-flags-noemail
294 // * block-log-flags-nousertalk
295 $msg = wfMessage( 'block-log-flags-' . $flag )->inLanguage( $lang );
296
297 if ( $msg->exists() ) {
298 $messages[$flag] = $msg->escaped();
299 }
300 }
301
302 return $messages[$flag];
303 }
304
306 protected function getParametersForApi() {
308 $params = $entry->getParameters();
309
310 static $map = [
311 // While this looks wrong to be starting at 5 rather than 4, it's
312 // because getMessageParameters uses $4 for its own purposes.
313 '5::duration',
314 '6:array:flags',
315 '6::flags' => '6:array:flags',
316 ];
317
318 foreach ( $map as $index => $key ) {
319 if ( isset( $params[$index] ) ) {
320 $params[$key] = $params[$index];
321 unset( $params[$index] );
322 }
323 }
324
325 ksort( $params );
326
327 $subtype = $entry->getSubtype();
328 if ( $subtype === 'block' || $subtype === 'reblock' ) {
329 // Defaults for old log entries missing some fields
330 $params += [
331 '5::duration' => 'infinity',
332 '6:array:flags' => [],
333 ];
334
335 if ( !is_array( $params['6:array:flags'] ) ) {
336 $params['6:array:flags'] = $params['6:array:flags'] === ''
337 ? []
338 : explode( ',', $params['6:array:flags'] );
339 }
340
341 if ( wfIsInfinity( $params['5::duration'] ) ) {
342 // Normalize all possible values to one for pre-T241709 rows
343 $params['5::duration'] = 'infinity';
344 $params[':plain:duration-l10n'] = $this->msg( 'infiniteblock' )->plain();
345 } else {
346 $ts = (int)wfTimestamp( TS::UNIX, $entry->getTimestamp() );
347 $expiry = strtotime( $params['5::duration'], $ts );
348 if ( $expiry !== false && $expiry > 0 ) {
349 $params[':timestamp:expiry'] = $expiry;
350 }
351 $params[':plain:duration-l10n'] = $this->context->getLanguage()
352 ->formatDurationBetweenTimestamps( $ts, $expiry );
353 }
354 }
355
356 return $params;
357 }
358
363 public function formatParametersForApi() {
364 $ret = parent::formatParametersForApi();
365 if ( isset( $ret['flags'] ) ) {
366 ApiResult::setIndexedTagName( $ret['flags'], 'f' );
367 }
368
369 if ( isset( $ret['restrictions']['pages'] ) ) {
370 $ret['restrictions']['pages'] = array_map( function ( $title ) {
371 return $this->formatParameterValueForApi( 'page', 'title-link', $title );
372 }, $ret['restrictions']['pages'] );
373 ApiResult::setIndexedTagName( $ret['restrictions']['pages'], 'p' );
374 }
375
376 if ( isset( $ret['restrictions']['namespaces'] ) ) {
377 // @phan-suppress-next-line PhanTypeMismatchArgument False positive
378 ApiResult::setIndexedTagName( $ret['restrictions']['namespaces'], 'ns' );
379 }
380
381 return $ret;
382 }
383
385 protected function getMessageKey() {
386 $type = $this->entry->getType();
387 $subtype = $this->entry->getSubtype();
388 $params = $this->entry->getParameters();
389 $sitewide = $params['sitewide'] ?? true;
390 $count = $params['finalTargetCount'] ?? 0;
391
392 $key = "logentry-$type-$subtype";
393 if ( ( $subtype === 'block' || $subtype === 'reblock' ) && !$sitewide ) {
394 // $this->getMessageParameters is doing too much. We just need
395 // to check the presence of restrictions ($param[6]) and calling
396 // on parent gives us that
397 $params = parent::getMessageParameters();
398
399 // message changes depending on whether there are editing restrictions or not
400 if ( isset( $params[6] ) ) {
401 $key = "logentry-partial$type-$subtype";
402 } else {
403 $key = "logentry-non-editing-$type-$subtype";
404 }
405 }
406 if ( $subtype === 'block' && $count > 1 ) {
407 // logentry-block-block-multi, logentry-partialblock-block-multi,
408 // logentry-non-editing-block-block-multi
409 $key .= '-multi';
410 }
411
412 return $key;
413 }
414}
415
417class_alias( BlockLogFormatter::class, 'BlockLogFormatter' );
const NS_MAIN
Definition Defines.php:51
wfTimestamp( $outputtype=TS::UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfIsInfinity( $str)
Determine input string is represents as infinity.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
This class represents the result of the API operations.
Definition ApiResult.php:33
Base class for language-specific code.
Definition Language.php:70
commaList(array $list)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
Some internal bits split of from Skin.php.
Definition Linker.php:47
This class formats block log entries.
static formatBlockFlag( $flag, Language $lang)
Translate a block log flag if possible.
__construct(LogEntry $entry, TitleParser $titleParser, NamespaceInfo $namespaceInfo)
formatParametersForApi()
Format parameters for API output.The result array should generally map named keys to values....
getPreloadTitles()
to override LinkTarget[] Array of titles that should be preloaded with LinkBatch
static formatBlockFlags( $flags, Language $lang)
Convert a comma-delimited list of block log flags into a more readable (and translated) form.
getMessageParameters()
Formats parameters intended for action message from array of all parameters.There are three hardcoded...
getMessageKey()
Returns a key to be used for formatting the action sentence.Default is logentry-TYPE-SUBTYPE for mode...
getParametersForApi()
Get the array of parameters, converted from legacy format if necessary.1.25 to override array
getActionLinks()
Returns extra links that comes after the action text, like "revert", etc.to override string
extractParameters()
Extracts the optional extra parameters for use in action messages.The array indexes start from number...
Implements the default log formatting.
makeUserLink(UserIdentity $user, $toolFlags=0)
makePageLink(?Title $title=null, $parameters=[], $html=null)
Helper to make a link to the page, taking the plaintext value in consideration.
msg( $key,... $params)
Shortcut for wfMessage which honors local context.
formatParameterValueForApi( $name, $type, $value)
Format a single parameter value for API output.
A class containing constants representing the names of configuration variables.
const UseCodexSpecialBlock
Name constant for the UseCodexSpecialBlock setting, for use with Config::get()
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:144
Parent class for all special pages.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
A title parser service for MediaWiki.
Represents a title within MediaWiki.
Definition Title.php:70
User class for the MediaWiki software.
Definition User.php:110
An individual log entry.
Definition LogEntry.php:23
getTimestamp()
Get the timestamp when the action was executed.
getParameters()
Get the extra parameters stored for this message.
getSubtype()
The log subtype.