MediaWiki  master
SpecialChangeContentModel.php
Go to the documentation of this file.
1 <?php
2 
13 
15 
17  private $contentHandlerFactory;
18 
20  private $contentModelChangeFactory;
21 
23  private $spamChecker;
24 
26  private $revisionLookup;
27 
29  private $wikiPageFactory;
30 
32  private $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 ( empty( $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',
176  'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
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  // Can also throw a ThrottledError, don't catch it
241  $status = $changer->doContentModelChange(
242  $this->getContext(),
243  $data['reason'],
244  true
245  );
246 
247  return $status;
248  }
249 
250  public function onSuccess() {
251  $out = $this->getOutput();
252  $out->setPageTitle( $this->msg( 'changecontentmodel-success-title' ) );
253  $out->addWikiMsg( 'changecontentmodel-success-text', $this->title );
254  }
255 
264  public function prefixSearchSubpages( $search, $limit, $offset ) {
265  return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
266  }
267 
268  protected function getGroupName() {
269  return 'pagetools';
270  }
271 }
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.
Special page which uses an HTMLForm to handle processing.
string null $par
The sub-page of the special page.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition: HTMLForm.php:153
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
Definition: HTMLForm.php:1635
setFormIdentifier( $ident)
Set an internal identifier for this form.
Definition: HTMLForm.php:1703
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Class to simplify the use of log pages.
Definition: LogPage.php:41
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.
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
Represents a title within MediaWiki.
Definition: Title.php:82
getContentModel( $flags=0)
Get the page's content model id, see the CONTENT_MODEL_XXX constants.
Definition: Title.php:1127
getPrefixedText()
Get the prefixed title with spaces.
Definition: Title.php:1927
Factory class for SearchEngine.
setParameter( $par)
Maybe do something interesting with the subpage parameter.
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
getFormFields()
Get an HTMLForm descriptor array.
getDisplayFormat()
Get display format for the form.
doesWrites()
Indicates whether this special page may perform database writes.
onSubmit(array $data)
Process the form on submission.
postHtml()
Add post-HTML to the form.
__construct(IContentHandlerFactory $contentHandlerFactory, ContentModelChangeFactory $contentModelChangeFactory, SpamChecker $spamChecker, RevisionLookup $revisionLookup, WikiPageFactory $wikiPageFactory, SearchEngineFactory $searchEngineFactory)
onSuccess()
Do something exciting on successful processing of the form, most likely to show a confirmation messag...
getOutput()
Get the OutputPage being used for this instance.
getContext()
Gets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getAuthority()
Shortcut to get the Authority executing this instance.
getRequest()
Get the WebRequest being used for this instance.
prefixSearchString( $search, $limit, $offset, SearchEngineFactory $searchEngineFactory=null)
Perform a regular substring search for prefixSearchSubpages.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:73
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:44
Service for changing the content model of wiki pages.
Service for looking up page revisions.
return true
Definition: router.php:90