MediaWiki master
SpecialChangeContentModel.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\Specials;
4
8use LogPage;
24use Xml;
25
27
28 private IContentHandlerFactory $contentHandlerFactory;
29 private ContentModelChangeFactory $contentModelChangeFactory;
30 private SpamChecker $spamChecker;
31 private RevisionLookup $revisionLookup;
32 private WikiPageFactory $wikiPageFactory;
33 private SearchEngineFactory $searchEngineFactory;
34 private CollationFactory $collationFactory;
35
45 public function __construct(
46 IContentHandlerFactory $contentHandlerFactory,
47 ContentModelChangeFactory $contentModelChangeFactory,
48 SpamChecker $spamChecker,
49 RevisionLookup $revisionLookup,
50 WikiPageFactory $wikiPageFactory,
51 SearchEngineFactory $searchEngineFactory,
52 CollationFactory $collationFactory
53 ) {
54 parent::__construct( 'ChangeContentModel', 'editcontentmodel' );
55
56 $this->contentHandlerFactory = $contentHandlerFactory;
57 $this->contentModelChangeFactory = $contentModelChangeFactory;
58 $this->spamChecker = $spamChecker;
59 $this->revisionLookup = $revisionLookup;
60 $this->wikiPageFactory = $wikiPageFactory;
61 $this->searchEngineFactory = $searchEngineFactory;
62 $this->collationFactory = $collationFactory;
63 }
64
65 public function doesWrites() {
66 return true;
67 }
68
72 private $title;
73
79 private $oldRevision;
80
81 protected function setParameter( $par ) {
82 $par = $this->getRequest()->getVal( 'pagetitle', $par );
83 $title = Title::newFromText( $par );
84 if ( $title ) {
85 $this->title = $title;
86 $this->par = $title->getPrefixedText();
87 } else {
88 $this->par = '';
89 }
90 }
91
92 protected function postHtml() {
93 $text = '';
94 if ( $this->title ) {
95 $contentModelLogPage = new LogPage( 'contentmodel' );
96 $text = Xml::element( 'h2', null, $contentModelLogPage->getName()->text() );
97 $out = '';
98 LogEventsList::showLogExtract( $out, 'contentmodel', $this->title );
99 $text .= $out;
100 }
101 return $text;
102 }
103
104 protected function getDisplayFormat() {
105 return 'ooui';
106 }
107
108 protected function alterForm( HTMLForm $form ) {
109 $this->addHelpLink( 'Help:ChangeContentModel' );
110
111 if ( $this->title ) {
112 $form->setFormIdentifier( 'modelform' );
113 } else {
114 $form->setFormIdentifier( 'titleform' );
115 }
116
117 // T120576
118 $form->setSubmitTextMsg( 'changecontentmodel-submit' );
119
120 if ( $this->title ) {
121 $this->getOutput()->addBacklinkSubtitle( $this->title );
122 }
123 }
124
125 public function validateTitle( $title ) {
126 // Already validated by HTMLForm, but if not, throw
127 // an exception instead of a fatal
128 $titleObj = Title::newFromTextThrow( $title );
129
130 $this->oldRevision = $this->revisionLookup->getRevisionByTitle( $titleObj ) ?: false;
131
132 if ( $this->oldRevision ) {
133 $oldContent = $this->oldRevision->getContent( SlotRecord::MAIN );
134 if ( !$oldContent->getContentHandler()->supportsDirectEditing() ) {
135 return $this->msg( 'changecontentmodel-nodirectediting' )
136 ->params( ContentHandler::getLocalizedName( $oldContent->getModel() ) )
137 ->escaped();
138 }
139 }
140
141 return true;
142 }
143
144 protected function getFormFields() {
145 $fields = [
146 'pagetitle' => [
147 'type' => 'title',
148 'creatable' => true,
149 'name' => 'pagetitle',
150 'default' => $this->par,
151 'label-message' => 'changecontentmodel-title-label',
152 'validation-callback' => [ $this, 'validateTitle' ],
153 ],
154 ];
155 if ( $this->title ) {
156 $options = $this->getOptionsForTitle( $this->title );
157 if ( !$options ) {
158 throw new ErrorPageError(
159 'changecontentmodel-emptymodels-title',
160 'changecontentmodel-emptymodels-text',
161 [ $this->title->getPrefixedText() ]
162 );
163 }
164 $fields['pagetitle']['readonly'] = true;
165 $fields += [
166 'model' => [
167 'type' => 'select',
168 'name' => 'model',
169 'default' => $this->title->getContentModel(),
170 'options' => $options,
171 'label-message' => 'changecontentmodel-model-label'
172 ],
173 'reason' => [
174 'type' => 'text',
175 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
176 'name' => 'reason',
177 'validation-callback' => function ( $reason ) {
178 if ( $reason === null || $reason === '' ) {
179 // Null on form display, or no reason given
180 return true;
181 }
182
183 $match = $this->spamChecker->checkSummary( $reason );
184
185 if ( $match ) {
186 return $this->msg( 'spamprotectionmatch', $match )->parse();
187 }
188
189 return true;
190 },
191 'label-message' => 'changecontentmodel-reason-label',
192 ],
193 ];
194 }
195
196 return $fields;
197 }
198
204 private function getOptionsForTitle( Title $title = null ) {
205 $models = $this->contentHandlerFactory->getContentModels();
206 $options = [];
207 foreach ( $models as $model ) {
208 $handler = $this->contentHandlerFactory->getContentHandler( $model );
209 if ( !$handler->supportsDirectEditing() ) {
210 continue;
211 }
212 if ( $title ) {
213 if ( !$handler->canBeUsedOn( $title ) ) {
214 continue;
215 }
216 }
217 $options[ContentHandler::getLocalizedName( $model )] = $model;
218 }
219
220 // Put the options in the drop-down list in alphabetical order.
221 // Sort by array key, case insensitive.
222 $collation = $this->collationFactory->getCategoryCollation();
223 uksort( $options, static function ( $a, $b ) use ( $collation ) {
224 $a = $collation->getSortKey( $a );
225 $b = $collation->getSortKey( $b );
226 return strcmp( $a, $b );
227 } );
228
229 return $options;
230 }
231
232 public function onSubmit( array $data ) {
233 $this->title = Title::newFromText( $data['pagetitle'] );
234 $page = $this->wikiPageFactory->newFromTitle( $this->title );
235
236 $changer = $this->contentModelChangeFactory->newContentModelChange(
237 $this->getContext()->getAuthority(),
238 $page,
239 $data['model']
240 );
241
242 $permissionStatus = $changer->authorizeChange();
243 if ( !$permissionStatus->isGood() ) {
244 $out = $this->getOutput();
245 $wikitext = $out->formatPermissionStatus( $permissionStatus );
246 // Hack to get our wikitext parsed
247 return Status::newFatal( new RawMessage( '$1', [ $wikitext ] ) );
248 }
249
250 $status = $changer->doContentModelChange(
251 $this->getContext(),
252 $data['reason'],
253 true
254 );
255
256 return $status;
257 }
258
259 public function onSuccess() {
260 $out = $this->getOutput();
261 $out->setPageTitleMsg( $this->msg( 'changecontentmodel-success-title' ) );
262 $out->addWikiMsg( 'changecontentmodel-success-text', $this->title );
263 }
264
273 public function prefixSearchSubpages( $search, $limit, $offset ) {
274 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
275 }
276
277 protected function getGroupName() {
278 return 'pagetools';
279 }
280}
281
283class_alias( SpecialChangeContentModel::class, 'SpecialChangeContentModel' );
A content handler knows how do deal with a specific type of content on a wiki page.
static getLocalizedName( $name, Language $lang=null)
Returns the localized name for a given content model.
An error page which can definitely be safely rendered using the OutputPage.
Class to simplify the use of log pages.
Definition LogPage.php:44
Common factory to construct collation classes.
Handle database storage of comments such as edit summaries and log reasons.
Service to check if text (either content or a summary) qualifies as spam.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:206
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.
Variant of the Message class.
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 sub-page of the special page.
prefixSearchString( $search, $limit, $offset, SearchEngineFactory $searchEngineFactory=null)
Perform a regular substring search for prefixSearchSubpages.
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.
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...
setParameter( $par)
Maybe do something interesting with the subpage parameter.
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
onSubmit(array $data)
Process the form on submission.
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
doesWrites()
Indicates whether this special page may perform database writes.
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:54
Represents a title within MediaWiki.
Definition Title.php:78
getPrefixedText()
Get the prefixed title with spaces.
Definition Title.php:1861
Factory class for SearchEngine.
Module of static functions for generating XML.
Definition Xml.php:33
Service for changing the content model of wiki pages.
Service for looking up page revisions.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Ge...