/**
  * @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;
 }
 public function doQuery()
 {
     $direction = $this->mIsBackwards ? 'rev' : 'fwd';
     // over-fetch so we can figure out if there's anything after what we're showing
     $this->mResult = $this->query->getResults($this->id, $this->getLimit() + 1, $this->mOffset, $direction);
     if (!$this->mResult) {
         throw new InvalidDataException('Unable to load history for ' . $this->id->getAlphadecimal(), 'fail-load-history');
     }
     $this->mQueryDone = true;
     // we over-fetched, now get rid of redundant value for our "real" data
     $overfetched = null;
     if (count($this->mResult) > $this->getLimit()) {
         // when traversing history reverse, the overfetched entry will be at
         // the beginning of the list; in normal mode it'll be last
         if ($this->mIsBackwards) {
             $overfetched = array_shift($this->mResult);
         } else {
             $overfetched = array_pop($this->mResult);
         }
     }
     // set some properties that'll be used to generate navigation bar
     $this->mLastShown = $this->mResult[count($this->mResult) - 1]->revision->getRevisionId()->getAlphadecimal();
     $this->mFirstShown = $this->mResult[0]->revision->getRevisionId()->getAlphadecimal();
     /*
      * By overfetching, we've already figured out if there's additional
      * entries at the next page (according to the current direction). Now
      * go fetch 1 more in the other direction (the one we likely came from,
      * when navigating)
      */
     $nextOffset = $this->mIsBackwards ? $this->mFirstShown : $this->mLastShown;
     $nextOffset = UUID::create($nextOffset);
     $reverseDirection = $this->mIsBackwards ? 'fwd' : 'rev';
     $this->mIsLast = !$overfetched;
     $this->mIsFirst = !$this->mOffset || count($this->query->getResults($this->id, 1, $nextOffset, $reverseDirection)) === 0;
     if ($this->mIsBackwards) {
         // swap values if we're going backwards
         list($this->mIsFirst, $this->mIsLast) = array($this->mIsLast, $this->mIsFirst);
         // id of the overfetched entry, used to build new links starting at
         // this offset
         if ($overfetched) {
             $this->mPastTheEndIndex = $overfetched->revision->getRevisionId()->getAlphadecimal();
         }
     }
 }
 /**
  * Returns the revision with the given id.
  *
  * @param UUID $uuid
  * @return AbstractRevision|null null if there is no such revision
  */
 public function getRevision(UUID $uuid)
 {
     // check if fetching last already res
     if (isset($this->revisions[$uuid->getAlphadecimal()])) {
         return $this->revisions[$uuid->getAlphadecimal()];
     }
     /*
      * The strategy here is to avoid having to call getAllRevisions(), which
      * is most likely to have to load (fresh) data that is not yet in
      * LocalBufferedCache's internal cache.
      * To do so, we'll build the $this->revisions array by hand. Starting at
      * the most recent revision and going up 1 revision at a time, checking
      * if it is already in LocalBufferedCache's cache.
      * If, however, we can't find the requested revisions (or one of the
      * revisions on our way to the requested revision) in the internal cache
      * of LocalBufferedCache, we'll just bail and load all revisions after
      * all: if we do have to fetch data, might as well do it all in 1 go!
      */
     while (!$this->loaded()) {
         // fetch current oldest revision
         $oldest = $this->getOldestLoaded();
         // fetch that one's preceding revision id
         $previousId = $oldest->getPrevRevisionId();
         // check if it's in local storage already
         if ($previousId && $this->getStorage()->got($previousId)) {
             $revision = $this->getStorage()->get($previousId);
             // add this revision to revisions array
             $this->revisions[$previousId->getAlphadecimal()] = $revision;
             // stop iterating if we've found the one we wanted
             if ($uuid->equals($previousId)) {
                 break;
             }
         } else {
             // revision not found in local storage: load all revisions
             $this->getAllRevisions();
             break;
         }
     }
     if (!isset($this->revisions[$uuid->getAlphadecimal()])) {
         return null;
     }
     return $this->revisions[$uuid->getAlphadecimal()];
 }
 public function getResult(UUID $uuid)
 {
     $alpha = $uuid->getAlphadecimal();
     // Minimal set of data needed for the CategoryViewFormatter
     $row = new FormatterRow();
     if (!isset($this->posts[$alpha])) {
         throw new FlowException("A required post has not been loaded: {$alpha}");
     }
     $row->revision = reset($this->posts[$alpha]);
     if (!isset($this->workflows[$alpha])) {
         throw new FlowException("A required workflow has not been loaded: {$alpha}");
     }
     $row->workflow = $this->workflows[$alpha];
     return $row;
 }
 /**
  * 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);
 }
 /**
  * Adds a moderation activity item to the log under the appropriate action
  *
  * @param PostRevision $post
  * @param string $action The action we'll be logging
  * @param string $reason Comment, reason for the moderation
  * @param UUID $workflowId Workflow being worked on
  * @return int The id of the newly inserted log entry
  */
 public function log(PostRevision $post, $action, $reason, UUID $workflowId)
 {
     if (!$this->canLog($post, $action)) {
         return null;
     }
     $params = array('topicId' => $workflowId->getAlphadecimal());
     if (!$post->isTopicTitle()) {
         $params['postId'] = $post->getPostId()->getAlphadecimal();
     }
     $logType = $this->getLogType($post, $action);
     // reasonably likely this is already loaded in-process and just returns that object
     /** @var Workflow $workflow */
     $workflow = Container::get('storage.workflow')->get($workflowId);
     if ($workflow) {
         $title = $workflow->getArticleTitle();
     } else {
         $title = false;
     }
     $error = false;
     if (!$title) {
         // We dont want to fail logging due to this, so repoint it at Main_Page which
         // will probably be noticed, also log it below once we know the logId
         $title = Title::newMainPage();
         $error = true;
     }
     // insert logging entry
     $logEntry = new ManualLogEntry($logType, "flow-{$action}");
     $logEntry->setTarget($title);
     $logEntry->setPerformer($post->getUserTuple()->createUser());
     $logEntry->setParameters($params);
     $logEntry->setComment($reason);
     $logEntry->setTimestamp($post->getModerationTimestamp());
     $logId = $logEntry->insert();
     if ($error) {
         wfDebugLog('Flow', __METHOD__ . ': Could not map workflowId to workflow object for ' . $workflowId->getAlphadecimal() . " log entry {$logId} defaulted to Main_Page");
     }
     return $logId;
 }
 /**
  * Finds the RecentChange object associated with this flow revision.
  *
  * @return null|RecentChange
  */
 public function getRecentChange()
 {
     $timestamp = $this->revId->getTimestamp();
     if (!RecentChange::isInRCLifespan($timestamp)) {
         // Too old to be in RC, don't even bother checking
         return null;
     }
     $workflow = $this->getCollection()->getWorkflow();
     if ($this->changeType === 'new-post') {
         $title = $workflow->getOwnerTitle();
     } else {
         $title = $workflow->getArticleTitle();
     }
     $namespace = $title->getNamespace();
     $conditions = array('rc_title' => $title->getDBkey(), 'rc_timestamp' => $timestamp, 'rc_namespace' => $namespace);
     $options = array('USE INDEX' => 'rc_timestamp');
     $dbr = wfGetDB(DB_SLAVE);
     $rows = $dbr->select('recentchanges', RecentChange::selectFields(), $conditions, __METHOD__, $options);
     if ($rows === false) {
         return null;
     }
     if ($rows->numRows() === 1) {
         return RecentChange::newFromRow($rows->fetchObject());
     }
     // it is possible that more than 1 changes on the same page have the same timestamp
     // the revision id is hidden in rc_params['flow-workflow-change']['revision']
     $revId = $this->revId->getAlphadecimal();
     while ($row = $rows->next()) {
         $rc = RecentChange::newFromRow($row);
         $params = $rc->parseParams();
         if ($params && $params['flow-workflow-change']['revision'] === $revId) {
             return $rc;
         }
     }
     return null;
 }
 public function setContentFormat($format, UUID $revisionId = null)
 {
     if (false === array_search($format, $this->allowedContentFormats)) {
         throw new FlowException("Unknown content format: {$format}");
     }
     if ($revisionId === null) {
         // set default content format
         $this->contentFormat = $format;
     } else {
         // set per-revision content format
         $this->revisionContentFormat[$revisionId->getAlphadecimal()] = $format;
     }
 }
 /**
  * Suppress the specified post within the specified workflow
  *
  * @param Title|null $title
  * @param UUID $workflowId
  * @param UUID $postId
  * @return Anchor
  */
 public function suppressPostAction(Title $title = null, UUID $workflowId, UUID $postId)
 {
     return new Anchor(wfMessage('flow-post-action-suppress-post'), $this->resolveTitle($title, $workflowId), array('action' => 'moderate-post', 'topic_postId' => $postId->getAlphadecimal(), 'topic_moderationState' => AbstractRevision::MODERATED_SUPPRESSED));
 }
 /**
  * Returns the revision with the given id.
  *
  * @param UUID $uuid
  * @return AbstractRevision|null null if there is no such revision
  */
 public function getRevision(UUID $uuid)
 {
     // make sure all revisions have been loaded
     $this->getAllRevisions();
     if (!isset($this->revisions[$uuid->getAlphadecimal()])) {
         return null;
     }
     // find requested id, based on given revision
     return $this->revisions[$uuid->getAlphadecimal()];
 }
 /**
  * Finds the root path for a single post ID.
  * @param  UUID   $descendant Post ID
  * @return UUID[]|null Path to the root of that node.
  */
 public function findRootPath(UUID $descendant)
 {
     $paths = $this->findRootPaths(array($descendant));
     return isset($paths[$descendant->getAlphadecimal()]) ? $paths[$descendant->getAlphadecimal()] : null;
 }
 /**
  * @param UUID|null $other
  * @return boolean
  */
 public function equals(UUID $other = null)
 {
     return $other && $other->getAlphadecimal() === $this->getAlphadecimal();
 }
 /**
  * Returns the storage row for this Reference.
  * For this abstract reference, only partial.
  *
  * @return array
  */
 public function getStorageRow()
 {
     return array('ref_src_workflow_id' => $this->workflowId->getAlphadecimal(), 'ref_src_namespace' => $this->srcTitle->getNamespace(), 'ref_src_title' => $this->srcTitle->getDBkey(), 'ref_src_object_type' => $this->objectType, 'ref_src_object_id' => $this->objectId->getAlphadecimal(), 'ref_type' => $this->type);
 }
 public function setAssociation(UUID $objectId, $importSourceKey)
 {
     $this->data[$importSourceKey] = $objectId->getAlphadecimal();
 }
 /**
  * Gets a Workflow object given its ID
  * @param  UUID   $workflowId The Workflow ID to retrieve.
  * @return Workflow           The Workflow.
  */
 protected function getWorkflowById(UUID $workflowId)
 {
     $alpha = $workflowId->getAlphadecimal();
     if (isset($this->workflowCache[$alpha])) {
         return $this->workflowCache[$alpha];
     } else {
         return $this->workflowCache[$alpha] = $this->storage->get('Workflow', $workflowId);
     }
 }