| Current File : /home/jvzmxxx/wiki1/extensions/Flow/includes/Model/Workflow.php |
<?php
namespace Flow\Model;
use Flow\Exception\CrossWikiException;
use Flow\Exception\DataModelException;
use Flow\Exception\FailCommitException;
use Flow\Exception\InvalidInputException;
use MapCacheLRU;
use MWTimestamp;
use Title;
use User;
class Workflow {
/**
* @var MapCacheLRU
*/
private static $titleCache;
/**
* @var string[]
*/
static private $allowedTypes = array( 'discussion', 'topic' );
/**
* @var UUID
*/
protected $id;
/**
* @var string e.g. topic, discussion, etc.
*/
protected $type;
/**
* @var string
*/
protected $wiki;
/**
* @var integer
*/
protected $pageId = 0;
/**
* @var integer
*/
protected $namespace;
/**
* @var string
*/
protected $titleText;
/**
* @var string
*/
protected $lastUpdated;
/**
* @var Title
*/
protected $title;
/**
* @var Title
*/
protected $ownerTitle;
/**
* @var bool|null Indicates if associated page_id exists (null if not yet looked up)
*/
protected $exists;
/**
* @param array $row
* @param Workflow|null $obj
* @return Workflow
* @throws DataModelException
*/
static public function fromStorageRow( array $row, $obj = null ) {
if ( $obj === null ) {
$obj = new self;
} elseif ( !$obj instanceof self ) {
throw new DataModelException( 'Wrong obj type: ' . get_class( $obj ), 'process-data' );
}
$obj->id = UUID::create( $row['workflow_id'] );
$obj->type = $row['workflow_type'];
$obj->wiki = $row['workflow_wiki'];
$obj->pageId = (int) $row['workflow_page_id'];
$obj->namespace = (int) $row['workflow_namespace'];
$obj->titleText = $row['workflow_title_text'];
$obj->lastUpdated = $row['workflow_last_update_timestamp'];
return $obj;
}
/**
* @param Workflow $obj
* @return array
* @throws FailCommitException
*/
static public function toStorageRow( Workflow $obj ) {
if ( $obj->pageId === 0 ) {
/*
* We try to defer creating a new page as long as possible, which
* means that a new board page won't have been created by the time
* Workflow object was created: new workflows will have a 0 pageId.
* This method is called when the workflow is about to be inserted.
* By now, the page has been inserted & we should store the real
* page_id this workflow is associated with.
*/
// store ID of newly created page & reset exists status
$title = $obj->getOwnerTitle();
$obj->pageId = $title->getArticleID( Title::GAID_FOR_UPDATE );
$obj->exists = null;
if ( $obj->pageId === 0 ) {
throw new FailCommitException( 'No page for workflow: ' . serialize( $obj ) );
}
}
return array(
'workflow_id' => $obj->id->getAlphadecimal(),
'workflow_type' => $obj->type,
'workflow_wiki' => $obj->wiki,
'workflow_page_id' => $obj->pageId,
'workflow_namespace' => $obj->namespace,
'workflow_title_text' => $obj->titleText,
'workflow_lock_state' => 0, // unused
'workflow_last_update_timestamp' => $obj->lastUpdated,
// not used, but set it to empty string so it doesn't fail in strict mode
'workflow_name' => '',
);
}
/**
* @param string $type
* @param Title $title
* @return Workflow
* @throws DataModelException
*/
static public function create( $type, Title $title ) {
// temporary limitation until we implement something more concrete
if ( !in_array( $type, self::$allowedTypes ) ) {
throw new DataModelException( 'Invalid workflow type provided: ' . $type, 'process-data' );
}
if ( $title->isLocal() ) {
$wiki = wfWikiID();
} else {
$wiki = $title->getTransWikiID();
}
$obj = new self;
$obj->id = UUID::create();
$obj->type = $type;
$obj->wiki = $wiki;
// for new pages, article id will be 0; it'll be fetched again in toStorageRow
$obj->pageId = $title->getArticleID();
$obj->namespace = $title->getNamespace();
$obj->titleText = $title->getDBkey();
$obj->updateLastUpdated( $obj->id );
// we just created a new workflow; wipe out any cached data for the
// associated title
if ( self::$titleCache !== null ) {
$key = implode( '|', array( $obj->wiki, $obj->namespace, $obj->titleText ) );
self::$titleCache->clear( array( $key ) );
}
return $obj;
}
/**
* Update the workflow after a change to title or ID (such as page move or
* restoration).
*
* @param int $oldPageId The page_id the workflow is currently located at
* @param Title $newPage The page the workflow is moving to
* @throws DataModelException
*/
public function updateFromPageId( $oldPageId, Title $newPage ) {
if ( $oldPageId !== $this->pageId ) {
throw new DataModelException( 'Must update from same page id. ' . $this->pageId . ' !== ' . $oldPageId );
}
$this->pageId = $newPage->getArticleID();
$this->namespace = $newPage->getNamespace();
$this->titleText = $newPage->getDBkey();
}
/**
* Return the title this workflow responds at
*
* @return Title
* @throws CrossWikiException
*/
public function getArticleTitle() {
if ( $this->title ) {
return $this->title;
}
// evil hax
if ( $this->type === 'topic' ) {
$namespace = NS_TOPIC;
$titleText = $this->id->getAlphadecimal();
} else {
$namespace = $this->namespace;
$titleText = $this->titleText;
}
return $this->title = self::getFromTitleCache( $this->wiki, $namespace, $titleText );
}
/**
* Return the title this workflow was created at
*
* @return Title
* @throws CrossWikiException
*/
public function getOwnerTitle() {
if ( $this->ownerTitle ) {
return $this->ownerTitle;
}
return $this->ownerTitle = self::getFromTitleCache( $this->wiki, $this->namespace, $this->titleText );
}
/**
* Can't use the title cache in Title class, it only operates on default namespace
*
* @param string $wiki
* @param int $namespace
* @param string $titleText
* @return Title
* @throws CrossWikiException
* @throws InvalidInputException
*/
public static function getFromTitleCache( $wiki, $namespace, $titleText ) {
if ( self::$titleCache === null ) {
self::$titleCache = new MapCacheLRU( 50 );
}
$key = implode( '|', array( $wiki, $namespace, $titleText ) );
$title = self::$titleCache->get( $key );
if ( $title === null ) {
$title = Title::makeTitleSafe( $namespace, $titleText );
if ( $title ) {
self::$titleCache->set( $key, $title );
} else {
throw new InvalidInputException( "Fail to create title from namespace $namespace and title text '$titleText'", 'invalid-input' );
}
}
return $title;
}
/**
* @return UUID
*/
public function getId() { return $this->id; }
/**
* @return string
*/
public function getType() { return $this->type; }
/**
* Get the wiki ID, e.g. eswiki
*
* @return string
*/
public function getWiki() { return $this->wiki; }
/**
* @return bool
*/
public function isDeleted() {
if ( $this->exists === null ) {
$this->exists = Title::newFromID( $this->pageId ) !== null;
}
// a board that does not yet exist (because workflow has not yet
// been stored) is not deleted, it just doesn't exist yet
return !$this->isNew() && !$this->exists;
}
/**
* Returns true if the workflow is new as of this request.
*
* @return boolean
*/
public function isNew() {
return $this->pageId === 0;
}
/**
* @return string
*/
public function getLastUpdated() { return $this->lastUpdated; }
/**
* @return \MWTimestamp
*/
public function getLastUpdatedObj() { return new MWTimestamp( $this->lastUpdated ); }
public function updateLastUpdated( UUID $latestRevisionId ) {
$this->lastUpdated = $latestRevisionId->getTimestamp();
}
/**
* @return string
*/
public function getNamespaceName() {
global $wgContLang;
return $wgContLang->getNsText( $this->namespace );
}
/**
* @return string
*/
public function getTitleFullText() {
$ns = $this->getNamespaceName();
if ( $ns ) {
return $ns . ':' . $this->titleText;
} else {
return $this->titleText;
}
}
/**
* these are exceptions currently to make debugging easier
* it should return false later on to allow wider use.
*
* @param Title $title
* @return boolean
* @throws InvalidInputException
* @throws InvalidInputException
*/
public function matchesTitle( Title $title ) {
return $this->getArticleTitle()->equals( $title );
}
/**
* Convenience wrapper for checking user permissions as boolean.
* getPermissionErrors 'quick' + blocked check only for logged in users
*
* @param string $permission Permission to check; for 'edit', 'create' will also be
* checked if the title does not exist
* @return bool Whether the user can take the action, based on a quick check
*/
public function userCan( $permission, $user ) {
return !count( $this->getPermissionErrors( $permission, $user, 'quick' ) ) &&
// We only check the blocked status of actual users and not anons, because
// the anonymous version can be cached and served to many different IP
// addresses which will not all be blocked.
// See T61928
!( $user->isLoggedIn() && $user->isBlockedFrom( $this->getOwnerTitle(), true ) );
}
/**
* Pass-through to Title::getUserPermissionsErrors
* with title, and owning title if needed.
*
* @param string $permission Permission to check; for 'edit', 'create' will also be
* checked if the title does not exist
* @param User $user User to check permissions for
* @param string $rigor Rigor of check; see Title->getUserPermissionsErrors
* @return array Array of arrays of the arguments to wfMessage to explain permissions problems.
*/
public function getPermissionErrors( $permission, $user, $rigor ) {
$title = $this->type === 'topic' ? $this->getOwnerTitle() : $this->getArticleTitle();
$editErrors = $title->getUserPermissionsErrors( $permission, $user, $rigor );
$errors = $editErrors;
$titleExistsFlags = ( $rigor === 'secure' ) ? Title::GAID_FOR_UPDATE : 0;
if ( $permission === 'edit' && !$title->exists( $titleExistsFlags ) ) {
// If it's 'edit', but the title doesn't exist, check 'create' as
// well.
$editErrorKeys = array_map( function ( $val ) {
return reset( $val );
}, $editErrors );
// Pass in the edit errors to avoid duplicates
$createErrors = $title->getUserPermissionsErrors( 'create', $user, $rigor, $editErrorKeys );
$errors = array_merge( $errors, $createErrors );
}
if ( count( $errors ) ) {
return $errors;
}
return array();
}
}