Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiFlow
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
0.00%
0 / 10
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 getModuleManager
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
72
 getPage
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 isWriteMode
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getHelpUrls
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 mustBePosted
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 needsToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace Flow\Api;
4
5use Flow\Container;
6use Flow\Exception\InvalidInputException;
7use Flow\Hooks\HookRunner;
8use MediaWiki\Api\ApiBase;
9use MediaWiki\Api\ApiMain;
10use MediaWiki\Api\ApiModuleManager;
11use MediaWiki\MediaWikiServices;
12use MediaWiki\Title\Title;
13use Wikimedia\ParamValidator\ParamValidator;
14
15class ApiFlow extends ApiBase {
16
17    /**
18     * @var ApiModuleManager
19     */
20    private $moduleManager;
21
22    private const ALWAYS_ENABLED_MODULES = [
23        // POST
24        'new-topic' => \Flow\Api\ApiFlowNewTopic::class,
25        'edit-header' => \Flow\Api\ApiFlowEditHeader::class,
26        'edit-post' => \Flow\Api\ApiFlowEditPost::class,
27        'edit-topic-summary' => \Flow\Api\ApiFlowEditTopicSummary::class,
28        'reply' => \Flow\Api\ApiFlowReply::class,
29        'moderate-post' => \Flow\Api\ApiFlowModeratePost::class,
30        'moderate-topic' => \Flow\Api\ApiFlowModerateTopic::class,
31        'edit-title' => \Flow\Api\ApiFlowEditTitle::class,
32        'lock-topic' => \Flow\Api\ApiFlowLockTopic::class,
33        'close-open-topic' => \Flow\Api\ApiFlowLockTopic::class, // BC: has been renamed to lock-topic
34        'undo-edit-header' => \Flow\Api\ApiFlowUndoEditHeader::class,
35        'undo-edit-post' => \Flow\Api\ApiFlowUndoEditPost::class,
36        'undo-edit-topic-summary' => \Flow\Api\ApiFlowUndoEditTopicSummary::class,
37
38        // GET
39        // action 'view' exists in Topic.php & TopicList.php, for topic, post &
40        // topiclist - we'll want to know topic-/post- or topiclist-view ;)
41        'view-topiclist' => \Flow\Api\ApiFlowViewTopicList::class,
42        'view-post' => \Flow\Api\ApiFlowViewPost::class,
43        'view-post-history' => \Flow\Api\ApiFlowViewPostHistory::class,
44        'view-topic' => \Flow\Api\ApiFlowViewTopic::class,
45        'view-topic-history' => \Flow\Api\ApiFlowViewTopicHistory::class,
46        'view-header' => \Flow\Api\ApiFlowViewHeader::class,
47        'view-topic-summary' => \Flow\Api\ApiFlowViewTopicSummary::class,
48    ];
49
50    /**
51     * @param ApiMain $main
52     * @param string $action
53     */
54    public function __construct( $main, $action ) {
55        parent::__construct( $main, $action );
56        $this->moduleManager = new ApiModuleManager(
57            $this,
58            MediaWikiServices::getInstance()->getObjectFactory()
59        );
60
61        $enabledModules = self::ALWAYS_ENABLED_MODULES;
62
63        $this->moduleManager->addModules( $enabledModules, 'submodule' );
64    }
65
66    public function getModuleManager() {
67        return $this->moduleManager;
68    }
69
70    public function execute() {
71        // To avoid API warning, register the parameter used to bust browser cache
72        $this->getMain()->getVal( '_' );
73
74        $params = $this->extractRequestParams();
75        /** @var ApiFlowBase $module */
76        $module = $this->moduleManager->getModule( $params['submodule'], 'submodule' );
77        '@phan-var ApiFlowBase $module';
78
79        // The checks for POST and tokens are the same as ApiMain.php
80        $wasPosted = $this->getRequest()->wasPosted();
81        if ( !$wasPosted && $module->mustBePosted() ) {
82            $this->dieWithErrorOrDebug( [ 'apierror-mustbeposted', $params['submodule'] ] );
83        }
84
85        if ( $module->needsToken() ) {
86            if ( !isset( $params['token'] ) ) {
87                $this->dieWithError( [ 'apierror-missingparam', 'token' ] );
88            }
89
90            $module->requirePostedParameters( [ 'token' ] );
91
92            if ( !$module->validateToken( $params['token'], $params ) ) {
93                $this->dieWithError( 'apierror-badtoken' );
94            }
95        }
96
97        $module->extractRequestParams();
98        if ( $module->needsPage() ) {
99            $module->setPage( $this->getPage( $params ) );
100        }
101        try {
102            $module->execute();
103        } catch ( InvalidInputException $e ) {
104            $this->dieWithError( $e->getMessageObject(), null, null, 400 );
105        }
106        ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )->onAPIFlowAfterExecute( $module );
107    }
108
109    /**
110     * @param array $params
111     * @return Title
112     */
113    protected function getPage( $params ) {
114        $page = Title::newFromText( $params['page'] );
115        if ( !$page ) {
116            $this->dieWithError(
117                [ 'apierror-invalidtitle', wfEscapeWikiText( $params['page'] ) ], 'invalid-page'
118            );
119        }
120
121        if ( $page->getNamespace() < 0 ) {
122            $this->dieWithError(
123                [ 'apierror-invalidtitle', wfEscapeWikiText( $params['page'] ) ], 'invalid-page-negative-namespace'
124            );
125        }
126
127        /** @var \Flow\TalkpageManager $controller */
128        $controller = Container::get( 'occupation_controller' );
129        if ( $page->getContentModel() !== CONTENT_MODEL_FLOW_BOARD ) {
130            // Just check for permissions, nothing else to do. The Flow board
131            // will be put in place right before the rest of the data is stored
132            // (in SubmissionHandler::commit), after everything's been validated.
133            $status = $controller->safeAllowCreation( $page, $this->getUser(),
134                /* $mustNotExist = */ true, /* $forWrite = */ false );
135            if ( !$status->isGood() ) {
136                $this->dieWithError( [ 'apierror-flow-safeallowcreationfailed', $status->getMessage() ], 'invalid-page' );
137            }
138        }
139
140        // @phan-suppress-next-line PhanTypeMismatchReturnNullable T240141
141        return $page;
142    }
143
144    public function getAllowedParams() {
145        return [
146            'submodule' => [
147                ParamValidator::PARAM_REQUIRED => true,
148                ParamValidator::PARAM_TYPE => 'submodule',
149            ],
150            'page' => [
151                // supply bogus default - not every action may *need* ?page=
152                ParamValidator::PARAM_DEFAULT => Title::newFromText( 'Flow-enabled page', NS_TOPIC )->getPrefixedDBkey(),
153            ],
154            'token' => '',
155        ];
156    }
157
158    public function isWriteMode() {
159        // We can't use extractRequestParams() here because getHelpFlags() calls this function,
160        // and we'd error out because the submodule parameter isn't set.
161        $moduleName = $this->getMain()->getVal( 'submodule' );
162        $module = $this->moduleManager->getModule( $moduleName, 'submodule' );
163        return $module && $module->isWriteMode();
164    }
165
166    public function getHelpUrls() {
167        return [
168            'https://www.mediawiki.org/wiki/Extension:Flow/API',
169        ];
170    }
171
172    /**
173     * @inheritDoc
174     */
175    protected function getExamplesMessages() {
176        return [
177            'action=flow&submodule=edit-header&page=Talk:Sandbox&ehprev_revision=???&ehcontent=Nice%20to&20meet%20you'
178                => 'apihelp-flow-example-1',
179        ];
180    }
181
182    public function mustBePosted() {
183        return false;
184    }
185
186    public function needsToken() {
187        return false;
188    }
189}