MediaWiki master
BlockLogFormatter.php
Go to the documentation of this file.
1<?php
26
38
45 private TitleParser $titleParser;
46 private NamespaceInfo $namespaceInfo;
47
48 public function __construct(
50 TitleParser $titleParser,
51 NamespaceInfo $namespaceInfo
52 ) {
53 parent::__construct( $entry );
54 $this->titleParser = $titleParser;
55 $this->namespaceInfo = $namespaceInfo;
56 }
57
58 protected function getMessageParameters() {
59 $params = parent::getMessageParameters();
60
61 $title = $this->entry->getTarget();
62 if ( substr( $title->getText(), 0, 1 ) === '#' ) {
63 // autoblock - no user link possible
64 $params[2] = $title->getText();
65 $params[3] = ''; // no user name for gender use
66 } else {
67 // Create a user link for the blocked
68 $username = $title->getText();
69 // @todo Store the user identifier in the parameters
70 // to make this faster for future log entries
71 $targetUser = User::newFromName( $username, false );
72 $params[2] = Message::rawParam( $this->makeUserLink( $targetUser, Linker::TOOL_LINKS_NOBLOCK ) );
73 $params[3] = $username; // plain user name for gender use
74 }
75
76 $subtype = $this->entry->getSubtype();
77 if ( $subtype === 'block' || $subtype === 'reblock' ) {
78 if ( !isset( $params[4] ) ) {
79 // Very old log entry without duration: means infinity
80 $params[4] = 'infinity';
81 }
82 // Localize the duration, and add a tooltip
83 // in English to help visitors from other wikis.
84 // The lrm is needed to make sure that the number
85 // is shown on the correct side of the tooltip text.
86 // @phan-suppress-next-line SecurityCheck-DoubleEscaped
87 $durationTooltip = '&lrm;' . htmlspecialchars( $params[4] );
88 $blockExpiry = $this->context->getLanguage()->translateBlockExpiry(
89 $params[4],
90 $this->context->getUser(),
91 (int)wfTimestamp( TS_UNIX, $this->entry->getTimestamp() )
92 );
93 if ( $this->plaintext ) {
94 // @phan-suppress-next-line SecurityCheck-XSS Unlikely positive, only if language format is bad
95 $params[4] = Message::rawParam( $blockExpiry );
96 } else {
97 $params[4] = Message::rawParam(
98 "<span class=\"blockExpiry\" title=\"$durationTooltip\">" .
99 // @phan-suppress-next-line SecurityCheck-DoubleEscaped language class does not escape
100 htmlspecialchars( $blockExpiry ) .
101 '</span>'
102 );
103 }
104 $params[5] = isset( $params[5] ) ?
105 self::formatBlockFlags( $params[5], $this->context->getLanguage() ) : '';
106
107 // block restrictions
108 if ( isset( $params[6] ) ) {
109 $pages = $params[6]['pages'] ?? [];
110 $pageLinks = [];
111 foreach ( $pages as $page ) {
112 $pageLinks[] = $this->makePageLink( Title::newFromText( $page ) );
113 }
114
115 $rawNamespaces = $params[6]['namespaces'] ?? [];
116 $namespaces = [];
117 foreach ( $rawNamespaces as $ns ) {
118 $text = (int)$ns === NS_MAIN
119 ? $this->msg( 'blanknamespace' )->escaped()
120 : htmlspecialchars( $this->context->getLanguage()->getFormattedNsText( $ns ) );
121 if ( $this->plaintext ) {
122 // Because the plaintext cannot link to the Special:AllPages
123 // link that is linked to in non-plaintext mode, just return
124 // the name of the namespace.
125 $namespaces[] = $text;
126 } else {
127 $namespaces[] = $this->makePageLink(
128 SpecialPage::getTitleFor( 'Allpages' ),
129 [ 'namespace' => $ns ],
130 $text
131 );
132 }
133 }
134
135 $rawActions = $params[6]['actions'] ?? [];
136 $actions = [];
137 foreach ( $rawActions as $action ) {
138 $actions[] = $this->msg( 'ipb-action-' . $action )->escaped();
139 }
140
141 $restrictions = [];
142 if ( $pageLinks ) {
143 $restrictions[] = $this->msg( 'logentry-partialblock-block-page' )
144 ->numParams( count( $pageLinks ) )
145 ->rawParams( $this->context->getLanguage()->listToText( $pageLinks ) )->escaped();
146 }
147
148 if ( $namespaces ) {
149 $restrictions[] = $this->msg( 'logentry-partialblock-block-ns' )
150 ->numParams( count( $namespaces ) )
151 ->rawParams( $this->context->getLanguage()->listToText( $namespaces ) )->escaped();
152 }
153 $enablePartialActionBlocks = $this->context->getConfig()
155 if ( $actions && $enablePartialActionBlocks ) {
156 $restrictions[] = $this->msg( 'logentry-partialblock-block-action' )
157 ->numParams( count( $actions ) )
158 ->rawParams( $this->context->getLanguage()->listToText( $actions ) )->escaped();
159 }
160
161 $params[6] = Message::rawParam( $this->context->getLanguage()->listToText( $restrictions ) );
162 }
163 }
164
165 return $params;
166 }
167
168 protected function extractParameters() {
169 $params = parent::extractParameters();
170 // Legacy log params returning the params in index 3 and 4, moved to 4 and 5
171 if ( $this->entry->isLegacy() && isset( $params[3] ) ) {
172 if ( isset( $params[4] ) ) {
173 $params[5] = $params[4];
174 }
175 $params[4] = $params[3];
176 $params[3] = '';
177 }
178 return $params;
179 }
180
181 public function getPreloadTitles() {
182 $title = $this->entry->getTarget();
183 $preload = [];
184 // Preload user page for non-autoblocks
185 if ( substr( $title->getText(), 0, 1 ) !== '#' && $title->canExist() ) {
186 $preload[] = $this->namespaceInfo->getTalkPage( $title );
187 }
188 // Preload page restriction
189 $params = $this->extractParameters();
190 if ( isset( $params[6]['pages'] ) ) {
191 foreach ( $params[6]['pages'] as $page ) {
192 try {
193 $preload[] = $this->titleParser->parseTitle( $page );
194 } catch ( MalformedTitleException $_ ) {
195 }
196 }
197 }
198 return $preload;
199 }
200
201 public function getActionLinks() {
202 $subtype = $this->entry->getSubtype();
203 $linkRenderer = $this->getLinkRenderer();
204
205 // Don't show anything if the action is hidden
206 if ( $this->entry->isDeleted( LogPage::DELETED_ACTION )
207 || !( $subtype === 'block' || $subtype === 'reblock' )
208 || !$this->context->getAuthority()->isAllowed( 'block' )
209 ) {
210 return '';
211 }
212
213 $title = $this->entry->getTarget();
214 if ( $this->context->getConfig()->get( MainConfigNames::UseCodexSpecialBlock ) ) {
215 $params = $this->entry->getParameters();
216 if ( isset( $params['blockId'] ) ) {
217 // If we have a block ID, show remove/change links
218 $query = isset( $params['blockId'] ) ? [ 'id' => $params['blockId'] ] : [];
219 $links = [
220 $linkRenderer->makeKnownLink(
221 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
222 $this->msg( 'remove-blocklink' )->text(),
223 [],
224 $query + [ 'remove' => '1' ]
225 ),
226 $linkRenderer->makeKnownLink(
227 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
228 $this->msg( 'change-blocklink' )->text(),
229 [],
230 $query
231 )
232 ];
233 } else {
234 // For legacy log entries, just show "manage blocks" since the
235 // Codex block page doesn't have an "unblock by target" mode
236 $links = [
237 $linkRenderer->makeKnownLink(
238 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
239 $this->msg( 'manage-blocklink' )->text(),
240 ),
241 ];
242 }
243 } else {
244 // Show unblock/change links
245 $links = [
246 $linkRenderer->makeKnownLink(
247 SpecialPage::getTitleFor( 'Unblock', $title->getDBkey() ),
248 $this->msg( 'unblocklink' )->text()
249 ),
250 $linkRenderer->makeKnownLink(
251 SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
252 $this->msg( 'change-blocklink' )->text()
253 )
254 ];
255 }
256
257 return $this->msg( 'parentheses' )->rawParams(
258 $this->context->getLanguage()->pipeList( $links ) )->escaped();
259 }
260
269 public static function formatBlockFlags( $flags, Language $lang ) {
270 $flags = trim( $flags );
271 if ( $flags === '' ) {
272 return ''; // nothing to do
273 }
274 $flags = explode( ',', $flags );
275 $flagsCount = count( $flags );
276
277 for ( $i = 0; $i < $flagsCount; $i++ ) {
278 $flags[$i] = self::formatBlockFlag( $flags[$i], $lang );
279 }
280
281 return wfMessage( 'parentheses' )->inLanguage( $lang )
282 ->rawParams( $lang->commaList( $flags ) )->escaped();
283 }
284
292 public static function formatBlockFlag( $flag, Language $lang ) {
293 static $messages = [];
294
295 if ( !isset( $messages[$flag] ) ) {
296 $messages[$flag] = htmlspecialchars( $flag ); // Fallback
297
298 // For grepping. The following core messages can be used here:
299 // * block-log-flags-angry-autoblock
300 // * block-log-flags-anononly
301 // * block-log-flags-hiddenname
302 // * block-log-flags-noautoblock
303 // * block-log-flags-nocreate
304 // * block-log-flags-noemail
305 // * block-log-flags-nousertalk
306 $msg = wfMessage( 'block-log-flags-' . $flag )->inLanguage( $lang );
307
308 if ( $msg->exists() ) {
309 $messages[$flag] = $msg->escaped();
310 }
311 }
312
313 return $messages[$flag];
314 }
315
316 protected function getParametersForApi() {
318 $params = $entry->getParameters();
319
320 static $map = [
321 // While this looks wrong to be starting at 5 rather than 4, it's
322 // because getMessageParameters uses $4 for its own purposes.
323 '5::duration',
324 '6:array:flags',
325 '6::flags' => '6:array:flags',
326 ];
327
328 foreach ( $map as $index => $key ) {
329 if ( isset( $params[$index] ) ) {
330 $params[$key] = $params[$index];
331 unset( $params[$index] );
332 }
333 }
334
335 ksort( $params );
336
337 $subtype = $entry->getSubtype();
338 if ( $subtype === 'block' || $subtype === 'reblock' ) {
339 // Defaults for old log entries missing some fields
340 $params += [
341 '5::duration' => 'infinity',
342 '6:array:flags' => [],
343 ];
344
345 if ( !is_array( $params['6:array:flags'] ) ) {
346 // @phan-suppress-next-line PhanSuspiciousValueComparison
347 $params['6:array:flags'] = $params['6:array:flags'] === ''
348 ? []
349 : explode( ',', $params['6:array:flags'] );
350 }
351
352 if ( wfIsInfinity( $params['5::duration'] ) ) {
353 // Normalize all possible values to one for pre-T241709 rows
354 $params['5::duration'] = 'infinity';
355 $params[':plain:duration-l10n'] = $this->msg( 'infiniteblock' )->plain();
356 } else {
357 $ts = (int)wfTimestamp( TS_UNIX, $entry->getTimestamp() );
358 $expiry = strtotime( $params['5::duration'], $ts );
359 if ( $expiry !== false && $expiry > 0 ) {
360 $params[':timestamp:expiry'] = $expiry;
361 }
362 $params[':plain:duration-l10n'] = $this->context->getLanguage()
363 ->formatDurationBetweenTimestamps( $ts, $expiry );
364 }
365 }
366
367 return $params;
368 }
369
374 public function formatParametersForApi() {
375 $ret = parent::formatParametersForApi();
376 if ( isset( $ret['flags'] ) ) {
377 ApiResult::setIndexedTagName( $ret['flags'], 'f' );
378 }
379
380 if ( isset( $ret['restrictions']['pages'] ) ) {
381 $ret['restrictions']['pages'] = array_map( function ( $title ) {
382 return $this->formatParameterValueForApi( 'page', 'title-link', $title );
383 }, $ret['restrictions']['pages'] );
384 ApiResult::setIndexedTagName( $ret['restrictions']['pages'], 'p' );
385 }
386
387 if ( isset( $ret['restrictions']['namespaces'] ) ) {
388 // @phan-suppress-next-line PhanTypeMismatchArgument False positive
389 ApiResult::setIndexedTagName( $ret['restrictions']['namespaces'], 'ns' );
390 }
391
392 return $ret;
393 }
394
395 protected function getMessageKey() {
396 $type = $this->entry->getType();
397 $subtype = $this->entry->getSubtype();
398 $params = $this->entry->getParameters();
399 $sitewide = $params['sitewide'] ?? true;
400 $count = $params['finalTargetCount'] ?? 0;
401
402 $key = "logentry-$type-$subtype";
403 if ( ( $subtype === 'block' || $subtype === 'reblock' ) && !$sitewide ) {
404 // $this->getMessageParameters is doing too much. We just need
405 // to check the presence of restrictions ($param[6]) and calling
406 // on parent gives us that
407 $params = parent::getMessageParameters();
408
409 // message changes depending on whether there are editing restrictions or not
410 if ( isset( $params[6] ) ) {
411 $key = "logentry-partial$type-$subtype";
412 } else {
413 $key = "logentry-non-editing-$type-$subtype";
414 }
415 }
416 if ( $subtype === 'block' && $count > 1 ) {
417 // logentry-block-block-multi, logentry-partialblock-block-multi,
418 // logentry-non-editing-block-block-multi
419 $key .= '-multi';
420 }
421
422 return $key;
423 }
424}
425
427class_alias( BlockLogFormatter::class, 'BlockLogFormatter' );
const NS_MAIN
Definition Defines.php:65
wfIsInfinity( $str)
Determine input string is represents as infinity.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
This class represents the result of the API operations.
Definition ApiResult.php:45
Base class for language-specific code.
Definition Language.php:81
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:61
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....
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.
getMessageKey()
Returns a key to be used for formatting the action sentence.
getParametersForApi()
Get the array of parameters, converted from legacy format if necessary.
getActionLinks()
Returns extra links that comes after the action text, like "revert", etc.
extractParameters()
Extracts the optional extra parameters for use in action messages.
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()
const EnablePartialActionBlocks
Name constant for the EnablePartialActionBlocks 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:157
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:78
User class for the MediaWiki software.
Definition User.php:121
An individual log entry.
Definition LogEntry.php:37
getTimestamp()
Get the timestamp when the action was executed.
getParameters()
Get the extra parameters stored for this message.
getSubtype()
The log subtype.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Ge...