MediaWiki master
SpecialChangeContentModel.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\Specials;
4
25
30
31 private IContentHandlerFactory $contentHandlerFactory;
32 private ContentModelChangeFactory $contentModelChangeFactory;
33 private SpamChecker $spamChecker;
34 private RevisionLookup $revisionLookup;
35 private WikiPageFactory $wikiPageFactory;
36 private SearchEngineFactory $searchEngineFactory;
37 private CollationFactory $collationFactory;
38
39 public function __construct(
40 IContentHandlerFactory $contentHandlerFactory,
41 ContentModelChangeFactory $contentModelChangeFactory,
42 SpamChecker $spamChecker,
43 RevisionLookup $revisionLookup,
44 WikiPageFactory $wikiPageFactory,
45 SearchEngineFactory $searchEngineFactory,
46 CollationFactory $collationFactory
47 ) {
48 parent::__construct( 'ChangeContentModel', 'editcontentmodel' );
49
50 $this->contentHandlerFactory = $contentHandlerFactory;
51 $this->contentModelChangeFactory = $contentModelChangeFactory;
52 $this->spamChecker = $spamChecker;
53 $this->revisionLookup = $revisionLookup;
54 $this->wikiPageFactory = $wikiPageFactory;
55 $this->searchEngineFactory = $searchEngineFactory;
56 $this->collationFactory = $collationFactory;
57 }
58
60 public function doesWrites() {
61 return true;
62 }
63
67 private $title;
68
74 private $oldRevision;
75
77 protected function setParameter( $par ) {
78 $par = $this->getRequest()->getVal( 'pagetitle', $par );
79 $title = Title::newFromText( $par );
80 if ( $title ) {
81 $this->title = $title;
82 $this->par = $title->getPrefixedText();
83 } else {
84 $this->par = '';
85 }
86 }
87
89 protected function postHtml() {
90 $text = '';
91 if ( $this->title ) {
92 $contentModelLogPage = new LogPage( 'contentmodel' );
93 $text = Html::element( 'h2', [], $contentModelLogPage->getName()->text() );
94 $out = '';
95 LogEventsList::showLogExtract( $out, 'contentmodel', $this->title );
96 $text .= $out;
97 }
98 return $text;
99 }
100
102 protected function getDisplayFormat() {
103 return 'ooui';
104 }
105
106 protected function alterForm( HTMLForm $form ) {
107 $this->addHelpLink( 'Help:ChangeContentModel' );
108
109 if ( $this->title ) {
110 $form->setFormIdentifier( 'modelform' );
111 } else {
112 $form->setFormIdentifier( 'titleform' );
113 }
114
115 // T120576
116 $form->setSubmitTextMsg( 'changecontentmodel-submit' );
117
118 if ( $this->title ) {
119 $this->getOutput()->addBacklinkSubtitle( $this->title );
120 }
121 }
122
127 private function validateTitle( $title ) {
128 // Already validated by HTMLForm, but if not, throw
129 // an exception instead of a fatal
130 $titleObj = Title::newFromTextThrow( $title );
131
132 $this->oldRevision = $this->revisionLookup->getRevisionByTitle( $titleObj ) ?: false;
133
134 if ( $this->oldRevision ) {
135 $oldContent = $this->oldRevision->getContent( SlotRecord::MAIN );
136 if ( !$oldContent->getContentHandler()->supportsDirectEditing() ) {
137 return $this->msg( 'changecontentmodel-nodirectediting' )
138 ->params( ContentHandler::getLocalizedName( $oldContent->getModel() ) )
139 ->escaped();
140 }
141 }
142
143 return true;
144 }
145
147 protected function getFormFields() {
148 $fields = [
149 'pagetitle' => [
150 'type' => 'title',
151 'creatable' => true,
152 'name' => 'pagetitle',
153 'default' => $this->par,
154 'label-message' => 'changecontentmodel-title-label',
155 'validation-callback' => $this->validateTitle( ... ),
156 ],
157 ];
158 if ( $this->title ) {
159 $options = $this->getOptionsForTitle( $this->title );
160 if ( !$options ) {
161 throw new ErrorPageError(
162 'changecontentmodel-emptymodels-title',
163 'changecontentmodel-emptymodels-text',
164 [ $this->title->getPrefixedText() ]
165 );
166 }
167 $fields['pagetitle']['readonly'] = true;
168 $fields += [
169 'model' => [
170 'type' => 'select',
171 'name' => 'model',
172 'default' => $this->title->getContentModel(),
173 'options' => $options,
174 'label-message' => 'changecontentmodel-model-label'
175 ],
176 'reason' => [
177 'type' => 'text',
178 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
179 'name' => 'reason',
180 'validation-callback' => function ( $reason ) {
181 if ( $reason === null || $reason === '' ) {
182 // Null on form display, or no reason given
183 return true;
184 }
185
186 $match = $this->spamChecker->checkSummary( $reason );
187
188 if ( $match ) {
189 return $this->msg( 'spamprotectionmatch', $match )->parse();
190 }
191
192 return true;
193 },
194 'label-message' => 'changecontentmodel-reason-label',
195 ],
196 ];
197 }
198
199 return $fields;
200 }
201
207 private function getOptionsForTitle( ?Title $title = null ) {
208 $models = $this->contentHandlerFactory->getContentModels();
209 $options = [];
210 foreach ( $models as $model ) {
211 $handler = $this->contentHandlerFactory->getContentHandler( $model );
212 if ( !$handler->supportsDirectEditing() ) {
213 continue;
214 }
215 if ( $title ) {
216 if ( !$handler->canBeUsedOn( $title ) ) {
217 continue;
218 }
219 }
220 $options[ContentHandler::getLocalizedName( $model )] = $model;
221 }
222
223 // Put the options in the drop-down list in alphabetical order.
224 // Sort by array key, case insensitive.
225 $collation = $this->collationFactory->getCategoryCollation();
226 uksort( $options, static function ( $a, $b ) use ( $collation ) {
227 $a = $collation->getSortKey( $a );
228 $b = $collation->getSortKey( $b );
229 return strcmp( $a, $b );
230 } );
231
232 return $options;
233 }
234
236 public function onSubmit( array $data ) {
237 $this->title = Title::newFromText( $data['pagetitle'] );
238 $page = $this->wikiPageFactory->newFromTitle( $this->title );
239
240 $changer = $this->contentModelChangeFactory->newContentModelChange(
241 $this->getContext()->getAuthority(),
242 $page,
243 $data['model']
244 );
245
246 $permissionStatus = $changer->authorizeChange();
247 if ( !$permissionStatus->isGood() ) {
248 $out = $this->getOutput();
249 $wikitext = $out->formatPermissionStatus( $permissionStatus );
250 // Hack to get our wikitext parsed
251 return Status::newFatal( new RawMessage( '$1', [ $wikitext ] ) );
252 }
253
254 $status = $changer->doContentModelChange(
255 $this->getContext(),
256 $data['reason'],
257 true
258 );
259
260 return $status;
261 }
262
263 public function onSuccess() {
264 $out = $this->getOutput();
265 $out->setPageTitleMsg( $this->msg( 'changecontentmodel-success-title' ) );
266 $out->addWikiMsg( 'changecontentmodel-success-text', $this->title->getPrefixedText() );
267 }
268
277 public function prefixSearchSubpages( $search, $limit, $offset ) {
278 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
279 }
280
282 protected function getGroupName() {
283 return 'pagetools';
284 }
285}
286
288class_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:195
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.
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.
__construct(IContentHandlerFactory $contentHandlerFactory, ContentModelChangeFactory $contentModelChangeFactory, SpamChecker $spamChecker, RevisionLookup $revisionLookup, WikiPageFactory $wikiPageFactory, SearchEngineFactory $searchEngineFactory, CollationFactory $collationFactory)
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.
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:70
getPrefixedText()
Get the prefixed title with spaces.
Definition Title.php:1858
Factory class for SearchEngine.
Service for changing the content model of wiki pages.
Service for looking up page revisions.
element(SerializerNode $parent, SerializerNode $node, $contents)