MediaWiki  master
SpecialChangeContentModel.php
Go to the documentation of this file.
1 <?php
2 
3 namespace MediaWiki\Specials;
4 
7 use HTMLForm;
8 use LogEventsList;
9 use LogPage;
23 use Xml;
24 
26 
27  private IContentHandlerFactory $contentHandlerFactory;
28  private ContentModelChangeFactory $contentModelChangeFactory;
29  private SpamChecker $spamChecker;
30  private RevisionLookup $revisionLookup;
31  private WikiPageFactory $wikiPageFactory;
32  private SearchEngineFactory $searchEngineFactory;
33 
42  public function __construct(
43  IContentHandlerFactory $contentHandlerFactory,
44  ContentModelChangeFactory $contentModelChangeFactory,
45  SpamChecker $spamChecker,
46  RevisionLookup $revisionLookup,
47  WikiPageFactory $wikiPageFactory,
48  SearchEngineFactory $searchEngineFactory
49  ) {
50  parent::__construct( 'ChangeContentModel', 'editcontentmodel' );
51 
52  $this->contentHandlerFactory = $contentHandlerFactory;
53  $this->contentModelChangeFactory = $contentModelChangeFactory;
54  $this->spamChecker = $spamChecker;
55  $this->revisionLookup = $revisionLookup;
56  $this->wikiPageFactory = $wikiPageFactory;
57  $this->searchEngineFactory = $searchEngineFactory;
58  }
59 
60  public function doesWrites() {
61  return true;
62  }
63 
67  private $title;
68 
74  private $oldRevision;
75 
76  protected function setParameter( $par ) {
77  $par = $this->getRequest()->getVal( 'pagetitle', $par );
78  $title = Title::newFromText( $par );
79  if ( $title ) {
80  $this->title = $title;
81  $this->par = $title->getPrefixedText();
82  } else {
83  $this->par = '';
84  }
85  }
86 
87  protected function postHtml() {
88  $text = '';
89  if ( $this->title ) {
90  $contentModelLogPage = new LogPage( 'contentmodel' );
91  $text = Xml::element( 'h2', null, $contentModelLogPage->getName()->text() );
92  $out = '';
93  LogEventsList::showLogExtract( $out, 'contentmodel', $this->title );
94  $text .= $out;
95  }
96  return $text;
97  }
98 
99  protected function getDisplayFormat() {
100  return 'ooui';
101  }
102 
103  protected function alterForm( HTMLForm $form ) {
104  $this->addHelpLink( 'Help:ChangeContentModel' );
105 
106  if ( $this->title ) {
107  $form->setFormIdentifier( 'modelform' );
108  } else {
109  $form->setFormIdentifier( 'titleform' );
110  }
111 
112  // T120576
113  $form->setSubmitTextMsg( 'changecontentmodel-submit' );
114 
115  if ( $this->title ) {
116  $this->getOutput()->addBacklinkSubtitle( $this->title );
117  }
118  }
119 
120  public function validateTitle( $title ) {
121  // Already validated by HTMLForm, but if not, throw
122  // an exception instead of a fatal
123  $titleObj = Title::newFromTextThrow( $title );
124 
125  $this->oldRevision = $this->revisionLookup->getRevisionByTitle( $titleObj ) ?: false;
126 
127  if ( $this->oldRevision ) {
128  $oldContent = $this->oldRevision->getContent( SlotRecord::MAIN );
129  if ( !$oldContent->getContentHandler()->supportsDirectEditing() ) {
130  return $this->msg( 'changecontentmodel-nodirectediting' )
131  ->params( ContentHandler::getLocalizedName( $oldContent->getModel() ) )
132  ->escaped();
133  }
134  }
135 
136  return true;
137  }
138 
139  protected function getFormFields() {
140  $fields = [
141  'pagetitle' => [
142  'type' => 'title',
143  'creatable' => true,
144  'name' => 'pagetitle',
145  'default' => $this->par,
146  'label-message' => 'changecontentmodel-title-label',
147  'validation-callback' => [ $this, 'validateTitle' ],
148  ],
149  ];
150  if ( $this->title ) {
151  $options = $this->getOptionsForTitle( $this->title );
152  if ( !$options ) {
153  throw new ErrorPageError(
154  'changecontentmodel-emptymodels-title',
155  'changecontentmodel-emptymodels-text',
156  [ $this->title->getPrefixedText() ]
157  );
158  }
159  $fields['pagetitle']['readonly'] = true;
160  $fields += [
161  'currentmodel' => [
162  'type' => 'text',
163  'name' => 'currentcontentmodel',
164  'default' => $this->title->getContentModel(),
165  'label-message' => 'changecontentmodel-current-label',
166  'readonly' => true
167  ],
168  'model' => [
169  'type' => 'select',
170  'name' => 'model',
171  'options' => $options,
172  'label-message' => 'changecontentmodel-model-label'
173  ],
174  'reason' => [
175  'type' => 'text',
177  'name' => 'reason',
178  'validation-callback' => function ( $reason ) {
179  if ( $reason === null || $reason === '' ) {
180  // Null on form display, or no reason given
181  return true;
182  }
183 
184  $match = $this->spamChecker->checkSummary( $reason );
185 
186  if ( $match ) {
187  return $this->msg( 'spamprotectionmatch', $match )->parse();
188  }
189 
190  return true;
191  },
192  'label-message' => 'changecontentmodel-reason-label',
193  ],
194  ];
195  }
196 
197  return $fields;
198  }
199 
200  private function getOptionsForTitle( Title $title = null ) {
201  $models = $this->contentHandlerFactory->getContentModels();
202  $options = [];
203  foreach ( $models as $model ) {
204  $handler = $this->contentHandlerFactory->getContentHandler( $model );
205  if ( !$handler->supportsDirectEditing() ) {
206  continue;
207  }
208  if ( $title ) {
209  if ( $title->getContentModel() === $model ) {
210  continue;
211  }
212  if ( !$handler->canBeUsedOn( $title ) ) {
213  continue;
214  }
215  }
216  $options[ContentHandler::getLocalizedName( $model )] = $model;
217  }
218 
219  return $options;
220  }
221 
222  public function onSubmit( array $data ) {
223  $this->title = Title::newFromText( $data['pagetitle'] );
224  $page = $this->wikiPageFactory->newFromTitle( $this->title );
225 
226  $changer = $this->contentModelChangeFactory->newContentModelChange(
227  $this->getContext()->getAuthority(),
228  $page,
229  $data['model']
230  );
231 
232  $permissionStatus = $changer->authorizeChange();
233  if ( !$permissionStatus->isGood() ) {
234  $out = $this->getOutput();
235  $wikitext = $out->formatPermissionStatus( $permissionStatus );
236  // Hack to get our wikitext parsed
237  return Status::newFatal( new RawMessage( '$1', [ $wikitext ] ) );
238  }
239 
240  $status = $changer->doContentModelChange(
241  $this->getContext(),
242  $data['reason'],
243  true
244  );
245 
246  return $status;
247  }
248 
249  public function onSuccess() {
250  $out = $this->getOutput();
251  $out->setPageTitleMsg( $this->msg( 'changecontentmodel-success-title' ) );
252  $out->addWikiMsg( 'changecontentmodel-success-text', $this->title );
253  }
254 
263  public function prefixSearchSubpages( $search, $limit, $offset ) {
264  return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
265  }
266 
267  protected function getGroupName() {
268  return 'pagetools';
269  }
270 }
271 
275 class_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.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition: HTMLForm.php:158
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
Definition: HTMLForm.php:1625
setFormIdentifier(string $ident, bool $single=false)
Set an internal identifier for this form.
Definition: HTMLForm.php:1696
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Class to simplify the use of log pages.
Definition: LogPage.php:43
Handle database storage of comments such as edit summaries and log reasons.
const COMMENT_CHARACTER_LIMIT
Maximum length of a comment in UTF-8 characters.
Service to check if text (either content or a summary) qualifies as spam.
Definition: SpamChecker.php:14
Variant of the Message class.
Definition: RawMessage.php:40
Service for creating WikiPage objects.
Page revision base class.
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40
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.
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.
getDisplayFormat()
Get display format for the form.
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
__construct(IContentHandlerFactory $contentHandlerFactory, ContentModelChangeFactory $contentModelChangeFactory, SpamChecker $spamChecker, RevisionLookup $revisionLookup, WikiPageFactory $wikiPageFactory, SearchEngineFactory $searchEngineFactory)
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:58
Represents a title within MediaWiki.
Definition: Title.php:76
static newFromTextThrow( $text, $defaultNamespace=NS_MAIN)
Like Title::newFromText(), but throws MalformedTitleException when the title is invalid,...
Definition: Title.php:435
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:400
getContentModel( $flags=0)
Get the page's content model id, see the CONTENT_MODEL_XXX constants.
Definition: Title.php:1080
getPrefixedText()
Get the prefixed title with spaces.
Definition: Title.php:1885
Factory class for SearchEngine.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:73
Module of static functions for generating XML.
Definition: Xml.php:33
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:50
Service for changing the content model of wiki pages.
Service for looking up page revisions.
return true
Definition: router.php:90