From: MtDu Date: Wed, 4 Jan 2017 00:51:24 +0000 (-0600) Subject: Add API action to set the language of a page X-Git-Tag: 1.31.0-rc.0~4397^2 X-Git-Url: https://git.heureux-cyclage.org/?a=commitdiff_plain;h=d08751030c97a60b066f70e6962d426367a67e8d;hp=046c463635e12a7d5336fd298aa6ab53381b016d;p=lhc%2Fweb%2Fwiklou.git Add API action to set the language of a page Currently, a user who wants to set a page's language must use the Special:PageLanguage interface. However, this is not easy for bots and scripts to use. The addition of action=setpagelanguage to the API makes this process easier for such users. Bug: T74958 Change-Id: I42e74ed3bcd0bfa9ec0c344ba67668210450c975 --- diff --git a/autoload.php b/autoload.php index cdbdf1f1bc..a38fca2655 100644 --- a/autoload.php +++ b/autoload.php @@ -139,6 +139,7 @@ $wgAutoloadLocalClasses = [ 'ApiRsd' => __DIR__ . '/includes/api/ApiRsd.php', 'ApiSerializable' => __DIR__ . '/includes/api/ApiSerializable.php', 'ApiSetNotificationTimestamp' => __DIR__ . '/includes/api/ApiSetNotificationTimestamp.php', + 'ApiSetPageLanguage' => __DIR__ . '/includes/api/ApiSetPageLanguage.php', 'ApiStashEdit' => __DIR__ . '/includes/api/ApiStashEdit.php', 'ApiTag' => __DIR__ . '/includes/api/ApiTag.php', 'ApiTokens' => __DIR__ . '/includes/api/ApiTokens.php', diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index 4220fb8d80..52f1d95830 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -106,6 +106,7 @@ class ApiMain extends ApiBase { 'managetags' => 'ApiManageTags', 'tag' => 'ApiTag', 'mergehistory' => 'ApiMergeHistory', + 'setpagelanguage' => 'ApiSetPageLanguage', ]; /** diff --git a/includes/api/ApiSetPageLanguage.php b/includes/api/ApiSetPageLanguage.php new file mode 100755 index 0000000000..3f03c02680 --- /dev/null +++ b/includes/api/ApiSetPageLanguage.php @@ -0,0 +1,147 @@ +" + * + * 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 + */ + +/** + * API module that facilitates changing the language of a page. + * The API equivalent of SpecialPageLanguage. + * Requires API write mode to be enabled. + * + * @ingroup API + */ +class ApiSetPageLanguage extends ApiBase { + // Check if change language feature is enabled + protected function getDescriptionMessage() { + if ( !$this->getConfig()->get( 'PageLanguageUseDB' ) ) { + return 'apihelp-setpagelanguage-description-disabled'; + } + return parent::getDescriptionMessage(); + } + + /** + * Extracts the title and language from the request parameters and invokes + * the static SpecialPageLanguage::changePageLanguage() function with these as arguments. + * If the language change succeeds, the title, old language, and new language + * of the article changed, as well as the performer of the language change + * are added to the result object. + */ + public function execute() { + // Check if change language feature is enabled + if ( !$this->getConfig()->get( 'PageLanguageUseDB' ) ) { + $this->dieWithError( 'apierror-pagelang-disabled' ); + } + + // Check if the user has permissions + $this->checkUserRightsAny( 'pagelang' ); + + $this->useTransactionalTimeLimit(); + + $params = $this->extractRequestParams(); + + $pageObj = $this->getTitleOrPageId( $params, 'fromdbmaster' ); + if ( !$pageObj->exists() ) { + $this->dieWithError( 'apierror-missingtitle' ); + } + + $titleObj = $pageObj->getTitle(); + $user = $this->getUser(); + + // Check that the user is allowed to edit the page + $this->checkTitleUserPermissions( $titleObj, 'edit' ); + + // If change tagging was requested, check that the user is allowed to tag, + // and the tags are valid + if ( count( $params['tags'] ) ) { + $tagStatus = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $user ); + if ( !$tagStatus->isOK() ) { + $this->dieStatus( $tagStatus ); + } + } + + $status = SpecialPageLanguage::changePageLanguage( + $this, + $titleObj, + $params['lang'], + $params['tags'] ?: [] + ); + + if ( !$status->isOK() ) { + $this->dieStatus( $status ); + } + + $r = [ + 'title' => $titleObj->getPrefixedText(), + 'oldlanguage' => $status->value->oldLanguage, + 'newlanguage' => $status->value->newLanguage, + 'logid' => $status->value->logId + ]; + $this->getResult()->addValue( null, $this->getModuleName(), $r ); + } + + public function mustBePosted() { + return true; + } + + public function isWriteMode() { + return true; + } + + public function getAllowedParams() { + return [ + 'title' => null, + 'pageid' => [ + ApiBase::PARAM_TYPE => 'integer' + ], + 'lang' => [ + ApiBase::PARAM_TYPE => array_merge( + [ 'default' ], + array_keys( Language::fetchLanguageNames( null, 'mwfile' ) ) + ), + ApiBase::PARAM_REQUIRED => true, + ], + 'tags' => [ + ApiBase::PARAM_TYPE => 'tags', + ApiBase::PARAM_ISMULTI => true, + ], + ]; + } + + public function needsToken() { + return 'csrf'; + } + + protected function getExamplesMessages() { + return [ + 'action=setpagelanguage&title=Main%20Page&lang=eu&token=123ABC' + => 'apihelp-setpagelanguage-example-language', + 'action=setpagelanguage&pageid=123&lang=default&token=123ABC' + => 'apihelp-setpagelanguage-example-default', + ]; + } + + public function getHelpUrls() { + return 'https://www.mediawiki.org/wiki/API:SetPageLanguage'; + } +} diff --git a/includes/api/i18n/en.json b/includes/api/i18n/en.json index bc9fbf6946..4b6e932a21 100644 --- a/includes/api/i18n/en.json +++ b/includes/api/i18n/en.json @@ -1353,6 +1353,15 @@ "apihelp-setnotificationtimestamp-example-pagetimestamp": "Set the notification timestamp for Main page so all edits since 1 January 2012 are unviewed.", "apihelp-setnotificationtimestamp-example-allpages": "Reset the notification status for pages in the {{ns:user}} namespace.", + "apihelp-setpagelanguage-description": "Change the language of a page.", + "apihelp-setpagelanguage-description-disabled": "Changing the language of a page is not allowed on this wiki.\n\nEnable [[mw:Manual:$wgPageLanguageUseDB|$wgPageLanguageUseDB]] to use the API.", + "apihelp-setpagelanguage-param-title": "Title of the page whose language you wish to change. Cannot be used together with $1pageid.", + "apihelp-setpagelanguage-param-pageid": "Page ID of the page whose language you wish to change. Cannot be used together with $1title.", + "apihelp-setpagelanguage-param-lang": "Language code of the language to change the page to. Use default to reset the page to the wiki's default content language.", + "apihelp-setpagelanguage-param-tags": "Change tags to apply to the log entry resulting from this action.", + "apihelp-setpagelanguage-example-language": "Change the language of Main Page to Basque.", + "apihelp-setpagelanguage-example-default": "Change the language of the page with ID 123 to the wiki's default content language.", + "apihelp-stashedit-description": "Prepare an edit in shared cache.\n\nThis is intended to be used via AJAX from the edit form to improve the performance of the page save.", "apihelp-stashedit-param-title": "Title of the page being edited.", "apihelp-stashedit-param-section": "Section number. 0 for the top section, new for a new section.", @@ -1673,6 +1682,7 @@ "apierror-opensearch-json-warnings": "Warnings cannot be represented in OpenSearch JSON format.", "apierror-pagecannotexist": "Namespace doesn't allow actual pages.", "apierror-pagedeleted": "The page has been deleted since you fetched its timestamp.", + "apierror-pagelang-disabled": "Changing the language of a page is not allowed on this wiki.", "apierror-paramempty": "The parameter $1 may not be empty.", "apierror-parsetree-notwikitext": "prop=parsetree is only supported for wikitext content.", "apierror-parsetree-notwikitext-title": "prop=parsetree is only supported for wikitext content. $1 uses content model $2.", diff --git a/includes/api/i18n/qqq.json b/includes/api/i18n/qqq.json index 9d467cc528..662020b067 100644 --- a/includes/api/i18n/qqq.json +++ b/includes/api/i18n/qqq.json @@ -1261,6 +1261,14 @@ "apihelp-setnotificationtimestamp-example-page": "{{doc-apihelp-example|setnotificationtimestamp}}", "apihelp-setnotificationtimestamp-example-pagetimestamp": "{{doc-apihelp-example|setnotificationtimestamp}}", "apihelp-setnotificationtimestamp-example-allpages": "{{doc-apihelp-example|setnotificationtimestamp}}", + "apihelp-setpagelanguage-description": "{{doc-apihelp-description|setpagelanguage}}", + "apihelp-setpagelanguage-description-disabled": "{{doc-apihelp-description|setpagelanguage|info=This message is used when changing the language of a page is not allowed on the wiki because [[mw:Manual:$wgPageLanguageUseDB|$wgPageLanguageUseDB]] is not enabled.|seealso={{msg-mw|apihelp-setpagelanguage-description}}}}", + "apihelp-setpagelanguage-param-title": "{{doc-apihelp-param|setpagelanguage|title}}", + "apihelp-setpagelanguage-param-pageid": "{{doc-apihelp-param|setpagelanguage|pageid}}", + "apihelp-setpagelanguage-param-lang": "{{doc-apihelp-param|setpagelanguage|lang}}", + "apihelp-setpagelanguage-param-tags": "{{doc-apihelp-param|setpagelanguage|tags}}", + "apihelp-setpagelanguage-example-language": "{{doc-apihelp-example|setpagelanguage}}", + "apihelp-setpagelanguage-example-default": "{{doc-apihelp-example|setpagelanguage}}", "apihelp-stashedit-description": "{{doc-apihelp-description|stashedit}}", "apihelp-stashedit-param-title": "{{doc-apihelp-param|stashedit|title}}", "apihelp-stashedit-param-section": "{{doc-apihelp-param|stashedit|section}}", @@ -1566,6 +1574,7 @@ "apierror-opensearch-json-warnings": "{{doc-apierror}}", "apierror-pagecannotexist": "{{doc-apierror}}", "apierror-pagedeleted": "{{doc-apierror}}", + "apierror-pagelang-disabled": "{{doc-apierror}}", "apierror-paramempty": "{{doc-apierror}}\n\nParameters:\n* $1 - Parameter name.", "apierror-parsetree-notwikitext": "{{doc-apierror}}", "apierror-parsetree-notwikitext-title": "{{doc-apierror}}\n\nParameters:\n* $1 - Page title.\n* $2 - Content model.", diff --git a/includes/specials/SpecialPageLanguage.php b/includes/specials/SpecialPageLanguage.php index 61b6a8cd92..55ae692621 100644 --- a/includes/specials/SpecialPageLanguage.php +++ b/includes/specials/SpecialPageLanguage.php @@ -111,68 +111,99 @@ class SpecialPageLanguage extends FormSpecialPage { /** * * @param array $data - * @return bool + * @return Status */ public function onSubmit( array $data ) { - $title = Title::newFromText( $data['pagename'] ); + $pageName = $data['pagename']; - // Check if title is valid - if ( !$title ) { - return false; + // Check if user wants to use default language + if ( $data['selectoptions'] == 1 ) { + $newLanguage = 'default'; + } else { + $newLanguage = $data['language']; + } + + try { + $title = Title::newFromTextThrow( $pageName ); + } catch ( MalformedTitleException $ex ) { + return Status::newFatal( $ex->getMessageObject() ); } + // Url to redirect to after the operation + $this->goToUrl = $title->getFullURL(); + + return self::changePageLanguage( $this->getContext(), $title, $newLanguage ); + } + + /** + * @param IContextSource $context + * @param Title $title + * @param string $newLanguage Language code + * @param array $tags Change tags to apply to the log entry + * @return Status + */ + public static function changePageLanguage( IContextSource $context, Title $title, + $newLanguage, array $tags = [] ) { // Get the default language for the wiki - $defLang = $this->getConfig()->get( 'LanguageCode' ); + $defLang = $context->getConfig()->get( 'LanguageCode' ); $pageId = $title->getArticleID(); // Check if article exists if ( !$pageId ) { - return false; + return Status::newFatal( + 'pagelang-nonexistent-page', + wfEscapeWikiText( $title->getPrefixedText() ) + ); } // Load the page language from DB $dbw = wfGetDB( DB_MASTER ); - $langOld = $dbw->selectField( + $oldLanguage = $dbw->selectField( 'page', 'page_lang', [ 'page_id' => $pageId ], __METHOD__ ); - // Url to redirect to after the operation - $this->goToUrl = $title->getFullURL(); - - // Check if user wants to use default language - if ( $data['selectoptions'] == 1 ) { - $langNew = null; - } else { - $langNew = $data['language']; + // Check if user wants to use the default language + if ( $newLanguage === 'default' ) { + $newLanguage = null; } // No change in language - if ( $langNew === $langOld ) { - return false; + if ( $newLanguage === $oldLanguage ) { + // Check if old language does not exist + if ( !$oldLanguage ) { + return Status::newFatal( + 'pagelang-unchanged-language-default', + wfEscapeWikiText( $title->getPrefixedText() ) + ); + } + return Status::newFatal( + 'pagelang-unchanged-language', + wfEscapeWikiText( $title->getPrefixedText() ), + $oldLanguage + ); } // Hardcoded [def] if the language is set to null - $logOld = $langOld ? $langOld : $defLang . '[def]'; - $logNew = $langNew ? $langNew : $defLang . '[def]'; + $logOld = $oldLanguage ? $oldLanguage : $defLang . '[def]'; + $logNew = $newLanguage ? $newLanguage : $defLang . '[def]'; // Writing new page language to database - $dbw = wfGetDB( DB_MASTER ); $dbw->update( 'page', - [ 'page_lang' => $langNew ], + [ 'page_lang' => $newLanguage ], [ 'page_id' => $pageId, - 'page_lang' => $langOld + 'page_lang' => $oldLanguage ], __METHOD__ ); if ( !$dbw->affectedRows() ) { - return false; + return Status::newFatal( 'pagelang-db-failed' ); } // Logging change of language @@ -181,9 +212,10 @@ class SpecialPageLanguage extends FormSpecialPage { '5::newlanguage' => $logNew ]; $entry = new ManualLogEntry( 'pagelang', 'pagelang' ); - $entry->setPerformer( $this->getUser() ); + $entry->setPerformer( $context->getUser() ); $entry->setTarget( $title ); $entry->setParameters( $logParams ); + $entry->setTags( $tags ); $logid = $entry->insert(); $entry->publish( $logid ); @@ -191,7 +223,11 @@ class SpecialPageLanguage extends FormSpecialPage { // Force re-render so that language-based content (parser functions etc.) gets updated $title->invalidateCache(); - return true; + return Status::newGood( (object)[ + 'oldLanguage' => $logOld, + 'newLanguage' => $logNew, + 'logId' => $logid, + ] ); } public function onSuccess() { diff --git a/languages/i18n/en.json b/languages/i18n/en.json index f2b27fc7fb..f31690c973 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -4047,6 +4047,10 @@ "pagelang-use-default": "Use default language", "pagelang-select-lang": "Select language", "pagelang-submit": "Submit", + "pagelang-nonexistent-page": "The page $1 does not exist.", + "pagelang-unchanged-language": "The page $1 is already set to language $2.", + "pagelang-unchanged-language-default": "The page $1 is already set to the wiki's default content language.", + "pagelang-db-failed": "The database failed to change the page language.", "right-pagelang": "Change page language", "action-pagelang": "change the page language", "log-name-pagelang": "Language change log", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index 99f7679e55..6eba2e2cc4 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -4231,6 +4231,10 @@ "pagelang-use-default": "Radio label for selector on Special:PageLanguage for default language", "pagelang-select-lang": "Radio label for selector on Special:PageLanguage for language selection\n{{Identical|Select language}}", "pagelang-submit": "Submit button label for Special:PageLanguage form\n{{Identical|Submit}}", + "pagelang-nonexistent-page": "Error message shown when the page the user is trying to change the language on does not exist.\n\nParameters:\n* $1 - the title of the nonexistent page", + "pagelang-unchanged-language": "Error message shown when the language the user is trying to change the page to and the current language the page is in are the same.\n\nParameters:\n* $1 - the title of the target page\n* $2 - the current language of the page", + "pagelang-unchanged-language-default": "Error message shown when the language the user is trying to change the page to is already the wiki's default content language.\n\nParameters:\n* $1 - the title of the target page", + "pagelang-db-failed": "Error message shown when the database fails to update the language of the page", "right-pagelang": "{{Doc-right|pagelang}}\nRight to change page language on Special:PageLanguage", "action-pagelang": "{{Doc-action|pagelang}}", "log-name-pagelang": "Display entry for log name for changes in page language in Special:Log.",