Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
92.00% |
46 / 50 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
PageDataRequestHandler | |
93.88% |
46 / 49 |
|
75.00% |
3 / 4 |
19.08 | |
0.00% |
0 / 1 |
canHandleRequest | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
5 | |||
handleRequest | |
81.25% |
13 / 16 |
|
0.00% |
0 / 1 |
6.24 | |||
httpContentNegotiation | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
5 | |||
getDocUrl | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | namespace MediaWiki\LinkedData; |
22 | |
23 | use HttpError; |
24 | use MediaWiki\MediaWikiServices; |
25 | use MediaWiki\Output\OutputPage; |
26 | use MediaWiki\Request\WebRequest; |
27 | use MediaWiki\Revision\SlotRecord; |
28 | use MediaWiki\Title\MalformedTitleException; |
29 | use MediaWiki\Title\Title; |
30 | use Wikimedia\Http\HttpAcceptNegotiator; |
31 | use Wikimedia\Http\HttpAcceptParser; |
32 | |
33 | /** |
34 | * Request handler implementing a data interface for mediawiki pages. |
35 | * |
36 | * @author Daniel Kinzler |
37 | * @author Amir Sarabadanai |
38 | */ |
39 | class PageDataRequestHandler { |
40 | |
41 | /** |
42 | * Checks whether the request is complete, i.e. whether it contains all information needed |
43 | * to reply with page data. |
44 | * |
45 | * This does not check whether the request is valid and will actually produce a successful |
46 | * response. |
47 | * |
48 | * @param string|null $subPage |
49 | * @param WebRequest $request |
50 | * |
51 | * @return bool |
52 | */ |
53 | public function canHandleRequest( $subPage, WebRequest $request ) { |
54 | if ( $subPage === '' || $subPage === null ) { |
55 | return $request->getText( 'target' ) !== ''; |
56 | } |
57 | |
58 | $parts = explode( '/', $subPage, 2 ); |
59 | $slot = $parts[0]; |
60 | $title = $parts[1] ?? ''; |
61 | return ( $slot === SlotRecord::MAIN || $slot === '' ) && $title !== ''; |
62 | } |
63 | |
64 | /** |
65 | * Main method for handling requests. |
66 | * |
67 | * @param string|null $subPage |
68 | * @param WebRequest $request The request parameters. Known parameters are: |
69 | * - title: the page title |
70 | * - format: the format |
71 | * - oldid|revision: the revision ID |
72 | * @param OutputPage $output |
73 | * |
74 | * @note Instead of an output page, a WebResponse could be sufficient, but |
75 | * redirect logic is currently implemented in OutputPage. |
76 | * |
77 | * @throws HttpError |
78 | */ |
79 | public function handleRequest( $subPage, WebRequest $request, OutputPage $output ) { |
80 | // No matter what: The response is always public |
81 | $output->getRequest()->response()->header( 'Access-Control-Allow-Origin: *' ); |
82 | |
83 | if ( !$this->canHandleRequest( $subPage, $request ) ) { |
84 | throw new HttpError( 400, wfMessage( 'pagedata-bad-title', $subPage ) ); |
85 | } |
86 | |
87 | $revision = 0; |
88 | |
89 | if ( $subPage !== '' ) { |
90 | $parts = explode( '/', $subPage, 2 ); |
91 | $title = $parts[1] ?? ''; |
92 | } else { |
93 | $title = $request->getText( 'target' ); |
94 | } |
95 | |
96 | $revision = $request->getInt( 'oldid', $revision ); |
97 | $revision = $request->getInt( 'revision', $revision ); |
98 | |
99 | if ( $title === null || $title === '' ) { |
100 | // TODO: different error message? |
101 | throw new HttpError( 400, wfMessage( 'pagedata-bad-title', (string)$title ) ); |
102 | } |
103 | |
104 | try { |
105 | $title = MediaWikiServices::getInstance()->getTitleFactory()->newFromTextThrow( $title ); |
106 | } catch ( MalformedTitleException $ex ) { |
107 | throw new HttpError( 400, wfMessage( 'pagedata-bad-title', $title ) ); |
108 | } |
109 | |
110 | $this->httpContentNegotiation( $request, $output, $title, $revision ); |
111 | } |
112 | |
113 | /** |
114 | * Applies HTTP content negotiation. |
115 | * If the negotiation is successful, this method will set the appropriate redirect |
116 | * in the OutputPage object and return. Otherwise, an HttpError is thrown. |
117 | * |
118 | * @param WebRequest $request |
119 | * @param OutputPage $output |
120 | * @param Title $title |
121 | * @param int $revision The desired revision |
122 | * |
123 | * @throws HttpError |
124 | */ |
125 | public function httpContentNegotiation( |
126 | WebRequest $request, |
127 | OutputPage $output, |
128 | Title $title, |
129 | $revision = 0 |
130 | ) { |
131 | $mimeTypes = MediaWikiServices::getInstance() |
132 | ->getContentHandlerFactory() |
133 | ->getContentHandler( $title->getContentModel() ) |
134 | ->getSupportedFormats(); |
135 | |
136 | $acceptHeader = $request->getHeader( 'Accept' ); |
137 | if ( $acceptHeader !== false ) { |
138 | $parser = new HttpAcceptParser(); |
139 | $accept = $parser->parseWeights( $acceptHeader ); |
140 | } else { |
141 | // anything goes |
142 | $accept = [ |
143 | '*' => 0.1 // just to make extra sure |
144 | ]; |
145 | // prefer the default |
146 | $accept[$mimeTypes[0]] = 1; |
147 | } |
148 | |
149 | $negotiator = new HttpAcceptNegotiator( $mimeTypes ); |
150 | $format = $negotiator->getBestSupportedKey( $accept ); |
151 | |
152 | if ( $format === null ) { |
153 | $format = isset( $accept['text/html'] ) ? 'text/html' : null; |
154 | } |
155 | |
156 | if ( $format === null ) { |
157 | throw new HttpError( 406, wfMessage( 'pagedata-not-acceptable', implode( ', ', $mimeTypes ) ) ); |
158 | } |
159 | |
160 | $url = $this->getDocUrl( $title, $format, $revision ); |
161 | $output->redirect( $url, 303 ); |
162 | } |
163 | |
164 | /** |
165 | * Returns a url representing the given title. |
166 | * |
167 | * @param Title $title |
168 | * @param string|null $format The (normalized) format name, or '' |
169 | * @param int $revision |
170 | * @return string |
171 | */ |
172 | private function getDocUrl( Title $title, $format = '', $revision = 0 ) { |
173 | $params = []; |
174 | |
175 | if ( $revision > 0 ) { |
176 | $params['oldid'] = $revision; |
177 | } |
178 | |
179 | if ( $format === 'text/html' ) { |
180 | return $title->getFullURL( $params ); |
181 | } |
182 | |
183 | $params[ 'action' ] = 'raw'; |
184 | |
185 | return $title->getFullURL( $params ); |
186 | } |
187 | |
188 | } |
189 | |
190 | /** @deprecated class alias since 1.42 */ |
191 | class_alias( PageDataRequestHandler::class, 'PageDataRequestHandler' ); |