Current File : /home/jvzmxxx/wiki1/extensions/Wikibase/client/includes/RecentChanges/RecentChangeFactory.php
<?php

namespace Wikibase\Client\RecentChanges;

use Language;
use Message;
use MWException;
use RecentChange;
use Title;
use Wikibase\EntityChange;
use Wikibase\ItemChange;
use Wikibase\SiteLinkCommentCreator;
use Wikimedia\Assert\Assert;

/**
 * @since 0.5
 *
 * @license GPL-2.0+
 * @author Katie Filbert < aude.wiki@gmail.com >
 * @author Daniel Kinzler
 */
class RecentChangeFactory {

	/**
	 * Classification of Wikibase changss in the rc_source column of the
	 * recentchanges table.
	 */
	const SRC_WIKIBASE = 'wb';

	/**
	 * @var Language
	 */
	private $language;

	/**
	 * @var SiteLinkCommentCreator
	 */
	private $siteLinkCommentCreator;

	/**
	 * @param Language $language
	 * @param SiteLinkCommentCreator $siteLinkCommentCreator
	 */
	public function __construct( Language $language, SiteLinkCommentCreator $siteLinkCommentCreator ) {
		$this->language = $language;
		$this->siteLinkCommentCreator = $siteLinkCommentCreator;
	}

	/**
	 * Creates a local RecentChange object that corresponds to the EntityChange from the
	 * repo, with respect to the given target page
	 *
	 * @since 0.5
	 *
	 * @param EntityChange $change A change reported from the wikibase repository
	 * @param Title $target The title of a page affected by the change
	 * @param array|null $preparedAttribs Attributes pre-calculated by calling prepareChangeAttributes()
	 *      to avoid re-calculating common change attributes for each target page.
	 *
	 * @return RecentChange
	 */
	public function newRecentChange( EntityChange $change, Title $target, array $preparedAttribs = null ) {
		if ( $preparedAttribs === null ) {
			$preparedAttribs = $this->prepareChangeAttributes( $change );
		}

		$targetSpecificAttributes = $this->buildTargetSpecificAttributes( $change, $target );
		$attribs = array_merge( $preparedAttribs, $targetSpecificAttributes );
		$rc = RecentChange::newFromRow( (object)$attribs );
		$rc->setExtra( array( 'pageStatus' => 'changed' ) );

		return $rc;
	}

	/**
	 * Prepare change attributes for the given EntityChange. This can be used to avoid
	 * re-calculating these attributes for each target page, when processing a change
	 * with respect to a batch of affected target pages.
	 *
	 * @param EntityChange $change
	 *
	 * @return array Associative array of prepared change attributes, for use with the
	 *      $preparedAttribs of newRecentChange().
	 */
	public function prepareChangeAttributes( EntityChange $change ) {
		$rcinfo = $change->getMetadata();

		$fields = $change->getFields();
		$fields['entity_type'] = $change->getEntityId()->getEntityType();

		if ( isset( $fields['info']['changes'] ) ) {
			$changesForComment = $fields['info']['changes'];
		} else {
			$changesForComment = array( $change );
		}

		$comment = $this->getEditCommentMulti( $changesForComment );

		unset( $fields['info'] );
		$metadata = array_merge( $fields, $rcinfo );

		$isBot = false;
		if ( array_key_exists( 'bot', $metadata ) ) {
			$isBot = $metadata['bot'];
		}

		// compatibility
		if ( array_key_exists( 'user_text', $metadata ) ) {
			$userText = $metadata['user_text'];
		} elseif ( array_key_exists( 'rc_user_text', $metadata ) ) {
			$userText = $metadata['rc_user_text'];
		} else {
			$userText = '';
		}

		$time = isset( $metadata['time'] ) ? $metadata['time'] : wfTimestamp( TS_MW );

		$params = array(
			'wikibase-repo-change' => $metadata,
		);

		return array(
			'rc_user' => 0,
			'rc_user_text' => $userText,
			'rc_comment' => $comment,
			'rc_type' => RC_EXTERNAL,
			'rc_minor' => true, // for now, always consider these minor
			'rc_bot' => $isBot,
			'rc_patrolled' => true,
			'rc_params' => serialize( $params ),
			'rc_timestamp' => $time,
			'rc_log_type' => null,
			'rc_log_action' => '',
			'rc_source' => self::SRC_WIKIBASE,
			'rc_deleted' => false,
		);
	}

