MediaWiki master
SpecialChangeContentModel.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\Specials;
4
25
30
31 public function __construct(
32 private readonly IContentHandlerFactory $contentHandlerFactory,
33 private readonly ContentModelChangeFactory $contentModelChangeFactory,
34 private readonly SpamChecker $spamChecker,
35 private readonly RevisionLookup $revisionLookup,
36 private readonly WikiPageFactory $wikiPageFactory,
37 private readonly SearchEngineFactory $searchEngineFactory,
38 private readonly CollationFactory $collationFactory
39 ) {
40 parent::__construct( 'ChangeContentModel', 'editcontentmodel' );
41 }
42
44 public function doesWrites() {
45 return true;
46 }
47
51 private $title;
52
58 private $oldRevision;
59
61 protected function setParameter( $par ) {
62 $par = $this->getRequest()->getVal( 'pagetitle', $par );
63 $title = Title::newFromText( $par );
64 if ( $title ) {
65 $this->title = $title;
66 $this->par = $title->getPrefixedText();
67 } else {
68 $this->par = '';
69 }
70 }
71
73 protected function postHtml() {
74 $text = '';
75 if ( $this->title ) {
76 $contentModelLogPage = new LogPage( 'contentmodel' );
77 $text = Html::element( 'h2', [], $contentModelLogPage->getName()->text() );
78 $out = '';
79 LogEventsList::showLogExtract( $out, 'contentmodel', $this->title );
80 $text .= $out;
81 }
82 return $text;
83 }
84
86 protected function getDisplayFormat() {
87 return 'ooui';
88 }
89
90 protected function alterForm( HTMLForm $form ) {
91 $this->addHelpLink( 'Help:ChangeContentModel' );
92
93 if ( $this->title ) {
94 $form->setFormIdentifier( 'modelform' );
95 } else {
96 $form->setFormIdentifier( 'titleform' );
97 }
98
99 // T120576
100 $form->setSubmitTextMsg( 'changecontentmodel-submit' );
101
102 if ( $this->title ) {
103 $this->getOutput()->addBacklinkSubtitle( $this->title );
104 }
105 }
106
111 private function validateTitle( $title ) {
112 // Already validated by HTMLForm, but if not, throw
113 // an exception instead of a fatal
114 $titleObj = Title::newFromTextThrow( $title );
115
116 $this->oldRevision = $this->revisionLookup->getRevisionByTitle( $titleObj ) ?: false;
117
118 if ( $this->oldRevision ) {
119 $oldContent = $this->oldRevision->getContent( SlotRecord::MAIN );
120 if ( !$oldContent->getContentHandler()->supportsDirectEditing() ) {
121 return $this->msg( 'changecontentmodel-nodirectediting' )
122 ->params( ContentHandler::getLocalizedName( $oldContent->getModel() ) )
123 ->escaped();
124 }
125 }
126
127 return true;
128 }
129
131 protected function getFormFields() {
132 $fields = [
133 'pagetitle' => [
134 'type' => 'title',
135 'creatable' => true,
136 'name' => 'pagetitle',
137 'default' => $this->par,
138 'label-message' => 'changecontentmodel-title-label',
139 'validation-callback' => $this->validateTitle( ... ),
140 ],
141 ];
142 if ( $this->title ) {
143 $options = $this->getOptionsForTitle( $this->title );
144 if ( !$options ) {
145 throw new ErrorPageError(
146 'changecontentmodel-emptymodels-title',
147 'changecontentmodel-emptymodels-text',
148 [ $this->title->getPrefixedText() ]
149 );
150 }
151 $fields['pagetitle']['readonly'] = true;
152 $fields += [
153 'model' => [
154 'type' => 'select',
155 'name' => 'model',
156 'default' => $this->title->getContentModel(),
157 'options' => $options,
158 'label-message' => 'changecontentmodel-model-label'
159 ],
160 'reason' => [
161 'type' => 'text',
162 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
163 'name' => 'reason',
164 'validation-callback' => function ( $reason ) {
165 if ( $reason === null || $reason === '' ) {
166 // Null on form display, or no reason given
167 return true;
168 }
169
170 $match = $this->spamChecker->checkSummary( $reason );
171
172 if ( $match ) {
173 return $this->msg( 'spamprotectionmatch', $match )->parse();
174 }
175
176 return true;
177 },
178 'label-message' => 'changecontentmodel-reason-label',
179 ],
180 ];
181 }
182
183 return $fields;
184 }
185
191 private function getOptionsForTitle( ?Title $title = null ) {
192 $models = $this->contentHandlerFactory->getContentModels();
193 $options = [];
194 foreach ( $models as $model ) {
195 $handler = $this->contentHandlerFactory->getContentHandler( $model );
196 if ( !$handler->supportsDirectEditing() ) {
197 continue;
198 }
199 if ( $title ) {
200 if ( !$handler->canBeUsedOn( $title ) ) {
201 continue;
202 }
203 }
204 $options[ContentHandler::getLocalizedName( $model )] = $model;
205 }
206
207 // Put the options in the drop-down list in alphabetical order.
208 // Sort by array key, case insensitive.
209 $collation = $this->collationFactory->getCategoryCollation();
210 uksort( $options, static function ( $a, $b ) use ( $collation ) {
211 $a = $collation->getSortKey( $a );
212 $b = $collation->getSortKey( $b );
213 return strcmp( $a, $b );
214 } );
215
216 return $options;
217 }
218
220 public function onSubmit( array $data ) {
221 $this->title = Title::newFromText( $data['pagetitle'] );
222 $page = $this->wikiPageFactory->newFromTitle( $this->title );
223
224 $changer = $this->contentModelChangeFactory->newContentModelChange(
225 $this->getContext()->getAuthority(),
226 $page,
227 $data['model']
228 );
229
230 $permissionStatus = $changer->authorizeChange();
231 if ( !$permissionStatus->isGood() ) {
232 $out = $this->getOutput();
233 $wikitext = $out->formatPermissionStatus( $permissionStatus );
234 // Hack to get our wikitext parsed
235 return Status::newFatal( new RawMessage( '$1', [ $wikitext ] ) );
236 }
237
238 $status = $changer->doContentModelChange(
239 $this->getContext(),
240 $data['reason'],
241 true
242 );
243
244 return $status;
245 }
246
247 public function onSuccess() {
248 $out = $this->getOutput();
249 $out->setPageTitleMsg( $this->msg( 'changecontentmodel-success-title' ) );
250 $out->addWikiMsg( 'changecontentmodel-success-text', $this->title->getPrefixedText() );
251 }
252
261 public function prefixSearchSubpages( $search, $limit, $offset ) {
262 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
263 }
264
266 protected function getGroupName() {
267 return 'pagetools';
268 }
269}
270
272class_alias( SpecialChangeContentModel::class, 'SpecialChangeContentModel' );
Common factory to construct collation classes.
Handle database storage of comments such as edit summaries and log reasons.
Base class for content handling.
Service to check if text (either content or a summary) qualifies as spam.
An error page which can definitely be safely rendered using the OutputPage.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:207
setFormIdentifier(string $ident, bool $single=false)
Set an internal identifier for this form.
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
This class is a collection of static functions that serve two purposes:
Definition Html.php:43
Variant of the Message class.
Class to simplify the use of log pages.
Definition LogPage.php:35
Service for creating WikiPage objects.
Page revision base class.
Value object representing a content slot associated with a page revision.
Factory class for SearchEngine.
Special page which uses an HTMLForm to handle processing.
string null $par
The subpage of the special page.
getContext()
Gets the context this SpecialPage is executed in.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
prefixSearchString( $search, $limit, $offset, ?SearchEngineFactory $searchEngineFactory=null)
Perform a regular substring search for prefixSearchSubpages.
getAuthority()
Shortcut to get the Authority executing this instance.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
postHtml()
Add post-HTML to the form.string HTML which will be sent to $form->addPostHtml() 1....
setParameter( $par)
Maybe do something interesting with the subpage parameter.
getDisplayFormat()
Get display format for the form.See HTMLForm documentation for available values.1....
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
__construct(private readonly IContentHandlerFactory $contentHandlerFactory, private readonly ContentModelChangeFactory $contentModelChangeFactory, private readonly SpamChecker $spamChecker, private readonly RevisionLookup $revisionLookup, private readonly WikiPageFactory $wikiPageFactory, private readonly SearchEngineFactory $searchEngineFactory, private readonly CollationFactory $collationFactory)
onSubmit(array $data)
Process the form on submission.bool|string|array|Status As documented for HTMLForm::trySubmit.
getFormFields()
Get an HTMLForm descriptor array.array
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
doesWrites()
Indicates whether POST requests to this special page require write access to the wiki....
onSuccess()
Do something exciting on successful processing of the form, most likely to show a confirmation messag...
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Represents a title within MediaWiki.
Definition Title.php:69
getPrefixedText()
Get the prefixed title with spaces.
Definition Title.php:1857
Service for changing the content model of wiki pages.
Service for looking up page revisions.
element(SerializerNode $parent, SerializerNode $node, $contents)