Merge "Add maintenance/view.php for viewing page contents"
[lhc/web/wiklou.git] / includes / api / ApiParse.php
index 83b5d93..2263b8f 100644 (file)
@@ -36,18 +36,18 @@ class ApiParse extends ApiBase {
        /** @var Content $pstContent */
        private $pstContent = null;
 
-       private function checkReadPermissions( Title $title ) {
-               if ( !$title->userCan( 'read', $this->getUser() ) ) {
-                       $this->dieUsage( "You don't have permission to view this page", 'permissiondenied' );
-               }
-       }
-
        public function execute() {
                // The data is hot but user-dependent, like page views, so we set vary cookies
                $this->getMain()->setCacheMode( 'anon-public-user-private' );
 
                // Get parameters
                $params = $this->extractRequestParams();
+
+               // No easy way to say that text & title are allowed together while the
+               // rest aren't, so just do it in two calls.
+               $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'text' );
+               $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'title' );
+
                $text = $params['text'];
                $title = $params['title'];
                if ( $title === null ) {
@@ -65,21 +65,12 @@ class ApiParse extends ApiBase {
                $model = $params['contentmodel'];
                $format = $params['contentformat'];
 
-               if ( !is_null( $page ) && ( !is_null( $text ) || $titleProvided ) ) {
-                       $this->dieUsage(
-                               'The page parameter cannot be used together with the text and title parameters',
-                               'params'
-                       );
-               }
-
                $prop = array_flip( $params['prop'] );
 
                if ( isset( $params['section'] ) ) {
                        $this->section = $params['section'];
                        if ( !preg_match( '/^((T-)?\d+|new)$/', $this->section ) ) {
-                               $this->dieUsage(
-                                       'The section parameter must be a valid section id or "new"', 'invalidsection'
-                               );
+                               $this->dieWithError( 'apierror-invalidsection' );
                        }
                } else {
                        $this->section = false;
@@ -97,21 +88,20 @@ class ApiParse extends ApiBase {
 
                if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) {
                        if ( $this->section === 'new' ) {
-                                       $this->dieUsage(
-                                               'section=new cannot be combined with oldid, pageid or page parameters. ' .
-                                               'Please use text', 'params'
-                                       );
+                               $this->dieWithError( 'apierror-invalidparammix-parse-new-section', 'invalidparammix' );
                        }
                        if ( !is_null( $oldid ) ) {
                                // Don't use the parser cache
                                $rev = Revision::newFromId( $oldid );
                                if ( !$rev ) {
-                                       $this->dieUsage( "There is no revision ID $oldid", 'missingrev' );
+                                       $this->dieWithError( [ 'apierror-nosuchrevid', $oldid ] );
                                }
 
-                               $this->checkReadPermissions( $rev->getTitle() );
+                               $this->checkTitleUserPermissions( $rev->getTitle(), 'read' );
                                if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
-                                       $this->dieUsage( "You don't have permission to view deleted revisions", 'permissiondenied' );
+                                       $this->dieWithError(
+                                               [ 'apierror-permissiondenied', $this->msg( 'action-deletedtext' ) ]
+                                       );
                                }
 
                                $titleObj = $rev->getTitle();
@@ -131,7 +121,9 @@ class ApiParse extends ApiBase {
                                        $this->content = $rev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
 
                                        if ( $this->section !== false ) {
-                                               $this->content = $this->getSectionContent( $this->content, 'r' . $rev->getId() );
+                                               $this->content = $this->getSectionContent(
+                                                       $this->content, $this->msg( 'revid', $rev->getId() )
+                                               );
                                        }
 
                                        // Should we save old revision parses to the parser cache?
@@ -167,10 +159,10 @@ class ApiParse extends ApiBase {
                                $pageObj = $this->getTitleOrPageId( $pageParams, 'fromdb' );
                                $titleObj = $pageObj->getTitle();
                                if ( !$titleObj || !$titleObj->exists() ) {
-                                       $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' );
+                                       $this->dieWithError( 'apierror-missingtitle' );
                                }
 
-                               $this->checkReadPermissions( $titleObj );
+                               $this->checkTitleUserPermissions( $titleObj, 'read' );
                                $wgTitle = $titleObj;
 
                                if ( isset( $prop['revid'] ) ) {
@@ -201,7 +193,7 @@ class ApiParse extends ApiBase {
                } else { // Not $oldid, $pageid, $page. Hence based on $text
                        $titleObj = Title::newFromText( $title );
                        if ( !$titleObj || $titleObj->isExternal() ) {
-                               $this->dieUsageMsg( [ 'invalidtitle', $title ] );
+                               $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $title ) ] );
                        }
                        $wgTitle = $titleObj;
                        if ( $titleObj->canExist() ) {
@@ -217,10 +209,7 @@ class ApiParse extends ApiBase {
 
                        if ( !$textProvided ) {
                                if ( $titleProvided && ( $prop || $params['generatexml'] ) ) {
-                                       $this->setWarning(
-                                               "'title' used without 'text', and parsed page properties were requested " .
-                                               "(did you mean to use 'page' instead of 'title'?)"
-                                       );
+                                       $this->addWarning( 'apiwarn-parse-titlewithouttext' );
                                }
                                // Prevent warning from ContentHandler::makeContent()
                                $text = '';
@@ -230,13 +219,17 @@ class ApiParse extends ApiBase {
                        // API title, but default to wikitext to keep BC.
                        if ( $textProvided && !$titleProvided && is_null( $model ) ) {
                                $model = CONTENT_MODEL_WIKITEXT;
-                               $this->setWarning( "No 'title' or 'contentmodel' was given, assuming $model." );
+                               $this->addWarning( [ 'apiwarn-parse-nocontentmodel', $model ] );
                        }
 
                        try {
                                $this->content = ContentHandler::makeContent( $text, $titleObj, $model, $format );
                        } catch ( MWContentSerializationException $ex ) {
-                               $this->dieUsage( $ex->getMessage(), 'parseerror' );
+                               // @todo: Internationalize MWContentSerializationException
+                               $this->dieWithError(
+                                       [ 'apierror-contentserializationexception', wfEscapeWikiText( $ex->getMessage() ) ],
+                                       'parseerror'
+                               );
                        }
 
                        if ( $this->section !== false ) {
@@ -357,10 +350,7 @@ class ApiParse extends ApiBase {
 
                if ( isset( $prop['headitems'] ) ) {
                        $result_array['headitems'] = $this->formatHeadItems( $p_result->getHeadItems() );
-                       $this->logFeatureUsage( 'action=parse&prop=headitems' );
-                       $this->setWarning( 'headitems is deprecated since MediaWiki 1.28. '
-                               . 'Use prop=headhtml when creating new HTML documents, or '
-                               . 'prop=modules|jsconfigvars when updating a document client-side.' );
+                       $this->addDeprecation( 'apiwarn-deprecation-parse-headitems', 'action=parse&prop=headitems' );
                }
 
                if ( isset( $prop['headhtml'] ) ) {
@@ -397,9 +387,7 @@ class ApiParse extends ApiBase {
 
                if ( isset( $prop['modules'] ) &&
                        !isset( $prop['jsconfigvars'] ) && !isset( $prop['encodedjsconfigvars'] ) ) {
-                       $this->setWarning( 'Property "modules" was set but not "jsconfigvars" ' .
-                               'or "encodedjsconfigvars". Configuration variables are necessary ' .
-                               'for proper module usage.' );
+                       $this->addWarning( 'apiwarn-moduleswithoutvars' );
                }
 
                if ( isset( $prop['indicators'] ) ) {
@@ -435,7 +423,7 @@ class ApiParse extends ApiBase {
 
                if ( isset( $prop['parsetree'] ) || $params['generatexml'] ) {
                        if ( $this->content->getModel() != CONTENT_MODEL_WIKITEXT ) {
-                               $this->dieUsage( 'parsetree is only supported for wikitext content', 'notwikitext' );
+                               $this->dieWithError( 'apierror-parsetree-notwikitext', 'notwikitext' );
                        }
 
                        $wgParser->startExternalParse( $titleObj, $popts, Parser::OT_PREPROCESS );
@@ -516,7 +504,7 @@ class ApiParse extends ApiBase {
                // getParserOutput will save to Parser cache if able
                $pout = $page->getParserOutput( $popts );
                if ( !$pout ) {
-                       $this->dieUsage( "There is no revision ID {$page->getLatest()}", 'missingrev' );
+                       $this->dieWithError( [ 'apierror-nosuchrevid', $page->getLatest() ] );
                }
                if ( $getWikitext ) {
                        $this->content = $page->getContent( Revision::RAW );
@@ -538,7 +526,9 @@ class ApiParse extends ApiBase {
                if ( $this->section !== false && $content !== null ) {
                        $content = $this->getSectionContent(
                                $content,
-                               !is_null( $pageId ) ? 'page id ' . $pageId : $page->getTitle()->getPrefixedText()
+                               !is_null( $pageId )
+                                       ? $this->msg( 'pageid', $pageId )
+                                       : $page->getTitle()->getPrefixedText()
                        );
                }
                return $content;
@@ -548,17 +538,17 @@ class ApiParse extends ApiBase {
         * Extract the requested section from the given Content
         *
         * @param Content $content
-        * @param string $what Identifies the content in error messages, e.g. page title.
+        * @param string|Message $what Identifies the content in error messages, e.g. page title.
         * @return Content|bool
         */
        private function getSectionContent( Content $content, $what ) {
                // Not cached (save or load)
                $section = $content->getSection( $this->section );
                if ( $section === false ) {
-                       $this->dieUsage( "There is no section {$this->section} in $what", 'nosuchsection' );
+                       $this->dieWithError( [ 'apierror-nosuchsection-what', $this->section, $what ], 'nosuchsection' );
                }
                if ( $section === null ) {
-                       $this->dieUsage( "Sections are not supported by $what", 'nosuchsection' );
+                       $this->dieWithError( [ 'apierror-sectionsnotsupported-what', $what ], 'nosuchsection' );
                        $section = false;
                }
 
@@ -641,6 +631,8 @@ class ApiParse extends ApiBase {
                        $hiddencats[$row->page_title] = isset( $row->pp_propname );
                }
 
+               $linkCache = LinkCache::singleton();
+
                foreach ( $links as $link => $sortkey ) {
                        $entry = [];
                        $entry['sortkey'] = $sortkey;
@@ -648,6 +640,14 @@ class ApiParse extends ApiBase {
                        ApiResult::setContentValue( $entry, 'category', (string)$link );
                        if ( !isset( $hiddencats[$link] ) ) {
                                $entry['missing'] = true;
+
+                               // We already know the link doesn't exist in the database, so
+                               // tell LinkCache that before calling $title->isKnown().
+                               $title = Title::makeTitle( NS_CATEGORY, $link );
+                               $linkCache->addBadLinkObj( $title );
+                               if ( $title->isKnown() ) {
+                                       $entry['known'] = true;
+                               }
                        } elseif ( $hiddencats[$link] ) {
                                $entry['hidden'] = true;
                        }