Current File : /home/jvzmxxx/wiki1/extensions/Flow/includes/Model/PostRevision.php
<?php

namespace Flow\Model;

use Flow\Collection\PostCollection;
use Flow\Container;
use Flow\Exception\DataModelException;
use Flow\Exception\FlowException;
use Flow\Repository\TreeRepository;
use Title;
use User;

class PostRevision extends AbstractRevision {
	const MAX_TOPIC_LENGTH = 260;

	/**
	 * @var UUID
	 */
	protected $postId;

	// The rest of the properties are denormalized data that
	// must not change between revisions of same post

	/**
	 * @var UserTuple
	 */
	protected $origUser;

	/**
	 * @var UUID|null
	 */
	protected $replyToId;

	/**
	 * @var PostRevision[]|null Optionally loaded list of children for this post.
	 */
	protected $children;

	/**
	 * @var int|null Optionally loaded distance of this post from the
	 *   root of this post tree.
	 */
	protected $depth;

	/**
	 * @var PostRevision|null Optionally loaded root of this posts tree.
	 *   This is always a topic title.
	 */
	protected $rootPost;

	/**
	 * Create a brand new root post for a brand new topic.  Creating replies to
	 * an existing post(incl topic root) should use self::reply.
	 *
	 * @param Workflow $topic
	 * @param User $user
	 * @param string $content The title of the topic (they are Collection as well), in
	 *  topic-title-wikitext format.
	 * @return PostRevision
	 */
	static public function createTopicPost( Workflow $topic, User $user, $content ) {
		$format = 'topic-title-wikitext';
		$obj = static::newFromId( $topic->getId(), $user, $content, $format, $topic->getArticleTitle() );

		$obj->changeType = 'new-post';
		// A newly created post has no children, a depth of 0, and
		// is the root of its tree.
		$obj->setChildren( array() );
		$obj->setDepth( 0 );
		$obj->rootPost = $obj;

		return $obj;
	}

	/**
	 * DO NOT USE THIS METHOD!
	 *
	 * Seriously, you probably don't want to use this method, except from within
	 * this class.
	 *
	 * Although it may seem similar to Title::newFrom* or User::newFrom*, chances are slim to none
	 * that this will do what you'd expect.
	 *
	 * Unlike Title & User etc, a post is not something some object that can be
	 * used in isolation: a post should always be retrieved via it's parents,
	 * via a workflow, ...
	 *
	 * The only reasons we have this method are for creating root posts
	 * (called from PostRevision->create), and so when failing to load a
	 * post, we can create a stub object.
	 *
	 * @param UUID $uuid
	 * @param User $user
	 * @param string $content
	 * @param string $format wikitext|html
	 * @param Title|null $title
	 * @return PostRevision
	 */
	static public function newFromId( UUID $uuid, User $user, $content, $format, Title $title = null ) {
		$obj = new self;
		$obj->revId = UUID::create();
		$obj->postId = $uuid;

		$obj->user = UserTuple::newFromUser( $user );
		$obj->origUser = $obj->user;

		$obj->setReplyToId( null ); // not a reply to anything
		$obj->prevRevision = null; // no parent revision
		$obj->setContent( $content, $format, $title );

		return $obj;
	}

	/**
	 * @var string[] $row
	 * @var PostRevision|null $obj
	 * @return PostRevision
	 * @throws DataModelException
	 */
	static public function fromStorageRow( array $row, $obj = null ) {
		/** @var $obj PostRevision */
		$obj = parent::fromStorageRow( $row, $obj );
		$treeRevId = UUID::create( $row['tree_rev_id'] );

		if ( ! $obj->revId->equals( $treeRevId ) ) {
			$treeRevIdStr = ( $treeRevId !== null )
				? $treeRevId->getAlphadecimal()
				: var_export( $row['tree_rev_id'], true );

			throw new DataModelException(
				'tree revision doesn\'t match provided revision: treeRevId ('
					. $treeRevIdStr . ') != obj->revId (' . $obj->revId->getAlphadecimal() . ')',
				'process-data'
			);
		}
		$obj->replyToId = $row['tree_parent_id'] ? UUID::create( $row['tree_parent_id'] ) : null;
		$obj->postId = UUID::create( $row['rev_type_id'] );
		$obj->origUser = UserTuple::newFromArray( $row, 'tree_orig_user_' );
		if ( !$obj->origUser ) {
			throw new DataModelException( 'Could not create UserTuple for tree_orig_user_' );
		}
		return $obj;
	}

	/**
	 * @param PostRevision $rev
	 * @return string[]
	 */
	static public function toStorageRow( $rev ) {
		return parent::toStorageRow( $rev ) + array(
			'tree_parent_id' => $rev->replyToId ? $rev->replyToId->getAlphadecimal() : null,
			'tree_rev_descendant_id' => $rev->postId->getAlphadecimal(),
			'tree_rev_id' => $rev->revId->getAlphadecimal(),
			// rest of tree_ is denormalized data about first post revision
			'tree_orig_user_id' => $rev->origUser->id,
			'tree_orig_user_ip' => $rev->origUser->ip,
			'tree_orig_user_wiki' => $rev->origUser->wiki,
		);
	}

