/**
  * @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');
 }
 /**
  * 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');
 }
 /**
  * @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');
 }
 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());
 }
 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);
 }
 /**
  * Creates the objects about to be inserted into storage:
  * * $this->topicWorkflow
  * * $this->topicListEntry
  * * $this->topicTitle
  * * $this->firstPost
  *
  * @throws \MWException
  * @throws \Flow\Exception\FailCommitException
  * @return array Array of [$topicWorkflow, $topicListEntry, $topicTitle, $firstPost]
  */
 protected function create()
 {
     $title = $this->workflow->getArticleTitle();
     $user = $this->context->getUser();
     $topicWorkflow = Workflow::create('topic', $title);
     $topicListEntry = TopicListEntry::create($this->workflow, $topicWorkflow);
     $topicTitle = PostRevision::create($topicWorkflow, $user, $this->submitted['topic'], 'wikitext');
     $firstPost = null;
     if (!empty($this->submitted['content'])) {
         $firstPost = $topicTitle->reply($topicWorkflow, $user, $this->submitted['content'], isset($this->submitted['format']) ? $this->submitted['format'] : 'wikitext');
         $topicTitle->setChildren(array($firstPost));
     }
     return array($topicWorkflow, $topicListEntry, $topicTitle, $firstPost);
 }
 public static function onIRCLineURLProvider()
 {
     // data providers do not run in the same context as the actual test, as such we
     // can't create Title objects because they can have the wrong wikiID.  Instead we
     // pass closures into the test that create the objects within the correct context.
     $newHeader = function (User $user) {
         $title = Title::newFromText('Talk:Hook_test');
         $workflow = Container::get('factory.loader.workflow')->createWorkflowLoader($title)->getWorkflow();
         $header = Header::create($workflow, $user, 'header content', 'wikitext');
         $metadata = array('workflow' => $workflow, 'revision' => $header);
         /** @var OccupationController $occupationController */
         $occupationController = Container::get('occupation_controller');
         // make sure user has rights to create board
         $user->mRights = array_merge($user->getRights(), array('flow-create-board'));
         $occupationController->allowCreation($title, $user);
         $occupationController->ensureFlowRevision(new \Article($title), $workflow);
         Container::get('storage')->put($workflow, $metadata);
         return $metadata;
     };
     $freshTopic = function (User $user) {
         $title = Title::newFromText('Talk:Hook_test');
         $boardWorkflow = Container::get('factory.loader.workflow')->createWorkflowLoader($title)->getWorkflow();
         $topicWorkflow = Workflow::create('topic', $boardWorkflow->getArticleTitle());
         $topicList = TopicListEntry::create($boardWorkflow, $topicWorkflow);
         $topicTitle = PostRevision::create($topicWorkflow, $user, 'some content', 'wikitext');
         $metadata = array('workflow' => $topicWorkflow, 'board-workflow' => $boardWorkflow, 'topic-title' => $topicTitle, 'revision' => $topicTitle);
         /** @var OccupationController $occupationController */
         $occupationController = Container::get('occupation_controller');
         // make sure user has rights to create board
         $user->mRights = array_merge($user->getRights(), array('flow-create-board'));
         $occupationController->allowCreation($title, $user);
         $occupationController->ensureFlowRevision(new \Article($title), $boardWorkflow);
         $storage = Container::get('storage');
         $storage->put($boardWorkflow, $metadata);
         $storage->put($topicWorkflow, $metadata);
         $storage->put($topicList, $metadata);
         $storage->put($topicTitle, $metadata);
         return $metadata;
     };
     $replyToTopic = function (User $user) use($freshTopic) {
         $metadata = $freshTopic($user);
         $firstPost = $metadata['topic-title']->reply($metadata['workflow'], $user, 'ffuts dna ylper', 'wikitext');
         $metadata = array('first-post' => $firstPost, 'revision' => $firstPost) + $metadata;
         Container::get('storage.post')->put($firstPost, $metadata);
         return $metadata;
     };
     return array(array('Freshly created topic', $freshTopic, array('action' => 'history')), array('Reply to topic', $replyToTopic, array('action' => 'history')), array('Edit topic title', function ($user) use($freshTopic) {
         $metadata = $freshTopic($user);
         $title = $metadata['workflow']->getArticleTitle();
         return array('revision' => $metadata['revision']->newNextRevision($user, 'gnihtemos gnihtemos', 'wikitext', 'edit-title', $title)) + $metadata;
     }, array('action' => 'compare-post-revisions')), array('Edit post', function ($user) use($replyToTopic) {
         $metadata = $replyToTopic($user);
         $title = $metadata['workflow']->getArticleTitle();
         return array('revision' => $metadata['revision']->newNextRevision($user, 'IT\'S CAPS LOCKS DAY!', 'wikitext', 'edit-post', $title)) + $metadata;
     }, array('action' => 'compare-post-revisions')), array('Edit board header', function ($user) use($newHeader) {
         $metadata = $newHeader($user);
         $title = $metadata['workflow']->getArticleTitle();
         return array('revision' => $metadata['revision']->newNextRevision($user, 'STILL CAPS LOCKS DAY!', 'wikitext', 'edit-header', $title)) + $metadata;
     }, array('action' => 'compare-header-revisions')), array('Moderate a post', function ($user) use($replyToTopic) {
         $metadata = $replyToTopic($user);
         return array('revision' => $metadata['revision']->moderate($user, $metadata['revision']::MODERATED_DELETED, 'delete-post', 'something about cruise control')) + $metadata;
     }, array('action' => 'history')), array('Moderate a topic', function ($user) use($freshTopic) {
         $metadata = $freshTopic($user);
         return array('revision' => $metadata['revision']->moderate($user, $metadata['revision']::MODERATED_HIDDEN, 'hide-topic', 'adorable kittens')) + $metadata;
     }, array('action' => 'history')));
 }
 /**
  * @param PageImportState $state
  * @param IImportTopic    $importTopic
  * @return TopicImportState
  */
 protected function createTopicState(PageImportState $state, IImportTopic $importTopic)
 {
     $state->logger->info('Importing new topic');
     $topicWorkflow = Workflow::create('topic', $state->boardWorkflow->getArticleTitle());
     $state->setWorkflowTimestamp($topicWorkflow, $this->getFirstRevision($importTopic)->getTimestamp());
     $topicListEntry = TopicListEntry::create($state->boardWorkflow, $topicWorkflow);
     $titleRevisions = $this->importObjectWithHistory($importTopic, function (IObjectRevision $rev) use($state, $topicWorkflow) {
         return PostRevision::create($topicWorkflow, $state->createUser($rev->getAuthor()), $rev->getText(), 'wikitext');
     }, 'edit-title', $state, $topicWorkflow->getArticleTitle());
     $topicState = new TopicImportState($state, $topicWorkflow, end($titleRevisions));
     $topicMetadata = $topicState->getMetadata();
     // This should all match the order in TopicListBlock->commit (board/
     // discussion workflow is inserted before this method is called).
     $state->put($topicWorkflow, $topicMetadata);
     // TLE must be before topic title, otherwise you get an error importing the Topic Title
     // Flow/includes/Data/Index/BoardHistoryIndex.php:
     // No topic list contains topic XXX, called for revision YYY
     $state->put($topicListEntry, $topicMetadata);
     $state->put($titleRevisions, $topicMetadata);
     $state->recordAssociation($topicWorkflow->getId(), $importTopic);
     $state->logger->info('Finished importing topic title with ' . count($titleRevisions) . ' revisions');
     return $topicState;
 }