Phantom42 has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/405547 )
Change subject: Fix multiple PHP class declarations in one file in includes/logging ...................................................................... Fix multiple PHP class declarations in one file in includes/logging All files containing more than one PHP class were splitted into multiple files. Autoloader references were updated to match new class locations. Bug: T177809 Change-Id: Ia53c82d7888469b8e587db5cbf0049ec262abaa8 --- M autoload.php A includes/logging/DatabaseLogEntry.php A includes/logging/LegacyLogFormatter.php M includes/logging/LogEntry.php A includes/logging/LogEntryBase.php M includes/logging/LogFormatter.php A includes/logging/ManualLogEntry.php A includes/logging/RCDatabaseLogEntry.php 8 files changed, 955 insertions(+), 811 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/47/405547/1 diff --git a/autoload.php b/autoload.php index 9e557e1..0e51c9e 100644 --- a/autoload.php +++ b/autoload.php @@ -343,7 +343,7 @@ 'DatabaseBase' => __DIR__ . '/includes/libs/rdbms/database/Database.php', 'DatabaseInstaller' => __DIR__ . '/includes/installer/DatabaseInstaller.php', 'DatabaseLag' => __DIR__ . '/maintenance/lag.php', - 'DatabaseLogEntry' => __DIR__ . '/includes/logging/LogEntry.php', + 'DatabaseLogEntry' => __DIR__ . '/includes/logging/DatabaseLogEntry.php', 'DatabaseMssql' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMssql.php', 'DatabaseMysqlBase' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMysqlBase.php', 'DatabaseMysqli' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMysqli.php', @@ -743,7 +743,7 @@ 'LanguageZh_hans' => __DIR__ . '/languages/classes/LanguageZh_hans.php', 'Languages' => __DIR__ . '/maintenance/language/languages.inc', 'LayeredParameterizedPassword' => __DIR__ . '/includes/password/LayeredParameterizedPassword.php', - 'LegacyLogFormatter' => __DIR__ . '/includes/logging/LogFormatter.php', + 'LegacyLogFormatter' => __DIR__ . '/includes/logging/LegacyLogFormatter.php', 'License' => __DIR__ . '/includes/specials/helpers/License.php', 'Licenses' => __DIR__ . '/includes/specials/formfields/Licenses.php', 'LinkBatch' => __DIR__ . '/includes/cache/LinkBatch.php', @@ -774,7 +774,7 @@ 'LockManager' => __DIR__ . '/includes/libs/lockmanager/LockManager.php', 'LockManagerGroup' => __DIR__ . '/includes/filebackend/lockmanager/LockManagerGroup.php', 'LogEntry' => __DIR__ . '/includes/logging/LogEntry.php', - 'LogEntryBase' => __DIR__ . '/includes/logging/LogEntry.php', + 'LogEntryBase' => __DIR__ . '/includes/logging/LogEntryBase.php', 'LogEventsList' => __DIR__ . '/includes/logging/LogEventsList.php', 'LogFormatter' => __DIR__ . '/includes/logging/LogFormatter.php', 'LogPage' => __DIR__ . '/includes/logging/LogPage.php', @@ -820,7 +820,7 @@ 'MakeTestEdits' => __DIR__ . '/maintenance/makeTestEdits.php', 'MalformedTitleException' => __DIR__ . '/includes/title/MalformedTitleException.php', 'ManageJobs' => __DIR__ . '/maintenance/manageJobs.php', - 'ManualLogEntry' => __DIR__ . '/includes/logging/LogEntry.php', + 'ManualLogEntry' => __DIR__ . '/includes/logging/ManualLogEntry.php', 'MapCacheLRU' => __DIR__ . '/includes/libs/MapCacheLRU.php', 'MappedIterator' => __DIR__ . '/includes/libs/MappedIterator.php', 'MarkpatrolledAction' => __DIR__ . '/includes/actions/MarkpatrolledAction.php', @@ -1201,7 +1201,7 @@ 'QuorumLockManager' => __DIR__ . '/includes/libs/lockmanager/QuorumLockManager.php', 'RCCacheEntry' => __DIR__ . '/includes/changes/RCCacheEntry.php', 'RCCacheEntryFactory' => __DIR__ . '/includes/changes/RCCacheEntryFactory.php', - 'RCDatabaseLogEntry' => __DIR__ . '/includes/logging/LogEntry.php', + 'RCDatabaseLogEntry' => __DIR__ . '/includes/logging/RCDatabaseLogEntry.php', 'RCFeed' => __DIR__ . '/includes/rcfeed/RCFeed.php', 'RCFeedEngine' => __DIR__ . '/includes/rcfeed/RCFeedEngine.php', 'RCFeedFormatter' => __DIR__ . '/includes/rcfeed/RCFeedFormatter.php', diff --git a/includes/logging/DatabaseLogEntry.php b/includes/logging/DatabaseLogEntry.php new file mode 100644 index 0000000..b783f45 --- /dev/null +++ b/includes/logging/DatabaseLogEntry.php @@ -0,0 +1,206 @@ +<?php +/** + * This class wraps around database result row. + * + * This is how I see the log system history: + * - appending to plain wiki pages + * - formatting log entries based on database fields + * - user is now part of the action message + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author Niklas Laxström + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later + * @since 1.19 + */ + +/** + * This class wraps around database result row. + * + * @since 1.19 + */ +class DatabaseLogEntry extends LogEntryBase { + + /** + * Returns array of information that is needed for querying + * log entries. Array contains the following keys: + * tables, fields, conds, options and join_conds + * + * @return array + */ + public static function getSelectQueryData() { + $commentQuery = CommentStore::newKey( 'log_comment' )->getJoin(); + + $tables = [ 'logging', 'user' ] + $commentQuery['tables']; + $fields = [ + 'log_id', 'log_type', 'log_action', 'log_timestamp', + 'log_user', 'log_user_text', + 'log_namespace', 'log_title', // unused log_page + 'log_params', 'log_deleted', + 'user_id', 'user_name', 'user_editcount', + ] + $commentQuery['fields']; + + $joins = [ + // IPs don't have an entry in user table + 'user' => [ 'LEFT JOIN', 'log_user=user_id' ], + ] + $commentQuery['joins']; + + return [ + 'tables' => $tables, + 'fields' => $fields, + 'conds' => [], + 'options' => [], + 'join_conds' => $joins, + ]; + } + + /** + * Constructs new LogEntry from database result row. + * Supports rows from both logging and recentchanges table. + * + * @param stdClass|array $row + * @return DatabaseLogEntry + */ + public static function newFromRow( $row ) { + $row = (object)$row; + if ( isset( $row->rc_logid ) ) { + return new RCDatabaseLogEntry( $row ); + } else { + return new self( $row ); + } + } + + /** @var stdClass Database result row. */ + protected $row; + + /** @var User */ + protected $performer; + + /** @var array Parameters for log entry */ + protected $params; + + /** @var int A rev id associated to the log entry */ + protected $revId = null; + + /** @var bool Whether the parameters for this log entry are stored in new or old format. */ + protected $legacy; + + protected function __construct( $row ) { + $this->row = $row; + } + + /** + * Returns the unique database id. + * + * @return int + */ + public function getId() { + return (int)$this->row->log_id; + } + + /** + * Returns whatever is stored in the database field. + * + * @return string + */ + protected function getRawParameters() { + return $this->row->log_params; + } + + public function isLegacy() { + // This extracts the property + $this->getParameters(); + return $this->legacy; + } + + public function getType() { + return $this->row->log_type; + } + + public function getSubtype() { + return $this->row->log_action; + } + + public function getParameters() { + if ( !isset( $this->params ) ) { + $blob = $this->getRawParameters(); + MediaWiki\suppressWarnings(); + $params = LogEntryBase::extractParams( $blob ); + MediaWiki\restoreWarnings(); + if ( $params !== false ) { + $this->params = $params; + $this->legacy = false; + } else { + $this->params = LogPage::extractParams( $blob ); + $this->legacy = true; + } + + if ( isset( $this->params['associated_rev_id'] ) ) { + $this->revId = $this->params['associated_rev_id']; + unset( $this->params['associated_rev_id'] ); + } + } + + return $this->params; + } + + public function getAssociatedRevId() { + // This extracts the property + $this->getParameters(); + return $this->revId; + } + + public function getPerformer() { + if ( !$this->performer ) { + $userId = (int)$this->row->log_user; + if ( $userId !== 0 ) { + // logged-in users + if ( isset( $this->row->user_name ) ) { + $this->performer = User::newFromRow( $this->row ); + } else { + $this->performer = User::newFromId( $userId ); + } + } else { + // IP users + $userText = $this->row->log_user_text; + $this->performer = User::newFromName( $userText, false ); + } + } + + return $this->performer; + } + + public function getTarget() { + $namespace = $this->row->log_namespace; + $page = $this->row->log_title; + $title = Title::makeTitle( $namespace, $page ); + + return $title; + } + + public function getTimestamp() { + return wfTimestamp( TS_MW, $this->row->log_timestamp ); + } + + public function getComment() { + return CommentStore::newKey( 'log_comment' )->getComment( $this->row )->text; + } + + public function getDeleted() { + return $this->row->log_deleted; + } +} diff --git a/includes/logging/LegacyLogFormatter.php b/includes/logging/LegacyLogFormatter.php new file mode 100644 index 0000000..89cb80a --- /dev/null +++ b/includes/logging/LegacyLogFormatter.php @@ -0,0 +1,125 @@ +<?php +/** + * Legacy log formatting. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author Niklas Laxström + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later + * @since 1.19 + */ +use MediaWiki\Linker\LinkRenderer; +use MediaWiki\MediaWikiServices; + +/** + * This class formats all log entries for log types + * which have not been converted to the new system. + * This is not about old log entries which store + * parameters in a different format - the new + * LogFormatter classes have code to support formatting + * those too. + * @since 1.19 + */ +class LegacyLogFormatter extends LogFormatter { + /** + * Backward compatibility for extension changing the comment from + * the LogLine hook. This will be set by the first call on getComment(), + * then it might be modified by the hook when calling getActionLinks(), + * so that the modified value will be returned when calling getComment() + * a second time. + * + * @var string|null + */ + private $comment = null; + + /** + * Cache for the result of getActionLinks() so that it does not need to + * run multiple times depending on the order that getComment() and + * getActionLinks() are called. + * + * @var string|null + */ + private $revert = null; + + public function getComment() { + if ( $this->comment === null ) { + $this->comment = parent::getComment(); + } + + // Make sure we execute the LogLine hook so that we immediately return + // the correct value. + if ( $this->revert === null ) { + $this->getActionLinks(); + } + + return $this->comment; + } + + protected function getActionMessage() { + $entry = $this->entry; + $action = LogPage::actionText( + $entry->getType(), + $entry->getSubtype(), + $entry->getTarget(), + $this->plaintext ? null : $this->context->getSkin(), + (array)$entry->getParameters(), + !$this->plaintext // whether to filter [[]] links + ); + + $performer = $this->getPerformerElement(); + if ( !$this->irctext ) { + $sep = $this->msg( 'word-separator' ); + $sep = $this->plaintext ? $sep->text() : $sep->escaped(); + $action = $performer . $sep . $action; + } + + return $action; + } + + public function getActionLinks() { + if ( $this->revert !== null ) { + return $this->revert; + } + + if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) { + $this->revert = ''; + return $this->revert; + } + + $title = $this->entry->getTarget(); + $type = $this->entry->getType(); + $subtype = $this->entry->getSubtype(); + + // Do nothing. The implementation is handled by the hook modifiying the + // passed-by-ref parameters. This also changes the default value so that + // getComment() and getActionLinks() do not call them indefinitely. + $this->revert = ''; + + // This is to populate the $comment member of this instance so that it + // can be modified when calling the hook just below. + if ( $this->comment === null ) { + $this->getComment(); + } + + $params = $this->entry->getParameters(); + + Hooks::run( 'LogLine', [ $type, $subtype, $title, $params, + &$this->comment, &$this->revert, $this->entry->getTimestamp() ] ); + + return $this->revert; + } +} diff --git a/includes/logging/LogEntry.php b/includes/logging/LogEntry.php index bf35d78..ba1d7f5 100644 --- a/includes/logging/LogEntry.php +++ b/includes/logging/LogEntry.php @@ -1,6 +1,6 @@ <?php /** - * Contain classes for dealing with individual log entries + * Interface for log entries. Every log entry has these methods. * * This is how I see the log system history: * - appending to plain wiki pages @@ -105,709 +105,4 @@ * @return bool */ public function isDeleted( $field ); -} - -/** - * Extends the LogEntryInterface with some basic functionality - * - * @since 1.19 - */ -abstract class LogEntryBase implements LogEntry { - - public function getFullType() { - return $this->getType() . '/' . $this->getSubtype(); - } - - public function isDeleted( $field ) { - return ( $this->getDeleted() & $field ) === $field; - } - - /** - * Whether the parameters for this log are stored in new or - * old format. - * - * @return bool - */ - public function isLegacy() { - return false; - } - - /** - * Create a blob from a parameter array - * - * @since 1.26 - * @param array $params - * @return string - */ - public static function makeParamBlob( $params ) { - return serialize( (array)$params ); - } - - /** - * Extract a parameter array from a blob - * - * @since 1.26 - * @param string $blob - * @return array - */ - public static function extractParams( $blob ) { - return unserialize( $blob ); - } -} - -/** - * This class wraps around database result row. - * - * @since 1.19 - */ -class DatabaseLogEntry extends LogEntryBase { - - /** - * Returns array of information that is needed for querying - * log entries. Array contains the following keys: - * tables, fields, conds, options and join_conds - * - * @return array - */ - public static function getSelectQueryData() { - $commentQuery = CommentStore::newKey( 'log_comment' )->getJoin(); - - $tables = [ 'logging', 'user' ] + $commentQuery['tables']; - $fields = [ - 'log_id', 'log_type', 'log_action', 'log_timestamp', - 'log_user', 'log_user_text', - 'log_namespace', 'log_title', // unused log_page - 'log_params', 'log_deleted', - 'user_id', 'user_name', 'user_editcount', - ] + $commentQuery['fields']; - - $joins = [ - // IPs don't have an entry in user table - 'user' => [ 'LEFT JOIN', 'log_user=user_id' ], - ] + $commentQuery['joins']; - - return [ - 'tables' => $tables, - 'fields' => $fields, - 'conds' => [], - 'options' => [], - 'join_conds' => $joins, - ]; - } - - /** - * Constructs new LogEntry from database result row. - * Supports rows from both logging and recentchanges table. - * - * @param stdClass|array $row - * @return DatabaseLogEntry - */ - public static function newFromRow( $row ) { - $row = (object)$row; - if ( isset( $row->rc_logid ) ) { - return new RCDatabaseLogEntry( $row ); - } else { - return new self( $row ); - } - } - - /** @var stdClass Database result row. */ - protected $row; - - /** @var User */ - protected $performer; - - /** @var array Parameters for log entry */ - protected $params; - - /** @var int A rev id associated to the log entry */ - protected $revId = null; - - /** @var bool Whether the parameters for this log entry are stored in new or old format. */ - protected $legacy; - - protected function __construct( $row ) { - $this->row = $row; - } - - /** - * Returns the unique database id. - * - * @return int - */ - public function getId() { - return (int)$this->row->log_id; - } - - /** - * Returns whatever is stored in the database field. - * - * @return string - */ - protected function getRawParameters() { - return $this->row->log_params; - } - - public function isLegacy() { - // This extracts the property - $this->getParameters(); - return $this->legacy; - } - - public function getType() { - return $this->row->log_type; - } - - public function getSubtype() { - return $this->row->log_action; - } - - public function getParameters() { - if ( !isset( $this->params ) ) { - $blob = $this->getRawParameters(); - MediaWiki\suppressWarnings(); - $params = LogEntryBase::extractParams( $blob ); - MediaWiki\restoreWarnings(); - if ( $params !== false ) { - $this->params = $params; - $this->legacy = false; - } else { - $this->params = LogPage::extractParams( $blob ); - $this->legacy = true; - } - - if ( isset( $this->params['associated_rev_id'] ) ) { - $this->revId = $this->params['associated_rev_id']; - unset( $this->params['associated_rev_id'] ); - } - } - - return $this->params; - } - - public function getAssociatedRevId() { - // This extracts the property - $this->getParameters(); - return $this->revId; - } - - public function getPerformer() { - if ( !$this->performer ) { - $userId = (int)$this->row->log_user; - if ( $userId !== 0 ) { - // logged-in users - if ( isset( $this->row->user_name ) ) { - $this->performer = User::newFromRow( $this->row ); - } else { - $this->performer = User::newFromId( $userId ); - } - } else { - // IP users - $userText = $this->row->log_user_text; - $this->performer = User::newFromName( $userText, false ); - } - } - - return $this->performer; - } - - public function getTarget() { - $namespace = $this->row->log_namespace; - $page = $this->row->log_title; - $title = Title::makeTitle( $namespace, $page ); - - return $title; - } - - public function getTimestamp() { - return wfTimestamp( TS_MW, $this->row->log_timestamp ); - } - - public function getComment() { - return CommentStore::newKey( 'log_comment' )->getComment( $this->row )->text; - } - - public function getDeleted() { - return $this->row->log_deleted; - } -} - -class RCDatabaseLogEntry extends DatabaseLogEntry { - - public function getId() { - return $this->row->rc_logid; - } - - protected function getRawParameters() { - return $this->row->rc_params; - } - - public function getAssociatedRevId() { - return $this->row->rc_this_oldid; - } - - public function getType() { - return $this->row->rc_log_type; - } - - public function getSubtype() { - return $this->row->rc_log_action; - } - - public function getPerformer() { - if ( !$this->performer ) { - $userId = (int)$this->row->rc_user; - if ( $userId !== 0 ) { - $this->performer = User::newFromId( $userId ); - } else { - $userText = $this->row->rc_user_text; - // Might be an IP, don't validate the username - $this->performer = User::newFromName( $userText, false ); - } - } - - return $this->performer; - } - - public function getTarget() { - $namespace = $this->row->rc_namespace; - $page = $this->row->rc_title; - $title = Title::makeTitle( $namespace, $page ); - - return $title; - } - - public function getTimestamp() { - return wfTimestamp( TS_MW, $this->row->rc_timestamp ); - } - - public function getComment() { - return CommentStore::newKey( 'rc_comment' ) - // Legacy because the row may have used RecentChange::selectFields() - ->getCommentLegacy( wfGetDB( DB_REPLICA ), $this->row )->text; - } - - public function getDeleted() { - return $this->row->rc_deleted; - } -} - -/** - * Class for creating log entries manually, to inject them into the database. - * - * @since 1.19 - */ -class ManualLogEntry extends LogEntryBase { - /** @var string Type of log entry */ - protected $type; - - /** @var string Sub type of log entry */ - protected $subtype; - - /** @var array Parameters for log entry */ - protected $parameters = []; - - /** @var array */ - protected $relations = []; - - /** @var User Performer of the action for the log entry */ - protected $performer; - - /** @var Title Target title for the log entry */ - protected $target; - - /** @var string Timestamp of creation of the log entry */ - protected $timestamp; - - /** @var string Comment for the log entry */ - protected $comment = ''; - - /** @var int A rev id associated to the log entry */ - protected $revId = 0; - - /** @var array Change tags add to the log entry */ - protected $tags = null; - - /** @var int Deletion state of the log entry */ - protected $deleted; - - /** @var int ID of the log entry */ - protected $id; - - /** @var bool Can this log entry be patrolled? */ - protected $isPatrollable = false; - - /** @var bool Whether this is a legacy log entry */ - protected $legacy = false; - - /** - * @since 1.19 - * @param string $type - * @param string $subtype - */ - public function __construct( $type, $subtype ) { - $this->type = $type; - $this->subtype = $subtype; - } - - /** - * Set extra log parameters. - * - * You can pass params to the log action message by prefixing the keys with - * a number and optional type, using colons to separate the fields. The - * numbering should start with number 4, the first three parameters are - * hardcoded for every message. - * - * If you want to store stuff that should not be available in messages, don't - * prefix the array key with a number and don't use the colons. - * - * Example: - * $entry->setParameters( - * '4::color' => 'blue', - * '5:number:count' => 3000, - * 'animal' => 'dog' - * ); - * - * @since 1.19 - * @param array $parameters Associative array - */ - public function setParameters( $parameters ) { - $this->parameters = $parameters; - } - - /** - * Declare arbitrary tag/value relations to this log entry. - * These can be used to filter log entries later on. - * - * @param array $relations Map of (tag => (list of values|value)) - * @since 1.22 - */ - public function setRelations( array $relations ) { - $this->relations = $relations; - } - - /** - * Set the user that performed the action being logged. - * - * @since 1.19 - * @param User $performer - */ - public function setPerformer( User $performer ) { - $this->performer = $performer; - } - - /** - * Set the title of the object changed. - * - * @since 1.19 - * @param Title $target - */ - public function setTarget( Title $target ) { - $this->target = $target; - } - - /** - * Set the timestamp of when the logged action took place. - * - * @since 1.19 - * @param string $timestamp - */ - public function setTimestamp( $timestamp ) { - $this->timestamp = $timestamp; - } - - /** - * Set a comment associated with the action being logged. - * - * @since 1.19 - * @param string $comment - */ - public function setComment( $comment ) { - $this->comment = $comment; - } - - /** - * Set an associated revision id. - * - * For example, the ID of the revision that was inserted to mark a page move - * or protection, file upload, etc. - * - * @since 1.27 - * @param int $revId - */ - public function setAssociatedRevId( $revId ) { - $this->revId = $revId; - } - - /** - * Set change tags for the log entry. - * - * @since 1.27 - * @param string|string[] $tags - */ - public function setTags( $tags ) { - if ( is_string( $tags ) ) { - $tags = [ $tags ]; - } - $this->tags = $tags; - } - - /** - * Set whether this log entry should be made patrollable - * This shouldn't depend on config, only on whether there is full support - * in the software for patrolling this log entry. - * False by default - * - * @since 1.27 - * @param bool $patrollable - */ - public function setIsPatrollable( $patrollable ) { - $this->isPatrollable = (bool)$patrollable; - } - - /** - * Set the 'legacy' flag - * - * @since 1.25 - * @param bool $legacy - */ - public function setLegacy( $legacy ) { - $this->legacy = $legacy; - } - - /** - * Set the 'deleted' flag. - * - * @since 1.19 - * @param int $deleted One of LogPage::DELETED_* bitfield constants - */ - public function setDeleted( $deleted ) { - $this->deleted = $deleted; - } - - /** - * Insert the entry into the `logging` table. - * - * @param IDatabase $dbw - * @return int ID of the log entry - * @throws MWException - */ - public function insert( IDatabase $dbw = null ) { - $dbw = $dbw ?: wfGetDB( DB_MASTER ); - - if ( $this->timestamp === null ) { - $this->timestamp = wfTimestampNow(); - } - - // Trim spaces on user supplied text - $comment = trim( $this->getComment() ); - - $params = $this->getParameters(); - $relations = $this->relations; - - // Additional fields for which there's no space in the database table schema - $revId = $this->getAssociatedRevId(); - if ( $revId ) { - $params['associated_rev_id'] = $revId; - $relations['associated_rev_id'] = $revId; - } - - $data = [ - 'log_type' => $this->getType(), - 'log_action' => $this->getSubtype(), - 'log_timestamp' => $dbw->timestamp( $this->getTimestamp() ), - 'log_user' => $this->getPerformer()->getId(), - 'log_user_text' => $this->getPerformer()->getName(), - 'log_namespace' => $this->getTarget()->getNamespace(), - 'log_title' => $this->getTarget()->getDBkey(), - 'log_page' => $this->getTarget()->getArticleID(), - 'log_params' => LogEntryBase::makeParamBlob( $params ), - ]; - if ( isset( $this->deleted ) ) { - $data['log_deleted'] = $this->deleted; - } - $data += CommentStore::newKey( 'log_comment' )->insert( $dbw, $comment ); - - $dbw->insert( 'logging', $data, __METHOD__ ); - $this->id = $dbw->insertId(); - - $rows = []; - foreach ( $relations as $tag => $values ) { - if ( !strlen( $tag ) ) { - throw new MWException( "Got empty log search tag." ); - } - - if ( !is_array( $values ) ) { - $values = [ $values ]; - } - - foreach ( $values as $value ) { - $rows[] = [ - 'ls_field' => $tag, - 'ls_value' => $value, - 'ls_log_id' => $this->id - ]; - } - } - if ( count( $rows ) ) { - $dbw->insert( 'log_search', $rows, __METHOD__, 'IGNORE' ); - } - - return $this->id; - } - - /** - * Get a RecentChanges object for the log entry - * - * @param int $newId - * @return RecentChange - * @since 1.23 - */ - public function getRecentChange( $newId = 0 ) { - $formatter = LogFormatter::newFromEntry( $this ); - $context = RequestContext::newExtraneousContext( $this->getTarget() ); - $formatter->setContext( $context ); - - $logpage = SpecialPage::getTitleFor( 'Log', $this->getType() ); - $user = $this->getPerformer(); - $ip = ""; - if ( $user->isAnon() ) { - // "MediaWiki default" and friends may have - // no IP address in their name - if ( IP::isIPAddress( $user->getName() ) ) { - $ip = $user->getName(); - } - } - - return RecentChange::newLogEntry( - $this->getTimestamp(), - $logpage, - $user, - $formatter->getPlainActionText(), - $ip, - $this->getType(), - $this->getSubtype(), - $this->getTarget(), - $this->getComment(), - LogEntryBase::makeParamBlob( $this->getParameters() ), - $newId, - $formatter->getIRCActionComment(), // Used for IRC feeds - $this->getAssociatedRevId(), // Used for e.g. moves and uploads - $this->getIsPatrollable() - ); - } - - /** - * Publish the log entry. - * - * @param int $newId Id of the log entry. - * @param string $to One of: rcandudp (default), rc, udp - */ - public function publish( $newId, $to = 'rcandudp' ) { - DeferredUpdates::addCallableUpdate( - function () use ( $newId, $to ) { - $log = new LogPage( $this->getType() ); - if ( !$log->isRestricted() ) { - $rc = $this->getRecentChange( $newId ); - - if ( $to === 'rc' || $to === 'rcandudp' ) { - // save RC, passing tags so they are applied there - $tags = $this->getTags(); - if ( is_null( $tags ) ) { - $tags = []; - } - $rc->addTags( $tags ); - $rc->save( 'pleasedontudp' ); - } - - if ( $to === 'udp' || $to === 'rcandudp' ) { - $rc->notifyRCFeeds(); - } - - // Log the autopatrol if the log entry is patrollable - if ( $this->getIsPatrollable() && - $rc->getAttribute( 'rc_patrolled' ) === 1 - ) { - PatrolLog::record( $rc, true, $this->getPerformer() ); - } - } - }, - DeferredUpdates::POSTSEND, - wfGetDB( DB_MASTER ) - ); - } - - public function getType() { - return $this->type; - } - - public function getSubtype() { - return $this->subtype; - } - - public function getParameters() { - return $this->parameters; - } - - /** - * @return User - */ - public function getPerformer() { - return $this->performer; - } - - /** - * @return Title - */ - public function getTarget() { - return $this->target; - } - - public function getTimestamp() { - $ts = $this->timestamp !== null ? $this->timestamp : wfTimestampNow(); - - return wfTimestamp( TS_MW, $ts ); - } - - public function getComment() { - return $this->comment; - } - - /** - * @since 1.27 - * @return int - */ - public function getAssociatedRevId() { - return $this->revId; - } - - /** - * @since 1.27 - * @return array - */ - public function getTags() { - return $this->tags; - } - - /** - * Whether this log entry is patrollable - * - * @since 1.27 - * @return bool - */ - public function getIsPatrollable() { - return $this->isPatrollable; - } - - /** - * @since 1.25 - * @return bool - */ - public function isLegacy() { - return $this->legacy; - } - - public function getDeleted() { - return (int)$this->deleted; - } } diff --git a/includes/logging/LogEntryBase.php b/includes/logging/LogEntryBase.php new file mode 100644 index 0000000..eb55b25 --- /dev/null +++ b/includes/logging/LogEntryBase.php @@ -0,0 +1,79 @@ +<?php +/** + * Extends the LogEntryInterface with some basic functionality + * + * This is how I see the log system history: + * - appending to plain wiki pages + * - formatting log entries based on database fields + * - user is now part of the action message + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author Niklas Laxström + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later + * @since 1.19 + */ + +use Wikimedia\Rdbms\IDatabase; + +/** + * Extends the LogEntryInterface with some basic functionality + * + * @since 1.19 + */ +abstract class LogEntryBase implements LogEntry { + + public function getFullType() { + return $this->getType() . '/' . $this->getSubtype(); + } + + public function isDeleted( $field ) { + return ( $this->getDeleted() & $field ) === $field; + } + + /** + * Whether the parameters for this log are stored in new or + * old format. + * + * @return bool + */ + public function isLegacy() { + return false; + } + + /** + * Create a blob from a parameter array + * + * @since 1.26 + * @param array $params + * @return string + */ + public static function makeParamBlob( $params ) { + return serialize( (array)$params ); + } + + /** + * Extract a parameter array from a blob + * + * @since 1.26 + * @param string $blob + * @return array + */ + public static function extractParams( $blob ) { + return unserialize( $blob ); + } +} diff --git a/includes/logging/LogFormatter.php b/includes/logging/LogFormatter.php index b28bb7d..e17815a 100644 --- a/includes/logging/LogFormatter.php +++ b/includes/logging/LogFormatter.php @@ -1,6 +1,6 @@ <?php /** - * Contains classes for formatting log entries + * Default log formatting. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -885,104 +885,5 @@ } return [ $name => $value ]; - } -} - -/** - * This class formats all log entries for log types - * which have not been converted to the new system. - * This is not about old log entries which store - * parameters in a different format - the new - * LogFormatter classes have code to support formatting - * those too. - * @since 1.19 - */ -class LegacyLogFormatter extends LogFormatter { - /** - * Backward compatibility for extension changing the comment from - * the LogLine hook. This will be set by the first call on getComment(), - * then it might be modified by the hook when calling getActionLinks(), - * so that the modified value will be returned when calling getComment() - * a second time. - * - * @var string|null - */ - private $comment = null; - - /** - * Cache for the result of getActionLinks() so that it does not need to - * run multiple times depending on the order that getComment() and - * getActionLinks() are called. - * - * @var string|null - */ - private $revert = null; - - public function getComment() { - if ( $this->comment === null ) { - $this->comment = parent::getComment(); - } - - // Make sure we execute the LogLine hook so that we immediately return - // the correct value. - if ( $this->revert === null ) { - $this->getActionLinks(); - } - - return $this->comment; - } - - protected function getActionMessage() { - $entry = $this->entry; - $action = LogPage::actionText( - $entry->getType(), - $entry->getSubtype(), - $entry->getTarget(), - $this->plaintext ? null : $this->context->getSkin(), - (array)$entry->getParameters(), - !$this->plaintext // whether to filter [[]] links - ); - - $performer = $this->getPerformerElement(); - if ( !$this->irctext ) { - $sep = $this->msg( 'word-separator' ); - $sep = $this->plaintext ? $sep->text() : $sep->escaped(); - $action = $performer . $sep . $action; - } - - return $action; - } - - public function getActionLinks() { - if ( $this->revert !== null ) { - return $this->revert; - } - - if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) { - $this->revert = ''; - return $this->revert; - } - - $title = $this->entry->getTarget(); - $type = $this->entry->getType(); - $subtype = $this->entry->getSubtype(); - - // Do nothing. The implementation is handled by the hook modifiying the - // passed-by-ref parameters. This also changes the default value so that - // getComment() and getActionLinks() do not call them indefinitely. - $this->revert = ''; - - // This is to populate the $comment member of this instance so that it - // can be modified when calling the hook just below. - if ( $this->comment === null ) { - $this->getComment(); - } - - $params = $this->entry->getParameters(); - - Hooks::run( 'LogLine', [ $type, $subtype, $title, $params, - &$this->comment, &$this->revert, $this->entry->getTimestamp() ] ); - - return $this->revert; } } diff --git a/includes/logging/ManualLogEntry.php b/includes/logging/ManualLogEntry.php new file mode 100644 index 0000000..d6e4a37 --- /dev/null +++ b/includes/logging/ManualLogEntry.php @@ -0,0 +1,449 @@ +<?php +/** + * Class for creating log entries manually, to inject them into the database. + * + * This is how I see the log system history: + * - appending to plain wiki pages + * - formatting log entries based on database fields + * - user is now part of the action message + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author Niklas Laxström + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later + * @since 1.19 + */ + +/** + * Class for creating log entries manually, to inject them into the database. + * + * @since 1.19 + */ +class ManualLogEntry extends LogEntryBase { + /** @var string Type of log entry */ + protected $type; + + /** @var string Sub type of log entry */ + protected $subtype; + + /** @var array Parameters for log entry */ + protected $parameters = []; + + /** @var array */ + protected $relations = []; + + /** @var User Performer of the action for the log entry */ + protected $performer; + + /** @var Title Target title for the log entry */ + protected $target; + + /** @var string Timestamp of creation of the log entry */ + protected $timestamp; + + /** @var string Comment for the log entry */ + protected $comment = ''; + + /** @var int A rev id associated to the log entry */ + protected $revId = 0; + + /** @var array Change tags add to the log entry */ + protected $tags = null; + + /** @var int Deletion state of the log entry */ + protected $deleted; + + /** @var int ID of the log entry */ + protected $id; + + /** @var bool Can this log entry be patrolled? */ + protected $isPatrollable = false; + + /** @var bool Whether this is a legacy log entry */ + protected $legacy = false; + + /** + * @since 1.19 + * @param string $type + * @param string $subtype + */ + public function __construct( $type, $subtype ) { + $this->type = $type; + $this->subtype = $subtype; + } + + /** + * Set extra log parameters. + * + * You can pass params to the log action message by prefixing the keys with + * a number and optional type, using colons to separate the fields. The + * numbering should start with number 4, the first three parameters are + * hardcoded for every message. + * + * If you want to store stuff that should not be available in messages, don't + * prefix the array key with a number and don't use the colons. + * + * Example: + * $entry->setParameters( + * '4::color' => 'blue', + * '5:number:count' => 3000, + * 'animal' => 'dog' + * ); + * + * @since 1.19 + * @param array $parameters Associative array + */ + public function setParameters( $parameters ) { + $this->parameters = $parameters; + } + + /** + * Declare arbitrary tag/value relations to this log entry. + * These can be used to filter log entries later on. + * + * @param array $relations Map of (tag => (list of values|value)) + * @since 1.22 + */ + public function setRelations( array $relations ) { + $this->relations = $relations; + } + + /** + * Set the user that performed the action being logged. + * + * @since 1.19 + * @param User $performer + */ + public function setPerformer( User $performer ) { + $this->performer = $performer; + } + + /** + * Set the title of the object changed. + * + * @since 1.19 + * @param Title $target + */ + public function setTarget( Title $target ) { + $this->target = $target; + } + + /** + * Set the timestamp of when the logged action took place. + * + * @since 1.19 + * @param string $timestamp + */ + public function setTimestamp( $timestamp ) { + $this->timestamp = $timestamp; + } + + /** + * Set a comment associated with the action being logged. + * + * @since 1.19 + * @param string $comment + */ + public function setComment( $comment ) { + $this->comment = $comment; + } + + /** + * Set an associated revision id. + * + * For example, the ID of the revision that was inserted to mark a page move + * or protection, file upload, etc. + * + * @since 1.27 + * @param int $revId + */ + public function setAssociatedRevId( $revId ) { + $this->revId = $revId; + } + + /** + * Set change tags for the log entry. + * + * @since 1.27 + * @param string|string[] $tags + */ + public function setTags( $tags ) { + if ( is_string( $tags ) ) { + $tags = [ $tags ]; + } + $this->tags = $tags; + } + + /** + * Set whether this log entry should be made patrollable + * This shouldn't depend on config, only on whether there is full support + * in the software for patrolling this log entry. + * False by default + * + * @since 1.27 + * @param bool $patrollable + */ + public function setIsPatrollable( $patrollable ) { + $this->isPatrollable = (bool)$patrollable; + } + + /** + * Set the 'legacy' flag + * + * @since 1.25 + * @param bool $legacy + */ + public function setLegacy( $legacy ) { + $this->legacy = $legacy; + } + + /** + * Set the 'deleted' flag. + * + * @since 1.19 + * @param int $deleted One of LogPage::DELETED_* bitfield constants + */ + public function setDeleted( $deleted ) { + $this->deleted = $deleted; + } + + /** + * Insert the entry into the `logging` table. + * + * @param IDatabase $dbw + * @return int ID of the log entry + * @throws MWException + */ + public function insert( IDatabase $dbw = null ) { + $dbw = $dbw ?: wfGetDB( DB_MASTER ); + + if ( $this->timestamp === null ) { + $this->timestamp = wfTimestampNow(); + } + + // Trim spaces on user supplied text + $comment = trim( $this->getComment() ); + + $params = $this->getParameters(); + $relations = $this->relations; + + // Additional fields for which there's no space in the database table schema + $revId = $this->getAssociatedRevId(); + if ( $revId ) { + $params['associated_rev_id'] = $revId; + $relations['associated_rev_id'] = $revId; + } + + $data = [ + 'log_type' => $this->getType(), + 'log_action' => $this->getSubtype(), + 'log_timestamp' => $dbw->timestamp( $this->getTimestamp() ), + 'log_user' => $this->getPerformer()->getId(), + 'log_user_text' => $this->getPerformer()->getName(), + 'log_namespace' => $this->getTarget()->getNamespace(), + 'log_title' => $this->getTarget()->getDBkey(), + 'log_page' => $this->getTarget()->getArticleID(), + 'log_params' => LogEntryBase::makeParamBlob( $params ), + ]; + if ( isset( $this->deleted ) ) { + $data['log_deleted'] = $this->deleted; + } + $data += CommentStore::newKey( 'log_comment' )->insert( $dbw, $comment ); + + $dbw->insert( 'logging', $data, __METHOD__ ); + $this->id = $dbw->insertId(); + + $rows = []; + foreach ( $relations as $tag => $values ) { + if ( !strlen( $tag ) ) { + throw new MWException( "Got empty log search tag." ); + } + + if ( !is_array( $values ) ) { + $values = [ $values ]; + } + + foreach ( $values as $value ) { + $rows[] = [ + 'ls_field' => $tag, + 'ls_value' => $value, + 'ls_log_id' => $this->id + ]; + } + } + if ( count( $rows ) ) { + $dbw->insert( 'log_search', $rows, __METHOD__, 'IGNORE' ); + } + + return $this->id; + } + + /** + * Get a RecentChanges object for the log entry + * + * @param int $newId + * @return RecentChange + * @since 1.23 + */ + public function getRecentChange( $newId = 0 ) { + $formatter = LogFormatter::newFromEntry( $this ); + $context = RequestContext::newExtraneousContext( $this->getTarget() ); + $formatter->setContext( $context ); + + $logpage = SpecialPage::getTitleFor( 'Log', $this->getType() ); + $user = $this->getPerformer(); + $ip = ""; + if ( $user->isAnon() ) { + // "MediaWiki default" and friends may have + // no IP address in their name + if ( IP::isIPAddress( $user->getName() ) ) { + $ip = $user->getName(); + } + } + + return RecentChange::newLogEntry( + $this->getTimestamp(), + $logpage, + $user, + $formatter->getPlainActionText(), + $ip, + $this->getType(), + $this->getSubtype(), + $this->getTarget(), + $this->getComment(), + LogEntryBase::makeParamBlob( $this->getParameters() ), + $newId, + $formatter->getIRCActionComment(), // Used for IRC feeds + $this->getAssociatedRevId(), // Used for e.g. moves and uploads + $this->getIsPatrollable() + ); + } + + /** + * Publish the log entry. + * + * @param int $newId Id of the log entry. + * @param string $to One of: rcandudp (default), rc, udp + */ + public function publish( $newId, $to = 'rcandudp' ) { + DeferredUpdates::addCallableUpdate( + function () use ( $newId, $to ) { + $log = new LogPage( $this->getType() ); + if ( !$log->isRestricted() ) { + $rc = $this->getRecentChange( $newId ); + + if ( $to === 'rc' || $to === 'rcandudp' ) { + // save RC, passing tags so they are applied there + $tags = $this->getTags(); + if ( is_null( $tags ) ) { + $tags = []; + } + $rc->addTags( $tags ); + $rc->save( 'pleasedontudp' ); + } + + if ( $to === 'udp' || $to === 'rcandudp' ) { + $rc->notifyRCFeeds(); + } + + // Log the autopatrol if the log entry is patrollable + if ( $this->getIsPatrollable() && + $rc->getAttribute( 'rc_patrolled' ) === 1 + ) { + PatrolLog::record( $rc, true, $this->getPerformer() ); + } + } + }, + DeferredUpdates::POSTSEND, + wfGetDB( DB_MASTER ) + ); + } + + public function getType() { + return $this->type; + } + + public function getSubtype() { + return $this->subtype; + } + + public function getParameters() { + return $this->parameters; + } + + /** + * @return User + */ + public function getPerformer() { + return $this->performer; + } + + /** + * @return Title + */ + public function getTarget() { + return $this->target; + } + + public function getTimestamp() { + $ts = $this->timestamp !== null ? $this->timestamp : wfTimestampNow(); + + return wfTimestamp( TS_MW, $ts ); + } + + public function getComment() { + return $this->comment; + } + + /** + * @since 1.27 + * @return int + */ + public function getAssociatedRevId() { + return $this->revId; + } + + /** + * @since 1.27 + * @return array + */ + public function getTags() { + return $this->tags; + } + + /** + * Whether this log entry is patrollable + * + * @since 1.27 + * @return bool + */ + public function getIsPatrollable() { + return $this->isPatrollable; + } + + /** + * @since 1.25 + * @return bool + */ + public function isLegacy() { + return $this->legacy; + } + + public function getDeleted() { + return (int)$this->deleted; + } +} diff --git a/includes/logging/RCDatabaseLogEntry.php b/includes/logging/RCDatabaseLogEntry.php new file mode 100644 index 0000000..bfa9b8f --- /dev/null +++ b/includes/logging/RCDatabaseLogEntry.php @@ -0,0 +1,89 @@ +<?php +/** + * Recent changes database log entry + * + * This is how I see the log system history: + * - appending to plain wiki pages + * - formatting log entries based on database fields + * - user is now part of the action message + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author Niklas Laxström + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later + * @since 1.19 + */ + +class RCDatabaseLogEntry extends DatabaseLogEntry { + + public function getId() { + return $this->row->rc_logid; + } + + protected function getRawParameters() { + return $this->row->rc_params; + } + + public function getAssociatedRevId() { + return $this->row->rc_this_oldid; + } + + public function getType() { + return $this->row->rc_log_type; + } + + public function getSubtype() { + return $this->row->rc_log_action; + } + + public function getPerformer() { + if ( !$this->performer ) { + $userId = (int)$this->row->rc_user; + if ( $userId !== 0 ) { + $this->performer = User::newFromId( $userId ); + } else { + $userText = $this->row->rc_user_text; + // Might be an IP, don't validate the username + $this->performer = User::newFromName( $userText, false ); + } + } + + return $this->performer; + } + + public function getTarget() { + $namespace = $this->row->rc_namespace; + $page = $this->row->rc_title; + $title = Title::makeTitle( $namespace, $page ); + + return $title; + } + + public function getTimestamp() { + return wfTimestamp( TS_MW, $this->row->rc_timestamp ); + } + + public function getComment() { + return CommentStore::newKey( 'rc_comment' ) + // Legacy because the row may have used RecentChange::selectFields() + ->getCommentLegacy( wfGetDB( DB_REPLICA ), $this->row )->text; + } + + public function getDeleted() { + return $this->row->rc_deleted; + } +} -- To view, visit https://gerrit.wikimedia.org/r/405547 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ia53c82d7888469b8e587db5cbf0049ec262abaa8 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Phantom42 <nikita...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits