MediaWiki REL1_35
CompareHandler.php
Go to the documentation of this file.
1<?php
2
4
14use Parser;
16use TextContent;
17use User;
20
21class CompareHandler extends Handler {
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}
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
__construct(RevisionLookup $revisionLookup, PermissionManager $permissionManager, Parser $parser)
getParamSettings()
Fetch ParamValidator settings for parameters.
Base class for REST route handlers.
Definition Handler.php:16
getValidatedParams()
Fetch the validated parameters.
Definition Handler.php:257
getResponseFactory()
Get the ResponseFactory which can be used to generate Response objects.
Definition Handler.php:151
const PARAM_SOURCE
(string) ParamValidator constant to specify the source of the parameter.
Definition Handler.php:22
A stream class which uses a string as the underlying storage.
Exception representing a failure to look up a revision.
Page revision base class.
Value object representing a content slot associated with a page revision.
Exception raised in response to an audience check when attempting to access suppressed information wi...
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:85
Group all the pieces relevant to the context of a request into one instance @newable.
Content object implementation for representing flat text.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:60
Value object representing a message for i18n.
Service for formatting and validating API parameters.
const CONTENT_MODEL_TEXT
Definition Defines.php:228
Service for looking up page revisions.
$content
Definition router.php:76