[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Contain log classes 4 * 5 * Copyright © 2002, 2004 Brion Vibber <[email protected]> 6 * https://www.mediawiki.org/ 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, write to the Free Software Foundation, Inc., 20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 * http://www.gnu.org/copyleft/gpl.html 22 * 23 * @file 24 */ 25 26 /** 27 * Class to simplify the use of log pages. 28 * The logs are now kept in a table which is easier to manage and trim 29 * than ever-growing wiki pages. 30 * 31 */ 32 class LogPage { 33 const DELETED_ACTION = 1; 34 const DELETED_COMMENT = 2; 35 const DELETED_USER = 4; 36 const DELETED_RESTRICTED = 8; 37 38 // Convenience fields 39 const SUPPRESSED_USER = 12; 40 const SUPPRESSED_ACTION = 9; 41 42 /** @var bool */ 43 public $updateRecentChanges; 44 45 /** @var bool */ 46 public $sendToUDP; 47 48 /** @var string Plaintext version of the message for IRC */ 49 private $ircActionText; 50 51 /** @var string Plaintext version of the message */ 52 private $actionText; 53 54 /** @var string One of '', 'block', 'protect', 'rights', 'delete', 55 * 'upload', 'move' 56 */ 57 private $type; 58 59 /** @var string One of '', 'block', 'protect', 'rights', 'delete', 60 * 'upload', 'move', 'move_redir' */ 61 private $action; 62 63 /** @var string Comment associated with action */ 64 private $comment; 65 66 /** @var string Blob made of a parameters array */ 67 private $params; 68 69 /** @var User The user doing the action */ 70 private $doer; 71 72 /** @var Title */ 73 private $target; 74 75 /** 76 * Constructor 77 * 78 * @param string $type One of '', 'block', 'protect', 'rights', 'delete', 79 * 'upload', 'move' 80 * @param bool $rc Whether to update recent changes as well as the logging table 81 * @param string $udp Pass 'UDP' to send to the UDP feed if NOT sent to RC 82 */ 83 public function __construct( $type, $rc = true, $udp = 'skipUDP' ) { 84 $this->type = $type; 85 $this->updateRecentChanges = $rc; 86 $this->sendToUDP = ( $udp == 'UDP' ); 87 } 88 89 /** 90 * @return int The log_id of the inserted log entry 91 */ 92 protected function saveContent() { 93 global $wgLogRestrictions; 94 95 $dbw = wfGetDB( DB_MASTER ); 96 $log_id = $dbw->nextSequenceValue( 'logging_log_id_seq' ); 97 98 // @todo FIXME private/protected/public property? 99 $this->timestamp = $now = wfTimestampNow(); 100 $data = array( 101 'log_id' => $log_id, 102 'log_type' => $this->type, 103 'log_action' => $this->action, 104 'log_timestamp' => $dbw->timestamp( $now ), 105 'log_user' => $this->doer->getId(), 106 'log_user_text' => $this->doer->getName(), 107 'log_namespace' => $this->target->getNamespace(), 108 'log_title' => $this->target->getDBkey(), 109 'log_page' => $this->target->getArticleID(), 110 'log_comment' => $this->comment, 111 'log_params' => $this->params 112 ); 113 $dbw->insert( 'logging', $data, __METHOD__ ); 114 $newId = !is_null( $log_id ) ? $log_id : $dbw->insertId(); 115 116 # And update recentchanges 117 if ( $this->updateRecentChanges ) { 118 $titleObj = SpecialPage::getTitleFor( 'Log', $this->type ); 119 120 RecentChange::notifyLog( 121 $now, $titleObj, $this->doer, $this->getRcComment(), '', 122 $this->type, $this->action, $this->target, $this->comment, 123 $this->params, $newId, $this->getRcCommentIRC() 124 ); 125 } elseif ( $this->sendToUDP ) { 126 # Don't send private logs to UDP 127 if ( isset( $wgLogRestrictions[$this->type] ) && $wgLogRestrictions[$this->type] != '*' ) { 128 return $newId; 129 } 130 131 # Notify external application via UDP. 132 # We send this to IRC but do not want to add it the RC table. 133 $titleObj = SpecialPage::getTitleFor( 'Log', $this->type ); 134 $rc = RecentChange::newLogEntry( 135 $now, $titleObj, $this->doer, $this->getRcComment(), '', 136 $this->type, $this->action, $this->target, $this->comment, 137 $this->params, $newId, $this->getRcCommentIRC() 138 ); 139 $rc->notifyRCFeeds(); 140 } 141 142 return $newId; 143 } 144 145 /** 146 * Get the RC comment from the last addEntry() call 147 * 148 * @return string 149 */ 150 public function getRcComment() { 151 $rcComment = $this->actionText; 152 153 if ( $this->comment != '' ) { 154 if ( $rcComment == '' ) { 155 $rcComment = $this->comment; 156 } else { 157 $rcComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . 158 $this->comment; 159 } 160 } 161 162 return $rcComment; 163 } 164 165 /** 166 * Get the RC comment from the last addEntry() call for IRC 167 * 168 * @return string 169 */ 170 public function getRcCommentIRC() { 171 $rcComment = $this->ircActionText; 172 173 if ( $this->comment != '' ) { 174 if ( $rcComment == '' ) { 175 $rcComment = $this->comment; 176 } else { 177 $rcComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . 178 $this->comment; 179 } 180 } 181 182 return $rcComment; 183 } 184 185 /** 186 * Get the comment from the last addEntry() call 187 * @return string 188 */ 189 public function getComment() { 190 return $this->comment; 191 } 192 193 /** 194 * Get the list of valid log types 195 * 196 * @return array Array of strings 197 */ 198 public static function validTypes() { 199 global $wgLogTypes; 200 201 return $wgLogTypes; 202 } 203 204 /** 205 * Is $type a valid log type 206 * 207 * @param string $type Log type to check 208 * @return bool 209 */ 210 public static function isLogType( $type ) { 211 return in_array( $type, LogPage::validTypes() ); 212 } 213 214 /** 215 * Get the name for the given log type 216 * 217 * @param string $type Log type 218 * @return string Log name 219 * @deprecated since 1.19, warnings in 1.21. Use getName() 220 */ 221 public static function logName( $type ) { 222 global $wgLogNames; 223 224 wfDeprecated( __METHOD__, '1.21' ); 225 226 if ( isset( $wgLogNames[$type] ) ) { 227 return str_replace( '_', ' ', wfMessage( $wgLogNames[$type] )->text() ); 228 } else { 229 // Bogus log types? Perhaps an extension was removed. 230 return $type; 231 } 232 } 233 234 /** 235 * Get the log header for the given log type 236 * 237 * @todo handle missing log types 238 * @param string $type Logtype 239 * @return string Header text of this logtype 240 * @deprecated since 1.19, warnings in 1.21. Use getDescription() 241 */ 242 public static function logHeader( $type ) { 243 global $wgLogHeaders; 244 245 wfDeprecated( __METHOD__, '1.21' ); 246 247 return wfMessage( $wgLogHeaders[$type] )->parse(); 248 } 249 250 /** 251 * Generate text for a log entry. 252 * Only LogFormatter should call this function. 253 * 254 * @param string $type Log type 255 * @param string $action Log action 256 * @param Title|null $title Title object or null 257 * @param Skin|null $skin Skin object or null. If null, we want to use the wiki 258 * content language, since that will go to the IRC feed. 259 * @param array $params Parameters 260 * @param bool $filterWikilinks Whether to filter wiki links 261 * @return string HTML 262 */ 263 public static function actionText( $type, $action, $title = null, $skin = null, 264 $params = array(), $filterWikilinks = false 265 ) { 266 global $wgLang, $wgContLang, $wgLogActions; 267 268 if ( is_null( $skin ) ) { 269 $langObj = $wgContLang; 270 $langObjOrNull = null; 271 } else { 272 $langObj = $wgLang; 273 $langObjOrNull = $wgLang; 274 } 275 276 $key = "$type/$action"; 277 278 if ( isset( $wgLogActions[$key] ) ) { 279 if ( is_null( $title ) ) { 280 $rv = wfMessage( $wgLogActions[$key] )->inLanguage( $langObj )->escaped(); 281 } else { 282 $titleLink = self::getTitleLink( $type, $langObjOrNull, $title, $params ); 283 284 if ( count( $params ) == 0 ) { 285 $rv = wfMessage( $wgLogActions[$key] )->rawParams( $titleLink ) 286 ->inLanguage( $langObj )->escaped(); 287 } else { 288 $details = ''; 289 array_unshift( $params, $titleLink ); 290 291 // User suppression 292 if ( preg_match( '/^(block|suppress)\/(block|reblock)$/', $key ) ) { 293 if ( $skin ) { 294 // Localize the duration, and add a tooltip 295 // in English to help visitors from other wikis. 296 // The lrm is needed to make sure that the number 297 // is shown on the correct side of the tooltip text. 298 $durationTooltip = '‎' . htmlspecialchars( $params[1] ); 299 $params[1] = "<span class='blockExpiry' title='$durationTooltip'>" . 300 $wgLang->translateBlockExpiry( $params[1] ) . '</span>'; 301 } else { 302 $params[1] = $wgContLang->translateBlockExpiry( $params[1] ); 303 } 304 305 $params[2] = isset( $params[2] ) ? 306 self::formatBlockFlags( $params[2], $langObj ) : ''; 307 // Page protections 308 } elseif ( $type == 'protect' && count( $params ) == 3 ) { 309 // Restrictions and expiries 310 if ( $skin ) { 311 $details .= $wgLang->getDirMark() . htmlspecialchars( " {$params[1]}" ); 312 } else { 313 $details .= " {$params[1]}"; 314 } 315 316 // Cascading flag... 317 if ( $params[2] ) { 318 $text = wfMessage( 'protect-summary-cascade' ) 319 ->inLanguage( $langObj )->text(); 320 $details .= ' '; 321 $details .= wfMessage( 'brackets', $text )->inLanguage( $langObj )->text(); 322 323 } 324 } 325 326 $rv = wfMessage( $wgLogActions[$key] )->rawParams( $params ) 327 ->inLanguage( $langObj )->escaped() . $details; 328 } 329 } 330 } else { 331 global $wgLogActionsHandlers; 332 333 if ( isset( $wgLogActionsHandlers[$key] ) ) { 334 $args = func_get_args(); 335 $rv = call_user_func_array( $wgLogActionsHandlers[$key], $args ); 336 } else { 337 wfDebug( "LogPage::actionText - unknown action $key\n" ); 338 $rv = "$action"; 339 } 340 } 341 342 // For the perplexed, this feature was added in r7855 by Erik. 343 // The feature was added because we liked adding [[$1]] in our log entries 344 // but the log entries are parsed as Wikitext on RecentChanges but as HTML 345 // on Special:Log. The hack is essentially that [[$1]] represented a link 346 // to the title in question. The first parameter to the HTML version (Special:Log) 347 // is that link in HTML form, and so this just gets rid of the ugly [[]]. 348 // However, this is a horrible hack and it doesn't work like you expect if, say, 349 // you want to link to something OTHER than the title of the log entry. 350 // The real problem, which Erik was trying to fix (and it sort-of works now) is 351 // that the same messages are being treated as both wikitext *and* HTML. 352 if ( $filterWikilinks ) { 353 $rv = str_replace( '[[', '', $rv ); 354 $rv = str_replace( ']]', '', $rv ); 355 } 356 357 return $rv; 358 } 359 360 /** 361 * @todo Document 362 * @param string $type 363 * @param Language|null $lang 364 * @param Title $title 365 * @param array $params 366 * @return string 367 */ 368 protected static function getTitleLink( $type, $lang, $title, &$params ) { 369 if ( !$lang ) { 370 return $title->getPrefixedText(); 371 } 372 373 switch ( $type ) { 374 case 'move': 375 $titleLink = Linker::link( 376 $title, 377 htmlspecialchars( $title->getPrefixedText() ), 378 array(), 379 array( 'redirect' => 'no' ) 380 ); 381 382 $targetTitle = Title::newFromText( $params[0] ); 383 384 if ( !$targetTitle ) { 385 # Workaround for broken database 386 $params[0] = htmlspecialchars( $params[0] ); 387 } else { 388 $params[0] = Linker::link( 389 $targetTitle, 390 htmlspecialchars( $params[0] ) 391 ); 392 } 393 break; 394 case 'block': 395 if ( substr( $title->getText(), 0, 1 ) == '#' ) { 396 $titleLink = $title->getText(); 397 } else { 398 // @todo Store the user identifier in the parameters 399 // to make this faster for future log entries 400 $id = User::idFromName( $title->getText() ); 401 $titleLink = Linker::userLink( $id, $title->getText() ) 402 . Linker::userToolLinks( $id, $title->getText(), false, Linker::TOOL_LINKS_NOBLOCK ); 403 } 404 break; 405 case 'merge': 406 $titleLink = Linker::link( 407 $title, 408 $title->getPrefixedText(), 409 array(), 410 array( 'redirect' => 'no' ) 411 ); 412 $params[0] = Linker::link( 413 Title::newFromText( $params[0] ), 414 htmlspecialchars( $params[0] ) 415 ); 416 $params[1] = $lang->timeanddate( $params[1] ); 417 break; 418 default: 419 if ( $title->isSpecialPage() ) { 420 list( $name, $par ) = SpecialPageFactory::resolveAlias( $title->getDBkey() ); 421 422 # Use the language name for log titles, rather than Log/X 423 if ( $name == 'Log' ) { 424 $logPage = new LogPage( $par ); 425 $titleLink = Linker::link( $title, $logPage->getName()->escaped() ); 426 $titleLink = wfMessage( 'parentheses' ) 427 ->inLanguage( $lang ) 428 ->rawParams( $titleLink ) 429 ->escaped(); 430 } else { 431 $titleLink = Linker::link( $title ); 432 } 433 } else { 434 $titleLink = Linker::link( $title ); 435 } 436 } 437 438 return $titleLink; 439 } 440 441 /** 442 * Add a log entry 443 * 444 * @param string $action One of '', 'block', 'protect', 'rights', 'delete', 445 * 'upload', 'move', 'move_redir' 446 * @param Title $target Title object 447 * @param string $comment Description associated 448 * @param array $params Parameters passed later to wfMessage function 449 * @param null|int|User $doer The user doing the action. null for $wgUser 450 * 451 * @return int The log_id of the inserted log entry 452 */ 453 public function addEntry( $action, $target, $comment, $params = array(), $doer = null ) { 454 global $wgContLang; 455 456 if ( !is_array( $params ) ) { 457 $params = array( $params ); 458 } 459 460 if ( $comment === null ) { 461 $comment = ''; 462 } 463 464 # Trim spaces on user supplied text 465 $comment = trim( $comment ); 466 467 # Truncate for whole multibyte characters. 468 $comment = $wgContLang->truncate( $comment, 255 ); 469 470 $this->action = $action; 471 $this->target = $target; 472 $this->comment = $comment; 473 $this->params = LogPage::makeParamBlob( $params ); 474 475 if ( $doer === null ) { 476 global $wgUser; 477 $doer = $wgUser; 478 } elseif ( !is_object( $doer ) ) { 479 $doer = User::newFromId( $doer ); 480 } 481 482 $this->doer = $doer; 483 484 $logEntry = new ManualLogEntry( $this->type, $action ); 485 $logEntry->setTarget( $target ); 486 $logEntry->setPerformer( $doer ); 487 $logEntry->setParameters( $params ); 488 489 $formatter = LogFormatter::newFromEntry( $logEntry ); 490 $context = RequestContext::newExtraneousContext( $target ); 491 $formatter->setContext( $context ); 492 493 $this->actionText = $formatter->getPlainActionText(); 494 $this->ircActionText = $formatter->getIRCActionText(); 495 496 return $this->saveContent(); 497 } 498 499 /** 500 * Add relations to log_search table 501 * 502 * @param string $field 503 * @param array $values 504 * @param int $logid 505 * @return bool 506 */ 507 public function addRelations( $field, $values, $logid ) { 508 if ( !strlen( $field ) || empty( $values ) ) { 509 return false; // nothing 510 } 511 512 $data = array(); 513 514 foreach ( $values as $value ) { 515 $data[] = array( 516 'ls_field' => $field, 517 'ls_value' => $value, 518 'ls_log_id' => $logid 519 ); 520 } 521 522 $dbw = wfGetDB( DB_MASTER ); 523 $dbw->insert( 'log_search', $data, __METHOD__, 'IGNORE' ); 524 525 return true; 526 } 527 528 /** 529 * Create a blob from a parameter array 530 * 531 * @param array $params 532 * @return string 533 */ 534 public static function makeParamBlob( $params ) { 535 return implode( "\n", $params ); 536 } 537 538 /** 539 * Extract a parameter array from a blob 540 * 541 * @param string $blob 542 * @return array 543 */ 544 public static function extractParams( $blob ) { 545 if ( $blob === '' ) { 546 return array(); 547 } else { 548 return explode( "\n", $blob ); 549 } 550 } 551 552 /** 553 * Convert a comma-delimited list of block log flags 554 * into a more readable (and translated) form 555 * 556 * @param string $flags Flags to format 557 * @param Language $lang 558 * @return string 559 */ 560 public static function formatBlockFlags( $flags, $lang ) { 561 $flags = trim( $flags ); 562 if ( $flags === '' ) { 563 return ''; //nothing to do 564 } 565 $flags = explode( ',', $flags ); 566 $flagsCount = count( $flags ); 567 568 for ( $i = 0; $i < $flagsCount; $i++ ) { 569 $flags[$i] = self::formatBlockFlag( $flags[$i], $lang ); 570 } 571 572 return wfMessage( 'parentheses' )->inLanguage( $lang ) 573 ->rawParams( $lang->commaList( $flags ) )->escaped(); 574 } 575 576 /** 577 * Translate a block log flag if possible 578 * 579 * @param int $flag Flag to translate 580 * @param Language $lang Language object to use 581 * @return string 582 */ 583 public static function formatBlockFlag( $flag, $lang ) { 584 static $messages = array(); 585 586 if ( !isset( $messages[$flag] ) ) { 587 $messages[$flag] = htmlspecialchars( $flag ); // Fallback 588 589 // For grepping. The following core messages can be used here: 590 // * block-log-flags-angry-autoblock 591 // * block-log-flags-anononly 592 // * block-log-flags-hiddenname 593 // * block-log-flags-noautoblock 594 // * block-log-flags-nocreate 595 // * block-log-flags-noemail 596 // * block-log-flags-nousertalk 597 $msg = wfMessage( 'block-log-flags-' . $flag )->inLanguage( $lang ); 598 599 if ( $msg->exists() ) { 600 $messages[$flag] = $msg->escaped(); 601 } 602 } 603 604 return $messages[$flag]; 605 } 606 607 /** 608 * Name of the log. 609 * @return Message 610 * @since 1.19 611 */ 612 public function getName() { 613 global $wgLogNames; 614 615 // BC 616 if ( isset( $wgLogNames[$this->type] ) ) { 617 $key = $wgLogNames[$this->type]; 618 } else { 619 $key = 'log-name-' . $this->type; 620 } 621 622 return wfMessage( $key ); 623 } 624 625 /** 626 * Description of this log type. 627 * @return Message 628 * @since 1.19 629 */ 630 public function getDescription() { 631 global $wgLogHeaders; 632 // BC 633 if ( isset( $wgLogHeaders[$this->type] ) ) { 634 $key = $wgLogHeaders[$this->type]; 635 } else { 636 $key = 'log-description-' . $this->type; 637 } 638 639 return wfMessage( $key ); 640 } 641 642 /** 643 * Returns the right needed to read this log type. 644 * @return string 645 * @since 1.19 646 */ 647 public function getRestriction() { 648 global $wgLogRestrictions; 649 if ( isset( $wgLogRestrictions[$this->type] ) ) { 650 $restriction = $wgLogRestrictions[$this->type]; 651 } else { 652 // '' always returns true with $user->isAllowed() 653 $restriction = ''; 654 } 655 656 return $restriction; 657 } 658 659 /** 660 * Tells if this log is not viewable by all. 661 * @return bool 662 * @since 1.19 663 */ 664 public function isRestricted() { 665 $restriction = $this->getRestriction(); 666 667 return $restriction !== '' && $restriction !== '*'; 668 } 669 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |