BlockLogFormatter.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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. use MediaWiki\MediaWikiServices;
  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. $title = $this->entry->getTarget();
  34. if ( substr( $title->getText(), 0, 1 ) === '#' ) {
  35. // autoblock - no user link possible
  36. $params[2] = $title->getText();
  37. $params[3] = ''; // no user name for gender use
  38. } else {
  39. // Create a user link for the blocked
  40. $username = $title->getText();
  41. // @todo Store the user identifier in the parameters
  42. // to make this faster for future log entries
  43. $targetUser = User::newFromName( $username, false );
  44. $params[2] = Message::rawParam( $this->makeUserLink( $targetUser, Linker::TOOL_LINKS_NOBLOCK ) );
  45. $params[3] = $username; // plain user name for gender use
  46. }
  47. $subtype = $this->entry->getSubtype();
  48. if ( $subtype === 'block' || $subtype === 'reblock' ) {
  49. if ( !isset( $params[4] ) ) {
  50. // Very old log entry without duration: means infinite
  51. $params[4] = 'infinite';
  52. }
  53. // Localize the duration, and add a tooltip
  54. // in English to help visitors from other wikis.
  55. // The lrm is needed to make sure that the number
  56. // is shown on the correct side of the tooltip text.
  57. $durationTooltip = '&lrm;' . htmlspecialchars( $params[4] );
  58. $blockExpiry = $this->context->getLanguage()->translateBlockExpiry(
  59. $params[4],
  60. $this->context->getUser(),
  61. wfTimestamp( TS_UNIX, $this->entry->getTimestamp() )
  62. );
  63. if ( $this->plaintext ) {
  64. $params[4] = Message::rawParam( $blockExpiry );
  65. } else {
  66. $params[4] = Message::rawParam(
  67. "<span class=\"blockExpiry\" title=\"$durationTooltip\">" .
  68. $blockExpiry .
  69. '</span>'
  70. );
  71. }
  72. $params[5] = isset( $params[5] ) ?
  73. self::formatBlockFlags( $params[5], $this->context->getLanguage() ) : '';
  74. // block restrictions
  75. if ( isset( $params[6] ) ) {
  76. $pages = $params[6]['pages'] ?? [];
  77. $pages = array_map( function ( $page ) {
  78. return $this->makePageLink( Title::newFromText( $page ) );
  79. }, $pages );
  80. $namespaces = $params[6]['namespaces'] ?? [];
  81. $namespaces = array_map( function ( $ns ) {
  82. $text = (int)$ns === NS_MAIN
  83. ? $this->msg( 'blanknamespace' )->text()
  84. : $this->context->getLanguage()->getFormattedNsText( $ns );
  85. $params = [ 'namespace' => $ns ];
  86. return $this->makePageLink( SpecialPage::getTitleFor( 'Allpages' ), $params, $text );
  87. }, $namespaces );
  88. $restrictions = [];
  89. if ( $pages ) {
  90. $restrictions[] = $this->msg( 'logentry-partialblock-block-page' )
  91. ->numParams( count( $pages ) )
  92. ->rawParams( $this->context->getLanguage()->listToText( $pages ) )->text();
  93. }
  94. if ( $namespaces ) {
  95. $restrictions[] = $this->msg( 'logentry-partialblock-block-ns' )
  96. ->numParams( count( $namespaces ) )
  97. ->rawParams( $this->context->getLanguage()->listToText( $namespaces ) )->text();
  98. }
  99. $params[6] = Message::rawParam( $this->context->getLanguage()->listToText( $restrictions ) );
  100. }
  101. }
  102. return $params;
  103. }
  104. protected function extractParameters() {
  105. $params = parent::extractParameters();
  106. // Legacy log params returning the params in index 3 and 4, moved to 4 and 5
  107. if ( $this->entry->isLegacy() && isset( $params[3] ) ) {
  108. if ( isset( $params[4] ) ) {
  109. $params[5] = $params[4];
  110. }
  111. $params[4] = $params[3];
  112. $params[3] = '';
  113. }
  114. return $params;
  115. }
  116. public function getPreloadTitles() {
  117. $title = $this->entry->getTarget();
  118. // Preload user page for non-autoblocks
  119. if ( substr( $title->getText(), 0, 1 ) !== '#' && $title->isValid() ) {
  120. return [ $title->getTalkPage() ];
  121. }
  122. return [];
  123. }
  124. public function getActionLinks() {
  125. $subtype = $this->entry->getSubtype();
  126. $linkRenderer = $this->getLinkRenderer();
  127. if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) // Action is hidden
  128. || !( $subtype === 'block' || $subtype === 'reblock' )
  129. || !MediaWikiServices::getInstance()
  130. ->getPermissionManager()
  131. ->userHasRight( $this->context->getUser(), 'block' )
  132. ) {
  133. return '';
  134. }
  135. // Show unblock/change block link
  136. $title = $this->entry->getTarget();
  137. $links = [
  138. $linkRenderer->makeKnownLink(
  139. SpecialPage::getTitleFor( 'Unblock', $title->getDBkey() ),
  140. $this->msg( 'unblocklink' )->text()
  141. ),
  142. $linkRenderer->makeKnownLink(
  143. SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
  144. $this->msg( 'change-blocklink' )->text()
  145. )
  146. ];
  147. return $this->msg( 'parentheses' )->rawParams(
  148. $this->context->getLanguage()->pipeList( $links ) )->escaped();
  149. }
  150. /**
  151. * Convert a comma-delimited list of block log flags
  152. * into a more readable (and translated) form
  153. *
  154. * @param string $flags Flags to format
  155. * @param Language $lang
  156. * @return string
  157. */
  158. public static function formatBlockFlags( $flags, Language $lang ) {
  159. $flags = trim( $flags );
  160. if ( $flags === '' ) {
  161. return ''; // nothing to do
  162. }
  163. $flags = explode( ',', $flags );
  164. $flagsCount = count( $flags );
  165. for ( $i = 0; $i < $flagsCount; $i++ ) {
  166. $flags[$i] = self::formatBlockFlag( $flags[$i], $lang );
  167. }
  168. return wfMessage( 'parentheses' )->inLanguage( $lang )
  169. ->rawParams( $lang->commaList( $flags ) )->escaped();
  170. }
  171. /**
  172. * Translate a block log flag if possible
  173. *
  174. * @param int $flag Flag to translate
  175. * @param Language $lang Language object to use
  176. * @return string
  177. */
  178. public static function formatBlockFlag( $flag, Language $lang ) {
  179. static $messages = [];
  180. if ( !isset( $messages[$flag] ) ) {
  181. $messages[$flag] = htmlspecialchars( $flag ); // Fallback
  182. // For grepping. The following core messages can be used here:
  183. // * block-log-flags-angry-autoblock
  184. // * block-log-flags-anononly
  185. // * block-log-flags-hiddenname
  186. // * block-log-flags-noautoblock
  187. // * block-log-flags-nocreate
  188. // * block-log-flags-noemail
  189. // * block-log-flags-nousertalk
  190. $msg = wfMessage( 'block-log-flags-' . $flag )->inLanguage( $lang );
  191. if ( $msg->exists() ) {
  192. $messages[$flag] = $msg->escaped();
  193. }
  194. }
  195. return $messages[$flag];
  196. }
  197. protected function getParametersForApi() {
  198. $entry = $this->entry;
  199. $params = $entry->getParameters();
  200. static $map = [
  201. // While this looks wrong to be starting at 5 rather than 4, it's
  202. // because getMessageParameters uses $4 for its own purposes.
  203. '5::duration',
  204. '6:array:flags',
  205. '6::flags' => '6:array:flags',
  206. ];
  207. foreach ( $map as $index => $key ) {
  208. if ( isset( $params[$index] ) ) {
  209. $params[$key] = $params[$index];
  210. unset( $params[$index] );
  211. }
  212. }
  213. ksort( $params );
  214. $subtype = $entry->getSubtype();
  215. if ( $subtype === 'block' || $subtype === 'reblock' ) {
  216. // Defaults for old log entries missing some fields
  217. $params += [
  218. '5::duration' => 'infinite',
  219. '6:array:flags' => [],
  220. ];
  221. if ( !is_array( $params['6:array:flags'] ) ) {
  222. $params['6:array:flags'] = $params['6:array:flags'] === ''
  223. ? []
  224. : explode( ',', $params['6:array:flags'] );
  225. }
  226. if ( !wfIsInfinity( $params['5::duration'] ) ) {
  227. $ts = wfTimestamp( TS_UNIX, $entry->getTimestamp() );
  228. $expiry = strtotime( $params['5::duration'], $ts );
  229. if ( $expiry !== false && $expiry > 0 ) {
  230. $params[':timestamp:expiry'] = $expiry;
  231. }
  232. }
  233. }
  234. return $params;
  235. }
  236. /**
  237. * @inheritDoc
  238. * @suppress PhanTypeInvalidDimOffset
  239. */
  240. public function formatParametersForApi() {
  241. $ret = parent::formatParametersForApi();
  242. if ( isset( $ret['flags'] ) ) {
  243. ApiResult::setIndexedTagName( $ret['flags'], 'f' );
  244. }
  245. if ( isset( $ret['restrictions']['pages'] ) ) {
  246. $ret['restrictions']['pages'] = array_map( function ( $title ) {
  247. return $this->formatParameterValueForApi( 'page', 'title-link', $title );
  248. }, $ret['restrictions']['pages'] );
  249. ApiResult::setIndexedTagName( $ret['restrictions']['pages'], 'p' );
  250. }
  251. if ( isset( $ret['restrictions']['namespaces'] ) ) {
  252. ApiResult::setIndexedTagName( $ret['restrictions']['namespaces'], 'ns' );
  253. }
  254. return $ret;
  255. }
  256. protected function getMessageKey() {
  257. $type = $this->entry->getType();
  258. $subtype = $this->entry->getSubtype();
  259. $sitewide = $this->entry->getParameters()['sitewide'] ?? true;
  260. $key = "logentry-$type-$subtype";
  261. if ( ( $subtype === 'block' || $subtype === 'reblock' ) && !$sitewide ) {
  262. // $this->getMessageParameters is doing too much. We just need
  263. // to check the presence of restrictions ($param[6]) and calling
  264. // on parent gives us that
  265. $params = parent::getMessageParameters();
  266. // message changes depending on whether there are editing restrictions or not
  267. if ( isset( $params[6] ) ) {
  268. $key = "logentry-partial$type-$subtype";
  269. } else {
  270. $key = "logentry-non-editing-$type-$subtype";
  271. }
  272. }
  273. return $key;
  274. }
  275. }