MediaWiki  master
CompareHandler.php
Go to the documentation of this file.
1 <?php
2 
3 namespace MediaWiki\Rest\Handler;
4 
14 use Parser;
15 use RequestContext;
16 use TextContent;
17 use User;
20 
21 class CompareHandler extends Handler {
23  private $revisionLookup;
24 
27 
29  private $parser;
30 
32  private $user;
33 
35  private $revisions = [];
36 
38  private $textCache = [];
39 
40  public function __construct(
44  ) {
45  $this->revisionLookup = $revisionLookup;
46  $this->permissionManager = $permissionManager;
47  $this->parser = $parser;
48 
49  // @todo Inject this, when there is a good way to do that
50  $this->user = RequestContext::getMain()->getUser();
51  }
52 
53  public function execute() {
54  $fromRev = $this->getRevisionOrThrow( 'from' );
55  $toRev = $this->getRevisionOrThrow( 'to' );
56 
57  if ( $fromRev->getPageId() !== $toRev->getPageId() ) {
58  throw new LocalizedHttpException(
59  new MessageValue( 'rest-compare-page-mismatch' ), 400 );
60  }
61 
62  if ( !$this->permissionManager->userCan( 'read', $this->user,
63  $toRev->getPageAsLinkTarget() )
64  ) {
65  throw new LocalizedHttpException(
66  new MessageValue( 'rest-compare-permission-denied' ), 403 );
67  }
68 
69  $data = [
70  'from' => [
71  'id' => $fromRev->getId(),
72  'slot_role' => $this->getRole(),
73  'sections' => $this->getSectionInfo( 'from' )
74  ],
75  'to' => [
76  'id' => $toRev->getId(),
77  'slot_role' => $this->getRole(),
78  'sections' => $this->getSectionInfo( 'to' )
79  ],
80  'diff' => [ 'PLACEHOLDER' => null ]
81  ];
82  $rf = $this->getResponseFactory();
83  $wrapperJson = $rf->encodeJson( $data );
84  $diff = $this->getJsonDiff();
85  $response = $rf->create();
86  $response->setHeader( 'Content-Type', 'application/json' );
87  // A hack until getJsonDiff() is moved to SlotDiffRenderer and only nested inner diff is returned
88  $innerDiff = substr( $diff, 1, -1 );
89  $response->setBody( new StringStream(
90  str_replace( '"diff":{"PLACEHOLDER":null}', $innerDiff, $wrapperJson ) ) );
91  return $response;
92  }
93 
98  private function getRevision( $paramName ) {
99  if ( !isset( $this->revisions[$paramName] ) ) {
100  $this->revisions[$paramName] =
101  $this->revisionLookup->getRevisionById( $this->getValidatedParams()[$paramName] );
102  }
103  return $this->revisions[$paramName];
104  }
105 
111  private function getRevisionOrThrow( $paramName ) {
112  $rev = $this->getRevision( $paramName );
113  if ( !$rev ) {
114  throw new LocalizedHttpException(
115  new MessageValue( 'rest-compare-nonexistent', [ $paramName ] ), 404 );
116  }
117 
118  if ( !$this->isAccessible( $rev ) ) {
119  throw new LocalizedHttpException(
120  new MessageValue( 'rest-compare-inaccessible', [ $paramName ] ), 403 );
121  }
122  return $rev;
123  }
124 
129  private function isAccessible( $rev ) {
130  return $rev->audienceCan(
131  RevisionRecord::DELETED_TEXT,
132  RevisionRecord::FOR_THIS_USER,
133  $this->user
134  );
135  }
136 
137  private function getRole() {
138  return SlotRecord::MAIN;
139  }
140 
141  private function getRevisionText( $paramName ) {
142  if ( !isset( $this->textCache[$paramName] ) ) {
143  $revision = $this->getRevision( $paramName );
144  try {
145  $content = $revision
146  ->getSlot( $this->getRole(), RevisionRecord::FOR_THIS_USER, $this->user )
147  ->getContent()
148  ->convert( CONTENT_MODEL_TEXT );
149  if ( $content instanceof TextContent ) {
150  $this->textCache[$paramName] = $content->getText();
151  } else {
152  throw new LocalizedHttpException(
153  new MessageValue(
154  'rest-compare-wrong-content',
155  [ $this->getRole(), $paramName ]
156  ),
157  400 );
158  }
159  } catch ( SuppressedDataException $e ) {
160  throw new LocalizedHttpException(
161  new MessageValue( 'rest-compare-inaccessible', [ $paramName ] ), 403 );
162  } catch ( RevisionAccessException $e ) {
163  throw new LocalizedHttpException(
164  new MessageValue( 'rest-compare-nonexistent', [ $paramName ] ), 404 );
165  }
166  }
167  return $this->textCache[$paramName];
168  }
169 
173  private function getJsonDiff() {
174  // TODO: properly implement
175  // This is a prototype only. SlotDiffRenderer should be extended to support this use case.
176  $fromText = $this->getRevisionText( 'from' );
177  $toText = $this->getRevisionText( 'to' );
178  if ( !function_exists( 'wikidiff2_inline_json_diff' ) ) {
179  throw new LocalizedHttpException(
180  new MessageValue( 'rest-compare-wikidiff2' ), 500 );
181  }
182  return wikidiff2_inline_json_diff( $fromText, $toText, 2 );
183  }
184 
189  private function getSectionInfo( $paramName ) {
190  $text = $this->getRevisionText( $paramName );
191  $parserSections = $this->parser->getFlatSectionInfo( $text );
192  $sections = [];
193  foreach ( $parserSections as $i => $parserSection ) {
194  // Skip section zero, which comes before the first heading, since
195  // its offset is always zero, so the client can assume its location.
196  if ( $i !== 0 ) {
197  $sections[] = [
198  'level' => $parserSection['level'],
199  'heading' => $parserSection['heading'],
200  'offset' => $parserSection['offset'],
201  ];
202  }
203  }
204  return $sections;
205  }
206 
207  public function getParamSettings() {
208  return [
209  'from' => [
210  ParamValidator::PARAM_TYPE => 'integer',
211  ParamValidator::PARAM_REQUIRED => true,
212  Handler::PARAM_SOURCE => 'path',
213  ],
214  'to' => [
215  ParamValidator::PARAM_TYPE => 'integer',
216  ParamValidator::PARAM_REQUIRED => true,
217  Handler::PARAM_SOURCE => 'path',
218  ],
219  ];
220  }
221 }
MediaWiki\Rest\Handler\CompareHandler\$user
User $user
Definition: CompareHandler.php:32
MediaWiki\Rest\Handler\CompareHandler\$revisionLookup
RevisionLookup $revisionLookup
Definition: CompareHandler.php:23
MediaWiki\Rest\Handler
Definition: ActionModuleBasedHandler.php:3
Revision\RevisionAccessException
Exception representing a failure to look up a revision.
Definition: RevisionAccessException.php:34
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
MediaWiki\Rest\Handler\getResponseFactory
getResponseFactory()
Get the ResponseFactory which can be used to generate Response objects.
Definition: Handler.php:125
MediaWiki\Rest\Handler\CompareHandler\getSectionInfo
getSectionInfo( $paramName)
Definition: CompareHandler.php:189
Revision\SuppressedDataException
Exception raised in response to an audience check when attempting to access suppressed information wi...
Definition: SuppressedDataException.php:33
MediaWiki\Rest\Handler\CompareHandler\getRevisionText
getRevisionText( $paramName)
Definition: CompareHandler.php:141
MediaWiki\Rest\Handler\PARAM_SOURCE
const PARAM_SOURCE
(string) ParamValidator constant to specify the source of the parameter.
Definition: Handler.php:22
MediaWiki\Rest\StringStream
A stream class which uses a string as the underlying storage.
Definition: StringStream.php:16
MediaWiki\Rest\Handler\CompareHandler\getRevision
getRevision( $paramName)
Definition: CompareHandler.php:98
MediaWiki\Rest\Handler\CompareHandler\$revisions
RevisionRecord[] $revisions
Definition: CompareHandler.php:35
Revision\RevisionLookup
Service for looking up page revisions.
Definition: RevisionLookup.php:38
Wikimedia\Message\MessageValue
Value object representing a message for i18n.
Definition: MessageValue.php:16
MediaWiki\Rest\Handler
Base class for REST route handlers.
Definition: Handler.php:16
MediaWiki\Rest\Handler\CompareHandler\__construct
__construct(RevisionLookup $revisionLookup, PermissionManager $permissionManager, Parser $parser)
Definition: CompareHandler.php:40
MediaWiki\Rest\Handler\CompareHandler\getRevisionOrThrow
getRevisionOrThrow( $paramName)
Definition: CompareHandler.php:111
RequestContext
Group all the pieces relevant to the context of a request into one instance @newable.
Definition: RequestContext.php:38
MediaWiki\Rest\Handler\getValidatedParams
getValidatedParams()
Fetch the validated parameters.
Definition: Handler.php:231
MediaWiki\Permissions\PermissionManager
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Definition: PermissionManager.php:49
$content
$content
Definition: router.php:76
MediaWiki\Rest\Handler\CompareHandler\getJsonDiff
getJsonDiff()
Definition: CompareHandler.php:173
MediaWiki\Rest\Handler\CompareHandler\$parser
Parser $parser
Definition: CompareHandler.php:29
Parser
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition: Parser.php:84
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:455
TextContent
Content object implementation for representing flat text.
Definition: TextContent.php:39
MediaWiki\Rest\Handler\CompareHandler\getParamSettings
getParamSettings()
Fetch ParamValidator settings for parameters.
Definition: CompareHandler.php:207
MediaWiki\Rest\Handler\CompareHandler\isAccessible
isAccessible( $rev)
Definition: CompareHandler.php:129
MediaWiki\Rest\Handler\CompareHandler\$textCache
string[] $textCache
Definition: CompareHandler.php:38
MediaWiki\Rest\Handler\CompareHandler\getRole
getRole()
Definition: CompareHandler.php:137
CONTENT_MODEL_TEXT
const CONTENT_MODEL_TEXT
Definition: Defines.php:227
MediaWiki\Rest\Handler\CompareHandler
Definition: CompareHandler.php:21
MediaWiki\Rest\Handler\CompareHandler\$permissionManager
PermissionManager $permissionManager
Definition: CompareHandler.php:26
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:59
MediaWiki\Rest\Handler\CompareHandler\execute
execute()
Execute the handler.
Definition: CompareHandler.php:53
Wikimedia\ParamValidator\ParamValidator
Service for formatting and validating API parameters.
Definition: ParamValidator.php:42
MediaWiki\Rest\LocalizedHttpException
@newable
Definition: LocalizedHttpException.php:10
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39