/**
  * @param Workflow $workflow
  * @param string $objectType
  * @param UUID $objectId
  */
 public function __construct(Workflow $workflow, $objectType, UUID $objectId)
 {
     $this->workflowId = $workflow->getId();
     $this->title = $workflow->getArticleTitle();
     $this->objectType = $objectType;
     $this->objectId = $objectId;
 }
 /**
  * @param Workflow $topicList
  * @param Workflow $topic
  * @return TopicListEntry
  */
 public static function create(Workflow $topicList, Workflow $topic)
 {
     $obj = new self();
     $obj->topicListId = $topicList->getId();
     $obj->topicId = $topic->getId();
     $obj->topicWorkflowLastUpdated = $topic->getLastModified();
     return $obj;
 }
 /**
  * @param Workflow $workflow
  * @param User $user
  * @param string $content
  * @param string $format wikitext|html
  * @param string[optional] $changeType
  * @return Header
  */
 public static function create(Workflow $workflow, User $user, $content, $format, $changeType = 'create-header')
 {
     $obj = new self();
     $obj->revId = UUID::create();
     $obj->workflowId = $workflow->getId();
     $obj->user = UserTuple::newFromUser($user);
     $obj->prevRevision = null;
     // no prior revision
     $obj->setContent($content, $format, $workflow->getArticleTitle());
     $obj->changeType = $changeType;
     return $obj;
 }
 public function doUpdate(Workflow $workflow)
 {
     $title = $workflow->getArticleTitle();
     $page = WikiPage::factory($title);
     $content = $page->getContent();
     if ($content === null) {
         $updates = array();
     } else {
         $updates = $content->getSecondaryDataUpdates($title);
     }
     DataUpdate::runUpdates($updates);
 }
 protected function log(PostRevision $post, Workflow $workflow)
 {
     $moderationChangeTypes = self::getModerationChangeTypes();
     if (!in_array($post->getChangeType(), $moderationChangeTypes)) {
         // Do nothing for non-moderation actions
         return;
     }
     if ($this->moderationLogger->canLog($post, $post->getChangeType())) {
         $workflowId = $workflow->getId();
         $this->moderationLogger->log($post, $post->getChangeType(), $post->getModeratedReason(), $workflowId);
     }
 }
 /**
  * @param Workflow $workflow Topic list workflow
  * @param array $links pagination link data
  *
  * @return array link structure
  */
 protected function buildPaginationLinks(Workflow $workflow, array $links)
 {
     $res = array();
     $title = $workflow->getArticleTitle();
     foreach ($links as $key => $options) {
         // prefix all options with topiclist_
         $realOptions = array();
         foreach ($options as $k => $v) {
             $realOptions["topiclist_{$k}"] = $v;
         }
         $res[$key] = new Anchor($key, $title, $realOptions);
     }
     return $res;
 }
 /**
  * @param Title $pageTitle
  * @param UUID|null $workflowId
  * @return WorkflowLoader
  * @throws InvalidInputException
  * @throws CrossWikiException
  */
 public function createWorkflowLoader(Title $pageTitle, $workflowId = null)
 {
     if ($pageTitle === null) {
         throw new InvalidInputException('Invalid article requested', 'invalid-title');
     }
     if ($pageTitle && $pageTitle->isExternal()) {
         throw new CrossWikiException('Interwiki to ' . $pageTitle->getInterwiki() . ' not implemented ', 'default');
     }
     // @todo: ideally, workflowId is always set and this stuff is done in the places that call this
     if ($workflowId === null) {
         if ($pageTitle->getNamespace() === NS_TOPIC) {
             // topic page: workflow UUID is page title
             $workflowId = self::uuidFromTitle($pageTitle);
         } else {
             // board page: workflow UUID is inside content model
             $page = \WikiPage::factory($pageTitle);
             $content = $page->getContent();
             if ($content instanceof BoardContent) {
                 $workflowId = $content->getWorkflowId();
             }
         }
     }
     if ($workflowId === null) {
         // no existing workflow found, create new one
         $workflow = Workflow::create($this->defaultWorkflowName, $pageTitle);
     } else {
         $workflow = $this->loadWorkflowById($pageTitle, $workflowId);
     }
     return new WorkflowLoader($workflow, $this->blockFactory->createBlocks($workflow), $this->submissionHandler);
 }
 public function testSortByOption()
 {
     $user = User::newFromId(1);
     $user->setOption('flow-topiclist-sortby', '');
     // reset flow state, so everything ($container['permissions'])
     // uses this particular $user
     \FlowHooks::resetFlowExtension();
     Container::reset();
     $container = Container::getContainer();
     $container['user'] = $user;
     $ctx = $this->getMock('IContextSource');
     $ctx->expects($this->any())->method('getUser')->will($this->returnValue($user));
     $workflow = Workflow::create('discussion', Title::newFromText('Talk:Flow_QA'));
     $block = new TopicListBlock($workflow, Container::get('storage'));
     $block->init($ctx, 'view');
     $res = $block->renderApi(array());
     $this->assertEquals('newest', $res['sortby'], 'With no sortby defaults to newest');
     $res = $block->renderApi(array('sortby' => 'foo'));
     $this->assertEquals('newest', $res['sortby'], 'With invalid sortby defaults to newest');
     $res = $block->renderApi(array('sortby' => 'updated'));
     $this->assertEquals('updated', $res['sortby'], 'With sortby updated output changes to updated');
     $res = $block->renderApi(array());
     $this->assertEquals('newest', $res['sortby'], 'Sort still defaults to newest');
     $res = $block->renderApi(array('sortby' => 'updated', 'savesortby' => '1'));
     $this->assertEquals('updated', $res['sortby'], 'Request saving sortby option');
     $res = $block->renderApi(array());
     $this->assertEquals('updated', $res['sortby'], 'Default sortby now changed to updated');
     $res = $block->renderApi(array('sortby' => ''));
     $this->assertEquals('updated', $res['sortby'], 'Default sortby with blank sortby still uses user default');
 }
 public function commit()
 {
     if ($this->action !== 'new-topic') {
         throw new FailCommitException('Unknown commit action', 'fail-commit');
     }
     $metadata = array('workflow' => $this->topicWorkflow, 'board-workflow' => $this->workflow, 'topic-title' => $this->topicTitle, 'first-post' => $this->firstPost);
     /*
      * Order of storage is important! We've been changing when we stored
      * workflow a couple of times. For now, it needs to be stored first:
      * * OccupationListener.php (workflow listener) must first create the
      *   board before NotificationListener.php (topic/post listeners)
      *   creates notifications (& mails) that link to the board
      * * ReferenceExtractor.php (run from ReferenceRecorder.php, a post
      *   listener) needs to parse content with Parsoid & for that it needs
      *   the board title. AbstractRevision::getContent() will figure out
      *   the title from the workflow: $this->getCollection()->getTitle()
      * If you even feel the need to change the order, make sure you come
      * up with a fix for the above things ;)
      */
     $this->storage->put($this->workflow, array());
     // 'discussion' workflow
     $this->storage->put($this->topicWorkflow, $metadata);
     // 'topic' workflow
     $this->storage->put($this->topicListEntry, $metadata);
     $this->storage->put($this->topicTitle, $metadata);
     if ($this->firstPost !== null) {
         $this->storage->put($this->firstPost, $metadata + array('reply-to' => $this->topicTitle));
     }
     $output = array('topic-page' => $this->topicWorkflow->getArticleTitle()->getPrefixedText(), 'topic-id' => $this->topicTitle->getPostId(), 'topic-revision-id' => $this->topicTitle->getRevisionId(), 'post-id' => $this->firstPost ? $this->firstPost->getPostId() : null, 'post-revision-id' => $this->firstPost ? $this->firstPost->getRevisionId() : null);
     return $output;
 }
 /**
  * This is a horrible test, it basically runs the whole thing
  * and sees if it falls over.
  */
 public function testImportDoesntCompletelyFail()
 {
     $workflow = Workflow::create('discussion', Title::newFromText('TalkpageImportOperationTest'));
     $storage = $this->getMockBuilder('Flow\\Data\\ManagerGroup')->disableOriginalConstructor()->getMock();
     $stored = array();
     $storage->expects($this->any())->method('put')->will($this->returnCallback(function ($obj) use(&$stored) {
         $stored[] = $obj;
     }));
     $storage->expects($this->any())->method('multiPut')->will($this->returnCallback(function ($objs) use(&$stored) {
         $stored = array_merge($stored, $objs);
     }));
     $now = time();
     $source = new MockImportSource(new MockImportHeader(array(new MockImportRevision(array('createdTimestamp' => $now)))), array(new MockImportTopic(new MockImportSummary(array(new MockImportRevision(array('createdTimestamp' => $now - 250)))), array(new MockImportRevision(array('createdTimestamp' => $now - 1000))), array(new MockImportPost(array(new MockImportRevision(array('createdTimestmap' => $now - 1000))), array(new MockImportPost(array(new MockImportRevision(array('createdTimestmap' => $now - 500, 'user' => User::newFromNAme('10.0.0.2', false)))), array())))))));
     $op = new TalkpageImportOperation($source, Container::get('occupation_controller'));
     $store = new NullImportSourceStore();
     $op->import(new PageImportState($workflow, $storage, $store, new NullLogger(), $this->getMockBuilder('Flow\\Data\\BufferedCache')->disableOriginalConstructor()->getMock(), Container::get('db.factory'), new ProcessorGroup(), new SplQueue()));
     // Count what actually came through
     $storedHeader = $storedDiscussion = $storedTopics = $storedTopicListEntry = $storedSummary = $storedPosts = 0;
     foreach ($stored as $obj) {
         if ($obj instanceof Workflow) {
             if ($obj->getType() === 'discussion') {
                 $this->assertSame($workflow, $obj);
                 $storedDiscussion++;
             } else {
                 $alpha = $obj->getId()->getAlphadecimal();
                 if (!isset($seenWorkflow[$alpha])) {
                     $seenWorkflow[$alpha] = true;
                     $this->assertEquals('topic', $obj->getType());
                     $storedTopics++;
                     $topicWorkflow = $obj;
                 }
             }
         } elseif ($obj instanceof PostSummary) {
             $storedSummary++;
         } elseif ($obj instanceof PostRevision) {
             $storedPosts++;
             if ($obj->isTopicTitle()) {
                 $topicTitle = $obj;
             }
         } elseif ($obj instanceof TopicListEntry) {
             $storedTopicListEntry++;
         } elseif ($obj instanceof Header) {
             $storedHeader++;
         } else {
             $this->fail('Unexpected object stored:' . get_class($obj));
         }
     }
     // Verify we wrote the expected objects to storage
     $this->assertEquals(1, $storedHeader);
     $this->assertEquals(1, $storedDiscussion);
     $this->assertEquals(1, $storedTopics);
     $this->assertEquals(1, $storedTopicListEntry);
     $this->assertEquals(1, $storedSummary);
     $this->assertEquals(3, $storedPosts);
     // This total expected number of insertions should match the sum of the left assertEquals parameters above.
     $this->assertCount(8, array_unique(array_map('spl_object_hash', $stored)));
     // Other special cases we need to check
     $this->assertTrue($topicTitle->getPostId()->equals($topicWorkflow->getId()), 'Root post id must match its workflow');
 }
 /**
  * @param \OutputPage $out
  */
 public function setPageTitle(\OutputPage $out)
 {
     if ($out->getPageTitle()) {
         // Don't override page title if another block has already set it.
         // If this should *really* be done, the specific block extending
         // this AbstractBlock should just implement this itself ;)
         return;
     }
     $out->setPageTitle($this->workflow->getArticleTitle()->getFullText());
 }
 /**
  * Many loaded references typically point to the same Title, cache those instead
  * of generating a bunch of duplicate title classes.
  */
 public static function makeTitle($namespace, $title)
 {
     try {
         return Workflow::getFromTitleCache(wfWikiId(), $namespace, $title);
     } catch (InvalidInputException $e) {
         // duplicate Title::makeTitleSafe which returns null on failure,
         // but only for InvalidInputException
         return null;
     }
 }
 protected function processParam($event, $param, $message, $user)
 {
     $extra = $event->getExtra();
     if ($param === 'subject') {
         if (isset($extra['topic-title']) && $extra['topic-title']) {
             $this->processParamEscaped($message, trim($extra['topic-title']));
         } else {
             $message->params('');
         }
     } elseif ($param === 'commentText') {
         if (isset($extra['content']) && $extra['content']) {
             // @todo assumes content is html, make explicit
             $message->params(Utils::htmlToPlaintext($extra['content'], 200));
         } else {
             $message->params('');
         }
     } elseif ($param === 'post-permalink') {
         $anchor = $this->getPostLinkAnchor($event, $user);
         if ($anchor) {
             $message->params($anchor->getFullUrl());
         } else {
             $message->params('');
         }
     } elseif ($param === 'topic-permalink') {
         // link to individual new-topic
         if (isset($extra['topic-workflow'])) {
             $title = Workflow::getFromTitleCache(wfWikiId(), NS_TOPIC, $extra['topic-workflow']->getAlphadecimal());
         } else {
             $title = $event->getTitle();
         }
         $anchor = $this->getUrlGenerator()->workflowLink($title, $extra['topic-workflow']);
         $anchor->query['fromnotif'] = 1;
         $message->params($anchor->getFullUrl());
     } elseif ($param === 'new-topics-permalink') {
         // link to board sorted by newest topics
         $anchor = $this->getUrlGenerator()->boardLink($event->getTitle(), 'newest');
         $anchor->query['fromnotif'] = 1;
         $message->params($anchor->getFullUrl());
     } elseif ($param == 'flow-title') {
         $title = $event->getTitle();
         if ($title) {
             $formatted = $this->formatTitle($title);
         } else {
             $formatted = $this->getMessage('echo-no-title')->text();
         }
         $message->params($formatted);
     } elseif ($param == 'old-subject') {
         $this->processParamEscaped($message, trim($extra['old-subject']));
     } elseif ($param == 'new-subject') {
         $this->processParamEscaped($message, trim($extra['new-subject']));
     } else {
         parent::processParam($event, $param, $message, $user);
     }
 }
 /**
  * @dataProvider spamProvider
  */
 public function testSpam($message, $expect, $content, $maxLength)
 {
     $title = Title::newFromText('UTPage');
     $user = User::newFromName('127.0.0.1', false);
     $workflow = Workflow::create('topic', $title);
     $topic = PostRevision::create($workflow, $user, 'title content', 'wikitext');
     $reply = $topic->reply($workflow, $user, $content, 'wikitext');
     $spamFilter = new ContentLengthFilter($maxLength);
     $status = $spamFilter->validate($this->getMock('IContextSource'), $reply, null, $title);
     $this->assertEquals($expect, $status->isOK());
 }
 /**
  * There was a bug where all anonymous users got the same
  * user links output, this checks that they are distinct.
  */
 public function testNonRepeatingUserLinksForAnonymousUsers()
 {
     $templating = $this->mockTemplating();
     $user = User::newFromName('127.0.0.1', false);
     $title = Title::newMainPage();
     $workflow = Workflow::create('topic', $title);
     $topicTitle = PostRevision::create($workflow, $user, 'some content', 'wikitext');
     $hidden = $topicTitle->moderate($user, $topicTitle::MODERATED_HIDDEN, 'hide-topic', 'hide and go seek');
     $this->assertContains('Special:Contributions/127.0.0.1', $templating->getUserLinks($hidden), 'User links should include anonymous contributions');
     $hidden = $topicTitle->moderate(User::newFromName('10.0.0.2', false), $topicTitle::MODERATED_HIDDEN, 'hide-topic', 'hide and go seek');
     $this->assertContains('Special:Contributions/10.0.0.2', $templating->getUserLinks($hidden), 'An alternate user should have the correct anonymous contributions');
 }
 /**
  * Load the header and topics from the requested discussion.  Does not return
  * anything, the goal here is to populate $this->hashBag.
  *
  * @param Workflow $workflow
  */
 protected function fetchDiscussion(Workflow $workflow)
 {
     $results = array();
     $pagers = array();
     /** @var ManagerGroup $storage */
     $storage = Container::get('storage');
     // 'newest' sort order
     $pagers[] = new Pager($storage->getStorage('TopicListEntry'), array('topic_list_id' => $workflow->getId()), array('pager-limit' => 499));
     // 'updated' sort order
     $pagers[] = new Pager($storage->getStorage('TopicListEntry'), array('topic_list_id' => $workflow->getId()), array('pager-limit' => 499, 'sort' => 'workflow_last_update_timestamp', 'order' => 'desc'));
     // Based on Header::init.
     $storage->find('Header', array('rev_type_id' => $workflow->getId()), array('sort' => 'rev_id', 'order' => 'DESC', 'limit' => 1));
     foreach ($pagers as $pager) {
         foreach ($pager->getPage()->getResults() as $entry) {
             // use array key to de-duplicate
             $results[$entry->getId()->getAlphadecimal()] = $entry->getId();
         }
     }
     $this->fetchTopics($results);
     // purge the board history
     $storage->find('BoardHistoryEntry', array('topic_list_id' => $workflow->getId()), array('sort' => 'rev_id', 'order' => 'DESC', 'limit' => 499));
 }
 public function testSetsRevIdAndPostIdForReplys()
 {
     $state = $this->createState();
     $user = User::newFromName('127.0.0.1', false);
     $title = Title::newMainPage();
     $topicWorkflow = Workflow::create('topic', $title);
     $topicTitle = PostRevision::create($topicWorkflow, $user, 'sing song', 'wikitext');
     $reply = $topicTitle->reply($topicWorkflow, $user, 'fantastic!', 'wikitext');
     $now = time();
     $state->setRevisionTimestamp($reply, $now - 54321);
     $this->assertEquals($now - 54321, $reply->getRevisionId()->getTimestampObj()->getTimestamp(TS_UNIX), 'The first reply revision must have its revision id set appropriatly');
     $this->assertTrue($reply->getPostId()->equals($reply->getRevisionId()), 'The first revision of a reply shares its postId and revId');
 }
 public function testContentLength()
 {
     $content = 'This is a topic title';
     $nextContent = 'Changed my mind';
     $title = Title::newMainPage();
     $user = User::newFromName('127.0.0.1', false);
     $workflow = Workflow::create('topic', $title);
     $topic = PostRevision::create($workflow, $user, $content, 'wikitext');
     $this->assertEquals(0, $topic->getPreviousContentLength());
     $this->assertEquals(mb_strlen($content), $topic->getContentLength());
     $next = $topic->newNextRevision($user, $nextContent, 'wikitext', 'edit-title', $title);
     $this->assertEquals(mb_strlen($content), $next->getPreviousContentLength());
     $this->assertEquals(mb_strlen($nextContent), $next->getContentLength());
 }
 /**
  * @param Workflow $workflow
  * @return AbstractBlock[]
  * @throws InvalidInputException When the workflow type is unrecognized
  * @throws InvalidDataException When multiple blocks share the same name
  */
 public function createBlocks(Workflow $workflow)
 {
     switch ($workflow->getType()) {
         case 'discussion':
             $blocks = array(new HeaderBlock($workflow, $this->storage), new TopicListBlock($workflow, $this->storage), new BoardHistoryBlock($workflow, $this->storage));
             break;
         case 'topic':
             $blocks = array(new TopicBlock($workflow, $this->storage, $this->rootPostLoader), new TopicSummaryBlock($workflow, $this->storage));
             break;
         default:
             throw new InvalidInputException('Not Implemented', 'invalid-definition');
             break;
     }
     $return = array();
     /** @var AbstractBlock[] $blocks */
     foreach ($blocks as $block) {
         if (isset($return[$block->getName()])) {
             throw new InvalidDataException('Multiple blocks with same name is not yet supported', 'fail-load-data');
         }
         $return[$block->getName()] = $block;
     }
     return $return;
 }
 public function testValidateDoesntBlowUp()
 {
     $filter = new ConfirmEdit();
     if (!$filter->enabled()) {
         $this->markTestSkipped('ConfirmEdit is not enabled');
     }
     $user = User::newFromName('127.0.0.1', false);
     $title = Title::newMainPage();
     $workflow = Workflow::create('topic', $title);
     $oldRevision = PostRevision::create($workflow, $user, 'foo', 'wikitext');
     $newRevision = $oldRevision->newNextRevision($user, 'bar', 'wikitext', 'change-type', $title);
     $context = $this->getMock('IContextSource');
     $context->expects($this->any())->method('getUser')->will($this->returnValue($user));
     $status = $filter->validate($context, $newRevision, $oldRevision, $title);
     $this->assertInstanceOf('Status', $status);
     $this->assertTrue($status->isGood());
 }
 /**
  * @dataProvider somethingProvider
  */
 public function testSomething($message, $expect, $init)
 {
     $actions = Container::get('flow_actions');
     $usernames = $this->getMockBuilder('Flow\\Repository\\UserNameBatch')->disableOriginalConstructor()->getMock();
     $rcFactory = $this->getMockBuilder('Flow\\Data\\Utils\\RecentChangeFactory')->disableOriginalConstructor()->getMock();
     $ircFormatter = $this->getMockBuilder('Flow\\Formatter\\IRCLineUrlFormatter')->disableOriginalConstructor()->getMock();
     $rc = new RecentChangesListener($actions, $usernames, $rcFactory, $ircFormatter);
     $change = $this->getMock('RecentChange');
     $rcFactory->expects($this->once())->method('newFromRow')->will($this->returnCallback(function ($obj) use(&$ref, $change) {
         $ref = $obj;
         return $change;
     }));
     $title = Title::newMainPage();
     $user = User::newFromName('127.0.0.1', false);
     $workflow = Workflow::create('topic', $title);
     $revision = $init($workflow, $user);
     $rc->onAfterInsert($revision, array('rev_user_id' => 0, 'rev_user_ip' => '127.0.0.1'), array('workflow' => $workflow));
     $this->assertNotNull($ref);
     $this->assertEquals($expect, $ref->rc_namespace, $message);
 }
 protected function buildApiActions(Workflow $workflow)
 {
     return array('newtopic' => array('url' => $this->urlGenerator->newTopicAction($workflow->getArticleTitle(), $workflow->getId())));
 }
 protected function getBlandTestObjects()
 {
     return array($this->workflow, $this->revision, $this->workflow->getArticleTitle());
 }
 /**
  * @param Workflow $workflow
  * @param string $action
  * @return \Title
  */
 public function getRcTitle(Workflow $workflow, $action)
 {
     if ($this->actions->getValue($action, 'rc_title') === 'owner') {
         return $workflow->getOwnerTitle();
     } else {
         return $workflow->getArticleTitle();
     }
 }
 /**
  * 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();
 }
 /**
  * @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;
 }
 /**
  * When a page is taken over by Flow, add a revision.
  *
  * First, it provides a clearer history should Flow be disabled again later,
  * and a descriptive message when people attempt to use regular API to fetch
  * data for this "Page", which will no longer contain any useful content,
  * since Flow has taken over.
  *
  * Also: Parsoid performs an API call to fetch page information, so we need
  * to make sure a page actually exists ;)
  *
  * This method does not do any security checks regarding content model changes
  * or the like.  Those happen much earlier in the request and should be checked
  * before even attempting to create revisions which, when written to the database,
  * trigger this method through the OccupationListener.
  *
  * @param \Article $article
  * @param Workflow $workflow
  * @return Status Status for revision creation; On success (including if it already
  *  had a top-most Flow revision), it will return a good status with an associative
  *  array value.  $status->getValue()['revision'] will be a Revision
  *  $status->getValue()['already-existed'] will be set to true if no revision needed
  *  to be created
  * @throws InvalidInputException
  */
 public function ensureFlowRevision(Article $article, Workflow $workflow)
 {
     // Comment to add to the Revision to indicate Flow taking over
     $comment = '/* Taken over by Flow */';
     $page = $article->getPage();
     $revision = $page->getRevision();
     if ($revision !== null) {
         $content = $revision->getContent();
         if ($content instanceof BoardContent && $content->getWorkflowId()) {
             // Revision is already a valid BoardContent
             return Status::newGood(array('revision' => $revision, 'already-existed' => true));
         }
     }
     $status = $page->doEditContent(new BoardContent(CONTENT_MODEL_FLOW_BOARD, $workflow->isNew() ? null : $workflow->getId()), $comment, EDIT_FORCE_BOT | EDIT_SUPPRESS_RC, false, $this->getTalkpageManager());
     $value = $status->getValue();
     $value['already-existed'] = false;
     $status->setResult($status->isOK(), $value);
     return $status;
 }
 /**
  * @param Workflow $obj
  * @return array
  * @throws FailCommitException
  */
 public static 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
         $title = $obj->getOwnerTitle();
         $obj->pageId = $title->getArticleID(Title::GAID_FOR_UPDATE);
         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, 'workflow_last_update_timestamp' => $obj->lastModified, 'workflow_name' => '');
 }
 protected function redirect(Workflow $workflow)
 {
     $link = $this->urlGenerator->workflowLink($workflow->getArticleTitle(), $workflow->getId());
     $this->getOutput()->redirect($link->getFullURL());
 }
 /**
  * Populate a fake workflow in the unittest database
  *
  * @param array $row
  * @return Workflow
  */
 protected function generateWorkflow($row = array())
 {
     $row = $row + array('workflow_id' => UUID::create()->getBinary(), 'workflow_type' => 'topic', 'workflow_wiki' => wfWikiId(), 'workflow_page_id' => 1, 'workflow_namespace' => NS_USER_TALK, 'workflow_title_text' => 'Test', 'workflow_lock_state' => 0, 'workflow_last_update_timestamp' => wfTimestampNow());
     $workflow = Workflow::fromStorageRow($row);
     // store workflow:
     // * so we can retrieve it should we want to store it (see store())
     // * so we can remove it on tearDown
     $this->workflows[$workflow->getId()->getAlphadecimal()] = $workflow;
     return $workflow;
 }