/**
  * 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;
     }
     // see if we're allowed to perform $action on anything inside this root
     $root = $this->getRoot($revision);
     if (!$revision->getRevisionId()->equals($root->getRevisionId()) && !$this->isRootAllowed($root, $action)) {
         return false;
     }
     // see if we're allowed to perform $action on anything inside this board
     $collection = $revision->getCollection();
     $workflow = $collection->getBoardWorkflow();
     if (!$this->isBoardAllowed($workflow, $action)) {
         return false;
     }
     // 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.
     /** @var CollectionCache $cache */
     $cache = Container::get('collection.cache');
     $last = $cache->getLastRevisionFor($revision);
     $isLastRevision = $last->getRevisionId()->equals($revision->getRevisionId());
     if (!$isLastRevision && !$this->isRevisionAllowed($last, $action)) {
         return false;
     }
     return true;
 }
 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 renderApi(array $options)
 {
     global $wgRequest;
     if ($this->workflow->isNew()) {
         return array('type' => $this->getName(), 'revisions' => array(), 'links' => array());
     }
     /** @var BoardHistoryQuery $query */
     $query = Container::get('query.board-history');
     /** @var RevisionFormatter $formatter */
     $formatter = Container::get('formatter.revision');
     $formatter->setIncludeHistoryProperties(true);
     list($limit, ) = $wgRequest->getLimitOffset();
     // don't use offset from getLimitOffset - that assumes an int, which our
     // UUIDs are not
     $offset = $wgRequest->getText('offset');
     $offset = $offset ? UUID::create($offset) : null;
     $pager = new HistoryPager($query, $this->workflow->getId());
     $pager->setLimit($limit);
     $pager->setOffset($offset);
     $pager->doQuery();
     $history = $pager->getResult();
     $revisions = array();
     foreach ($history as $row) {
         $serialized = $formatter->formatApi($row, $this->context, 'history');
         if ($serialized) {
             $revisions[$serialized['revisionId']] = $serialized;
         }
     }
     return array('type' => $this->getName(), 'revisions' => $revisions, 'navbar' => $pager->getNavigationBar(), 'links' => array());
 }
 protected function processPosts($recorder)
 {
     $storage = Container::get('storage.post');
     $count = $this->mBatchSize;
     $id = '';
     $dbr = Container::get('db.factory')->getDB(DB_SLAVE);
     while ($count === $this->mBatchSize) {
         $count = 0;
         $res = $dbr->select(array('flow_tree_revision'), array('tree_rev_id'), array('tree_parent_id IS NOT NULL', 'tree_rev_id > ' . $dbr->addQuotes($id)), __METHOD__, array('ORDER BY' => 'tree_rev_id ASC', 'LIMIT' => $this->mBatchSize));
         if (!$res) {
             throw new \MWException('SQL error in maintenance script ' . __METHOD__);
         }
         foreach ($res as $row) {
             $count++;
             $id = $row->tree_rev_id;
             $uuid = UUID::create($id);
             $alpha = $uuid->getAlphadecimal();
             $post = $storage->get($uuid);
             if ($post) {
                 echo "Processing post {$alpha}\n";
                 $recorder->onAfterInsert($post, array(), array('workflow' => $post->getCollection()->getWorkflow()));
             }
         }
     }
 }
 protected function doDBUpdates()
 {
     $dbf = Container::get('db.factory');
     $dbw = $dbf->getDB(DB_MASTER);
     $hasRun = false;
     $runUpdate = function ($callback) use($dbf, $dbw, &$hasRun) {
         $hasRun = true;
         $continue = "";
         do {
             $continue = call_user_func($callback, $dbw, $continue);
             $dbf->waitForSlaves();
         } while ($continue !== null);
     };
     // run updates only if we have the required source data
     if ($dbw->fieldExists('flow_workflow', 'workflow_user_text')) {
         $runUpdate(array($this, 'updateWorkflow'));
     }
     if ($dbw->fieldExists('flow_tree_revision', 'tree_orig_user_text')) {
         $runUpdate(array($this, 'updateTreeRevision'));
     }
     if ($dbw->fieldExists('flow_revision', 'rev_user_text') && $dbw->fieldExists('flow_revision', 'rev_mod_user_text') && $dbw->fieldExists('flow_revision', 'rev_edit_user_text')) {
         $runUpdate(array($this, 'updateRevision'));
     }
     if ($hasRun) {
         $dbw->sourceFile(__DIR__ . '/../db_patches/patch-remove_usernames_2.sql');
     }
     return true;
 }
 public function updateTreeRevision(DatabaseBase $dbw, $continue = null)
 {
     $rows = $dbw->select('flow_tree_revision', array('tree_rev_id'), array('tree_rev_id > ' . $dbw->addQuotes($continue), 'tree_orig_user_ip IS NOT NULL', 'tree_orig_user_id > 0'), __METHOD__, array('LIMIT' => $this->mBatchSize, 'ORDER BY' => 'tree_rev_id'));
     $om = Container::get('storage')->getStorage('PostRevision');
     $objs = $ids = array();
     foreach ($rows as $row) {
         $id = UUID::create($row->tree_rev_id);
         $found = $om->get($id);
         if ($found) {
             $ids[] = $row->tree_rev_id;
             $objs[] = $found;
         } else {
             $this->error(__METHOD__ . ': Failed loading Flow\\Model\\PostRevision: ' . $id->getAlphadecimal());
         }
     }
     if (!$ids) {
         return null;
     }
     $dbw->update('flow_tree_revision', array('tree_orig_user_ip' => null), array('tree_rev_id' => $ids), __METHOD__);
     foreach ($objs as $obj) {
         $om->cachePurge($obj);
     }
     $this->completeCount += count($ids);
     return end($ids);
 }
 public function testSomething()
 {
     // meaningless set of ids used for repeatability
     $ids = array_map(array('Flow\\Model\\UUID', 'create'), array("s3z44zhp93j5vvc8", "s3z44zhqt7yt8220", "s46w00pmmw0otc0q", "s3qvc7cnor86wvb4", "s3qvc7bbcxr3f340", "s3gre9r27pobtg0n", "s3cdl3dfqf8brx18", "s3cdl3dhajnz43r0"));
     // Use 2 repos with 2 caches, the one you insert with reads from cache
     // the other reads from db due to different cache
     $cache[] = new BufferedCache(new BufferedBagOStuff(new \HashBagOStuff()), 600);
     $cache[] = new BufferedCache(new BufferedBagOStuff(new \HashBagOStuff()), 600);
     $dbf = Container::get('db.factory');
     $repo[] = new TreeRepository($dbf, $cache[0]);
     $repo[] = new TreeRepository($dbf, $cache[1]);
     // id0 as new root
     wfDebugLog('Flow', "\n\n************** id0 as new root ************");
     $repo[0]->insert($ids[0]);
     $this->assertEquals(array($ids[0]), $repo[0]->findRootPath($ids[0]));
     $this->assertEquals(array($ids[0]), $repo[1]->findRootPath($ids[0]));
     // id1 as child of id0
     wfDebugLog('Flow', "\n\n************** id1 as child of id0 ************");
     $repo[0]->insert($ids[1], $ids[0]);
     $this->assertEquals(array($ids[0], $ids[1]), $repo[0]->findRootPath($ids[1]));
     $this->assertEquals(array($ids[0], $ids[1]), $repo[1]->findRootPath($ids[1]));
     // id2 as child of id0
     wfDebugLog('Flow', "\n\n************** id2 as child of id0 ************");
     $repo[0]->insert($ids[2], $ids[0]);
     $this->assertEquals(array($ids[0], $ids[2]), $repo[0]->findRootPath($ids[2]));
     $this->assertEquals(array($ids[0], $ids[2]), $repo[1]->findRootPath($ids[2]));
     // id3 as child of id1
     wfDebugLog('Flow', "\n\n************** id3 as child of id1 ************");
     $repo[0]->insert($ids[3], $ids[1]);
     $this->assertEquals(array($ids[0], $ids[1], $ids[3]), $repo[0]->findRootPath($ids[3]));
     $this->assertEquals(array($ids[0], $ids[1], $ids[3]), $repo[1]->findRootPath($ids[3]));
 }
 /**
  * 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');
 }
 protected function renderTemplate($templateName, array $args = array())
 {
     $lc = Container::get('lightncandy');
     $filenames = $lc->getTemplateFilenames($templateName);
     $phpCode = $lc::compile(file_get_contents($filenames['template']), Container::get('lightncandy.template_dir'));
     $renderer = LightnCandy::prepare($phpCode);
     return new Crawler($renderer($args));
 }
 /**
  * @fixme this looks slow, likely a better way
  */
 protected function serializeRcRevision(RecentChange $rc, IContextSource $ctx)
 {
     /** @var RecentChangesQuery $query */
     $query = Container::get('query.recentchanges');
     $query->loadMetadataBatch(array((object) $rc->mAttribs));
     $rcRow = $query->getResult(null, $rc);
     $this->serializer->setIncludeHistoryProperties(true);
     return $this->serializer->formatApi($rcRow, $ctx, 'recentchanges');
 }
 /**
  * In the future we can add more Flow related info here
  * @param Title $title
  * @return array
  */
 protected function getPageInfo(Title $title)
 {
     /** @var \Flow\TalkpageManager $manager */
     $manager = Container::get('occupation_controller');
     $result = array('flow' => array());
     if ($manager->isTalkpageOccupied($title)) {
         $result['flow']['enabled'] = '';
     }
     return $result;
 }
 /**
  * @return UUID
  * @throws \Flow\Exception\DataModelException
  */
 public function getWorkflowId()
 {
     // the root post (topic title) has the same id as the workflow
     if (!$this->rootId) {
         /** @var \Flow\Repository\TreeRepository $treeRepo */
         $treeRepo = Container::get('repository.tree');
         $this->rootId = $treeRepo->findRoot($this->getId());
     }
     return $this->rootId;
 }
 public function execute()
 {
     global $wgFlowSearchMaintenanceTimeout;
     // Set the timeout for maintenance actions
     Connection::getSingleton()->setTimeout2($wgFlowSearchMaintenanceTimeout);
     /** @var Updater[] $updaters */
     $updaters = Container::get('searchindex.updaters');
     foreach ($updaters as $updaterType => $updater) {
         $fromId = $this->getOption('fromId', null);
         $fromId = $fromId ? UUID::create($fromId) : null;
         $toId = $this->getOption('toId', null);
         $toId = $toId ? UUID::create($toId) : null;
         $namespace = $this->getOption('namespace', null);
         $numRevisionsToIndex = $this->getOption('limit', null);
         $total = 0;
         while (true) {
             // if a limit was provided, we should make sure to not fetch
             // more revisions than asked for
             $options = array('LIMIT' => $this->mBatchSize);
             if ($numRevisionsToIndex) {
                 $options['LIMIT'] = min($numRevisionsToIndex, $this->mBatchSize);
                 // since we do this in batches, we'll subtract the size of
                 // each batch until $numRevisionsToIndex is reached
                 $numRevisionsToIndex -= $this->mBatchSize;
                 if ($options['LIMIT'] <= 0) {
                     break;
                 }
             }
             $conditions = $updater->buildQueryConditions($fromId, $toId, $namespace);
             $revisions = $updater->getRevisions($conditions, $options);
             // stop if we're all out of revisions
             if (!$revisions) {
                 break;
             }
             $total += $updater->updateRevisions($revisions, null, null);
             $this->output("Indexed {$total} {$updaterType} document(s)\n");
             // prepare for next batch, starting at the next id
             // prevFromId will default to around unix epoch - there can be
             // no data before that
             $prevFromId = $fromId ?: UUID::getComparisonUUID('1');
             $fromId = $this->getNextFromId($revisions);
             // make sure we don't get stuck in an infinite loop
             $diff = $prevFromId->getTimestampObj()->diff($fromId->getTimestampObj());
             // invert will be 1 if the diff is a negative time period from
             // $prevFromId to $fromId, which means that the new $timestamp is
             // more recent than our current $result
             if ($diff->invert) {
                 $this->error('Got stuck in an infinite loop.' . "\n" . 'workflow_last_update_timestamp is likely incorrect ' . 'for some workflows.' . "\n" . 'Run maintenance/FlowFixWorkflowLastUpdateTimestamp.php ' . 'to automatically fix those.', 1);
             }
             // prevent memory from being filled up
             Container::get('storage')->clear();
         }
     }
 }
 /**
  * Ensures Flow is reset before passing control on
  * to parent::doApiRequest. Defaults all requests to
  * the sysop user if not specified.
  */
 protected function doApiRequest(array $params, array $session = null, $appendModule = false, User $user = null)
 {
     if ($user === null) {
         $user = self::$users['sysop']->user;
     }
     // reset flow state before each request
     FlowHooks::resetFlowExtension();
     Container::reset();
     $container = Container::getContainer();
     $container['user'] = $user;
     return parent::doApiRequest($params, $session, $appendModule, $user);
 }
 /**
  * Returns list of rev_change_type values that warrant an editcount increase.
  *
  * @return array
  */
 protected function getCountableActions()
 {
     $allowedActions = array();
     /** @var FlowActions $actions */
     $actions = \Flow\Container::get('flow_actions');
     foreach ($actions->getActions() as $action) {
         if ($actions->getValue($action, 'editcount')) {
             $allowedActions[] = $action;
         }
     }
     return $allowedActions;
 }
 public function execute()
 {
     global $wgFlowCluster;
     $dbFactory = Container::get('db.factory');
     $storage = Container::get('storage');
     $rootPostLoader = Container::get('loader.root_post');
     $iterator = new EchoBatchRowIterator($dbFactory->getDB(DB_SLAVE), 'flow_workflow', 'workflow_id', $this->mBatchSize);
     $iterator->setFetchColumns(array('workflow_id', 'workflow_type', 'workflow_last_update_timestamp'));
     $iterator->addConditions(array('workflow_wiki' => wfWikiId()));
     $updater = new EchoBatchRowUpdate($iterator, new UpdateWorkflowLastUpdateTimestampWriter($storage, $wgFlowCluster), new UpdateWorkflowLastUpdateTimestampGenerator($storage, $rootPostLoader));
     $updater->setOutput(array($this, 'output'));
     $updater->execute();
 }
 /**
  * {@inheritDoc}
  */
 public function getRevisions(array $conditions = array(), array $options = array())
 {
     $dbr = $this->dbFactory->getDB(DB_SLAVE);
     // get the current (=most recent, =max) revision id for all headers
     $rows = $dbr->select(array('flow_revision', 'flow_workflow'), array('rev_id' => 'MAX(rev_id)'), $conditions, __METHOD__, array('ORDER BY' => 'rev_id ASC', 'GROUP BY' => 'rev_type_id') + $options, array('flow_workflow' => array('INNER JOIN', array('workflow_id = rev_type_id', 'rev_type' => 'header'))));
     $uuids = array();
     foreach ($rows as $row) {
         $uuids[] = UUID::create($row->rev_id);
     }
     /** @var ManagerGroup $storage */
     $storage = Container::get('storage');
     return $storage->getStorage('Header')->getMulti($uuids);
 }
 /**
  * @dataProvider objectManagerKeyProvider
  */
 public function testSomething($key)
 {
     $c = Container::getContainer();
     $this->assertNotNull($c[$key]);
     foreach ($c["{$key}.indexes"] as $pos => $index) {
         $this->assertInstanceOf('Flow\\Data\\Index', $index, "At {$key}.indexes[{$pos}]");
     }
     if (isset($c["{$key}.listeners"])) {
         foreach ($c["{$key}.listeners"] as $pos => $listener) {
             $this->assertInstanceOf("Flow\\Data\\LifecycleHandler", $listener, "At {$key}.listeners[{$pos}]");
         }
     }
 }
 /**
  * Formats an activity log entry.
  *
  * @return string The log entry
  */
 protected function getActionMessage()
 {
     global $wgContLang;
     /*
      * At this point, all log entries will already have been created & we've
      * gathered all uuids in constructor: we can now batch-load all of them.
      * We won't directly be using that batch-loaded data (nothing will even
      * be returned) but it'll ensure that everything we need will be
      * retrieved from cache/storage efficiently & waiting in memory for when
      * we request it again.
      */
     static $loaded = false;
     if (!$loaded) {
         /** @var ManagerGroup storage */
         $storage = Container::get('storage');
         /** @var TreeRepository $treeRepository */
         $treeRepository = Container::get('repository.tree');
         $query = new LogQuery($storage, $treeRepository);
         $query->loadMetadataBatch(static::$uuids);
         $loaded = true;
     }
     $root = $this->getRoot();
     if (!$root) {
         // failed to load required data
         return '';
     }
     $type = $this->entry->getType();
     $action = $this->entry->getSubtype();
     $title = $this->entry->getTarget();
     $skin = $this->plaintext ? null : $this->context->getSkin();
     $params = $this->entry->getParameters();
     // @todo: we should probably check if user isAllowed( <this-revision>, 'log' )
     // unlike RC, Contributions, ... this one does not batch-load all Flow
     // revisions & does not use the same Formatter, i18n message text, etc
     if (isset($params['postId'])) {
         /** @var UrlGenerator $urlGenerator */
         $urlGenerator = Container::get('url_generator');
         // generate link that highlights the post
         $anchor = $urlGenerator->postLink($title, UUID::create($params['topicId']), UUID::create($params['postId']));
         $title = $anchor->resolveTitle();
     }
     // Give grep a chance to find the usages:
     // logentry-delete-flow-delete-post, logentry-delete-flow-restore-post,
     // logentry-suppress-flow-restore-post, logentry-suppress-flow-suppress-post,
     // logentry-delete-flow-delete-topic, logentry-delete-flow-restore-topic,
     // logentry-suppress-flow-restore-topic, logentry-suppress-flow-suppress-topic,
     $language = $skin === null ? $wgContLang : $skin->getLanguage();
     $message = wfMessage("logentry-{$type}-{$action}")->params(array(Message::rawParam($this->getPerformerElement()), $this->entry->getPerformer()->getName(), $title, $title->getFullUrl(), $root->getLastRevision()->getContent(), $root->getWorkflow()->getOwnerTitle()))->inLanguage($language)->parse();
     return \Html::rawElement('span', array('class' => 'plainlinks'), $message);
 }
 protected function setUp()
 {
     parent::setUp();
     // We don't want local config getting in the way of testing whether or
     // not our permissions implementation works well.
     // This will load default $wgGroupPermissions + Flow settings, so we can
     // test if permissions work well, regardless of any custom config.
     global $IP, $wgFlowGroupPermissions;
     $wgGroupPermissions = array();
     require "{$IP}/includes/DefaultSettings.php";
     $wgGroupPermissions = array_merge_recursive($wgGroupPermissions, $wgFlowGroupPermissions);
     $this->setMwGlobals('wgGroupPermissions', $wgGroupPermissions);
     // load actions object
     $this->actions = Container::get('flow_actions');
 }
 protected function createFormatter($class)
 {
     $permissions = $this->getMockBuilder('Flow\\RevisionActionPermissions')->disableOriginalConstructor()->getMock();
     $permissions->expects($this->any())->method('isAllowed')->will($this->returnValue(true));
     $permissions->expects($this->any())->method('getActions')->will($this->returnValue(Container::get('flow_actions')));
     $templating = $this->getMockBuilder('Flow\\Templating')->disableOriginalConstructor()->getMock();
     $occupier = $this->getMockBuilder('Flow\\OccupationController')->disableOriginalConstructor()->getMock();
     $workflowMapper = $this->getMockBuilder('Flow\\Data\\Mapper\\CachingObjectMapper')->disableOriginalConstructor()->getMock();
     $urlGenerator = new UrlGenerator($workflowMapper, $occupier);
     $templating->expects($this->any())->method('getUrlGenerator')->will($this->returnValue($urlGenerator));
     $usernames = $this->getMockBuilder('Flow\\Repository\\UserNameBatch')->disableOriginalConstructor()->getMock();
     global $wgFlowMaxThreadingDepth;
     $serializer = new RevisionFormatter($permissions, $templating, $usernames, $wgFlowMaxThreadingDepth);
     return new $class($permissions, $serializer);
 }
 /**
  * Creates a flow board.
  * Archives any pre-existing wikitext talk page.
  *
  * @param array $data Form data
  * @return Status Status indicating result
  */
 public function onSubmit(array $data)
 {
     $page = $data['page'];
     $title = Title::newFromText($page);
     if (!$title) {
         return Status::newFatal('flow-special-enableflow-invalid-title', $page);
     }
     // Canonicalize so the error or confirmation message looks nicer (no underscores).
     $page = $title->getPrefixedText();
     if ($this->occupationController->isTalkpageOccupied($title, true)) {
         return Status::newFatal('flow-special-enableflow-board-already-exists', $page);
     }
     $status = Status::newGood();
     if ($title->exists(Title::GAID_FOR_UPDATE)) {
         if (class_exists('LqtDispatch') && \LqtDispatch::isLqtPage($title)) {
             return Status::newFatal('flow-special-enableflow-page-is-liquidthreads', $page);
         }
         $logger = Container::get('default_logger');
         $converter = new Converter(wfGetDB(DB_MASTER), Container::get('importer'), $logger, $this->occupationController->getTalkpageManager(), new EnableFlowWikitextConversionStrategy(Container::get('parser'), new NullImportSourceStore(), $logger, array(), $data['header']));
         try {
             $converter->convert($title);
         } catch (\Exception $e) {
             \MWExceptionHandler::logException($e);
             $status->fatal('flow-error-external', $e->getMessage());
         }
     } else {
         $allowCreationStatus = $this->occupationController->allowCreation($title, $this->getUser(), false);
         if (!$allowCreationStatus->isGood()) {
             return Status::newFatal('flow-special-enableflow-board-creation-not-allowed', $page);
         }
         $loader = $this->loaderFactory->createWorkflowLoader($title);
         $blocks = $loader->getBlocks();
         $action = 'edit-header';
         $params = array('header' => array('content' => $data['header'], 'format' => 'wikitext'));
         $blocksToCommit = $loader->handleSubmit($this->getContext(), $action, $params);
         foreach ($blocks as $block) {
             if ($block->hasErrors()) {
                 $errors = $block->getErrors();
                 foreach ($errors as $errorKey) {
                     $status->fatal($block->getErrorMessage($errorKey));
                 }
             }
         }
         $loader->commit($blocksToCommit);
     }
     $this->page = $data['page'];
     return $status;
 }
 /**
  * @param string $key
  * @param array $ids
  * @param callable $loadCallback
  * @return array
  * @throws InvalidInputException
  */
 public function get($key, array $ids, $loadCallback)
 {
     $key = implode(':', (array) $key);
     $cacheKeys = array();
     foreach ($ids as $id) {
         if ($id instanceof UUID) {
             $cacheId = $id->getAlphadecimal();
         } elseif (!is_scalar($id)) {
             $type = is_object($id) ? get_class($id) : gettype($id);
             throw new InvalidInputException('Not scalar:' . $type, 'invalid-input');
         } else {
             $cacheId = $id;
         }
         $cacheKeys[wfForeignMemcKey('flow', '', $key, $cacheId, Container::get('cache.version'))] = $id;
     }
     return $this->getByKey($cacheKeys, $loadCallback);
 }
 protected function doDBUpdates()
 {
     $container = Container::getContainer();
     $dbFactory = $container['db.factory'];
     $dbw = $dbFactory->getDb(DB_MASTER);
     $storage = $container['storage'];
     $moderationLoggingListener = $container['storage.post.listeners.moderation_logging'];
     $rowIterator = new EchoBatchRowIterator($dbw, 'flow_revision', 'rev_id', $this->mBatchSize);
     $rowIterator->setFetchColumns(array('rev_id', 'rev_type'));
     // Fetch rows that are a moderation action
     $rowIterator->addConditions(array('rev_change_type' => ModerationLoggingListener::getModerationChangeTypes(), 'rev_user_wiki' => wfWikiID()));
     $start = $this->getOption('start');
     $startId = UUID::create($start);
     $rowIterator->addConditions(array('rev_id > ' . $dbw->addQuotes($startId->getBinary())));
     $stop = $this->getOption('stop');
     $stopId = UUID::create($stop);
     $rowIterator->addConditions(array('rev_id < ' . $dbw->addQuotes($stopId->getBinary())));
     $total = $fail = 0;
     foreach ($rowIterator as $batch) {
         $dbw->begin();
         foreach ($batch as $row) {
             $total++;
             $objectManager = $storage->getStorage($row->rev_type);
             $revId = UUID::create($row->rev_id);
             $obj = $objectManager->get($revId);
             if (!$obj) {
                 $this->error('Could not load revision: ' . $revId->getAlphadecimal());
                 $fail++;
                 continue;
             }
             $workflow = $obj->getCollection()->getWorkflow();
             $moderationLoggingListener->onAfterInsert($obj, array(), array('workflow' => $workflow));
         }
         $dbw->commit();
         $storage->clear();
         $dbFactory->waitForSlaves();
     }
     $this->output("Processed a total of {$total} moderation revisions.\n");
     if ($fail !== 0) {
         $this->error("Errors were encountered while processing {$fail} of them.\n");
     }
     return true;
 }
 /**
  * @param array $params
  * @return Title
  */
 protected function getPage($params)
 {
     $page = Title::newFromText($params['page']);
     if (!$page) {
         $this->dieUsage('Invalid page provided', 'invalid-page');
     }
     /** @var \Flow\TalkpageManager $controller */
     $controller = Container::get('occupation_controller');
     if (!$controller->isTalkpageOccupied($page)) {
         // just check for permissions, nothing else to do. if the commit
         // is successful the OccupationListener will see the new revision
         // and put the flow board in place.
         $status = $controller->allowCreation($page, $this->getUser());
         if (!$status->isGood()) {
             $this->dieUsage("Page provided does not have Flow enabled and allowCreation failed with: " . $status->getMessage()->parse(), 'invalid-page');
         }
     }
     return $page;
 }
 public function setUp()
 {
     parent::setUp();
     // create a workflow & revision associated with it
     $this->revision = $this->generateObject();
     $this->workflow = $this->workflows[$this->revision->getCollectionId()->getAlphadecimal()];
     $this->storage = Container::get('storage');
     $this->extractor = Container::get('reference.extractor');
     $this->recorder = Container::get('reference.recorder');
     $this->updater = Container::get('reference.updater.links-tables');
     // Check for Parsoid
     try {
         Utils::convert('html', 'wikitext', 'Foo', $this->workflow->getOwnerTitle());
     } catch (WikitextException $excep) {
         $this->markTestSkipped('Parsoid not enabled');
     }
     // These tests don't provide sufficient data to properly run all listeners
     $this->clearExtraLifecycleHandlers();
 }
 public function execute()
 {
     $logger = new MaintenanceDebugLogger($this);
     if ($this->getOption('debug')) {
         $logger->setMaximumLevel(LogLevel::DEBUG);
     } else {
         $logger->setMaximumLevel(LogLevel::INFO);
     }
     $importer = Flow\Container::get('importer');
     $talkpageManagerUser = FlowHooks::getOccupationController()->getTalkpageManager();
     $dbw = wfGetDB(DB_MASTER);
     $strategy = new ConversionStrategy($dbw, new FileImportSourceStore($this->getOption('logfile')), new LocalApiBackend($talkpageManagerUser), Container::get('url_generator'), $talkpageManagerUser, Container::get('controller.notification'));
     $converter = new \Flow\Import\Converter($dbw, $importer, $logger, $talkpageManagerUser, $strategy);
     $startId = $this->getOption('startId');
     $stopId = $this->getOption('stopId');
     $logger->info("Starting full wiki LQT conversion of all pages with use-liquid-threads property");
     $titles = new PagesWithPropertyIterator($dbw, 'use-liquid-threads', $startId, $stopId);
     $converter->convertAll($titles);
     $logger->info("Finished conversion");
 }
 /**
  * @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);
 }
 /**
  * @dataProvider referenceExtractorProvider
  */
 public function testReferenceExtractor($description, $wikitext, $expectedClass, $expectedType, $expectedTarget, $page = 'UTPage')
 {
     $referenceExtractor = Container::get('reference.extractor');
     $workflow = $this->getMock('Flow\\Model\\Workflow');
     $workflow->expects($this->any())->method('getId')->will($this->returnValue(UUID::create()));
     $workflow->expects($this->any())->method('getArticleTitle')->will($this->returnValue(Title::newMainPage()));
     $factory = new ReferenceFactory($workflow, 'foo', UUID::create());
     $reflMethod = new ReflectionMethod($referenceExtractor, 'extractReferences');
     $reflMethod->setAccessible(true);
     $reflProperty = new \ReflectionProperty($referenceExtractor, 'extractors');
     $reflProperty->setAccessible(true);
     $extractors = $reflProperty->getValue($referenceExtractor);
     $html = Utils::convert('wt', 'html', $wikitext, Title::newFromText($page));
     $result = $reflMethod->invoke($referenceExtractor, $factory, $extractors['post'], $html);
     $this->assertCount(1, $result, $html);
     $result = reset($result);
     $this->assertInstanceOf($expectedClass, $result, $description);
     $this->assertEquals($expectedType, $result->getType(), $description);
     $this->assertEquals($expectedTarget, $result->getTargetIdentifier(), $description);
 }
 /**
  * Returns an array of user ids to subscribe to the title.
  *
  * @param string $changeType
  * @param string $watchType Key of the corresponding 'watch' array in FlowActions.php
  * @param WatchedTopicItems[] $params Params to feed to callback function that will return
  *   an array of users to subscribe
  * @return User[]
  */
 public static function getUsersToSubscribe($changeType, $watchType, array $params = array())
 {
     /** @var FlowActions $actions */
     $actions = Container::get('flow_actions');
     // Find users defined for this action, in FlowActions.php
     try {
         $users = $actions->getValue($changeType, 'watch', $watchType);
     } catch (\Exception $e) {
         return array();
     }
     // Null will be returned if nothing is defined for this changeType
     if (!$users) {
         return array();
     }
     // Some actions may have more complex logic to determine watching users
     if (is_callable($users)) {
         $users = call_user_func_array($users, $params);
     }
     return $users;
 }