	/**
	 * @param Workflow $workflow
	 * @param User $user
	 * @param string $content
	 * @param string $format wikitext|html
	 * @param string[optional] $changeType
	 * @return PostRevision
	 */
	public function reply( Workflow $workflow, User $user, $content, $format, $changeType = 'reply' ) {
		$reply = new self;

		// UUIDs should not be reused for different entities/entity types in the future.
		// (It is also inconsistent with newFromId, which uses separate ones.)
		// This may be changed here in the future.
		$reply->revId = $reply->postId = UUID::create();

		$reply->user = UserTuple::newFromUser( $user );
		$reply->origUser = $reply->user;
		$reply->replyToId = $this->postId;
		$reply->setContent( $content, $format, $workflow->getArticleTitle() );
		$reply->changeType = $changeType;
		$reply->setChildren( array() );
		$reply->setDepth( $this->getDepth() + 1 );
		$reply->rootPost = $this->rootPost;

		return $reply;
	}

	/**
	 * @return UUID
	 */
	public function getPostId() {
		return $this->postId;
	}

	/**
	 * @return UserTuple
	 */
	public function getCreatorTuple() {
		return $this->origUser;
	}

	/**
	 * @return boolean
	 */
	public function isTopicTitle() {
		return $this->replyToId === null;
	}

	public function getContentFormat() {
		// The canonical format must always be topic-title-wikitext, because we
		// can not convert 'topic-title-html' to 'topic-title-wikitext'.
		if ( $this->isTopicTitle() ) {
			return 'topic-title-wikitext';
		} else {
			return parent::getContentFormat();
		}
	}

	/**
	 * Gets the desired storage format.
	 *
	 * @return string
	 */
	protected function getStorageFormat() {
		if ( $this->isTopicTitle() ) {
			return 'topic-title-wikitext';
		} else {
			return parent::getStorageFormat();
		}
	}

	/**
	 * Gets the appropriate wikitext format string for this revision.
	 *
	 * @return string 'wikitext' or 'topic-title-wikitext'
	 */
	public function getWikitextFormat() {
		if ( $this->isTopicTitle() ) {
			return 'topic-title-wikitext';
		} else {
			return parent::getWikitextFormat();
		}
	}

	/**
	 * Gets the appropriate HTML format string for this revision.
	 *
	 * @return string 'html' or 'topic-title-html'
	 */
	public function getHtmlFormat() {
		if ( $this->isTopicTitle() ) {
			return 'topic-title-html';
		} else {
			return parent::getHtmlFormat();
		}
	}

	/**
	 * @param UUID|null $id
	 */
	public function setReplyToId( UUID $id = null ) {
		$this->replyToId = $id;
	}

	/**
	 * @return UUID|null Id of the parent post, or null if this is the root
	 */
	public function getReplyToId() {
		return $this->replyToId;
	}

	/**
	 * @param PostRevision[] $children
	 */
	public function setChildren( array $children ) {
		$this->children = $children;
		if ( $this->rootPost ) {
			// Propagate root post into children.
			$this->setRootPost( $this->rootPost );
		}
	}

	/**
	 * @return PostRevision[]
	 * @throws DataModelException
	 */
	public function getChildren() {
		if ( $this->children === null ) {
			throw new DataModelException( 'Children not loaded for post: ' . $this->postId->getAlphadecimal(), 'process-data' );
		}
		return $this->children;
	}

	/**
	 * @param integer $depth
	 */
	public function setDepth( $depth ) {
		$this->depth = (int)$depth;
	}

	/**
	 * @return integer
	 * @throws DataModelException
	 */
	public function getDepth() {
		if ( $this->depth === null ) {
			/** @var TreeRepository $treeRepo */
			$treeRepo = Container::get( 'repository.tree' );
			$rootPath = $treeRepo->findRootPath( $this->getCollectionId() );
			$this->setDepth( count( $rootPath ) - 1 );
		}

		return $this->depth;
	}

	/**
	 * @param PostRevision $root
	 * @deprecated Use PostCollection::getRoot instead
	 */
	public function setRootPost( PostRevision $root ) {
		$this->rootPost = $root;
		if ( $this->children ) {
			// Propagate root post into children.
			foreach ( $this->children as $child ) {
				$child->setRootPost( $root );
			}
		}
	}

	/**
	 * @return PostRevision
	 * @throws DataModelException
	 * @deprecated Use PostCollection::getRoot instead
	 */
	public function getRootPost() {
		if ( $this->isTopicTitle() ) {
			return $this;
		} elseif ( $this->rootPost === null ) {
			$collection = $this->getCollection();
			$root = $collection->getRoot();
			return $root->getLastRevision();
		}
		return $this->rootPost;
	}

	/**
	 * Get the amount of posts in this topic.
	 *
	 * @return int
	 */
	public function getChildCount() {
		return count( $this->getChildren() );
	}

	/**
	 * Finds the provided postId within this posts descendants
	 *
	 * @param UUID $postId The id of the post to find.
	 * @return PostRevision|null
	 * @throws FlowException
	 */
	public function getDescendant( UUID $postId ) {
		if ( $this->children === null ) {
			throw new FlowException( 'Attempted to access post descendant, but children haven\'t yet been loaded.' );
		}
		foreach ( $this->children as $child ) {
			if ( $child->getPostId()->equals( $postId ) ) {
				return $child;
			}
			$found = $child->getDescendant( $postId );
			if ( $found !== null ) {
				return $found;
			}
		}

		return null;
	}

	/**
	 * @return string
	 */
	public function getRevisionType() {
		return 'post';
	}

	/**
	 * @param User $user
	 * @return boolean
	 */
	public function isCreator( User $user ) {
		if ( $user->isAnon() ) {
			return false;
		}
		return $user->getId() == $this->getCreatorId();
	}

	/**
	 * @return UUID
	 */
	public function getCollectionId() {
		return $this->getPostId();
	}

	/**
	 * @return PostCollection
	 */
	public function getCollection() {
		return PostCollection::newFromRevision( $this );
	}
}