Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
3.57% covered (danger)
3.57%
2 / 56
5.00% covered (danger)
5.00%
1 / 20
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractBlock
3.57% covered (danger)
3.57%
2 / 56
5.00% covered (danger)
5.00%
1 / 20
730.96
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 validate
n/a
0 / 0
n/a
0 / 0
0
 commit
n/a
0 / 0
n/a
0 / 0
0
 init
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getContext
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 canSubmit
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 canRender
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getTemplate
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 onSubmit
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 wasSubmitted
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 hasErrors
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getErrors
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getErrorMessage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getErrorExtra
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addError
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getWorkflow
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWorkflowId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getStorage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getActionName
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 checkSpamFilters
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 getEditToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPageTitle
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace Flow\Block;
4
5use Flow\Container;
6use Flow\Conversion\Utils;
7use Flow\Data\ManagerGroup;
8use Flow\Exception\InvalidInputException;
9use Flow\FlowActions;
10use Flow\Model\AbstractRevision;
11use Flow\Model\Workflow;
12use Flow\RevisionActionPermissions;
13use MediaWiki\Context\IContextSource;
14use MediaWiki\Message\Message;
15use MediaWiki\Output\OutputPage;
16
17abstract class AbstractBlock implements Block {
18
19    /** @var Workflow */
20    protected $workflow;
21    /** @var ManagerGroup */
22    protected $storage;
23
24    /** @var IContextSource */
25    protected $context;
26    /** @var array|null */
27    protected $submitted = null;
28    /** @var array */
29    protected $errors = [];
30
31    /**
32     * @var string|null The commitable action being submitted, or null
33     *  for read-only actions.
34     */
35    protected $action;
36
37    /** @var RevisionActionPermissions */
38    protected $permissions;
39
40    /**
41     * A list of supported post actions
42     * @var array
43     */
44    protected $supportedPostActions = [];
45
46    /**
47     * A list of supported get actions
48     * @var array
49     */
50    protected $supportedGetActions = [];
51
52    /**
53     * Templates for each view actions
54     * @var string[]
55     */
56    protected $templates = [];
57
58    public function __construct( Workflow $workflow, ManagerGroup $storage ) {
59        $this->workflow = $workflow;
60        $this->storage = $storage;
61    }
62
63    /**
64     * Called by $this->onSubmit to populate $this->errors based
65     * on $this->action and $this->submitted.
66     */
67    abstract protected function validate();
68
69    /**
70     * @inheritDoc
71     */
72    abstract public function commit();
73
74    /**
75     * @param IContextSource $context
76     * @param string $action
77     */
78    public function init( IContextSource $context, $action ) {
79        $this->context = $context;
80        $this->action = $action;
81        $this->permissions = new RevisionActionPermissions( Container::get( 'flow_actions' ), $context->getUser() );
82    }
83
84    /**
85     * @return IContextSource
86     */
87    public function getContext() {
88        return $this->context;
89    }
90
91    /**
92     * Returns true if the block can submit the requested action, or false
93     * otherwise.
94     *
95     * @param string $action
96     * @return bool
97     */
98    public function canSubmit( $action ) {
99        return in_array( $this->getActionName( $action ), $this->supportedPostActions );
100    }
101
102    /**
103     * Returns true if the block can render the requested action, or false
104     * otherwise.
105     *
106     * @param string $action
107     * @return bool
108     */
109    public function canRender( $action ) {
110        return // GET actions can be rendered
111            in_array( $this->getActionName( $action ), $this->supportedGetActions ) ||
112            // POST actions are usually redirected to 'view' after successfully
113            // completing the request, but can also be rendered (e.g. to show
114            // error message after unsuccessful submission)
115            $this->canSubmit( $action );
116    }
117
118    /**
119     * Get the template name for a specific action or an array of template
120     * for all possible view actions in this block
121     *
122     * @param string|null $action
123     * @return string|array
124     * @throws InvalidInputException
125     */
126    public function getTemplate( $action = null ) {
127        if ( $action === null ) {
128            return $this->templates;
129        }
130        if ( !isset( $this->templates[$action] ) ) {
131            throw new InvalidInputException( 'Template is not defined for action: ' . $action, 'invalid-input' );
132        }
133        return $this->templates[$action];
134    }
135
136    /**
137     * @param array $data
138     * @return bool|null true when accepted, false when not accepted.
139     *  null when this action does not support submission.
140     */
141    public function onSubmit( array $data ) {
142        if ( !$this->canSubmit( $this->action ) ) {
143            return null;
144        }
145
146        $this->submitted = $data;
147        $this->validate();
148
149        return !$this->hasErrors();
150    }
151
152    public function wasSubmitted() {
153        return $this->submitted !== null;
154    }
155
156    /**
157     * Checks if any errors have occurred in the block (no argument), or if a
158     * specific error has occurred (argument being the error type)
159     *
160     * @param string|null $type
161     * @return bool
162     */
163    public function hasErrors( $type = null ) {
164        if ( $type === null ) {
165            return (bool)$this->errors;
166        }
167        return isset( $this->errors[$type] );
168    }
169
170    /**
171     * Returns an array of all error types encountered in this block. The values
172     * in the returned array can be used to pass to getErrorMessage() or
173     * getErrorExtra() to respectively fetch the specific error message or
174     * additional details.
175     *
176     * @return array
177     */
178    public function getErrors() {
179        return array_keys( $this->errors );
180    }
181
182    /**
183     * @param string $type
184     * @return Message
185     */
186    public function getErrorMessage( $type ) {
187        return $this->errors[$type]['message'] ?? null;
188    }
189
190    /**
191     * @param string $type
192     * @return mixed
193     */
194    public function getErrorExtra( $type ) {
195        return $this->errors[$type]['extra'] ?? null;
196    }
197
198    /**
199     * @param string $type
200     * @param Message $message
201     * @param mixed|null $extra
202     */
203    public function addError( $type, Message $message, $extra = null ) {
204        $this->errors[$type] = [
205            'message' => $message,
206            'extra' => $extra,
207        ];
208    }
209
210    public function getWorkflow() {
211        return $this->workflow;
212    }
213
214    public function getWorkflowId() {
215        return $this->workflow->getId();
216    }
217
218    public function getStorage() {
219        return $this->storage;
220    }
221
222    /**
223     * Given a certain action name, this returns the valid action name. This is
224     * meant for BC compatibility with renamed actions.
225     *
226     * @param string $action
227     * @return string
228     */
229    public function getActionName( $action ) {
230        // BC for renamed actions
231        /** @var FlowActions $actions */
232        $actions = Container::get( 'flow_actions' );
233        $alias = $actions->getValue( $action );
234        if ( is_string( $alias ) ) {
235            // All proper actions return arrays, but aliases return a string
236            $action = $alias;
237        }
238
239        return $action;
240    }
241
242    /**
243     * Run through AbuseFilter and friends.
244     * @todo Having to call spamFilter in each place that creates a revision
245     *  is error-prone.
246     *
247     * @param AbstractRevision|null $old null when $new is first revision
248     * @param AbstractRevision $new
249     * @return bool True when content is allowed by spam filter
250     */
251    protected function checkSpamFilters( ?AbstractRevision $old, AbstractRevision $new ) {
252        /** @var \Flow\SpamFilter\Controller $spamFilter */
253        $spamFilter = Container::get( 'controller.spamfilter' );
254        $status = $spamFilter->validate( $this->context, $new, $old, $this->workflow->getArticleTitle(), $this->workflow->getOwnerTitle() );
255        if ( $status->isOK() ) {
256            return true;
257        }
258
259        $message = $status->getMessage();
260
261        $details = $status->getValue();
262
263        $this->addError( 'spamfilter', $message, [
264            'messageKey' => $message->getKey(),
265            'details' => $details,
266        ] );
267        return false;
268    }
269
270    /**
271     * @return string The new edit token
272     */
273    public function getEditToken() {
274        return $this->context->getUser()->getEditToken();
275    }
276
277    /**
278     * @param OutputPage $out
279     */
280    public function setPageTitle( OutputPage $out ) {
281        if ( $out->getPageTitle() ) {
282            // Don't override page title if another block has already set it.
283            // If this should *really* be done, the specific block extending
284            // this AbstractBlock should just implement this itself ;)
285            return;
286        }
287
288        $title = $this->workflow->getArticleTitle();
289        $convertedTitle = Utils::getConvertedTitle( $title );
290        $out->setPageTitle( $convertedTitle );
291    }
292}