wikimedia/mediawiki-core

View on GitHub
includes/linkeddata/PageDataRequestHandler.php

Summary

Maintainability
A
25 mins
Test Coverage
<?php
/**
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * http://www.gnu.org/copyleft/gpl.html
 *
 * @file
 */

namespace MediaWiki\LinkedData;

use HttpError;
use MediaWiki\MediaWikiServices;
use MediaWiki\Output\OutputPage;
use MediaWiki\Request\WebRequest;
use MediaWiki\Revision\SlotRecord;
use MediaWiki\Title\MalformedTitleException;
use MediaWiki\Title\Title;
use Wikimedia\Http\HttpAcceptNegotiator;
use Wikimedia\Http\HttpAcceptParser;

/**
 * Request handler implementing a data interface for mediawiki pages.
 *
 * @author Daniel Kinzler
 * @author Amir Sarabadanai
 */
class PageDataRequestHandler {

    /**
     * Checks whether the request is complete, i.e. whether it contains all information needed
     * to reply with page data.
     *
     * This does not check whether the request is valid and will actually produce a successful
     * response.
     *
     * @param string|null $subPage
     * @param WebRequest $request
     *
     * @return bool
     */
    public function canHandleRequest( $subPage, WebRequest $request ) {
        if ( $subPage === '' || $subPage === null ) {
            return $request->getText( 'target' ) !== '';
        }

        $parts = explode( '/', $subPage, 2 );
        $slot = $parts[0];
        $title = $parts[1] ?? '';
        return ( $slot === SlotRecord::MAIN || $slot === '' ) && $title !== '';
    }

    /**
     * Main method for handling requests.
     *
     * @param string|null $subPage
     * @param WebRequest $request The request parameters. Known parameters are:
     *        - title: the page title
     *        - format: the format
     *        - oldid|revision: the revision ID
     * @param OutputPage $output
     *
     * @note Instead of an output page, a WebResponse could be sufficient, but
     *        redirect logic is currently implemented in OutputPage.
     *
     * @throws HttpError
     */
    public function handleRequest( $subPage, WebRequest $request, OutputPage $output ) {
        // No matter what: The response is always public
        $output->getRequest()->response()->header( 'Access-Control-Allow-Origin: *' );

        if ( !$this->canHandleRequest( $subPage, $request ) ) {
            throw new HttpError( 400, wfMessage( 'pagedata-bad-title', $subPage ) );
        }

        $revision = 0;

        if ( $subPage !== '' ) {
            $parts = explode( '/', $subPage, 2 );
            $title = $parts[1] ?? '';
        } else {
            $title = $request->getText( 'target' );
        }

        $revision = $request->getInt( 'oldid', $revision );
        $revision = $request->getInt( 'revision', $revision );

        if ( $title === null || $title === '' ) {
            // TODO: different error message?
            throw new HttpError( 400, wfMessage( 'pagedata-bad-title', $title ) );
        }

        try {
            $title = MediaWikiServices::getInstance()->getTitleFactory()->newFromTextThrow( $title );
        } catch ( MalformedTitleException $ex ) {
            throw new HttpError( 400, wfMessage( 'pagedata-bad-title', $title ) );
        }

        $this->httpContentNegotiation( $request, $output, $title, $revision );
    }

    /**
     * Applies HTTP content negotiation.
     * If the negotiation is successful, this method will set the appropriate redirect
     * in the OutputPage object and return. Otherwise, an HttpError is thrown.
     *
     * @param WebRequest $request
     * @param OutputPage $output
     * @param Title $title
     * @param int $revision The desired revision
     *
     * @throws HttpError
     */
    public function httpContentNegotiation(
        WebRequest $request,
        OutputPage $output,
        Title $title,
        $revision = 0
    ) {
        $mimeTypes = MediaWikiServices::getInstance()
            ->getContentHandlerFactory()
            ->getContentHandler( $title->getContentModel() )
            ->getSupportedFormats();

        $acceptHeader = $request->getHeader( 'Accept' );
        if ( $acceptHeader !== false ) {
            $parser = new HttpAcceptParser();
            $accept = $parser->parseWeights( $acceptHeader );
        } else {
            // anything goes
            $accept = [
                '*' => 0.1 // just to make extra sure
            ];
            // prefer the default
            $accept[$mimeTypes[0]] = 1;
        }

        $negotiator = new HttpAcceptNegotiator( $mimeTypes );
        $format = $negotiator->getBestSupportedKey( $accept );

        if ( $format === null ) {
            $format = isset( $accept['text/html'] ) ? 'text/html' : null;
        }

        if ( $format === null ) {
            throw new HttpError( 406, wfMessage( 'pagedata-not-acceptable', implode( ', ', $mimeTypes ) ) );
        }

        $url = $this->getDocUrl( $title, $format, $revision );
        $output->redirect( $url, 303 );
    }

    /**
     * Returns a url representing the given title.
     *
     * @param Title $title
     * @param string|null $format The (normalized) format name, or ''
     * @param int $revision
     * @return string
     */
    private function getDocUrl( Title $title, $format = '', $revision = 0 ) {
        $params = [];

        if ( $revision > 0 ) {
            $params['oldid'] = $revision;
        }

        if ( $format === 'text/html' ) {
            return $title->getFullURL( $params );
        }

        $params[ 'action' ] = 'raw';

        return $title->getFullURL( $params );
    }

}

/** @deprecated class alias since 1.41 */
class_alias( PageDataRequestHandler::class, 'PageDataRequestHandler' );