Committing my new logging classes for review. Will later commit changes that use...
authorNiklas Laxström <nikerabbit@users.mediawiki.org>
Wed, 7 Sep 2011 15:32:37 +0000 (15:32 +0000)
committerNiklas Laxström <nikerabbit@users.mediawiki.org>
Wed, 7 Sep 2011 15:32:37 +0000 (15:32 +0000)
includes/AutoLoader.php
includes/LogPage.php
includes/logging/LogEntry.php [new file with mode: 0644]
includes/logging/LogFormatter.php [new file with mode: 0644]

index 2d0cc8c..1cbfaf0 100644 (file)
@@ -531,6 +531,16 @@ $wgAutoloadLocalClasses = array(
        'JSMinPlus' => 'includes/libs/jsminplus.php',
        'JSParser' => 'includes/libs/jsminplus.php',
 
+       # includes/logging
+       'LogEntry' => 'includes/logging/LogEntry.php',
+       'LogEntryBase' => 'includes/logging/LogEntry.php',
+       'DatabaseLogEntry' => 'includes/logging/LogEntry.php',
+       'RCDatabaseLogEntry' => 'includes/logging/LogEntry.php',
+       'ManualLogEntry' => 'includes/logging/LogEntry.php',
+       'LogFormatter' => 'includes/logging/LogFormatter.php',
+       'LegacyLogFormatter' => 'includes/logging/LogFormatter.php',
+       'BlockLogFormatter' => 'includes/logging/LogFormatter.php',
+
        # includes/media
        'BitmapHandler' => 'includes/media/Bitmap.php',
        'BitmapHandler_ClientOnly' => 'includes/media/Bitmap_ClientOnly.php',
@@ -866,6 +876,7 @@ $wgAutoloadLocalClasses = array(
        # tests/parser
        'ParserTest' => 'tests/parser/parserTest.inc',
        'ParserTestParserHook' => 'tests/parser/parserTestsParserHook.php',
+       'ParserTestStaticParserHook' => 'tests/parser/parserTestsStaticParserHook.php',
 
        # tests/selenium
        'Selenium' => 'tests/selenium/Selenium.php',
index f098c52..aa57929 100644 (file)
@@ -172,6 +172,7 @@ class LogPage {
         *
         * @param $type String: logtype
         * @return String: log name
+        * @deprecated in 1.19, warnings in 1.21. Use getName()
         */
        public static function logName( $type ) {
                global $wgLogNames;
@@ -190,6 +191,7 @@ class LogPage {
         * @todo handle missing log types
         * @param $type String: logtype
         * @return String: headertext of this logtype
+        * @deprecated in 1.19, warnings in 1.21. Use getDescription()
         */
        public static function logHeader( $type ) {
                global $wgLogHeaders;
@@ -576,4 +578,66 @@ class LogPage {
 
                return $messages[$flag];
        }
+
+
+       /**
+        * Name of the log.
+        * @return Message
+        * @since 1.19
+        */
+       public function getName() {
+               global $wgLogNames;
+
+               // BC
+               if ( isset( $wgLogNames[$this->type] ) ) {
+                       $key = $wgLogNames[$this->type];
+               } else {
+                       $key = 'log-name-' . $this->type;
+               }
+
+               return wfMessage( $key );
+       }
+
+       /**
+        * Description of this log type.
+        * @return Message
+        * @since 1.19
+        */
+       public function getDescription() {
+               global $wgLogHeaders;
+               // BC
+               if ( isset( $wgLogHeaders[$this->type] ) ) {
+                       $key = $wgLogHeaders[$this->type];
+               } else {
+                       $key = 'log-description-' . $this->type;
+               }
+               return wfMessage( $key );
+       }
+
+       /**
+        * Returns the right needed to read this log type.
+        * @return string
+        * @since 1.19
+        */
+       public function getRestriction() {
+               global $wgLogRestrictions;
+               if ( isset( $wgLogRestrictions[$this->type] ) ) {
+                       $restriction = $wgLogRestrictions[$this->type];
+               } else {
+                       // '' always returns true with $user->isAllowed()
+                       $restriction = '';
+               }
+               return $restriction;
+       }
+
+       /**
+        * Tells if this log is not viewable by all.
+        * @return bool
+        * @since 1.19
+        */
+       public function isRestricted() {
+               $restriction = $this->getRestriction();
+               return $restriction !== '' && $restriction !== '*';
+       }
+
 }
diff --git a/includes/logging/LogEntry.php b/includes/logging/LogEntry.php
new file mode 100644 (file)
index 0000000..12f1b23
--- /dev/null
@@ -0,0 +1,465 @@
+<?php
+/**
+ * Contain classes for dealing with individual log entries
+ *
+ * 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
+ *
+ * @file
+ * @author Niklas Laxström
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ * @since 1.19
+ */
+
+/**
+ * Interface for log entries. Every log entry has these methods.
+ * @since 1.19
+ */
+interface LogEntry {
+
+       /**
+        * The main log type.
+        * @return string
+        */
+       public function getType();
+
+       /**
+        * The log subtype.
+        * @return string
+        */
+       public function getSubtype();
+
+       /**
+        * The full logtype in format maintype/subtype.
+        * @return string
+        */
+       public function getFullType();
+
+       /**
+        * Get the extra parameters stored for this message.
+        * @return array
+        */
+       public function getParameters();
+
+       /**
+        * Get the user for performed this action.
+        * @return User
+        */
+       public function getPerformer();
+
+       /**
+        * Get the target page of this action.
+        * @return Title
+        */
+       public function getTarget();
+
+       /**
+        * Get the timestamp when the action was executed.
+        * @return string
+        */
+       public function getTimestamp();
+
+       /**
+        * Get the user provided comment.
+        * @return string
+        */
+       public function getComment();
+
+       /**
+        * Get the access restriction.
+        * @return string
+        */
+       public function getDeleted();
+
+       /**
+        * @param $field Integer: one of LogPage::DELETED_* bitfield constants
+        * @return Boolean
+        */
+       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.
+        */
+       public function isLegacy() {
+               return false;
+       }
+
+}
+
+/**
+ * This class wraps around database result row.
+ * @since 1.19
+ */
+class DatabaseLogEntry extends LogEntryBase {
+       // Static->
+
+       /**
+        * 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() {
+               $tables = array( 'logging', 'user' );
+               $fields = array(
+                       'log_id', 'log_type', 'log_action', 'log_timestamp',
+                       'log_user', 'log_user_text',
+                       'log_namespace', 'log_title', //unused log_page
+                       'log_comment', 'log_params', 'log_deleted',
+                       'user_id', 'user_name', 'user_editcount',
+               );
+
+               $conds = array();
+
+               $joins = array(
+                       // IP's don't have an entry in user table
+                       'user' => array( 'LEFT JOIN', 'log_user=user_id' ),
+               );
+
+               return array(
+                       'tables' => $tables,
+                       'fields' => $fields,
+                       'conds'  => array(),
+                       'options' => array(),
+                       'join_conds' => $joins,
+               );
+       }
+
+       /**
+        * Constructs new LogEntry from database result row.
+        * Supports rows from both logging and recentchanges table.
+        * @param $row
+        * @return DatabaseLogEntry
+        */
+       public static function newFromRow( $row ) {
+               if ( is_array( $row ) && isset( $row['rc_logid'] ) ) {
+                       return new RCDatabaseLogEntry( (object) $row );
+               } else {
+                       return new self( $row );
+               }
+       }
+
+       // Non-static->
+
+       /// Database result row.
+       protected $row;
+
+       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;
+       }
+
+       // LogEntryBase->
+
+       public function isLegacy() {
+               // This does the check
+               $this->getParameters();
+               return $this->legacy;
+       }
+
+       // LogEntry->
+
+       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();
+                       $params = FormatJson::decode( $blob, true /* array */ );
+                       if ( $params !== null ) {
+                               $this->params = $params;
+                               $this->legacy = false;
+                       } else {
+                               $this->params = explode( "\n", $blob );
+                               $this->legacy = true;
+                       }
+               }
+               return $this->params;
+       }
+
+       public function getPerformer() {
+               $userId = (int) $this->row->log_user;
+               if ( $userId !== 0 ) {
+                       return User::newFromRow( $this->row );
+               } else {
+                       $userText = $this->row->log_user_text;
+                       return User::newFromName( $userText, false );
+               }
+       }
+
+       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 $this->row->log_comment;
+       }
+
+       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;
+       }
+
+       // LogEntry->
+
+       public function getType() {
+               return $this->row->rc_log_type;
+       }
+
+       public function getSubtype() {
+               return $this->row->rc_log_action;
+       }
+
+       public function getPerformer() {
+               $userId = (int) $this->row->rc_user;
+               if ( $userId !== 0 ) {
+                       return User::newFromId( $userId );
+               } else {
+                       $userText = $this->row->rc_user_text;
+                       // Might be an IP, don't validate the username
+                       return User::newFromName( $userText, false );
+               }
+       }
+
+       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 $this->row->rc_comment;
+       }
+
+       public function getDeleted() {
+               return $this->row->rc_deleted;
+       }
+
+}
+
+/**
+ * Class for creating log entries manually, for
+ * example to inject them into the database.
+ * @since 1.19
+ */
+class ManualLogEntry extends LogEntryBase {
+       protected $type; ///!< @var string
+       protected $subtype; ///!< @var string
+       protected $parameters = array(); ///!< @var array
+       protected $performer; ///!< @var User
+       protected $target; ///!< @var Title
+       protected $timestamp; ///!< @var string
+       protected $comment; ///!< @var string
+       protected $deleted; ///!< @var int
+
+       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 colon.
+        * The numbering should start with number 4, the
+        * first three parameters are hardcoded for every
+        * message. Example:
+        * $entry->setParameters(
+        *   '4:color' => 'blue',
+        *   'animal' => 'dog'
+        * );
+        * @param $parameters Associative array
+        */
+       public function setParameters( $parameters ) {
+               $this->parameters = $parameters;
+       }
+
+       public function setPerformer( User $performer ) {
+               $this->performer = $performer;
+       }
+
+       public function setTarget( Title $target ) {
+               $this->target = $target;
+       }
+
+       public function setTimestamp( $timestamp ) {
+               $this->timestamp = $timestamp;
+       }
+
+       public function setComment( $comment ) {
+               $this->comment = $comment;
+       }
+
+       public function setDeleted( $deleted ) {
+               $this->deleted = $deleted;
+       }
+
+       /**
+        * Inserts the entry into the logging table.
+        * @return int If of the log entry
+        */
+       public function insert() {
+               global $wgLogRestrictions;
+
+               $dbw = wfGetDB( DB_MASTER );
+               $id = $dbw->nextSequenceValue( 'logging_log_id_seq' );
+
+               if ( $this->timestamp === null ) {
+                       $this->timestamp = wfTimestampNow();
+               }
+
+               $data = array(
+                       'log_id' => $id,
+                       '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_comment' => $this->getComment(),
+                       'log_params' => FormatJson::encode( (array) $this->getParameters() ),
+               );
+               $dbw->insert( 'logging', $data, __METHOD__ );
+               $this->id = !is_null( $id ) ? $id : $dbw->insertId();
+               return $this->id;
+       }
+
+       /**
+        * Publishes the log entry.
+        * @param $newId int id of the log entry.
+        * @param $to string: rcandudp (default), rc, udp
+        */
+       public function publish( $newId, $to = 'rcandudp' ) {
+               $log = new LogPage( $this->getType() );
+               if ( $log->isRestricted() ) {
+                       return;
+               }
+
+               $formatter = LogFormatter::newFromEntry( $this );
+               $context = RequestContext::newExtraneousContext( $this->getTarget() );
+               $formatter->setContext( $context );
+
+               $logpage = SpecialPage::getTitleFor( 'Log', $this->getType() );
+               $user = $this->getPerformer();
+               $rc = RecentChange::newLogEntry(
+                       $this->getTimestamp(),
+                       $logpage,
+                       $user,
+                       $formatter->getPlainActionText(), // Used for IRC feeds
+                       $user->isAnon() ? $user->getName() : '',
+                       $this->getType(),
+                       $this->getSubtype(),
+                       $this->getTarget(),
+                       $this->getComment(),
+                       FormatJson::encode( (array) $this->getParameters() ),
+                       $newId
+               );
+
+               if ( $to === 'rc' || $to === 'rcandudp' ) {
+                       $rc->save();
+               }
+
+               if ( $to === 'udp' || $to === 'rcandudp' ) {
+                       $rc->notifyRC2UDP();
+               }
+       }
+
+       // LogEntry->
+
+       public function getType() {
+               return $this->type;
+       }
+
+       public function getSubtype() {
+               return $this->subtype;
+       }
+
+       public function getParameters() {
+               return $this->parameters;
+       }
+
+       public function getPerformer() {
+               return $this->performer;
+       }
+
+       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;
+       }
+
+       public function getDeleted() {
+               return (int) $this->deleted;
+       }
+
+}
\ No newline at end of file
diff --git a/includes/logging/LogFormatter.php b/includes/logging/LogFormatter.php
new file mode 100644 (file)
index 0000000..7ff4bb7
--- /dev/null
@@ -0,0 +1,342 @@
+<?php
+/**
+ * Contains classes for formatting log entries
+ *
+ * @file
+ * @author Niklas Laxström
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ * @since 1.19
+ */
+
+/**
+ * Implements the default log formatting.
+ * Can be overridden by subclassing and setting
+ * $wgLogActionsHandlers['type/subtype'] = 'class'; or
+ * $wgLogActionsHandlers['type/*'] = 'class';
+ * @since 1.19
+ */
+class LogFormatter {
+
+       // Static->
+
+       /**
+        * Constructs a new formatter suitable for given entry.
+        * @param $entry LogEntry
+        * @return LogFormatter
+        */
+       public static function newFromEntry( LogEntry $entry ) {
+               global $wgLogActionsHandlers;
+               $fulltype = $entry->getFullType();
+               $wildcard = $entry->getType() . '/*';
+               $handler = '';
+
+               if ( isset( $wgLogActionsHandlers[$fulltype] ) ) {
+                       $handler = $wgLogActionsHandlers[$fulltype];
+               } elseif ( isset( $wgLogActionsHandlers[$wildcard] ) ) {
+                       $handler = $wgLogActionsHandlers[$wildcard];
+               }
+
+               if ( $handler !== '' && class_exists( $handler ) ) {
+                       return new $handler( $entry );
+               }
+
+               return new LegacyLogFormatter( $entry );
+       }
+
+       /**
+        * Handy shortcut for constructing a formatter directly from
+        * database row.
+        * @param $row
+        * @see DatabaseLogEntry::getSelectQueryData
+        * @return LogFormatter
+        */
+       public static function newFromRow( $row ) {
+               return self::newFromEntry( DatabaseLogEntry::newFromRow( $row ) );
+       }
+
+       // Nonstatic->
+
+       /// @var LogEntry
+       protected $entry;
+
+       /// Whether to output user tool links
+       protected $linkFlood = false;
+
+       /**
+        * Set to true if we are constructing a message text that is going to
+        * be included in page history or send to IRC feed. Links are replaced
+        * with plaintext or with [[pagename]] kind of syntax, that is parsed
+        * by page histories and IRC feeds.
+        * @var boolean
+        */
+       protected $plaintext = false;
+
+       protected function __construct( LogEntry $entry ) {
+               $this->entry = $entry;
+               $this->context = RequestContext::getMain();
+       }
+
+       /**
+        * Replace the default context
+        * @param $context RequestContext
+        */
+       public function setContext( RequestContext $context ) {
+               $this->context = $context;
+       }
+
+       /**
+        * If set to true, will produce user tool links after
+        * the user name. This should be replaced with generic
+        * CSS/JS solution.
+        * @param $value boolean
+        */
+       public function setShowUserToolLinks( $value ) {
+               $this->linkFlood = $value;
+       }
+
+       /**
+        * Ugly hack to produce plaintext version of the message.
+        * Usually you also want to set extraneous request context
+        * to avoid formatting for any particular user.
+        * @see getActionText()
+        * @return string text
+        */
+       public function getPlainActionText() {
+               $this->plaintext = true;
+               $text = $this->getActionText();
+               $this->plaintext = false;
+               return $text;
+       }
+
+       /**
+        * Gets the log action, including username.
+        * @return string HTML
+        */
+       public function getActionText() {
+               $element = $this->getActionMessage();
+               if ( $element instanceof Message ) {
+                       $element = $this->plaintext ? $element->text() : $element->escaped();
+               }
+
+               if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) {
+                       $performer = $this->getPerformerElement() . $this->msg( 'word-separator' )->text();
+                       $element = $performer . self::getRestrictedElement( 'rev-deleted-event' );
+               }
+
+               return $element;
+       }
+
+       /**
+        * Returns a sentence describing the log action. Usually
+        * a Message object is returned, but old style log types
+        * and entries might return pre-escaped html string.
+        * @return Message|pre-escaped html
+        */
+       protected function getActionMessage() {
+               $message =  $this->msg( $this->getMessageKey() );
+               $message->params( $this->getMessageParameters() );
+               return $message;
+       }
+
+       /**
+        * Returns a key to be used for formatting the action sentence.
+        * Default is logentry-TYPE-SUBTYPE for modern logs. Legacy log
+        * types will use custom keys, and subclasses can also alter the
+        * key depending on the entry itself.
+        * @return string message key
+        */
+       protected function getMessageKey() {
+               $type = $this->entry->getType();
+               $subtype = $this->entry->getSubtype();
+               $key = "logentry-$type-$subtype";
+               return $key;
+       }
+
+       /**
+        * Extract parameters intented for action message from
+        * array of all parameters. The are three hardcoded
+        * parameters (array zero-indexed, this list not):
+        *  - 1: user name with premade link
+        *  - 2: usable for gender magic function
+        *  - 3: target page with premade link
+        * @return array
+        */
+       protected function getMessageParameters() {
+               $entry = $this->entry;
+
+               $params = array();
+               $params[0] = Message::rawParam( $this->getPerformerElement() );
+               $params[1] = $entry->getPerformer()->getName();
+               $params[2] = Message::rawParam( $this->makePageLink( $entry->getTarget() ) );
+
+               if ( $entry->isLegacy() ) {
+                       foreach ( $entry->getParameters() as $index => $value ) {
+                               $params[$index+3] = $value;
+                       }
+               }
+
+               // Filter out parameters which are not in format #:foo
+               foreach ( $entry->getParameters() as $key => $value ) {
+                       if ( strpos( $key, ':' ) === false ) continue;
+                       list( $index, $type, $name ) = explode( ':', $key, 3 );
+                       $params[$index-1] = $value;
+               }
+
+               /* Message class doesn't like non consecutive numbering.
+                * Fill in missing indexes with empty strings to avoid
+                * incorrect renumbering.
+                */
+               $max = max( array_keys( $params ) );
+               for ( $i = 4; $i < $max; $i++ ) {
+                       if ( !isset( $params[$i] ) ) {
+                               $params[$i] = '';
+                       }
+               }
+
+               return $params;
+       }
+
+       /**
+        * Helper to make a link to the page, taking the plaintext
+        * value in consideration.
+        * @param $title Title the page
+        * @param $parameters array query parameters
+        * @return String
+        */
+       protected function makePageLink( Title $title, $parameters = array() ) {
+               if ( !$this->plaintext ) {
+                       $link = Linker::link( $title, null, array(), $parameters );
+               } else {
+                       $link = '[[' . $title->getPrefixedText() . ']]';
+               }
+               return $link;
+       }
+
+       /**
+        * Provides the name of the user who performed the log action.
+        * Used as part of log action message or standalone, depending
+        * which parts of the log entry has been hidden.
+        */
+       public function getPerformerElement() {
+               $performer = $this->entry->getPerformer();
+
+               if ( $this->plaintext ) {
+                       $element = $performer->getName();
+               } else {
+                       $element = Linker::userLink(
+                               $performer->getId(),
+                               $performer->getName()
+                       );
+
+                       if ( $this->linkFlood ) {
+                               $element .= Linker::userToolLinks(
+                                       $performer->getId(),
+                                       $performer->getName(),
+                                       true, // Red if no edits
+                                       0, // Flags
+                                       $performer->getEditCount()
+                               );
+                       }
+               }
+
+               if ( $this->entry->isDeleted( LogPage::DELETED_USER ) ) {
+                       $element = self::getRestrictedElement( 'rev-deleted-user' );
+               }
+
+               return $element;
+       }
+
+       /**
+        * Gets the luser provided comment
+        * @return string HTML
+        */
+       public function getComment() {
+               $lang = $this->context->getLang();
+               $element = $lang->getDirMark() . Linker::commentBlock( $this->entry->getComment() );
+
+               if ( $this->entry->isDeleted( LogPage::DELETED_COMMENT ) ) {
+                       $element = self::getRestrictedElement( 'rev-deleted-comment' );
+               }
+
+               return $element;
+       }
+
+       /**
+        * Helper method for displaying restricted element.
+        * @param $message string
+        * @return string HTML
+        */
+       protected function getRestrictedElement( $message ) {
+               if ( $this->plaintext ) {
+                       return $this->msg( $message )->text();
+               }
+
+               $content =  $this->msg( $message )->escaped();
+               $attribs = array( 'class' => 'history-deleted' );
+               return Html::rawElement( 'span', $attribs, $content );
+       }
+
+       /**
+        * Shortcut for wfMessage which honors local context.
+        * @todo Would it be better to require replacing the global context instead?
+        * @param $key string
+        * @return Message
+        */
+       protected function msg( $key ) {
+               return wfMessage( $key )
+                       ->inLanguage( $this->context->getLang() )
+                       ->title( $this->context->getTitle() );
+       }
+
+}
+
+/**
+ * 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 {
+       protected function getActionMessage() {
+               $entry = $this->entry;
+               $action = LogPage::actionText(
+                       $entry->getType(),
+                       $entry->getSubtype(),
+                       $entry->getTarget(),
+                       $this->context->getUser()->getSkin(),
+                       (array)$entry->getParameters(),
+                       true
+               );
+
+               $performer = $this->getPerformerElement();
+               return $performer .  $this->msg( 'word-separator' )->text() . $action;
+       }
+
+}
+
+/**
+ * This class formats Block log entries.
+ * @since 1.19
+ */
+class BlockLogFormatter extends LogFormatter {
+       protected function getMessageKey() {
+               $key = parent::getMessageKey();
+               $params = $this->getMessageParameters();
+               if ( isset( $params[4] ) && $params[4] === '1' ) {
+                       $key .= '-noredirect';
+               }
+               return $key;
+       }
+
+       protected function getMessageParameters() {
+               $params = parent::getMessageParameters();
+               $oldname = $this->makePageLink( $this->entry->getTarget(), array( 'redirect' => 'no' ) );
+               $newname = $this->makePageLink( Title::newFromText( $params[3] ) );
+               $params[2] = Message::rawParam( $oldname );
+               $params[3] = Message::rawParam( $newname );
+               return $params;
+       }
+}
\ No newline at end of file