Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
39 / 39 |
|
100.00% |
5 / 5 |
CRAP | |
100.00% |
1 / 1 |
EditFilterMergedContentHookConstraint | |
100.00% |
39 / 39 |
|
100.00% |
5 / 5 |
12 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
checkConstraint | |
100.00% |
24 / 24 |
|
100.00% |
1 / 1 |
7 | |||
getLegacyStatus | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getHookError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
formatStatusErrors | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | namespace MediaWiki\EditPage\Constraint; |
22 | |
23 | use ApiMessage; |
24 | use Content; |
25 | use Language; |
26 | use MediaWiki\Context\IContextSource; |
27 | use MediaWiki\HookContainer\HookContainer; |
28 | use MediaWiki\HookContainer\HookRunner; |
29 | use MediaWiki\Html\Html; |
30 | use MediaWiki\Message\Message; |
31 | use MediaWiki\Status\Status; |
32 | use MediaWiki\User\User; |
33 | use StatusValue; |
34 | |
35 | /** |
36 | * Verify `EditFilterMergedContent` hook |
37 | * |
38 | * @since 1.36 |
39 | * @author DannyS712 |
40 | * @internal |
41 | */ |
42 | class EditFilterMergedContentHookConstraint implements IEditConstraint { |
43 | |
44 | /** @var HookRunner */ |
45 | private $hookRunner; |
46 | |
47 | /** @var Content */ |
48 | private $content; |
49 | |
50 | /** @var IContextSource */ |
51 | private $hookContext; |
52 | |
53 | /** @var string */ |
54 | private $summary; |
55 | |
56 | /** @var bool */ |
57 | private $minorEdit; |
58 | |
59 | /** @var Language */ |
60 | private $language; |
61 | |
62 | /** @var User */ |
63 | private $hookUser; |
64 | |
65 | /** @var Status */ |
66 | private $status; |
67 | |
68 | /** @var string */ |
69 | private $hookError = ''; |
70 | |
71 | /** |
72 | * @param HookContainer $hookContainer |
73 | * @param Content $content |
74 | * @param IContextSource $hookContext NOTE: This should only be passed to the hook. |
75 | * @param string $summary |
76 | * @param bool $minorEdit |
77 | * @param Language $language |
78 | * @param User $hookUser NOTE: This should only be passed to the hook. |
79 | */ |
80 | public function __construct( |
81 | HookContainer $hookContainer, |
82 | Content $content, |
83 | IContextSource $hookContext, |
84 | string $summary, |
85 | bool $minorEdit, |
86 | Language $language, |
87 | User $hookUser |
88 | ) { |
89 | $this->hookRunner = new HookRunner( $hookContainer ); |
90 | $this->content = $content; |
91 | $this->hookContext = $hookContext; |
92 | $this->summary = $summary; |
93 | $this->minorEdit = $minorEdit; |
94 | $this->language = $language; |
95 | $this->hookUser = $hookUser; |
96 | $this->status = Status::newGood(); |
97 | } |
98 | |
99 | public function checkConstraint(): string { |
100 | $hookResult = $this->hookRunner->onEditFilterMergedContent( |
101 | $this->hookContext, |
102 | $this->content, |
103 | $this->status, |
104 | $this->summary, |
105 | $this->hookUser, |
106 | $this->minorEdit |
107 | ); |
108 | if ( !$hookResult ) { |
109 | // Error messages etc. could be handled within the hook... |
110 | if ( $this->status->isGood() ) { |
111 | $this->status->fatal( 'hookaborted' ); |
112 | // Not setting $this->hookError here is a hack to allow the hook |
113 | // to cause a return to the edit page without $this->hookError |
114 | // being set. This is used by ConfirmEdit to display a captcha |
115 | // without any error message cruft. |
116 | } else { |
117 | if ( !$this->status->getErrors() ) { |
118 | // Provide a fallback error message if none was set |
119 | $this->status->fatal( 'hookaborted' ); |
120 | } |
121 | $this->hookError = $this->formatStatusErrors( $this->status ); |
122 | } |
123 | // Use the existing $status->value if the hook set it |
124 | if ( !$this->status->value ) { |
125 | // T273354: Should be AS_HOOK_ERROR_EXPECTED to display error message |
126 | $this->status->value = self::AS_HOOK_ERROR_EXPECTED; |
127 | } |
128 | return self::CONSTRAINT_FAILED; |
129 | } |
130 | |
131 | if ( !$this->status->isOK() ) { |
132 | // ...or the hook could be expecting us to produce an error |
133 | // FIXME this sucks, we should just use the Status object throughout |
134 | if ( !$this->status->getErrors() ) { |
135 | // Provide a fallback error message if none was set |
136 | $this->status->fatal( 'hookaborted' ); |
137 | } |
138 | $this->hookError = $this->formatStatusErrors( $this->status ); |
139 | $this->status->value = self::AS_HOOK_ERROR_EXPECTED; |
140 | return self::CONSTRAINT_FAILED; |
141 | } |
142 | |
143 | return self::CONSTRAINT_PASSED; |
144 | } |
145 | |
146 | public function getLegacyStatus(): StatusValue { |
147 | // This returns a Status instead of a StatusValue since a Status object is |
148 | // used in the hook |
149 | return $this->status; |
150 | } |
151 | |
152 | /** |
153 | * TODO this is really ugly. The constraint shouldn't know that the status |
154 | * will be used as wikitext, with is what the hookError represents, rather |
155 | * than just the error code. This needs a big refactor to remove the hook |
156 | * error string and just rely on the status object entirely. |
157 | * |
158 | * @internal |
159 | * @return string |
160 | */ |
161 | public function getHookError(): string { |
162 | return $this->hookError; |
163 | } |
164 | |
165 | /** |
166 | * Wrap status errors in error boxes for increased visibility. |
167 | * @param Status $status |
168 | * @return string |
169 | */ |
170 | private function formatStatusErrors( Status $status ): string { |
171 | $ret = ''; |
172 | foreach ( $status->getErrors() as $rawError ) { |
173 | // XXX: This interface is ugly, but it seems to be the only convenient way to convert a message specifier |
174 | // as used in Status to a Message without all the cruft that Status::getMessage & friends add. |
175 | $msg = Message::newFromSpecifier( ApiMessage::create( $rawError ) ); |
176 | $ret .= Html::errorBox( "\n" . $msg->inLanguage( $this->language )->plain() . "\n" ); |
177 | } |
178 | return $ret; |
179 | } |
180 | |
181 | } |