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 $durationTooltip = '&lrm;' . htmlspecialchars( $params[4] );
75 $blockExpiry = $this->context->getLanguage()->translateBlockExpiry(
76 $params[4],
77 $this->context->getUser(),
78 (int)wfTimestamp( TS::UNIX, $this->entry->getTimestamp() )
79 );
80 if ( $this->plaintext ) {
81 $params[4] = $blockExpiry;
82 } else {
83 $params[4] = Message::rawParam(
84 "<span class=\"blockExpiry\" title=\"$durationTooltip\">" .
85 htmlspecialchars( $blockExpiry ) .
86 '</span>'
87 );
88 }
89 $params[5] = isset( $params[5] ) ?
90 self::formatBlockFlags( $params[5], $this->context->getLanguage() ) : '';
91
92 // block restrictions
93 if ( isset( $params[6] ) ) {
94 $pages = $params[6]['pages'] ?? [];
95 $pageLinks = [];
96 foreach ( $pages as $page ) {
97 $pageLinks[] = $this->makePageLink( Title::newFromText( $page ) );
98 }
99
100 $rawNamespaces = $params[6]['namespaces'] ?? [];
101 $namespaces = [];
102 foreach ( $rawNamespaces as $ns ) {
103 $text = (int)$ns === NS_MAIN
104 ? $this->msg( 'blanknamespace' )->escaped()
105 : htmlspecialchars( $this->context->getLanguage()->getFormattedNsText( $ns ) );
106 if ( $this->plaintext ) {
107 // Because the plaintext cannot link to the Special:AllPages
108 // link that is linked to in non-plaintext mode, just return
109 // the name of the namespace.
110 $namespaces[] = $text;
111 } else {
112 $namespaces[] = $this->makePageLink(
113 SpecialPage::getTitleFor( 'Allpages' ),
114 [ 'namespace' => $ns ],
115 $text
116 );
117 }
118 }
119
120 $rawActions = $params[6]['actions'] ?? [];
121 $actions = [];
122 foreach ( $rawActions as $action ) {
123 $actions[] = $this->msg( 'ipb-action-' . $action )->escaped();
124 }
125
126 $restrictions = [];
127 if ( $pageLinks ) {
128 $restrictions[] = $this->msg( 'logentry-partialblock-block-page' )
129 ->numParams( count( $pageLinks ) )
130 ->rawParams( $this->context->getLanguage()->listToText( $pageLinks ) )->escaped();
131 }
132
133 if ( $namespaces ) {
134 $restrictions[] = $this->msg( 'logentry-partialblock-block-ns' )
135 ->numParams( count( $namespaces ) )
136 ->rawParams( $this->context->getLanguage()->listToText( $namespaces ) )->escaped();
137 }
138 if ( $actions ) {
139 $restrictions[] = $this->msg( 'logentry-partialblock-block-action' )
140 ->numParams( count( $actions ) )
141 ->rawParams( $this->context->getLanguage()->listToText( $actions ) )->escaped();
142 }
143
144 $params[6] = Message::rawParam( $this->context->getLanguage()->listToText( $restrictions ) );
145 }
146 }
147
148 return $params;
149 }
150
152 protected function extractParameters() {
153 $params = parent::extractParameters();
154 // Legacy log params returning the params in index 3 and 4, moved to 4 and 5
155 if ( $this->entry->isLegacy() && isset( $params[3] ) ) {
156 if ( isset( $params[4] ) ) {
157 $params[5] = $params[4];
158 }
159 $params[4] = $params[3];
160 $params[3] = '';
161 }
162 return $params;
163 }
164
166 public function getPreloadTitles() {
167 $title = $this->entry->getTarget();
168 $preload = [];
169 // Preload user page for non-autoblocks
170 if ( substr( $title->getText(), 0, 1 ) !== '#' && $title->canExist() ) {
171 $preload[] = $this->namespaceInfo->getTalkPage( $title );
172 }
173 // Preload page restriction
174 $params = $this->extractParameters();
175 if ( isset( $params[6]['pages'] ) ) {
176 foreach ( $params[6]['pages'] as $page ) {
177 try {
178 $preload[] = $this->titleParser->parseTitle( $page );
179 } catch ( MalformedTitleException ) {
180 }
181 }
182 }
183 return $preload;
184 }
185
187 public function getActionLinks() {
188 $subtype = $this->entry->getSubtype();
189 $linkRenderer = $this->getLinkRenderer();
190
191 // Don't show anything if the action is hidden
192 if ( $this->entry->isDeleted( LogPage::DELETED_ACTION )
193 || !( $subtype === 'block' || $subtype === 'reblock' )
194 || !$this->context->getAuthority()->isAllowed( 'block' )
195 ) {
196 return '';
197 }
198
199 $title = $this->entry->getTarget();
200 if ( $this->context->getConfig()->get( MainConfigNames::UseCodexSpecialBlock ) ) {
201 $params = $this->entry->getParameters();
202 if ( isset( $params['blockId'] ) ) {
203 // If we have a block ID, show remove/change links
204 $query = isset( $params['blockId'] ) ? [ 'id' => $params['blockId'] ] : [];
205 $links = [
206 $linkRenderer->makeKnownLink(
207 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
208 $this->msg( 'remove-blocklink' )->text(),
209 [],
210 $query + [ 'remove' => '1' ]
211 ),
212 $linkRenderer->makeKnownLink(
213 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
214 $this->msg( 'change-blocklink' )->text(),
215 [],
216 $query
217 )
218 ];
219 } else {
220 // For legacy log entries, just show "manage blocks" since the
221 // Codex block page doesn't have an "unblock by target" mode
222 $links = [
223 $linkRenderer->makeKnownLink(
224 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
225 $this->msg( 'manage-blocklink' )->text(),
226 ),
227 ];
228 }
229 } else {
230 // Show unblock/change links
231 $links = [
232 $linkRenderer->makeKnownLink(
233 SpecialPage::getTitleFor( 'Unblock', $title->getDBkey() ),
234 $this->msg( 'unblocklink' )->text()
235 ),
236 $linkRenderer->makeKnownLink(
237 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
238 $this->msg( 'change-blocklink' )->text()
239 )
240 ];
241 }
242
243 return $this->msg( 'parentheses' )->rawParams(
244 $this->context->getLanguage()->pipeList( $links ) )->escaped();
245 }
246
255 public static function formatBlockFlags( $flags, Language $lang ) {
256 $flags = trim( $flags );
257 if ( $flags === '' ) {
258 return ''; // nothing to do
259 }
260 $flags = explode( ',', $flags );
261 $flagsCount = count( $flags );
262
263 for ( $i = 0; $i < $flagsCount; $i++ ) {
264 $flags[$i] = self::formatBlockFlag( $flags[$i], $lang );
265 }
266
267 return wfMessage( 'parentheses' )->inLanguage( $lang )
268 ->rawParams( $lang->commaList( $flags ) )->escaped();
269 }
270
278 public static function formatBlockFlag( $flag, Language $lang ) {
279 static $messages = [];
280
281 if ( !isset( $messages[$flag] ) ) {
282 $messages[$flag] = htmlspecialchars( $flag ); // Fallback
283
284 // For grepping. The following core messages can be used here:
285 // * block-log-flags-angry-autoblock
286 // * block-log-flags-anononly
287 // * block-log-flags-hiddenname
288 // * block-log-flags-noautoblock
289 // * block-log-flags-nocreate
290 // * block-log-flags-noemail
291 // * block-log-flags-nousertalk
292 $msg = wfMessage( 'block-log-flags-' . $flag )->inLanguage( $lang );
293
294 if ( $msg->exists() ) {
295 $messages[$flag] = $msg->escaped();
296 }
297 }
298
299 return $messages[$flag];
300 }
301
303 protected function getParametersForApi() {
305 $params = $entry->getParameters();
306
307 static $map = [
308 // While this looks wrong to be starting at 5 rather than 4, it's
309 // because getMessageParameters uses $4 for its own purposes.
310 '5::duration',
311 '6:array:flags',
312 '6::flags' => '6:array:flags',
313 ];
314
315 foreach ( $map as $index => $key ) {
316 if ( isset( $params[$index] ) ) {
317 $params[$key] = $params[$index];
318 unset( $params[$index] );
319 }
320 }
321
322 ksort( $params );
323
324 $subtype = $entry->getSubtype();
325 if ( $subtype === 'block' || $subtype === 'reblock' ) {
326 // Defaults for old log entries missing some fields
327 $params += [
328 '5::duration' => 'infinity',
329 '6:array:flags' => [],
330 ];
331
332 if ( !is_array( $params['6:array:flags'] ) ) {
333 $params['6:array:flags'] = $params['6:array:flags'] === ''
334 ? []
335 : explode( ',', $params['6:array:flags'] );
336 }
337
338 if ( wfIsInfinity( $params['5::duration'] ) ) {
339 // Normalize all possible values to one for pre-T241709 rows
340 $params['5::duration'] = 'infinity';
341 $params[':plain:duration-l10n'] = $this->msg( 'infiniteblock' )->plain();
342 } else {
343 $ts = (int)wfTimestamp( TS::UNIX, $entry->getTimestamp() );
344 $expiry = strtotime( $params['5::duration'], $ts );
345 if ( $expiry !== false && $expiry > 0 ) {
346 $params[':timestamp:expiry'] = $expiry;
347 }
348 $params[':plain:duration-l10n'] = $this->context->getLanguage()
349 ->formatDurationBetweenTimestamps( $ts, $expiry );
350 }
351 }
352
353 return $params;
354 }
355
360 public function formatParametersForApi() {
361 $ret = parent::formatParametersForApi();
362 if ( isset( $ret['flags'] ) ) {
363 ApiResult::setIndexedTagName( $ret['flags'], 'f' );
364 }
365
366 if ( isset( $ret['restrictions']['pages'] ) ) {
367 $ret['restrictions']['pages'] = array_map( function ( $title ) {
368 return $this->formatParameterValueForApi( 'page', 'title-link', $title );
369 }, $ret['restrictions']['pages'] );
370 ApiResult::setIndexedTagName( $ret['restrictions']['pages'], 'p' );
371 }
372
373 if ( isset( $ret['restrictions']['namespaces'] ) ) {
374 // @phan-suppress-next-line PhanTypeMismatchArgument False positive
375 ApiResult::setIndexedTagName( $ret['restrictions']['namespaces'], 'ns' );
376 }
377
378 return $ret;
379 }
380
382 protected function getMessageKey() {
383 $type = $this->entry->getType();
384 $subtype = $this->entry->getSubtype();
385 $params = $this->entry->getParameters();
386 $sitewide = $params['sitewide'] ?? true;
387 $count = $params['finalTargetCount'] ?? 0;
388
389 $key = "logentry-$type-$subtype";
390 if ( ( $subtype === 'block' || $subtype === 'reblock' ) && !$sitewide ) {
391 // $this->getMessageParameters is doing too much. We just need
392 // to check the presence of restrictions ($param[6]) and calling
393 // on parent gives us that
394 $params = parent::getMessageParameters();
395
396 // message changes depending on whether there are editing restrictions or not
397 if ( isset( $params[6] ) ) {
398 $key = "logentry-partial$type-$subtype";
399 } else {
400 $key = "logentry-non-editing-$type-$subtype";
401 }
402 }
403 if ( $subtype === 'block' && $count > 1 ) {
404 // logentry-block-block-multi, logentry-partialblock-block-multi,
405 // logentry-non-editing-block-block-multi
406 $key .= '-multi';
407 }
408
409 return $key;
410 }
411}
412
414class_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:34
Base class for language-specific code.
Definition Language.php:65
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:48
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:69
User class for the MediaWiki software.
Definition User.php:129
An individual log entry.
Definition LogEntry.php:24
getTimestamp()
Get the timestamp when the action was executed.
getParameters()
Get the extra parameters stored for this message.
getSubtype()
The log subtype.