	/**
	 * Builds change attribute specific to the given target page.
	 *
	 * @param EntityChange $change
	 * @param Title $target
	 *
	 * @return array
	 */
	private function buildTargetSpecificAttributes( EntityChange $change, Title $target ) {
		$attribs = array(
			'rc_namespace' => $target->getNamespace(),
			'rc_title' => $target->getDBkey(),
			'rc_old_len' => $target->getLength(),
			'rc_new_len' => $target->getLength(),
			'rc_this_oldid' => $target->getLatestRevID(),
			'rc_last_oldid' => $target->getLatestRevID(),
			'rc_cur_id' => $target->getArticleID(),
		);

		$comment = $this->buildTargetSpecificComment( $change, $target );
		if ( $comment !== null ) {
			$attribs['rc_comment'] = $comment;
		}

		return $attribs;
	}

	/**
	 * Get a title specific rc_comment, in case that is needed. Null otherwise.
	 *
	 * @param EntityChange $change
	 * @param Title $target
	 *
	 * @return string|null
	 */
	private function buildTargetSpecificComment( EntityChange $change, Title $target ) {
		if ( !( $change instanceof ItemChange ) ) {
			// Not an ItemChange
			return null;
		}

		$siteLinkDiff = $change->getSiteLinkDiff();
		if ( !$this->siteLinkCommentCreator->needsTargetSpecificSummary( $siteLinkDiff, $target ) ) {
			return null;
		}

		$fields = $change->getFields();

		if ( isset( $fields['info']['changes'] ) ) {
			$changesForComment = $fields['info']['changes'];
		} else {
			$changesForComment = array( $change );
		}

		return $this->getEditCommentMulti( $changesForComment, $target );
	}

	/**
	 * Returns a human readable comment representing the given changes.
	 *
	 * @param EntityChange[] $changes
	 * @param Title|null $target The page we create an edit summary for. Needed to create an article
	 *         specific edit summary on site link changes. Ignored otherwise.
	 *
	 * @throws MWException
	 * @return string
	 */
	private function getEditCommentMulti( array $changes, Title $target = null ) {
		$comments = array();
		$c = 0;

		foreach ( $changes as $change ) {
			$c++;
			$comments[] = $this->getEditComment( $change, $target );
		}

		if ( $c === 0 ) {
			return '';
		} elseif ( $c === 1 ) {
			return reset( $comments );
		} else {
			//@todo: handle overly long lists nicely!
			return $this->language->semicolonList( $comments );
		}
	}

	/**
	 * Returns a human readable comment representing the change.
	 *
	 * @since 0.4
	 *
	 * @param EntityChange $change the change to get a comment for
	 * @param Title|null $target The page we create an edit summary for. Needed to create an article
	 *         specific edit summary on site link changes. Ignored otherwise.
	 *
	 * @throws MWException
	 * @return string
	 */
	private function getEditComment( EntityChange $change, Title $target = null ) {
		$siteLinkDiff = $change instanceof ItemChange
			? $change->getSiteLinkDiff()
			: null;

		$editComment = '';

		if ( $siteLinkDiff !== null && !$siteLinkDiff->isEmpty() ) {
			$action = $change->getAction();
			$siteLinkComment = $this->siteLinkCommentCreator->getEditComment( $siteLinkDiff, $action, $target );
			$editComment = $siteLinkComment === null ? '' : $siteLinkComment;
		}

		if ( $editComment === '' ) {
			$editComment = $change->getComment();
		}

		if ( $editComment === '' ) {
			// If there is no comment, use something generic. This shouldn't happen.
			wfWarn( 'Failed to find edit comment for EntityChange' );
			$editComment = $this->msg( 'wikibase-comment-update' )->text();
		}

		Assert::postcondition( is_string( $editComment ), '$editComment must be a string' );
		return $editComment;
	}

	/**
	 * @param string $key
	 *
	 * @return Message
	 * @throws MWException
	 */
	private function msg( $key ) {
		$params = func_get_args();
		array_shift( $params );
		if ( isset( $params[0] ) && is_array( $params[0] ) ) {
			$params = $params[0];
		}

		return wfMessage( $key, $params )->inLanguage( $this->language );
	}

}