Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
70.00% |
77 / 110 |
|
60.00% |
6 / 10 |
CRAP | |
0.00% |
0 / 1 |
EntitySchemaEditAction | |
70.00% |
77 / 110 |
|
60.00% |
6 / 10 |
35.07 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 | |||
show | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
1 | |||
onSubmit | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
72 | |||
alterForm | |
96.15% |
25 / 26 |
|
0.00% |
0 / 1 |
4 | |||
getCopyrightHTML | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getFormFields | |
96.43% |
27 / 28 |
|
0.00% |
0 / 1 |
2 | |||
usesOOUI | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
onSuccess | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRestriction | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | declare( strict_types = 1 ); |
4 | |
5 | namespace EntitySchema\MediaWiki\Actions; |
6 | |
7 | use Article; |
8 | use EntitySchema\DataAccess\EditConflict; |
9 | use EntitySchema\DataAccess\MediaWikiRevisionEntitySchemaUpdater; |
10 | use EntitySchema\Domain\Model\EntitySchemaId; |
11 | use EntitySchema\MediaWiki\Content\EntitySchemaContent; |
12 | use EntitySchema\Presentation\InputValidator; |
13 | use EntitySchema\Services\Converter\EntitySchemaConverter; |
14 | use FormAction; |
15 | use HTMLForm; |
16 | use IContextSource; |
17 | use MediaWiki\MediaWikiServices; |
18 | use MediaWiki\Revision\SlotRecord; |
19 | use MediaWiki\Status\Status; |
20 | use MediaWiki\User\Options\UserOptionsLookup; |
21 | use MediaWiki\User\TempUser\TempUserConfig; |
22 | use RuntimeException; |
23 | use Wikibase\Repo\CopyrightMessageBuilder; |
24 | use Wikibase\Repo\Specials\SpecialPageCopyrightView; |
25 | |
26 | /** |
27 | * Edit a EntitySchema via the mediawiki editing action |
28 | * |
29 | * @license GPL-2.0-or-later |
30 | */ |
31 | class EntitySchemaEditAction extends FormAction { |
32 | |
33 | public const FIELD_SCHEMA_TEXT = 'schema-text'; |
34 | public const FIELD_BASE_REV = 'base-rev'; |
35 | public const FIELD_EDIT_SUMMARY = 'edit-summary'; |
36 | public const FIELD_IGNORE_EMPTY_SUMMARY = 'ignore-blank-summary'; |
37 | |
38 | private InputValidator $inputValidator; |
39 | private string $submitMsgKey; |
40 | private UserOptionsLookup $userOptionsLookup; |
41 | private SpecialPageCopyrightView $copyrightView; |
42 | private TempUserConfig $tempUserConfig; |
43 | |
44 | public function __construct( |
45 | Article $article, |
46 | IContextSource $context, |
47 | InputValidator $inputValidator, |
48 | bool $editSubmitButtonLabelPublish, |
49 | UserOptionsLookup $userOptionsLookup, |
50 | string $dataRightsUrl, |
51 | string $dataRightsText, |
52 | TempUserConfig $tempUserConfig |
53 | ) { |
54 | parent::__construct( $article, $context ); |
55 | $this->inputValidator = $inputValidator; |
56 | $this->userOptionsLookup = $userOptionsLookup; |
57 | $this->submitMsgKey = $editSubmitButtonLabelPublish ? 'publishchanges' : 'savechanges'; |
58 | $this->copyrightView = new SpecialPageCopyrightView( |
59 | new CopyrightMessageBuilder(), |
60 | $dataRightsUrl, |
61 | $dataRightsText |
62 | ); |
63 | $this->tempUserConfig = $tempUserConfig; |
64 | } |
65 | |
66 | public function show(): void { |
67 | parent::show(); |
68 | |
69 | $output = $this->getOutput(); |
70 | $output->clearSubtitle(); |
71 | $output->addModules( [ |
72 | 'ext.EntitySchema.action.edit', |
73 | ] ); |
74 | $output->addJsConfigVars( |
75 | 'wgEntitySchemaSchemaTextMaxSizeBytes', |
76 | intval( $this->getContext()->getConfig()->get( 'EntitySchemaSchemaTextMaxSizeBytes' ) ) |
77 | ); |
78 | } |
79 | |
80 | /** |
81 | * Process the form on POST submission. |
82 | * |
83 | * If you don't want to do anything with the form, just return false here. |
84 | * |
85 | * This method will be passed to the HTMLForm as a submit callback (see |
86 | * HTMLForm::setSubmitCallback) and must return as documented for HTMLForm::trySubmit. |
87 | * |
88 | * @see HTMLForm::setSubmitCallback() |
89 | * @see HTMLForm::trySubmit() |
90 | * |
91 | * @param array $data |
92 | * |
93 | * @return bool|string|array|Status Must return as documented for HTMLForm::trySubmit |
94 | */ |
95 | public function onSubmit( $data ) { |
96 | /** |
97 | * @var $content EntitySchemaContent |
98 | */ |
99 | $request = $this->getContext()->getRequest(); |
100 | $output = $this->getOutput(); |
101 | $context = $this->getContext(); |
102 | $revisionStore = MediaWikiServices::getInstance()->getRevisionStore(); |
103 | $currentRevision = $revisionStore->getKnownCurrentRevision( $context->getTitle() ); |
104 | if ( !$currentRevision ) { |
105 | return Status::newFatal( $this->msg( 'entityschema-error-schemadeleted' ) ); |
106 | } |
107 | |
108 | $content = $currentRevision->getContent( SlotRecord::MAIN ); |
109 | if ( !$content instanceof EntitySchemaContent ) { |
110 | return Status::newFatal( $this->msg( 'entityschema-error-schemadeleted' ) ); |
111 | } |
112 | |
113 | $user = $this->getUser(); |
114 | if ( |
115 | $data['edit-summary'] === '' |
116 | && $this->userOptionsLookup->getOption( $user, 'forceeditsummary' ) === '1' |
117 | && !$request->getBool( self::FIELD_IGNORE_EMPTY_SUMMARY ) |
118 | ) { |
119 | return $output->wrapWikiMsg( "<div id='mw-missingsummary'>\n$1\n</div>", |
120 | [ 'missingsummary', $this->msg( $this->submitMsgKey )->text() ] ); |
121 | } |
122 | $id = new EntitySchemaId( $this->getTitle()->getText() ); |
123 | $schemaUpdater = MediaWikiRevisionEntitySchemaUpdater::newFromContext( $this->getContext() ); |
124 | |
125 | try { |
126 | $schemaUpdater->updateSchemaText( |
127 | $id, |
128 | $data[self::FIELD_SCHEMA_TEXT], |
129 | (int)$data[self::FIELD_BASE_REV], |
130 | trim( $data[self::FIELD_EDIT_SUMMARY] ) |
131 | ); |
132 | } catch ( EditConflict $e ) { |
133 | return Status::newFatal( 'entityschema-error-schematext-conflict' ); |
134 | } catch ( RuntimeException $e ) { |
135 | return Status::newFatal( 'entityschema-error-schemaupdate-failed' ); |
136 | } |
137 | |
138 | return Status::newGood(); |
139 | } |
140 | |
141 | protected function alterForm( HTMLForm $form ): void { |
142 | $form->suppressDefaultSubmit(); |
143 | $request = $this->getContext()->getRequest(); |
144 | if ( $request->getVal( 'wpedit-summary' ) === '' ) { |
145 | $form->addHiddenField( self::FIELD_IGNORE_EMPTY_SUMMARY, true ); |
146 | } |
147 | |
148 | $form->addFields( [ [ |
149 | 'type' => 'info', |
150 | 'default' => $this->getCopyrightHTML(), |
151 | 'raw' => true, |
152 | ] ] ); |
153 | if ( $this->getUser()->isAnon() && !$this->tempUserConfig->isEnabled() ) { |
154 | $form->addFields( [ [ |
155 | 'type' => 'info', |
156 | 'default' => $this->msg( |
157 | 'entityschema-anonymouseditwarning' |
158 | )->parse(), |
159 | 'raw' => true, |
160 | ] ] ); |
161 | } |
162 | |
163 | $form->addButton( [ |
164 | 'name' => 'wpSave', |
165 | 'value' => $this->msg( $this->submitMsgKey )->text(), |
166 | 'label' => $this->msg( $this->submitMsgKey )->text(), |
167 | 'attribs' => [ 'accessKey' => $this->msg( 'accesskey-save' )->plain() ], |
168 | 'flags' => [ 'primary', 'progressive' ], |
169 | 'type' => 'submit', |
170 | ] ); |
171 | $form->setValidationErrorMessage( [ [ 'entityschema-error-one-more-message-available' ] ] ); |
172 | } |
173 | |
174 | /** |
175 | * @return string HTML |
176 | */ |
177 | private function getCopyrightHTML() { |
178 | return $this->copyrightView |
179 | ->getHtml( $this->getLanguage(), $this->submitMsgKey ); |
180 | } |
181 | |
182 | protected function getFormFields(): array { |
183 | $context = $this->getContext(); |
184 | $revisionStore = MediaWikiServices::getInstance()->getRevisionStore(); |
185 | $currentRevRecord = $revisionStore->getKnownCurrentRevision( $context->getTitle() ); |
186 | if ( !$currentRevRecord ) { |
187 | throw new RuntimeException( $this->msg( 'entityschema-error-schemadeleted' )->text() ); |
188 | } |
189 | |
190 | /** @var EntitySchemaContent $content */ |
191 | $content = $currentRevRecord->getContent( SlotRecord::MAIN ); |
192 | // @phan-suppress-next-line PhanUndeclaredMethod |
193 | $schemaText = ( new EntitySchemaConverter() )->getSchemaText( $content->getText() ); |
194 | $baseRev = $this->getContext()->getRequest()->getInt( |
195 | 'wp' . self::FIELD_BASE_REV, |
196 | $currentRevRecord->getId() |
197 | ); |
198 | |
199 | return [ |
200 | self::FIELD_SCHEMA_TEXT => [ |
201 | 'type' => 'textarea', |
202 | 'default' => $schemaText, |
203 | 'label-message' => 'entityschema-editpage-schema-inputlabel', |
204 | 'validation-callback' => [ $this->inputValidator, 'validateSchemaTextLength' ], |
205 | ], |
206 | self::FIELD_BASE_REV => [ |
207 | 'type' => 'hidden', |
208 | 'default' => $baseRev, |
209 | ], |
210 | self::FIELD_EDIT_SUMMARY => [ |
211 | 'type' => 'text', |
212 | 'default' => '', |
213 | 'label-message' => 'entityschema-summary-generated', |
214 | ], |
215 | ]; |
216 | } |
217 | |
218 | protected function usesOOUI(): bool { |
219 | return true; |
220 | } |
221 | |
222 | /** |
223 | * Do something exciting on successful processing of the form. This might be to show |
224 | * a confirmation message (watch, rollback, etc) or to redirect somewhere else (edit, |
225 | * protect, etc). |
226 | */ |
227 | public function onSuccess(): void { |
228 | $redirectParams = $this->getRequest()->getVal( 'redirectparams', '' ); |
229 | $this->getOutput()->redirect( $this->getTitle()->getFullURL( $redirectParams ) ); |
230 | } |
231 | |
232 | /** |
233 | * Return the name of the action this object responds to |
234 | * |
235 | * @return string Lowercase name |
236 | */ |
237 | public function getName(): string { |
238 | return 'edit'; |
239 | } |
240 | |
241 | public function getRestriction(): string { |
242 | return $this->getName(); |
243 | } |
244 | |
245 | } |