getMainWANObjectCache(); }; $c['ttl.default'] = function ( $c ) { global $wgFlowCacheTime; return $wgFlowCacheTime; }; $c['flowcache'] = function ( $c ) { return new FlowObjectCache( $c['wancache'], $c['db.factory'], $c['ttl.default'] ); }; // Batched username loader $c['repository.username.query'] = function ( $c ) { return new Flow\Repository\UserName\TwoStepUserNameQuery( $c['db.factory'] ); }; $c['repository.username'] = function ( $c ) { return new Flow\Repository\UserNameBatch( $c['repository.username.query'] ); }; $c['collection.cache'] = function ( $c ) { return new Flow\Collection\CollectionCache(); }; // Individual workflow instances $c['storage.workflow.class'] = 'Flow\Model\Workflow'; $c['storage.workflow.table'] = 'flow_workflow'; $c['storage.workflow.primary_key'] = [ 'workflow_id' ]; $c['storage.workflow.mapper'] = function ( $c ) { return CachingObjectMapper::model( $c['storage.workflow.class'], $c['storage.workflow.primary_key'] ); }; $c['storage.workflow.backend'] = function ( $c ) { return new BasicDbStorage( $c['db.factory'], $c['storage.workflow.table'], $c['storage.workflow.primary_key'] ); }; $c['storage.workflow.indexes.primary'] = function ( $c ) { return new UniqueFeatureIndex( $c['flowcache'], $c['storage.workflow.backend'], $c['storage.workflow.mapper'], 'flow_workflow:v2:pk', $c['storage.workflow.primary_key'] ); }; $c['storage.workflow.indexes.title_lookup'] = function ( $c ) { return new TopKIndex( $c['flowcache'], $c['storage.workflow.backend'], $c['storage.workflow.mapper'], 'flow_workflow:title:v2:', [ 'workflow_wiki', 'workflow_namespace', 'workflow_title_text', 'workflow_type' ], [ 'shallow' => $c['storage.workflow.indexes.primary'], 'limit' => 1, 'sort' => 'workflow_id' ] ); }; $c['storage.workflow.indexes'] = function ( $c ) { return [ $c['storage.workflow.indexes.primary'], $c['storage.workflow.indexes.title_lookup'] ]; }; $c['storage.workflow.listeners.topiclist'] = function ( $c ) { return new Flow\Data\Listener\WorkflowTopicListListener( $c['storage.topic_list'], $c['storage.topic_list.indexes.last_updated'] ); }; $c['storage.workflow.listeners'] = function ( $c ) { return [ 'listener.topicpagecreation' => $c['listener.topicpagecreation'], 'storage.workflow.listeners.topiclist' => $c['storage.workflow.listeners.topiclist'], ]; }; $c['storage.workflow'] = function ( $c ) { return new ObjectManager( $c['storage.workflow.mapper'], $c['storage.workflow.backend'], $c['db.factory'], $c['storage.workflow.indexes'], $c['storage.workflow.listeners'] ); }; $c['listener.recentchanges'] = function ( $c ) { // Recent change listeners go out to external services and // as such must only be run after the transaction is commited. return new Flow\Data\Listener\DeferredInsertLifecycleHandler( $c['deferred_queue'], new Flow\Data\Listener\RecentChangesListener( $c['flow_actions'], $c['repository.username'], new Flow\Data\Utils\RecentChangeFactory, $c['formatter.irclineurl'] ) ); }; $c['listener.topicpagecreation'] = function ( $c ) { return new Flow\Data\Listener\TopicPageCreationListener( $c['occupation_controller'], $c['deferred_queue'] ); }; $c['listeners.notification'] = function ( $c ) { // Defer notifications triggering till end of request so we could get // article_id in the case of a new topic return new Flow\Data\Listener\DeferredInsertLifecycleHandler( $c['deferred_queue'], new Flow\Data\Listener\NotificationListener( $c['controller.notification'] ) ); }; $c['storage.post_board_history.backend'] = function ( $c ) { return new PostRevisionBoardHistoryStorage( $c['db.factory'] ); }; $c['storage.post_board_history.indexes.primary'] = function ( $c ) { return new PostRevisionBoardHistoryIndex( $c['flowcache'], // backend storage $c['storage.post_board_history.backend'], // data mapper $c['storage.post.mapper'], // key prefix 'flow_revision:topic_list_history:post:v2', // primary key [ 'topic_list_id' ], // index options [ 'limit' => $c['board_topic_history_post_index_limit'], 'sort' => 'rev_id', 'order' => 'DESC' ], $c['storage.topic_list'] ); }; $c['storage.post_board_history.indexes'] = function ( $c ) { return [ $c['storage.post_board_history.indexes.primary'] ]; }; $c['storage.post_board_history'] = function ( $c ) { return new ObjectLocator( $c['storage.post.mapper'], $c['storage.post_board_history.backend'], $c['db.factory'], $c['storage.post_board_history.indexes'] ); }; $c['storage.post_summary_board_history.backend'] = function ( $c ) { return new PostSummaryRevisionBoardHistoryStorage( $c['db.factory'] ); }; $c['storage.post_summary_board_history.indexes.primary'] = function ( $c ) { return new PostSummaryRevisionBoardHistoryIndex( $c['flowcache'], // backend storage $c['storage.post_summary_board_history.backend'], // data mapper $c['storage.post_summary.mapper'], // key prefix 'flow_revision:topic_list_history:post_summary:v2', // primary key [ 'topic_list_id' ], // index options [ 'limit' => $c['history_index_limit'], 'sort' => 'rev_id', 'order' => 'DESC' ], $c['storage.topic_list'] ); }; $c['storage.post_summary_board_history.indexes'] = function ( $c ) { return [ $c['storage.post_summary_board_history.indexes.primary'] ]; }; $c['storage.post_summary_board_history'] = function ( $c ) { return new ObjectLocator( $c['storage.post_summary.mapper'], $c['storage.post_summary_board_history.backend'], $c['db.factory'], $c['storage.post_summary_board_history.indexes'] ); }; $c['storage.header.listeners.username'] = function ( $c ) { return new Flow\Data\Listener\UserNameListener( $c['repository.username'], [ 'rev_user_id' => 'rev_user_wiki', 'rev_mod_user_id' => 'rev_mod_user_wiki', 'rev_edit_user_id' => 'rev_edit_user_wiki' ] ); }; $c['storage.header.listeners'] = function ( $c ) { return [ 'reference.recorder' => $c['reference.recorder'], 'storage.header.listeners.username' => $c['storage.header.listeners.username'], 'listeners.notification' => $c['listeners.notification'], 'listener.recentchanges' => $c['listener.recentchanges'], 'listener.editcount' => $c['listener.editcount'], ]; }; $c['storage.header.primary_key'] = [ 'rev_id' ]; $c['storage.header.mapper'] = function ( $c ) { return CachingObjectMapper::model( 'Flow\\Model\\Header', [ 'rev_id' ] ); }; $c['storage.header.backend'] = function ( $c ) { global $wgFlowExternalStore; return new HeaderRevisionStorage( $c['db.factory'], $wgFlowExternalStore ); }; $c['storage.header.indexes.primary'] = function ( $c ) { return new UniqueFeatureIndex( $c['flowcache'], $c['storage.header.backend'], $c['storage.header.mapper'], 'flow_header:v2:pk', $c['storage.header.primary_key'] ); }; $c['storage.header.indexes.header_lookup'] = function ( $c ) { return new TopKIndex( $c['flowcache'], $c['storage.header.backend'], $c['storage.header.mapper'], 'flow_header:workflow:v3', [ 'rev_type_id' ], [ 'limit' => $c['history_index_limit'], 'sort' => 'rev_id', 'order' => 'DESC', 'shallow' => $c['storage.header.indexes.primary'], 'create' => function ( array $row ) { return $row['rev_parent_id'] === null; }, ] ); }; $c['storage.header.indexes'] = function ( $c ) { return [ $c['storage.header.indexes.primary'], $c['storage.header.indexes.header_lookup'] ]; }; $c['storage.header'] = function ( $c ) { return new ObjectManager( $c['storage.header.mapper'], $c['storage.header.backend'], $c['db.factory'], $c['storage.header.indexes'], $c['storage.header.listeners'] ); }; $c['storage.post_summary.class'] = 'Flow\Model\PostSummary'; $c['storage.post_summary.primary_key'] = [ 'rev_id' ]; $c['storage.post_summary.mapper'] = function ( $c ) { return CachingObjectMapper::model( $c['storage.post_summary.class'], $c['storage.post_summary.primary_key'] ); }; $c['storage.post_summary.listeners.username'] = function ( $c ) { return new Flow\Data\Listener\UserNameListener( $c['repository.username'], [ 'rev_user_id' => 'rev_user_wiki', 'rev_mod_user_id' => 'rev_mod_user_wiki', 'rev_edit_user_id' => 'rev_edit_user_wiki' ] ); }; $c['storage.post_summary.listeners'] = function ( $c ) { return [ 'listener.recentchanges' => $c['listener.recentchanges'], 'storage.post_summary.listeners.username' => $c['storage.post_summary.listeners.username'], 'listeners.notification' => $c['listeners.notification'], 'storage.post_summary_board_history.indexes.primary' => $c['storage.post_summary_board_history.indexes.primary'], 'listener.editcount' => $c['listener.editcount'], 'reference.recorder' => $c['reference.recorder'], ]; }; $c['storage.post_summary.backend'] = function ( $c ) { global $wgFlowExternalStore; return new PostSummaryRevisionStorage( $c['db.factory'], $wgFlowExternalStore ); }; $c['storage.post_summary.indexes.primary'] = function ( $c ) { return new UniqueFeatureIndex( $c['flowcache'], $c['storage.post_summary.backend'], $c['storage.post_summary.mapper'], 'flow_post_summary:v2:pk', $c['storage.post_summary.primary_key'] ); }; $c['storage.post_summary.indexes.topic_lookup'] = function ( $c ) { return new TopKIndex( $c['flowcache'], $c['storage.post_summary.backend'], $c['storage.post_summary.mapper'], 'flow_post_summary:workflow:v3', [ 'rev_type_id' ], [ 'limit' => $c['history_index_limit'], 'sort' => 'rev_id', 'order' => 'DESC', 'shallow' => $c['storage.post_summary.indexes.primary'], 'create' => function ( array $row ) { return $row['rev_parent_id'] === null; }, ] ); }; $c['storage.post_summary.indexes'] = function ( $c ) { return [ $c['storage.post_summary.indexes.primary'], $c['storage.post_summary.indexes.topic_lookup'], ]; }; $c['storage.post_summary'] = function ( $c ) { return new ObjectManager( $c['storage.post_summary.mapper'], $c['storage.post_summary.backend'], $c['db.factory'], $c['storage.post_summary.indexes'], $c['storage.post_summary.listeners'] ); }; $c['storage.topic_list.class'] = 'Flow\Model\TopicListEntry'; $c['storage.topic_list.table'] = 'flow_topic_list'; $c['storage.topic_list.primary_key'] = [ 'topic_list_id', 'topic_id' ]; $c['storage.topic_list.mapper'] = function ( $c ) { // Must be BasicObjectMapper, due to variance in when // we have workflow_last_update_timestamp return BasicObjectMapper::model( $c['storage.topic_list.class'], $c['storage.topic_list.primary_key'] ); }; $c['storage.topic_list.backend'] = function ( $c ) { return new TopicListStorage( // factory and table $c['db.factory'], $c['storage.topic_list.table'], $c['storage.topic_list.primary_key'] ); }; // Lookup from topic_id to its owning board id $c['storage.topic_list.indexes.primary'] = function ( $c ) { return new UniqueFeatureIndex( $c['flowcache'], $c['storage.topic_list.backend'], $c['storage.topic_list.mapper'], 'flow_topic_list:topic', [ 'topic_id' ] ); }; // Lookup from board to contained topics /// In reverse order by topic_id $c['storage.topic_list.indexes.reverse_lookup'] = function ( $c ) { return new TopKIndex( $c['flowcache'], $c['storage.topic_list.backend'], $c['storage.topic_list.mapper'], 'flow_topic_list:list', [ 'topic_list_id' ], [ 'sort' => 'topic_id' ] ); }; /// In reverse order by topic last_updated $c['storage.topic_list.indexes.last_updated'] = function ( $c ) { return new TopKIndex( $c['flowcache'], $c['storage.topic_list.backend'], $c['storage.topic_list.mapper'], 'flow_topic_list_last_updated:list', [ 'topic_list_id' ], [ 'sort' => 'workflow_last_update_timestamp', 'order' => 'desc' ] ); }; $c['storage.topic_list.indexes'] = function ( $c ) { return [ $c['storage.topic_list.indexes.primary'], $c['storage.topic_list.indexes.reverse_lookup'], $c['storage.topic_list.indexes.last_updated'], ]; }; $c['storage.topic_list'] = function ( $c ) { return new ObjectManager( $c['storage.topic_list.mapper'], $c['storage.topic_list.backend'], $c['db.factory'], $c['storage.topic_list.indexes'] ); }; $c['storage.post.class'] = 'Flow\Model\PostRevision'; $c['storage.post.primary_key'] = [ 'rev_id' ]; $c['storage.post.mapper'] = function ( $c ) { return CachingObjectMapper::model( $c['storage.post.class'], $c['storage.post.primary_key'] ); }; $c['storage.post.backend'] = function ( $c ) { global $wgFlowExternalStore; return new PostRevisionStorage( $c['db.factory'], $wgFlowExternalStore, $c['repository.tree'] ); }; $c['storage.post.listeners.moderation_logging'] = function ( $c ) { return new Flow\Data\Listener\ModerationLoggingListener( $c['logger.moderation'] ); }; $c['storage.post.listeners.username'] = function ( $c ) { return new Flow\Data\Listener\UserNameListener( $c['repository.username'], [ 'rev_user_id' => 'rev_user_wiki', 'rev_mod_user_id' => 'rev_mod_user_wiki', 'rev_edit_user_id' => 'rev_edit_user_wiki', 'tree_orig_user_id' => 'tree_orig_user_wiki' ] ); }; $c['storage.post.listeners.watch_topic'] = function ( $c ) { // Auto-subscribe users to the topic after performing specific actions return new Flow\Data\Listener\ImmediateWatchTopicListener( $c['watched_items'] ); }; $c['storage.post.listeners'] = function ( $c ) { return [ 'reference.recorder' => $c['reference.recorder'], 'collection.cache' => $c['collection.cache'], 'storage.post.listeners.username' => $c['storage.post.listeners.username'], 'storage.post.listeners.watch_topic' => $c['storage.post.listeners.watch_topic'], 'listeners.notification' => $c['listeners.notification'], 'storage.post.listeners.moderation_logging' => $c['storage.post.listeners.moderation_logging'], 'listener.recentchanges' => $c['listener.recentchanges'], 'listener.editcount' => $c['listener.editcount'], 'storage.post_board_history.indexes.primary' => $c['storage.post_board_history.indexes.primary'], ]; }; $c['storage.post.indexes.primary'] = function ( $c ) { return new UniqueFeatureIndex( $c['flowcache'], $c['storage.post.backend'], $c['storage.post.mapper'], 'flow_revision:v4:pk', $c['storage.post.primary_key'] ); }; // Each bucket holds a list of revisions in a single post $c['storage.post.indexes.post_lookup'] = function ( $c ) { return new TopKIndex( $c['flowcache'], $c['storage.post.backend'], $c['storage.post.mapper'], 'flow_revision:descendant', [ 'rev_type_id' ], [ 'limit' => 100, 'sort' => 'rev_id', 'order' => 'DESC', 'shallow' => $c['storage.post.indexes.primary'], 'create' => function ( array $row ) { // return true to create instead of merge index return $row['rev_parent_id'] === null; }, ] ); }; $c['storage.post.indexes'] = function ( $c ) { return [ $c['storage.post.indexes.primary'], $c['storage.post.indexes.post_lookup'], $c['storage.post_topic_history.indexes.topic_lookup'] ]; }; $c['storage.post'] = function ( $c ) { return new ObjectManager( $c['storage.post.mapper'], $c['storage.post.backend'], $c['db.factory'], $c['storage.post.indexes'], $c['storage.post.listeners'] ); }; $c['storage.post_topic_history.backend'] = function ( $c ) { return new PostRevisionTopicHistoryStorage( $c['storage.post.backend'], $c['repository.tree'] ); }; $c['storage.post_topic_history.indexes.topic_lookup'] = function ( $c ) { return new PostRevisionTopicHistoryIndex( $c['flowcache'], $c['storage.post_topic_history.backend'], $c['storage.post.mapper'], 'flow_revision:topic_history:post:v2', [ 'topic_root_id' ], [ 'limit' => $c['board_topic_history_post_index_limit'], 'sort' => 'rev_id', 'order' => 'DESC', // Why does topic history have a shallow compactor, but not board history? 'shallow' => $c['storage.post.indexes.primary'], 'create' => function ( array $row ) { // only create new indexes for new topics, so it has to be // of type 'post' and have no parent post & revision if ( $row['rev_type'] !== 'post' ) { return false; } return $row['tree_parent_id'] === null && $row['rev_parent_id'] === null; }, ] ); }; $c['storage.post_topic_history.indexes'] = function ( $c ) { return [ $c['storage.post_topic_history.indexes.topic_lookup'], ]; }; $c['storage.post_topic_history'] = function ( $c ) { return new ObjectLocator( $c['storage.post.mapper'], $c['storage.post_topic_history.backend'], $c['db.factory'], $c['storage.post_topic_history.indexes'] ); }; $c['storage.manager_list'] = function ( $c ) { return [ 'Flow\\Model\\Workflow' => 'storage.workflow', 'Workflow' => 'storage.workflow', 'Flow\\Model\\PostRevision' => 'storage.post', 'PostRevision' => 'storage.post', 'post' => 'storage.post', 'Flow\\Model\\PostSummary' => 'storage.post_summary', 'PostSummary' => 'storage.post_summary', 'post-summary' => 'storage.post_summary', 'Flow\\Model\\TopicListEntry' => 'storage.topic_list', 'TopicListEntry' => 'storage.topic_list', 'Flow\\Model\\Header' => 'storage.header', 'Header' => 'storage.header', 'header' => 'storage.header', 'PostRevisionBoardHistoryEntry' => 'storage.post_board_history', 'PostSummaryBoardHistoryEntry' => 'storage.post_summary_board_history', 'PostRevisionTopicHistoryEntry' => 'storage.post_topic_history', 'Flow\\Model\\WikiReference' => 'storage.wiki_reference', 'WikiReference' => 'storage.wiki_reference', 'Flow\\Model\\URLReference' => 'storage.url_reference', 'URLReference' => 'storage.url_reference', ]; }; $c['storage'] = function ( $c ) { return new \Flow\Data\ManagerGroup( $c, $c['storage.manager_list'] ); }; $c['loader.root_post'] = function ( $c ) { return new \Flow\Repository\RootPostLoader( $c['storage'], $c['repository.tree'] ); }; // Queue of callbacks to run by DeferredUpdates, but only // on successfull commit $c['deferred_queue'] = function ( $c ) { return new SplQueue; }; $c['submission_handler'] = function ( $c ) { return new Flow\SubmissionHandler( $c['storage'], $c['db.factory'], $c['deferred_queue'] ); }; $c['factory.block'] = function ( $c ) { return new Flow\BlockFactory( $c['storage'], $c['loader.root_post'] ); }; $c['factory.loader.workflow'] = function ( $c ) { return new Flow\WorkflowLoaderFactory( $c['storage'], $c['factory.block'], $c['submission_handler'] ); }; // Initialized in FlowHooks to facilitate only loading the flow container // when flow is specifically requested to run. Extension initialization // must always happen before calling flow code. $c['occupation_controller'] = FlowHooks::getOccupationController(); $c['controller.notification'] = function ( $c ) { global $wgContLang; return new Flow\NotificationController( $wgContLang, $c['repository.tree'] ); }; // Initialized in FlowHooks to faciliate only loading the flow container // when flow is specifically requested to run. Extension initialization // must always happen before calling flow code. $c['controller.abusefilter'] = FlowHooks::getAbuseFilter(); $c['controller.spamregex'] = function ( $c ) { return new Flow\SpamFilter\SpamRegex; }; $c['controller.spamblacklist'] = function ( $c ) { return new Flow\SpamFilter\SpamBlacklist; }; $c['controller.confirmedit'] = function ( $c ) { return new Flow\SpamFilter\ConfirmEdit; }; $c['controller.contentlength'] = function ( $c ) { global $wgMaxArticleSize; // wgMaxArticleSize is in kilobytes, // whereas this really is characters (it uses // mb_strlen), so it's not the exact same limit. $maxCharCount = $wgMaxArticleSize * 1024; return new Flow\SpamFilter\ContentLengthFilter( $maxCharCount ); }; $c['controller.ratelimits'] = function ( $c ) { return new Flow\SpamFilter\RateLimits; }; $c['controller.spamfilter'] = function ( $c ) { return new Flow\SpamFilter\Controller( $c['controller.contentlength'], $c['controller.spamregex'], $c['controller.ratelimits'], $c['controller.spamblacklist'], $c['controller.abusefilter'], $c['controller.confirmedit'] ); }; $c['query.categoryviewer'] = function ( $c ) { return new Flow\Formatter\CategoryViewerQuery( $c['storage'], $c['repository.tree'] ); }; $c['formatter.categoryviewer'] = function ( $c ) { return new Flow\Formatter\CategoryViewerFormatter( $c['permissions'] ); }; $c['query.singlepost'] = function ( $c ) { return new Flow\Formatter\SinglePostQuery( $c['storage'], $c['repository.tree'] ); }; $c['query.checkuser'] = function ( $c ) { return new Flow\Formatter\CheckUserQuery( $c['storage'], $c['repository.tree'] ); }; $c['formatter.irclineurl'] = function ( $c ) { return new Flow\Formatter\IRCLineUrlFormatter( $c['permissions'], $c['formatter.revision'] ); }; $c['formatter.checkuser'] = function ( $c ) { return new Flow\Formatter\CheckUserFormatter( $c['permissions'], $c['formatter.revision'] ); }; $c['formatter.revisionview'] = function ( $c ) { return new Flow\Formatter\RevisionViewFormatter( $c['url_generator'], $c['formatter.revision'] ); }; $c['formatter.revision.diff.view'] = function ( $c ) { return new Flow\Formatter\RevisionDiffViewFormatter( $c['formatter.revisionview'], $c['url_generator'] ); }; $c['query.topiclist'] = function ( $c ) { return new Flow\Formatter\TopicListQuery( $c['storage'], $c['repository.tree'], $c['permissions'], $c['watched_items'] ); }; $c['query.topic.history'] = function ( $c ) { return new Flow\Formatter\TopicHistoryQuery( $c['storage'], $c['repository.tree'], $c['flow_actions'] ); }; $c['query.post.history'] = function ( $c ) { return new Flow\Formatter\PostHistoryQuery( $c['storage'], $c['repository.tree'], $c['flow_actions'] ); }; $c['query.changeslist'] = function ( $c ) { $query = new Flow\Formatter\ChangesListQuery( $c['storage'], $c['repository.tree'], $c['flow_actions'] ); $query->setExtendWatchlist( $c['user']->getOption( 'extendwatchlist' ) ); return $query; }; $c['query.postsummary'] = function ( $c ) { return new Flow\Formatter\PostSummaryQuery( $c['storage'], $c['repository.tree'], $c['flow_actions'] ); }; $c['query.header.view'] = function ( $c ) { return new Flow\Formatter\HeaderViewQuery( $c['storage'], $c['repository.tree'], $c['permissions'] ); }; $c['query.post.view'] = function ( $c ) { return new Flow\Formatter\PostViewQuery( $c['storage'], $c['repository.tree'], $c['permissions'] ); }; $c['query.postsummary.view'] = function ( $c ) { return new Flow\Formatter\PostSummaryViewQuery( $c['storage'], $c['repository.tree'], $c['permissions'] ); }; $c['formatter.changeslist'] = function ( $c ) { return new Flow\Formatter\ChangesListFormatter( $c['permissions'], $c['formatter.revision'] ); }; $c['query.contributions'] = function ( $c ) { return new Flow\Formatter\ContributionsQuery( $c['storage'], $c['repository.tree'], $c['memcache'], $c['db.factory'], $c['flow_actions'] ); }; $c['formatter.contributions'] = function ( $c ) { return new Flow\Formatter\ContributionsFormatter( $c['permissions'], $c['formatter.revision'] ); }; $c['formatter.contributions.feeditem'] = function ( $c ) { return new Flow\Formatter\FeedItemFormatter( $c['permissions'], $c['formatter.revision'] ); }; $c['query.board.history'] = function ( $c ) { return new Flow\Formatter\BoardHistoryQuery( $c['storage'], $c['repository.tree'], $c['flow_actions'] ); }; // The RevisionFormatter holds internal state like // contentType of output and if it should include history // properties. To prevent different code using the formatter // from causing problems return a new RevisionFormatter every // time it is requested. $c['formatter.revision'] = $c->factory( function ( $c ) { global $wgFlowMaxThreadingDepth; return new Flow\Formatter\RevisionFormatter( $c['permissions'], $c['templating'], $c['repository.username'], $wgFlowMaxThreadingDepth ); } ); $c['formatter.topiclist'] = function ( $c ) { return new Flow\Formatter\TopicListFormatter( $c['url_generator'], $c['formatter.revision'] ); }; $c['formatter.topiclist.toc'] = function ( $c ) { return new Flow\Formatter\TocTopicListFormatter( $c['templating'] ); }; $c['formatter.topic'] = function ( $c ) { return new Flow\Formatter\TopicFormatter( $c['url_generator'], $c['formatter.revision'] ); }; $c['search.connection'] = function ( $c ) { if ( defined( 'MW_PHPUNIT_TEST' ) && !class_exists( 'ElasticaConnection' ) ) { /* * ContainerTest::testInstantiateAll instantiates everything * in container and doublechecks it's not null. * Flow runs on Jenkins don't currently load Extension:Elastica, * which is required to be able to construct this object. * Because search is not currently in use, let's not add the * dependency in Jenkins and just return a bogus value to not * make the test fail ;) */ return 'not-supported'; } global $wgFlowSearchServers, $wgFlowSearchConnectionAttempts; return new Flow\Search\Connection( $wgFlowSearchServers, $wgFlowSearchConnectionAttempts ); }; $c['search.index.iterators.header'] = function ( $c ) { return new \Flow\Search\Iterators\HeaderIterator( $c['db.factory'] ); }; $c['search.index.iterators.topic'] = function ( $c ) { return new \Flow\Search\Iterators\TopicIterator( $c['db.factory'], $c['loader.root_post'] ); }; $c['search.index.updaters'] = function ( $c ) { // permissions for anon user $anonPermissions = new Flow\RevisionActionPermissions( $c['flow_actions'], new User ); return [ 'topic' => new \Flow\Search\Updaters\TopicUpdater( $c['search.index.iterators.topic'], $anonPermissions, $c['loader.root_post'] ), 'header' => new \Flow\Search\Updaters\HeaderUpdater( $c['search.index.iterators.header'], $anonPermissions ) ]; }; $c['logger.moderation'] = function ( $c ) { return new Flow\Log\ModerationLogger( $c['flow_actions'] ); }; $c['storage.wiki_reference.class'] = 'Flow\Model\WikiReference'; $c['storage.wiki_reference.table'] = 'flow_wiki_ref'; $c['storage.wiki_reference.primary_key'] = function ( $c ) { return [ 'ref_src_wiki', 'ref_src_namespace', 'ref_src_title', 'ref_src_object_id', 'ref_type', 'ref_target_namespace', 'ref_target_title' ]; }; $c['storage.wiki_reference.mapper'] = function ( $c ) { return BasicObjectMapper::model( $c['storage.wiki_reference.class'] ); }; $c['storage.wiki_reference.backend'] = function ( $c ) { return new BasicDbStorage( $c['db.factory'], $c['storage.wiki_reference.table'], $c['storage.wiki_reference.primary_key'] ); }; $c['storage.wiki_reference.indexes.source_lookup'] = function ( $c ) { return new TopKIndex( $c['flowcache'], $c['storage.wiki_reference.backend'], $c['storage.wiki_reference.mapper'], 'flow_ref:wiki:by-source:v3', [ 'ref_src_wiki', 'ref_src_namespace', 'ref_src_title', ], [ 'order' => 'ASC', 'sort' => 'ref_src_object_id', ] ); }; $c['storage.wiki_reference.indexes.revision_lookup'] = function ( $c ) { return new TopKIndex( $c['flowcache'], $c['storage.wiki_reference.backend'], $c['storage.wiki_reference.mapper'], 'flow_ref:wiki:by-revision:v3', [ 'ref_src_wiki', 'ref_src_object_type', 'ref_src_object_id', ], [ 'order' => 'ASC', 'sort' => [ 'ref_target_namespace', 'ref_target_title' ], ] ); }; $c['storage.wiki_reference.indexes'] = function ( $c ) { return [ $c['storage.wiki_reference.indexes.source_lookup'], $c['storage.wiki_reference.indexes.revision_lookup'], ]; }; $c['storage.wiki_reference'] = function ( $c ) { return new ObjectManager( $c['storage.wiki_reference.mapper'], $c['storage.wiki_reference.backend'], $c['db.factory'], $c['storage.wiki_reference.indexes'], [] ); }; $c['storage.url_reference.class'] = 'Flow\Model\URLReference'; $c['storage.url_reference.table'] = 'flow_ext_ref'; $c['storage.url_reference.primary_key'] = function ( $c ) { return [ 'ref_src_wiki', 'ref_src_namespace', 'ref_src_title', 'ref_src_object_id', 'ref_type', 'ref_target', ]; }; $c['storage.url_reference.mapper'] = function ( $c ) { return BasicObjectMapper::model( $c['storage.url_reference.class'] ); }; $c['storage.url_reference.backend'] = function ( $c ) { return new BasicDbStorage( // factory and table $c['db.factory'], $c['storage.url_reference.table'], $c['storage.url_reference.primary_key'] ); }; $c['storage.url_reference.indexes.source_lookup'] = function ( $c ) { return new TopKIndex( $c['flowcache'], $c['storage.url_reference.backend'], $c['storage.url_reference.mapper'], 'flow_ref:url:by-source:v3', [ 'ref_src_wiki', 'ref_src_namespace', 'ref_src_title', ], [ 'order' => 'ASC', 'sort' => 'ref_src_object_id', ] ); }; $c['storage.url_reference.indexes.revision_lookup'] = function ( $c ) { return new TopKIndex( $c['flowcache'], $c['storage.url_reference.backend'], $c['storage.url_reference.mapper'], 'flow_ref:url:by-revision:v3', [ 'ref_src_wiki', 'ref_src_object_type', 'ref_src_object_id', ], [ 'order' => 'ASC', 'sort' => [ 'ref_target' ], ] ); }; $c['storage.url_reference.indexes'] = function ( $c ) { return [ $c['storage.url_reference.indexes.source_lookup'], $c['storage.url_reference.indexes.revision_lookup'], ]; }; $c['storage.url_reference'] = function ( $c ) { return new ObjectManager( $c['storage.url_reference.mapper'], $c['storage.url_reference.backend'], $c['db.factory'], $c['storage.url_reference.indexes'], [] ); }; $c['reference.updater.links-tables'] = function ( $c ) { return new Flow\LinksTableUpdater( $c['storage'] ); }; $c['reference.clarifier'] = function ( $c ) { return new Flow\ReferenceClarifier( $c['storage'], $c['url_generator'] ); }; $c['reference.extractor'] = function ( $c ) { $default = [ new Flow\Parsoid\Extractor\ImageExtractor, new Flow\Parsoid\Extractor\PlaceholderExtractor, new Flow\Parsoid\Extractor\WikiLinkExtractor, new Flow\Parsoid\Extractor\ExtLinkExtractor, new Flow\Parsoid\Extractor\TransclusionExtractor, ]; $extractors = [ 'header' => $default, 'post-summary' => $default, 'post' => $default, ]; // In addition to the defaults header and summaries collect // the related categories. $extractors['header'][] = $extractors['post-summary'][] = new Flow\Parsoid\Extractor\CategoryExtractor; return new Flow\Parsoid\ReferenceExtractor( $extractors ); }; $c['reference.recorder'] = function ( $c ) { return new Flow\Data\Listener\ReferenceRecorder( $c['reference.extractor'], $c['reference.updater.links-tables'], $c['storage'], $c['repository.tree'], $c['deferred_queue'] ); }; $c['user_merger'] = function ( $c ) { return new Flow\Data\Utils\UserMerger( $c['db.factory'], $c['storage'] ); }; $c['importer'] = function ( $c ) { $importer = new Flow\Import\Importer( $c['storage'], $c['factory.loader.workflow'], $c['db.factory'], $c['deferred_queue'], $c['occupation_controller'] ); $importer->addPostprocessor( new Flow\Import\Postprocessor\SpecialLogTopic( $c['occupation_controller']->getTalkpageManager() ) ); return $importer; }; $c['listener.editcount'] = function ( $c ) { return new \Flow\Data\Listener\EditCountListener( $c['flow_actions'] ); }; $c['formatter.undoedit'] = function ( $c ) { return new Flow\Formatter\RevisionUndoViewFormatter( $c['formatter.revisionview'] ); }; $c['board_mover'] = function ( $c ) { return new Flow\BoardMover( $c['db.factory'], $c['storage'], $c['occupation_controller']->getTalkpageManager() ); }; $c['parser'] = function () { global $wgParser; return $wgParser; }; $c['default_logger'] = function () { return MediaWiki\Logger\LoggerFactory::getInstance( 'Flow' ); }; return $c;