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

namespace Flow;

use Flow\Collection\CollectionCache;
use Flow\Collection\PostCollection;
use Flow\Exception\DataModelException;
use Flow\Model\AbstractRevision;
use Flow\Model\PostRevision;
use Closure;
use Flow\Model\PostSummary;
use Flow\Model\Workflow;
use User;

/**
 * Role based security for revisions based on moderation state
 */
class RevisionActionPermissions {
	/**
	 * @var FlowActions
	 */
	protected $actions = array();

	/**
	 * @var User
	 */
	protected $user;

	/**
	 * @param FlowActions $actions
	 * @param User $user
	 */
	public function __construct( FlowActions $actions, User $user ) {
		$this->user = $user;
		$this->actions = $actions;
	}

	/**
	 * Get the name of all the actions the user is allowed to perform.
	 *
	 * @param AbstractRevision|null $revision The revision to check permissions against
	 * @return array Array of action names that are allowed
	 */
	public function getAllowedActions( AbstractRevision $revision = null ) {
		$allowed = array();
		foreach( array_keys( $this->actions->getActions() ) as $action ) {
			if ( $this->isAllowedAny( $revision, $action ) ) {
				$allowed[] = $action;
			}
		}
		return $allowed;
	}

	/**
	 * Check if a user is allowed to perform a certain action.
	 *
	 * @param AbstractRevision $revision
	 * @param string $action
	 * @return bool
	 */
	public function isAllowed( AbstractRevision $revision, $action ) {
		// check if we're allowed to $action on this revision
		if ( !$this->isRevisionAllowed( $revision, $action ) ) {
			return false;
		}

		/** @var AbstractRevision[] $roots */
		static $roots = array();
		/** @var Workflow[] $workflows */
		static $workflows = array();

		$revisionId = $revision->getRevisionId()->getAlphadecimal();

		if ( !isset( $roots[$revisionId] ) ) {
			$roots[$revisionId] = $this->getRoot( $revision );
		}
		// see if we're allowed to perform $action on anything inside this root
		if ( !$revision->getRevisionId()->equals( $roots[$revisionId]->getRevisionId() ) && !$this->isRootAllowed( $roots[$revisionId], $action ) ) {
			return false;
		}

		if ( !isset( $workflows[$revisionId] ) ) {
			$collection = $revision->getCollection();
			$workflows[$revisionId] = $collection->getBoardWorkflow();
		}
		// see if we're allowed to perform $action on anything inside this board
		if ( !$this->isBoardAllowed( $workflows[$revisionId], $action ) ) {
			return false;
		}

		/** @var CollectionCache $cache */
		$cache = Container::get( 'collection.cache' );
		$last = $cache->getLastRevisionFor( $revision );
		// Also check if the user would be allowed to perform this
		// against the most recent revision - the last revision is the
		// current state of an object, so checking against a revision at
		// one point in time alone isn't enough.
		$isLastRevision = $last->getRevisionId()->equals( $revision->getRevisionId() );
		if ( !$isLastRevision && !$this->isRevisionAllowed( $last, $action ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Check if a user is allowed to perform certain actions.
	 *
	 * @param AbstractRevision|null $revision
	 * @param string $action Multiple parameters to check if either of the provided actions are allowed
	 * @return bool
	 */
	public function isAllowedAny( AbstractRevision $revision = null, $action /* [, $action2 [, ... ]] */ ) {
		$actions = func_get_args();
		// Pull $revision out of the actions list
		array_shift( $actions );

		foreach ( $actions as $action ) {
			if ( $this->isAllowed( $revision, $action ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Check if a user is allowed to perform a certain action, against the latest
	 * root(topic) post related to the provided revision.  This is required for
	 * things like preventing replies to locked topics.
	 *
	 * @param AbstractRevision $root
	 * @param string $action
	 * @return bool
	 */
	public function isRootAllowed( AbstractRevision $root, $action ) {
		// If the `root-permissions` key is not set then it is allowed
		if ( !$this->actions->hasValue( $action, 'root-permissions' ) ) {
			return true;
		}

		$permission = $this->getPermission( $root, $action, 'root-permissions' );

		// If `root-permissions` is defined but not for the current state
		// then action is denied
		if ( $permission === null ) {
			return false;
		}

		return call_user_func_array(
			array( $this->user, 'isAllowedAny' ),
			(array) $permission
		);
	}

	/**
	 * Check if a user is allowed to perform a certain action, depending on the
	 * status (deleted?) of the board.
	 *
	 * @param Workflow $workflow
	 * @param string $action
	 * @return bool
	 */
	public function isBoardAllowed( Workflow $workflow, $action ) {
		$permissions = $this->actions->getValue( $action, 'core-delete-permissions' );

		// If user is allowed to see deleted page content, there's no need to
		// even check if it's been deleted (additional storage lookup)
		$allowed = call_user_func_array(
			array( $this->user, 'isAllowedAny' ),
			(array) $permissions
		);
		if ( $allowed ) {
			return true;
		}

		return !$workflow->isDeleted();
	}

	/**
	 * Check if a user is allowed to perform a certain action, only against 1
	 * specific revision (whereas the default isAllowed() will check if the
	 * given $action is allowed for both given and the most current revision)
	 *
	 * @param AbstractRevision|null $revision
	 * @param string $action
	 * @return bool
	 */
	public function isRevisionAllowed( AbstractRevision $revision = null, $action ) {
		// Users must have the core 'edit' permission to perform any write action in flow
		$performsWrites = $this->actions->getValue( $action, 'performs-writes' );
		if ( $performsWrites && !$this->user->isAllowed( 'edit' ) ) {
			return false;
		}

		$permission = $this->getPermission( $revision, $action );

		// If no permission is defined for this state, then the action is not allowed
		// check if permission is set for this action
		if ( $permission === null ) {
			return false;
		}

		// Check if user is allowed to perform action against this revision
		return call_user_func_array(
			array( $this->user, 'isAllowedAny' ),
			(array) $permission
		);
	}

	/**
	 * Returns the permission specified in FlowActions for the given action
	 * against the given revision's moderation state.
	 *
	 * @param AbstractRevision|null $revision
	 * @param string $action
	 * @param string $type
	 * @return Closure|string
	 */
	public function getPermission( AbstractRevision $revision = null, $action, $type = 'permissions' ) {
		// $revision may be null if the revision has yet to be created
		$moderationState = AbstractRevision::MODERATED_NONE;
		if ( $revision !== null ) {
			$moderationState = $revision->getModerationState();
		}
		$permission = $this->actions->getValue( $action, $type, $moderationState );

		// Some permissions may be more complex to be defined as simple array
		// values, in which case they're a Closure (which will accept
		// AbstractRevision & FlowActionPermissions as arguments)
		if ( $permission instanceof Closure ) {
			$permission = $permission( $revision, $this );
		}

		return $permission;
	}

	/**
	 * @param AbstractRevision $revision
	 * @return AbstractRevision
	 */
	protected function getRoot( AbstractRevision $revision ) {
		if ( $revision instanceof PostSummary ) {
			$topicId = $revision->getSummaryTargetId();
		} elseif ( $revision instanceof PostRevision && !$revision->isTopicTitle() ) {
			try {
				$topicId = $revision->getCollection()->getWorkflowId();
			} catch ( DataModelException $e ) {
				// failed to locate root post (most likely in unit tests, where
				// we didn't store the tree)
				return $revision;
			}
		} else {
			// if we can't the revision it back to a root, this revision is root
			return $revision;
		}

		$collection = PostCollection::newFromId( $topicId );
		return $collection->getLastRevision();
	}

	/**
	 * @return User
	 */
	public function getUser() {
		return $this->user;
	}

	/**
	 * @return FlowActions
	 */
	public function getActions() {
		return $this->actions;
	}

	/**
	 * @param $user
	 */
	public function setUser( User $user ) {
		$this->user = $user;
	}
}