MediaWiki  master
ContentModelChange.php
Go to the documentation of this file.
1 <?php
2 
9 
20 
23 
25  private $hookRunner;
26 
28  private $permManager;
29 
31  private $revLookup;
32 
34  private $user;
35 
37  private $page;
38 
40  private $newModel;
41 
43  private $tags;
44 
46  private $newContent;
47 
49  private $latestRevId;
50 
52  private $logAction;
53 
55  private $msgPrefix;
56 
66  public function __construct(
68  HookContainer $hookContainer,
71  User $user,
73  string $newModel
74  ) {
75  $this->contentHandlerFactory = $contentHandlerFactory;
76  $this->hookRunner = new HookRunner( $hookContainer );
77  $this->permManager = $permManager;
78  $this->revLookup = $revLookup;
79 
80  $this->user = $user;
81  $this->page = $page;
82  $this->newModel = $newModel;
83 
84  // SpecialChangeContentModel doesn't support tags
85  // api can specify tags via ::setTags, which also checks if user can add
86  // the tags specified
87  $this->tags = [];
88 
89  // Requires createNewContent to be called first
90  $this->logAction = '';
91 
92  // Defaults to nothing, for special page
93  $this->msgPrefix = '';
94  }
95 
101  public function setMessagePrefix( $msgPrefix ) {
102  $this->msgPrefix = $msgPrefix;
103  }
104 
110  public function checkPermissions() {
111  $user = $this->user;
112  $current = $this->page->getTitle();
113  $titleWithNewContentModel = clone $current;
114  $titleWithNewContentModel->setContentModel( $this->newModel );
115 
116  $pm = $this->permManager;
117  $errors = wfMergeErrorArrays(
118  // edit the contentmodel of the page
119  $pm->getPermissionErrors( 'editcontentmodel', $user, $current ),
120  // edit the page under the old content model
121  $pm->getPermissionErrors( 'edit', $user, $current ),
122  // edit the contentmodel under the new content model
123  $pm->getPermissionErrors( 'editcontentmodel', $user, $titleWithNewContentModel ),
124  // edit the page under the new content model
125  $pm->getPermissionErrors( 'edit', $user, $titleWithNewContentModel )
126  );
127 
128  return $errors;
129  }
130 
137  public function setTags( $tags ) {
138  $tagStatus = ChangeTags::canAddTagsAccompanyingChange( $tags, $this->user );
139  if ( $tagStatus->isOK() ) {
140  $this->tags = $tags;
141  return Status::newGood();
142  } else {
143  return $tagStatus;
144  }
145  }
146 
152  private function createNewContent() {
154 
155  $title = $this->page->getTitle();
156  $latestRevRecord = $this->revLookup->getRevisionByTitle( $title );
157 
158  if ( $latestRevRecord ) {
159  $latestContent = $latestRevRecord->getContent( SlotRecord::MAIN );
160  $latestHandler = $latestContent->getContentHandler();
161  $latestModel = $latestContent->getModel();
162  if ( !$latestHandler->supportsDirectEditing() ) {
163  // Only reachable via api
164  return Status::newFatal(
165  'apierror-changecontentmodel-nodirectediting',
166  ContentHandler::getLocalizedName( $latestModel )
167  );
168  }
169 
171  if ( $newModel === $latestModel ) {
172  // Only reachable via api
173  return Status::newFatal( 'apierror-nochanges' );
174  }
176  if ( !$newHandler->canBeUsedOn( $title ) ) {
177  // Only reachable via api
178  return Status::newFatal(
179  'apierror-changecontentmodel-cannotbeused',
181  Message::plaintextParam( $title->getPrefixedText() )
182  );
183  }
184 
185  try {
186  $newContent = $newHandler->unserializeContent(
187  $latestContent->serialize()
188  );
189  } catch ( MWException $e ) {
190  // Messages: changecontentmodel-cannot-convert,
191  // apierror-changecontentmodel-cannot-convert
192  return Status::newFatal(
193  $this->msgPrefix . 'changecontentmodel-cannot-convert',
194  Message::plaintextParam( $title->getPrefixedText() ),
196  );
197  }
198  $this->latestRevId = $latestRevRecord->getId();
199  $this->logAction = 'change';
200  } else {
201  // Page doesn't exist, create an empty content object
203  ->getContentHandler( $this->newModel )
204  ->makeEmptyContent();
205  $this->latestRevId = false;
206  $this->logAction = 'new';
207  }
208  $this->newContent = $newContent;
209  return Status::newGood();
210  }
211 
223  public function doContentModelChange(
224  IContextSource $context,
225  $comment,
226  $bot
227  ) {
228  $status = $this->createNewContent();
229  if ( !$status->isGood() ) {
230  return $status;
231  }
232 
233  $page = $this->page;
234  $title = $page->getTitle();
235  $user = $this->user;
236 
237  if ( $user->pingLimiter( 'editcontentmodel' ) ) {
238  throw new ThrottledError();
239  }
240 
241  // Create log entry
242  $log = new ManualLogEntry( 'contentmodel', $this->logAction );
243  $log->setPerformer( $user );
244  $log->setTarget( $title );
245  $log->setComment( $comment );
246  $log->setParameters( [
247  '4::oldmodel' => $title->getContentModel(),
248  '5::newmodel' => $this->newModel
249  ] );
250  $log->addTags( $this->tags );
251 
252  $formatter = LogFormatter::newFromEntry( $log );
253  $formatter->setContext( RequestContext::newExtraneousContext( $title ) );
254  $reason = $formatter->getPlainActionText();
255 
256  if ( $comment !== '' ) {
257  $reason .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $comment;
258  }
259 
260  // Run edit filters
261  $derivativeContext = new DerivativeContext( $context );
262  $derivativeContext->setTitle( $title );
263  $derivativeContext->setWikiPage( $page );
264  $status = new Status();
265 
267 
268  if ( !$this->hookRunner->onEditFilterMergedContent( $derivativeContext, $newContent,
269  $status, $reason, $user, false )
270  ) {
271  if ( $status->isGood() ) {
272  // TODO: extensions should really specify an error message
273  $status->fatal( 'hookaborted' );
274  }
275  return $status;
276  }
277 
278  // Make the edit
279  $flags = $this->latestRevId ? EDIT_UPDATE : EDIT_NEW;
280  $flags |= EDIT_INTERNAL;
281  if ( $bot && $this->permManager->userHasRight( $user, 'bot' ) ) {
282  $flags |= EDIT_FORCE_BOT;
283  }
284 
285  $status = $page->doEditContent(
286  $newContent,
287  $reason,
288  $flags,
289  $this->latestRevId,
290  $user,
291  null,
292  $this->tags
293  );
294 
295  if ( !$status->isOK() ) {
296  return $status;
297  }
298 
299  $logid = $log->insert();
300  $log->publish( $logid );
301 
302  $values = [
303  'logid' => $logid
304  ];
305 
306  return Status::newGood( $values );
307  }
308 
309 }
wfMergeErrorArrays
wfMergeErrorArrays(... $args)
Merge arrays in the style of PermissionManager::getPermissionErrors, with duplicate removal e....
Definition: GlobalFunctions.php:180
ContentModelChange\$user
User $user
user making the change
Definition: ContentModelChange.php:34
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
ContentModelChange\$newModel
string $newModel
Definition: ContentModelChange.php:40
EDIT_FORCE_BOT
const EDIT_FORCE_BOT
Definition: Defines.php:145
EDIT_INTERNAL
const EDIT_INTERNAL
Definition: Defines.php:148
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:52
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1219
RequestContext\newExtraneousContext
static newExtraneousContext(Title $title, $request=[])
Create a new extraneous context.
Definition: RequestContext.php:623
ContentModelChange\__construct
__construct(IContentHandlerFactory $contentHandlerFactory, HookContainer $hookContainer, PermissionManager $permManager, RevisionLookup $revLookup, User $user, WikiPage $page, string $newModel)
Definition: ContentModelChange.php:66
ContentModelChange\createNewContent
createNewContent()
Create the new content.
Definition: ContentModelChange.php:152
User\pingLimiter
pingLimiter( $action='edit', $incrBy=1)
Primitive rate limits: enforce maximum actions per time period to put a brake on flooding.
Definition: User.php:1640
ContentModelChange\setTags
setTags( $tags)
Specify the tags the user wants to add, and check permissions.
Definition: ContentModelChange.php:137
Revision\RevisionLookup
Service for looking up page revisions.
Definition: RevisionLookup.php:38
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:44
ContentModelChange\$hookRunner
HookRunner $hookRunner
Definition: ContentModelChange.php:25
DerivativeContext
An IContextSource implementation which will inherit context from another source but allow individual ...
Definition: DerivativeContext.php:31
MWException
MediaWiki exception.
Definition: MWException.php:29
ContentModelChange\setMessagePrefix
setMessagePrefix( $msgPrefix)
Set the message prefix.
Definition: ContentModelChange.php:101
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:281
ThrottledError
Show an error when the user hits a rate limit.
Definition: ThrottledError.php:28
$title
$title
Definition: testCompression.php:38
MediaWiki\Permissions\PermissionManager
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Definition: PermissionManager.php:49
EDIT_UPDATE
const EDIT_UPDATE
Definition: Defines.php:142
ContentHandler\getLocalizedName
static getLocalizedName( $name, Language $lang=null)
Returns the localized name for a given content model.
Definition: ContentHandler.php:299
ContentModelChange\$page
WikiPage $page
Definition: ContentModelChange.php:37
MediaWiki\Content\IContentHandlerFactory
Definition: IContentHandlerFactory.php:10
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
Message\plaintextParam
static plaintextParam( $plaintext)
Definition: Message.php:1114
ContentModelChange\$msgPrefix
string $msgPrefix
'apierror-' or empty string, for status messages
Definition: ContentModelChange.php:55
ContentModelChange\$latestRevId
int false $latestRevId
latest revision id, or false if creating
Definition: ContentModelChange.php:49
ContentModelChange\doContentModelChange
doContentModelChange(IContextSource $context, $comment, $bot)
Handle change and logging after validatio.
Definition: ContentModelChange.php:223
ContentModelChange\$newContent
Content $newContent
Definition: ContentModelChange.php:46
WikiPage\doEditContent
doEditContent(Content $content, $summary, $flags=0, $originalRevId=false, User $user=null, $serialFormat=null, $tags=[], $undidRevId=0)
Change an existing article or create a new article.
Definition: WikiPage.php:1903
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:55
Content
Base interface for content objects.
Definition: Content.php:35
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:141
ChangeTags\canAddTagsAccompanyingChange
static canAddTagsAccompanyingChange(array $tags, User $user=null)
Is it OK to allow the user to apply all the specified tags at the same time as they edit/make the cha...
Definition: ChangeTags.php:544
MediaWiki\Content\IContentHandlerFactory\getContentHandler
getContentHandler(string $modelID)
Returns a ContentHandler instance for the given $modelID.
ContentModelChange\$permManager
PermissionManager $permManager
Definition: ContentModelChange.php:28
ContentModelChange\checkPermissions
checkPermissions()
Check user can edit and editcontentmodel before and after.
Definition: ContentModelChange.php:110
ContentModelChange\$revLookup
RevisionLookup $revLookup
Definition: ContentModelChange.php:31
ManualLogEntry
Class for creating new log entries and inserting them into the database.
Definition: ManualLogEntry.php:42
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:44
ContentModelChange\$logAction
string $logAction
'new' or 'change'
Definition: ContentModelChange.php:52
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:570
ContentModelChange\$tags
string[] $tags
tags to add
Definition: ContentModelChange.php:43
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:55
ContentModelChange\$contentHandlerFactory
IContentHandlerFactory $contentHandlerFactory
Definition: ContentModelChange.php:22
ContentModelChange
Helper class to change the content model of pages.
Definition: ContentModelChange.php:19
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39
LogFormatter\newFromEntry
static newFromEntry(LogEntry $entry)
Constructs a new formatter suitable for given entry.
Definition: LogFormatter.php:52