From: jenkins-bot Date: Sun, 13 Mar 2016 07:54:39 +0000 (+0000) Subject: Merge "build: Update grunt-jscs to 2.8.0" X-Git-Tag: 1.31.0-rc.0~7635 X-Git-Url: https://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/banques/ajouter.php?a=commitdiff_plain;h=1fc267206c21a2ad44c7bb6a19ea1788cc621b17;hp=5a1a6e8440ed39861d06575fece27c2312f20f57;p=lhc%2Fweb%2Fwiklou.git Merge "build: Update grunt-jscs to 2.8.0" --- diff --git a/RELEASE-NOTES-1.27 b/RELEASE-NOTES-1.27 index 437f8e660b..82d8103e3f 100644 --- a/RELEASE-NOTES-1.27 +++ b/RELEASE-NOTES-1.27 @@ -172,7 +172,8 @@ HHVM 3.1. ==== Upgraded external libraries ==== * Updated oojs/oojs-ui from v0.12.12 to v0.13.3. * Updated composer/semver from v1.0.0 to v1.2.0. -* Update liuggio/statsd-php-client to 1.0.18. +* Updated liuggio/statsd-php-client to 1.0.18. +* Updated QUnit from v1.18.0 to v1.22.0. ==== New external libraries ==== * Added wikimedia/base-convert v1.0.1. @@ -213,13 +214,35 @@ HHVM 3.1. ** ApiFormatDbg ** ApiFormatTxt ** ApiFormatYaml +* ApiBase::addTokenProperties() was removed (deprecated since 1.24). +* ApiBase::getFinalPossibleErrors() was removed (deprecated since 1.24). +* ApiBase::getFinalResultProperties() was removed (deprecated since 1.24). +* ApiBase::getRequireAtLeastOneParameterErrorMessages() was removed (deprecated since 1.24). +* ApiBase::getPossibleErrors() was removed (deprecated since 1.24). +* ApiBase::getRequireMaxOneParameterErrorMessages() was removed (deprecated since 1.24). +* ApiBase::getRequireOnlyOneParameterErrorMessages() was removed (deprecated since 1.24). +* ApiBase::getResultProperties() was removed (deprecated since 1.24). +* ApiBase::getTitleOrPageIdErrorMessage() was removed (deprecated since 1.24). +* ApiBase::parseErrors() was removed (deprecated since 1.24). * ApiQueryBase::titleToKey(), ApiQueryBase::keyToTitle() and ApiQueryBase::keyPartToTitle() all removed (deprecated since 1.24). * ApiQueryBase::checkRowCount() was removed (deprecated since 1.24). * ApiQueryBase::getDirectionDescription() was removed (deprecated since 1.25). +* ApiQuery::getGenerators() was removed (deprecated since 1.21). * ApiQuery::getModules() was removed (deprecated since 1.21). +* ApiQuery::getModuleType() was removed (deprecated since 1.21). +* ApiQuery::setGeneratorContinue() was removed (deprecated since 1.24). * ApiMain::getModules() was removed (deprecated since 1.21). * ApiBase::getVersion() was removed (deprecated since 1.21). +* Language::getLangObj() was removed (deprecated since 1.24). +* Language::getLanguageName() was removed (deprecated since 1.20). +* Language::getLanguageNames() was removed (deprecated since 1.20). +* Language::getTranslatedLanguageNames() was removed (deprecated since 1.20). +* Language::specialPage() was removed (deprecated since 1.24). +* OutputPage::getHeadItems() was removed (deprecated since 1.24). +* OutputPage::getScript() was removed (deprecated since 1.24). +* OutputPage::out() was removed (deprecated since 1.22). +* OutputPage::setAllowedModules() was removed (deprecated since 1.24). === Languages updated in 1.27 === @@ -324,6 +347,18 @@ changes to languages because of Phabricator reports. does not support categories. * wikidiff difference engine is no longer supported, anyone still using it are encouraged to upgrade to wikidiff2 which is actively maintained and has better package availability. +* Database logic was removed from WatchedItem and a WatchedItemStore was created: +** WatchedItem::IGNORE_USER_RIGHTS and WatchedItem::CHECK_USER_RIGHTS were deprecated. + User::IGNORE_USER_RIGHTS and User::CHECK_USER_RIGHTS were introduced. +** WatchedItem::fromUserTitle was deprecated in favour of the constructor. +** WatchedItem::resetNotificationTimestamp was deprecated. +** WatchedItem::batchAddWatch was deprecated. +** WatchedItem::addWatch was deprecated. +** WatchedItem::removeWatch was deprecated. +** WatchedItem::isWatched was deprecated. +** WatchedItem::duplicateEntries was deprecated. +** EmailNotification::updateWatchlistTimestamp was deprecated. +** User::getWatchedItem was removed. == Compatibility == diff --git a/composer.json b/composer.json index a545bb1c55..a21b4e8e98 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "ext-iconv": "*", "liuggio/statsd-php-client": "1.0.18", "mediawiki/at-ease": "1.1.0", - "oojs/oojs-ui": "0.16.1", + "oojs/oojs-ui": "0.16.2", "oyejorge/less.php": "1.7.0.10", "php": ">=5.5.9", "psr/log": "1.0.0", @@ -29,7 +29,7 @@ "wikimedia/base-convert": "1.0.1", "wikimedia/cdb": "1.3.0", "wikimedia/cldr-plural-rule-parser": "1.0.0", - "wikimedia/composer-merge-plugin": "1.3.0", + "wikimedia/composer-merge-plugin": "1.3.1", "wikimedia/ip-set": "1.0.1", "wikimedia/php-session-serializer": "1.0.3", "wikimedia/relpath": "1.0.3", diff --git a/docs/hooks.txt b/docs/hooks.txt index c5f2424ef0..a431f1b3fb 100644 --- a/docs/hooks.txt +++ b/docs/hooks.txt @@ -178,7 +178,8 @@ once for 'TimStarling', and once for 'brion'. Hooks can return three possible values: - * true: the hook has operated successfully + * No return value (or null): the hook has operated successfully. Previously, + true was required. This is the default since MediaWiki 1.23. * "some string": an error occurred; processing should stop and the error should be shown to the user * false: the hook has successfully done the work necessary and the calling diff --git a/includes/Block.php b/includes/Block.php index 764592d0cd..b8e900d90a 100644 --- a/includes/Block.php +++ b/includes/Block.php @@ -844,7 +844,7 @@ class Block { 'ipb_expiry' => $dbw->timestamp( $this->mExpiry ), ], [ /* WHERE */ - 'ipb_address' => (string)$this->getTarget() + 'ipb_id' => $this->getId(), ], __METHOD__ ); diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 2a9986e28e..c04602c69b 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -2096,7 +2096,7 @@ $wgTransactionalTimeLimit = 120; /** * Directory for caching data in the local filesystem. Should not be accessible - * from the web. Set this to false to not use any local caches. + * from the web. * * Note: if multiple wikis share the same localisation cache directory, they * must all have the same set of extensions. You can set a directory just for diff --git a/includes/EditPage.php b/includes/EditPage.php index 482fcc636c..32687003c1 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -2109,7 +2109,7 @@ class EditPage { $watch = $this->watchthis; // Do this in its own transaction to reduce contention... DeferredUpdates::addCallableUpdate( function () use ( $user, $title, $watch ) { - if ( $watch == $user->isWatched( $title, WatchedItem::IGNORE_USER_RIGHTS ) ) { + if ( $watch == $user->isWatched( $title, User::IGNORE_USER_RIGHTS ) ) { return; // nothing to change } WatchAction::doWatchOrUnwatch( $watch, $title, $user ); diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index e48a399db5..3fa91fa700 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -1738,7 +1738,7 @@ function wfEscapeWikiText( $text ) { $repl2[] = preg_quote( substr( $prot, 0, -1 ), '/' ); } } - $repl2 = $repl2 ? '/\b(' . join( '|', $repl2 ) . '):/i' : '/^(?!)/'; + $repl2 = $repl2 ? '/\b(' . implode( '|', $repl2 ) . '):/i' : '/^(?!)/'; } $text = substr( strtr( "\n$text", $repl ), 1 ); $text = preg_replace( $repl2, '$1:', $text ); diff --git a/includes/HttpFunctions.php b/includes/HttpFunctions.php index 600bd7969b..f980a93d56 100644 --- a/includes/HttpFunctions.php +++ b/includes/HttpFunctions.php @@ -80,7 +80,7 @@ class Http { } else { $errors = $status->getErrorsByType( 'error' ); $logger = LoggerFactory::getInstance( 'http' ); - $logger->warning( $status->getWikiText(), + $logger->warning( $status->getWikiText( null, null, 'en' ), [ 'error' => $errors, 'caller' => $caller, 'content' => $req->getContent() ] ); return false; } diff --git a/includes/MovePage.php b/includes/MovePage.php index afa4e1cdbb..321b7e3339 100644 --- a/includes/MovePage.php +++ b/includes/MovePage.php @@ -369,7 +369,8 @@ class MovePage { $oldsnamespace = MWNamespace::getSubject( $this->oldTitle->getNamespace() ); $newsnamespace = MWNamespace::getSubject( $this->newTitle->getNamespace() ); if ( $oldsnamespace != $newsnamespace || $oldtitle != $newtitle ) { - WatchedItem::duplicateEntries( $this->oldTitle, $this->newTitle ); + $store = WatchedItemStore::getDefaultInstance(); + $store->duplicateAllAssociatedEntries( $this->oldTitle, $this->newTitle ); } Hooks::run( diff --git a/includes/OutputPage.php b/includes/OutputPage.php index 5d1d5d0cdf..6774072d11 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -519,17 +519,6 @@ class OutputPage extends ContextSource { $this->mScripts .= Html::inlineScript( "\n$script\n" ) . "\n"; } - /** - * Get all registered JS and CSS tags for the header. - * - * @return string - * @deprecated since 1.24 Use OutputPage::headElement to build the full header. - */ - function getScript() { - wfDeprecated( __METHOD__, '1.24' ); - return $this->mScripts . $this->getHeadItems(); - } - /** * Filter an array of modules to remove insufficiently trustworthy members, and modules * which are no longer registered (eg a page is cached before an extension is disabled) @@ -678,22 +667,6 @@ class OutputPage extends ContextSource { return $this->mHeadItems; } - /** - * Get all header items in a string - * - * @return string - * @deprecated since 1.24 Use OutputPage::headElement or - * if absolutely necessary use OutputPage::getHeadItemsArray - */ - function getHeadItems() { - wfDeprecated( __METHOD__, '1.24' ); - $s = ''; - foreach ( $this->mHeadItems as $item ) { - $s .= $item; - } - return $s; - } - /** * Add or replace an header item to the output * @@ -1495,19 +1468,6 @@ class OutputPage extends ContextSource { } } - /** - * Set the highest level of CSS/JS untrustworthiness allowed - * - * @deprecated since 1.24 Raising level of allowed untrusted content is no longer supported. - * Use reduceAllowedModules() instead - * @param string $type ResourceLoaderModule TYPE_ constant - * @param int $level ResourceLoaderModule class constant - */ - public function setAllowedModules( $type, $level ) { - wfDeprecated( __METHOD__, '1.24' ); - $this->reduceAllowedModules( $type, $level ); - } - /** * Limit the highest level of CSS/JS untrustworthiness allowed. * @@ -2069,7 +2029,7 @@ class OutputPage extends ContextSource { foreach ( SessionManager::singleton()->getVaryHeaders() as $header => $options ) { $this->addVaryHeader( $header, $options ); } - return 'Vary: ' . join( ', ', array_keys( $this->mVaryHeader ) ); + return 'Vary: ' . implode( ', ', array_keys( $this->mVaryHeader ) ); } /** @@ -2213,8 +2173,12 @@ class OutputPage extends ContextSource { if ( $this->mEnableClientCache ) { if ( - $config->get( 'UseSquid' ) && !SessionManager::getGlobalSession()->isPersistent() && - !$this->isPrintable() && $this->mCdnMaxage != 0 && !$this->haveCacheVaryCookies() + $config->get( 'UseSquid' ) && + !$response->hasCookies() && + !SessionManager::getGlobalSession()->isPersistent() && + !$this->isPrintable() && + $this->mCdnMaxage != 0 && + !$this->haveCacheVaryCookies() ) { if ( $config->get( 'UseESI' ) ) { # We'll purge the proxy cache explicitly, but require end user agents @@ -2363,17 +2327,6 @@ class OutputPage extends ContextSource { } - /** - * Actually output something with print. - * - * @param string $ins The string to output - * @deprecated since 1.22 Use echo yourself. - */ - public function out( $ins ) { - wfDeprecated( __METHOD__, '1.22' ); - print $ins; - } - /** * Prepare this object to display an error page; disable caching and * indexing, clear the current text and redirect, set the page's title @@ -3214,7 +3167,7 @@ class OutputPage extends ContextSource { $articleId = $wikiPage->getId(); } - $lang = $title->getPageLanguage(); + $lang = $title->getPageViewLanguage(); // Pre-process information $separatorTransTable = $lang->separatorTransformTable(); diff --git a/includes/Setup.php b/includes/Setup.php index f92c8c239b..f26d789496 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -536,6 +536,35 @@ if ( !class_exists( 'AutoLoader' ) ) { require_once "$IP/includes/AutoLoader.php"; } +// Install a header callback to prevent caching of responses with cookies (T127993) +if ( !$wgCommandLineMode ) { + header_register_callback( function () { + $headers = []; + foreach ( headers_list() as $header ) { + list( $name, $value ) = explode( ':', $header, 2 ); + $headers[strtolower( trim( $name ) )][] = trim( $value ); + } + + if ( isset( $headers['set-cookie'] ) ) { + $cacheControl = isset( $headers['cache-control'] ) + ? implode( ', ', $headers['cache-control'] ) + : ''; + + if ( !preg_match( '/(?:^|,)\s*(?:private|no-cache|no-store)\s*(?:$|,)/i', $cacheControl ) ) { + header( 'Expires: Thu, 01 Jan 1970 00:00:00 GMT' ); + header( 'Cache-Control: private, max-age=0, s-maxage=0' ); + MediaWiki\Logger\LoggerFactory::getInstance( 'cache-cookies' )->warning( + 'Cookies set on {url} with Cache-Control "{cache-control}"', [ + 'url' => WebRequest::getGlobalRequestURL(), + 'cookies' => $headers['set-cookie'], + 'cache-control' => $cacheControl ?: '', + ] + ); + } + } + } ); +} + MWExceptionHandler::installHandler(); require_once "$IP/includes/compat/normal/UtfNormalUtil.php"; diff --git a/includes/Status.php b/includes/Status.php index 75fe190a02..cc7abc8200 100644 --- a/includes/Status.php +++ b/includes/Status.php @@ -178,15 +178,36 @@ class Status { return $cleanParams; } + /** + * @param string|Language|null $lang Language to use for processing + * messages, or null to default to the user language. + * @return Language + */ + protected function languageFromParam( $lang ) { + global $wgLang; + + if ( $lang === null ) { + // @todo: Use RequestContext::getMain()->getLanguage() instead + return $wgLang; + } elseif ( $lang instanceof Language || $lang instanceof StubUserLang ) { + return $lang; + } else { + return Language::factory( $lang ); + } + } + /** * Get the error list as a wikitext formatted list * * @param string|bool $shortContext A short enclosing context message name, to * be used when there is a single error * @param string|bool $longContext A long enclosing context message name, for a list + * @param string|Language $lang Language to use for processing messages * @return string */ - public function getWikiText( $shortContext = false, $longContext = false ) { + public function getWikiText( $shortContext = false, $longContext = false, $lang = null ) { + $lang = $this->languageFromParam( $lang ); + $rawErrors = $this->sv->getErrors(); if ( count( $rawErrors ) == 0 ) { if ( $this->sv->isOK() ) { @@ -199,22 +220,22 @@ class Status { $rawErrors = $this->sv->getErrors(); // just added a fatal } if ( count( $rawErrors ) == 1 ) { - $s = $this->getErrorMessage( $rawErrors[0] )->plain(); + $s = $this->getErrorMessage( $rawErrors[0], $lang )->plain(); if ( $shortContext ) { - $s = wfMessage( $shortContext, $s )->plain(); + $s = wfMessage( $shortContext, $s )->inLanguage( $lang )->plain(); } elseif ( $longContext ) { - $s = wfMessage( $longContext, "* $s\n" )->plain(); + $s = wfMessage( $longContext, "* $s\n" )->inLanguage( $lang )->plain(); } } else { - $errors = $this->getErrorMessageArray( $rawErrors ); + $errors = $this->getErrorMessageArray( $rawErrors, $lang ); foreach ( $errors as &$error ) { $error = $error->plain(); } $s = '* ' . implode( "\n* ", $errors ) . "\n"; if ( $longContext ) { - $s = wfMessage( $longContext, $s )->plain(); + $s = wfMessage( $longContext, $s )->inLanguage( $lang )->plain(); } elseif ( $shortContext ) { - $s = wfMessage( $shortContext, "\n$s\n" )->plain(); + $s = wfMessage( $shortContext, "\n$s\n" )->inLanguage( $lang )->plain(); } } return $s; @@ -227,10 +248,12 @@ class Status { * message names), to be used when there is a single error. * @param string|string[] $longContext A long enclosing context message name (or an array of * message names), for a list. - * + * @param string|Language $lang Language to use for processing messages * @return Message */ - public function getMessage( $shortContext = false, $longContext = false ) { + public function getMessage( $shortContext = false, $longContext = false, $lang = null ) { + $lang = $this->languageFromParam( $lang ); + $rawErrors = $this->sv->getErrors(); if ( count( $rawErrors ) == 0 ) { if ( $this->sv->isOK() ) { @@ -243,16 +266,16 @@ class Status { $rawErrors = $this->sv->getErrors(); // just added a fatal } if ( count( $rawErrors ) == 1 ) { - $s = $this->getErrorMessage( $rawErrors[0] ); + $s = $this->getErrorMessage( $rawErrors[0], $lang ); if ( $shortContext ) { - $s = wfMessage( $shortContext, $s ); + $s = wfMessage( $shortContext, $s )->inLanguage( $lang ); } elseif ( $longContext ) { $wrapper = new RawMessage( "* \$1\n" ); $wrapper->params( $s )->parse(); - $s = wfMessage( $longContext, $wrapper ); + $s = wfMessage( $longContext, $wrapper )->inLanguage( $lang ); } } else { - $msgs = $this->getErrorMessageArray( $rawErrors ); + $msgs = $this->getErrorMessageArray( $rawErrors, $lang ); $msgCount = count( $msgs ); if ( $shortContext ) { @@ -263,11 +286,11 @@ class Status { $s->params( $msgs )->parse(); if ( $longContext ) { - $s = wfMessage( $longContext, $s ); + $s = wfMessage( $longContext, $s )->inLanguage( $lang ); } elseif ( $shortContext ) { $wrapper = new RawMessage( "\n\$1\n", [ $s ] ); $wrapper->parse(); - $s = wfMessage( $shortContext, $wrapper ); + $s = wfMessage( $shortContext, $wrapper )->inLanguage( $lang ); } } @@ -280,10 +303,10 @@ class Status { * 'message' and 'params', use those keys-value pairs. * Otherwise, if its an array, just use the first value as the * message and the remaining items as the params. - * + * @param string|Language $lang Language to use for processing messages * @return Message */ - protected function getErrorMessage( $error ) { + protected function getErrorMessage( $error, $lang = null ) { if ( is_array( $error ) ) { if ( isset( $error['message'] ) && $error['message'] instanceof Message ) { $msg = $error['message']; @@ -298,6 +321,8 @@ class Status { } else { $msg = wfMessage( $error ); } + + $msg->inLanguage( $this->languageFromParam( $lang ) ); return $msg; } @@ -307,21 +332,27 @@ class Status { * @param string $shortContext A short enclosing context message name, to * be used when there is a single error * @param string $longContext A long enclosing context message name, for a list + * @param string|Language $lang Language to use for processing messages * @return string */ - public function getHTML( $shortContext = false, $longContext = false ) { - $text = $this->getWikiText( $shortContext, $longContext ); - $out = MessageCache::singleton()->parse( $text, null, true, true ); + public function getHTML( $shortContext = false, $longContext = false, $lang = null ) { + $lang = $this->languageFromParam( $lang ); + $text = $this->getWikiText( $shortContext, $longContext, $lang ); + $out = MessageCache::singleton()->parse( $text, null, true, true, $lang ); return $out instanceof ParserOutput ? $out->getText() : $out; } /** * Return an array with a Message object for each error. * @param array $errors + * @param string|Language $lang Language to use for processing messages * @return Message[] */ - protected function getErrorMessageArray( $errors ) { - return array_map( [ $this, 'getErrorMessage' ], $errors ); + protected function getErrorMessageArray( $errors, $lang = null ) { + $lang = $this->languageFromParam( $lang ); + return array_map( function ( $e ) use ( $lang ) { + return $this->getErrorMessage( $e, $lang ); + }, $errors ); } /** diff --git a/includes/StubObject.php b/includes/StubObject.php index 4abc283299..211afda671 100644 --- a/includes/StubObject.php +++ b/includes/StubObject.php @@ -165,9 +165,7 @@ class StubObject { } /** - * Stub object for the user language. It depends of the user preferences and - * "uselang" parameter that can be passed to index.php. This object have to be - * in $wgLang global. + * Stub object for the user language. Assigned to the $wgLang global. */ class StubUserLang extends StubObject { @@ -175,10 +173,6 @@ class StubUserLang extends StubObject { parent::__construct( 'wgLang' ); } - public function __call( $name, $args ) { - return $this->_call( $name, $args ); - } - /** * Call Language::findVariantLink after unstubbing $wgLang. * diff --git a/includes/Title.php b/includes/Title.php index c0ec97f219..0ac3e461a6 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -174,7 +174,7 @@ class Title implements LinkTarget { // make sure we are using the right one. To detect changes over the course // of a request, we remember a fingerprint of the config used to create the // codec singleton, and re-create it if the fingerprint doesn't match. - $fingerprint = spl_object_hash( $wgContLang ) . '|' . join( '+', $wgLocalInterwikis ); + $fingerprint = spl_object_hash( $wgContLang ) . '|' . implode( '+', $wgLocalInterwikis ); if ( $fingerprint !== $titleCodecFingerprint ) { $titleCodec = null; @@ -4466,8 +4466,12 @@ class Title implements LinkTarget { $this->mNotificationTimestamp = []; } - $watchedItem = WatchedItem::fromUserTitle( $user, $this ); - $this->mNotificationTimestamp[$uid] = $watchedItem->getNotificationTimestamp(); + $watchedItem = WatchedItemStore::getDefaultInstance()->getWatchedItem( $user, $this ); + if ( $watchedItem ) { + $this->mNotificationTimestamp[$uid] = $watchedItem->getNotificationTimestamp(); + } else { + $this->mNotificationTimestamp[$uid] = false; + } return $this->mNotificationTimestamp[$uid]; } diff --git a/includes/WatchedItem.php b/includes/WatchedItem.php index b597f99d93..5b4a4fcfe1 100644 --- a/includes/WatchedItem.php +++ b/includes/WatchedItem.php @@ -1,7 +1,5 @@ mUser = $user; - $wl->mTitle = $title; - $wl->mCheckRights = $checkRights; - - return $wl; - } + const DEPRECATED_USAGE_TIMESTAMP = -100; /** - * Title being watched - * @return Title + * @var bool + * @deprecated Internal class use only */ - protected function getTitle() { - return $this->mTitle; - } + public $checkRights = User::CHECK_USER_RIGHTS; /** - * Helper to retrieve the title namespace - * @return int + * @var Title + * @deprecated Internal class use only */ - protected function getTitleNs() { - return $this->getTitle()->getNamespace(); - } + private $title; /** - * Helper to retrieve the title DBkey - * @return string + * @var LinkTarget */ - protected function getTitleDBkey() { - return $this->getTitle()->getDBkey(); - } + private $linkTarget; /** - * Helper to retrieve the user id - * @return int + * @var User */ - protected function getUserId() { - return $this->mUser->getId(); - } + private $user; /** - * Return an array of conditions to select or update the appropriate database - * row. - * - * @return array + * @var null|string the value of the wl_notificationtimestamp field */ - private function dbCond() { - return [ - 'wl_user' => $this->getUserId(), - 'wl_namespace' => $this->getTitleNs(), - 'wl_title' => $this->getTitleDBkey(), - ]; - } + private $notificationTimestamp; /** - * Load the object from the database + * @param User $user + * @param LinkTarget $linkTarget + * @param null|string $notificationTimestamp the value of the wl_notificationtimestamp field + * @param bool|null $checkRights DO NOT USE - used internally for backward compatibility */ - private function load() { - if ( $this->loaded ) { - return; - } - $this->loaded = true; - - // Only loggedin user can have a watchlist - if ( $this->mUser->isAnon() ) { - $this->watched = false; - return; - } - - // some pages cannot be watched - if ( !$this->getTitle()->isWatchable() ) { - $this->watched = false; - return; - } - - # Pages and their talk pages are considered equivalent for watching; - # remember that talk namespaces are numbered as page namespace+1. - - $dbr = wfGetDB( DB_SLAVE ); - $row = $dbr->selectRow( 'watchlist', 'wl_notificationtimestamp', - $this->dbCond(), __METHOD__ ); - - if ( $row === false ) { - $this->watched = false; - } else { - $this->watched = true; - $this->timestamp = $row->wl_notificationtimestamp; + public function __construct( + User $user, + LinkTarget $linkTarget, + $notificationTimestamp, + $checkRights = null + ) { + $this->user = $user; + $this->linkTarget = $linkTarget; + $this->notificationTimestamp = $notificationTimestamp; + if ( $checkRights !== null ) { + $this->checkRights = $checkRights; } } /** - * Check permissions - * @param string $what 'viewmywatchlist' or 'editmywatchlist' - * @return bool + * @return User */ - private function isAllowed( $what ) { - return !$this->mCheckRights || $this->mUser->isAllowed( $what ); + public function getUser() { + return $this->user; } /** - * Is mTitle being watched by mUser? - * @return bool + * @return LinkTarget */ - public function isWatched() { - if ( !$this->isAllowed( 'viewmywatchlist' ) ) { - return false; - } - - $this->load(); - return $this->watched; + public function getLinkTarget() { + return $this->linkTarget; } /** * Get the notification timestamp of this entry. * - * @return bool|null|string False if the page is not watched, the value of - * the wl_notificationtimestamp field otherwise + * @return bool|null|string */ public function getNotificationTimestamp() { - if ( !$this->isAllowed( 'viewmywatchlist' ) ) { - return false; - } - - $this->load(); - if ( $this->watched ) { - return $this->timestamp; - } else { - return false; + // Back compat for objects constructed using self::fromUserTitle + if ( $this->notificationTimestamp === self::DEPRECATED_USAGE_TIMESTAMP ) { + // wfDeprecated( __METHOD__, '1.27' ); + if ( $this->checkRights && !$this->user->isAllowed( 'viewmywatchlist' ) ) { + return false; + } + $item = WatchedItemStore::getDefaultInstance() + ->loadWatchedItem( $this->user, $this->linkTarget ); + if ( $item ) { + $this->notificationTimestamp = $item->getNotificationTimestamp(); + } else { + $this->notificationTimestamp = false; + } } + return $this->notificationTimestamp; } /** - * Reset the notification timestamp of this entry - * - * @param bool $force Whether to force the write query to be executed even if the - * page is not watched or the notification timestamp is already NULL. - * @param int $oldid The revision id being viewed. If not given or 0, latest revision is assumed. + * Back compat pre 1.27 with the WatchedItemStore introduction + * @todo remove in 1.28/9 + * ------------------------------------------------- */ - public function resetNotificationTimestamp( - $force = '', $oldid = 0 - ) { - // Only loggedin user can have a watchlist - if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) { - return; - } - if ( $force != 'force' ) { - $this->load(); - if ( !$this->watched || $this->timestamp === null ) { - return; + /** + * @return Title + * @deprecated Internal class use only + */ + public function getTitle() { + if ( !$this->title ) { + if ( $this->linkTarget instanceof Title ) { + $this->title = $this->linkTarget; + } else { + $this->title = Title::newFromLinkTarget( $this->linkTarget ); } } + return $this->title; + } - $title = $this->getTitle(); - if ( !$oldid ) { - // No oldid given, assuming latest revision; clear the timestamp. - $notificationTimestamp = null; - } elseif ( !$title->getNextRevisionID( $oldid ) ) { - // Oldid given and is the latest revision for this title; clear the timestamp. - $notificationTimestamp = null; - } else { - // See if the version marked as read is more recent than the one we're viewing. - // Call load() if it wasn't called before due to $force. - $this->load(); - - if ( $this->timestamp === null ) { - // This can only happen if $force is enabled. - $notificationTimestamp = null; - } else { - // Oldid given and isn't the latest; update the timestamp. - // This will result in no further notification emails being sent! - $notificationTimestamp = Revision::getTimestampFromId( $title, $oldid ); - // We need to go one second to the future because of various strict comparisons - // throughout the codebase - $ts = new MWTimestamp( $notificationTimestamp ); - $ts->timestamp->add( new DateInterval( 'PT1S' ) ); - $notificationTimestamp = $ts->getTimestamp( TS_MW ); + /** + * @deprecated since 1.27 Use the constructor, WatchedItemStore::getWatchedItem() + * or WatchedItemStore::loadWatchedItem() + */ + public static function fromUserTitle( $user, $title, $checkRights = User::CHECK_USER_RIGHTS ) { + // wfDeprecated( __METHOD__, '1.27' ); + return new self( $user, $title, self::DEPRECATED_USAGE_TIMESTAMP, (bool)$checkRights ); + } - if ( $notificationTimestamp < $this->timestamp ) { - if ( $force != 'force' ) { - return; - } else { - // This is a little silly… - $notificationTimestamp = $this->timestamp; - } - } - } + /** + * @deprecated since 1.27 Use WatchedItemStore::resetNotificationTimestamp() + */ + public function resetNotificationTimestamp( $force = '', $oldid = 0 ) { + // wfDeprecated( __METHOD__, '1.27' ); + if ( $this->checkRights && !$this->user->isAllowed( 'editmywatchlist' ) ) { + return; } - - // If the page is watched by the user (or may be watched), update the timestamp - $job = new ActivityUpdateJob( - $title, - [ - 'type' => 'updateWatchlistNotification', - 'userid' => $this->getUserId(), - 'notifTime' => $notificationTimestamp, - 'curTime' => time() - ] + WatchedItemStore::getDefaultInstance()->resetNotificationTimestamp( + $this->user, + $this->getTitle(), + $force, + $oldid ); - // Try to run this post-send - DeferredUpdates::addCallableUpdate( function() use ( $job ) { - $job->run(); - } ); - - $this->timestamp = null; } /** - * @param WatchedItem[] $items - * @return bool + * @deprecated since 1.27 Use WatchedItemStore::addWatchBatch() */ public static function batchAddWatch( array $items ) { - - if ( wfReadOnly() ) { - return false; - } - - $rows = []; - foreach ( $items as $item ) { - // Only loggedin user can have a watchlist - if ( $item->mUser->isAnon() || !$item->isAllowed( 'editmywatchlist' ) ) { + // wfDeprecated( __METHOD__, '1.27' ); + $userTargetCombinations = []; + /** @var WatchedItem $watchedItem */ + foreach ( $items as $watchedItem ) { + if ( $watchedItem->checkRights && !$watchedItem->getUser()->isAllowed( 'editmywatchlist' ) ) { continue; } - $rows[] = [ - 'wl_user' => $item->getUserId(), - 'wl_namespace' => MWNamespace::getSubject( $item->getTitleNs() ), - 'wl_title' => $item->getTitleDBkey(), - 'wl_notificationtimestamp' => null, + $userTargetCombinations[] = [ + $watchedItem->getUser(), + $watchedItem->getTitle()->getSubjectPage() ]; - // Every single watched page needs now to be listed in watchlist; - // namespace:page and namespace_talk:page need separate entries: - $rows[] = [ - 'wl_user' => $item->getUserId(), - 'wl_namespace' => MWNamespace::getTalk( $item->getTitleNs() ), - 'wl_title' => $item->getTitleDBkey(), - 'wl_notificationtimestamp' => null + $userTargetCombinations[] = [ + $watchedItem->getUser(), + $watchedItem->getTitle()->getTalkPage() ]; - $item->watched = true; - } - - if ( !$rows ) { - return false; - } - - $dbw = wfGetDB( DB_MASTER ); - foreach ( array_chunk( $rows, 100 ) as $toInsert ) { - // Use INSERT IGNORE to avoid overwriting the notification timestamp - // if there's already an entry for this page - $dbw->insert( 'watchlist', $toInsert, __METHOD__, 'IGNORE' ); } - - return true; + $store = WatchedItemStore::getDefaultInstance(); + return $store->addWatchBatch( $userTargetCombinations ); } /** - * Given a title and user (assumes the object is setup), add the watch to the database. + * @deprecated since 1.27 Use User::addWatch() * @return bool */ public function addWatch() { - return self::batchAddWatch( [ $this ] ); + // wfDeprecated( __METHOD__, '1.27' ); + $this->user->addWatch( $this->getTitle(), $this->checkRights ); + return true; } /** - * Same as addWatch, only the opposite. + * @deprecated since 1.27 Use User::removeWatch() * @return bool */ public function removeWatch() { - - // Only loggedin user can have a watchlist - if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) { + // wfDeprecated( __METHOD__, '1.27' ); + if ( $this->checkRights && !$this->user->isAllowed( 'editmywatchlist' ) ) { return false; } + $this->user->removeWatch( $this->getTitle(), $this->checkRights ); + return true; + } - $success = false; - $dbw = wfGetDB( DB_MASTER ); - $dbw->delete( 'watchlist', - [ - 'wl_user' => $this->getUserId(), - 'wl_namespace' => MWNamespace::getSubject( $this->getTitleNs() ), - 'wl_title' => $this->getTitleDBkey(), - ], __METHOD__ - ); - if ( $dbw->affectedRows() ) { - $success = true; - } - - # the following code compensates the new behavior, introduced by the - # enotif patch, that every single watched page needs now to be listed - # in watchlist namespace:page and namespace_talk:page had separate - # entries: clear them - $dbw->delete( 'watchlist', - [ - 'wl_user' => $this->getUserId(), - 'wl_namespace' => MWNamespace::getTalk( $this->getTitleNs() ), - 'wl_title' => $this->getTitleDBkey(), - ], __METHOD__ - ); - - if ( $dbw->affectedRows() ) { - $success = true; - } - - $this->watched = false; - - return $success; + /** + * @deprecated since 1.27 Use User::isWatched() + * @return bool + */ + public function isWatched() { + // wfDeprecated( __METHOD__, '1.27' ); + return $this->user->isWatched( $this->getTitle(), $this->checkRights ); } /** - * @deprecated since 1.27. See WatchedItemStore::duplicateEntry - * - * @param Title $oldTitle - * @param Title $newTitle + * @deprecated since 1.27 Use WatchedItemStore::duplicateAllAssociatedEntries() */ public static function duplicateEntries( Title $oldTitle, Title $newTitle ) { + // wfDeprecated( __METHOD__, '1.27' ); $store = WatchedItemStore::getDefaultInstance(); - $store->duplicateEntry( $oldTitle->getSubjectPage(), $newTitle->getSubjectPage() ); - $store->duplicateEntry( $oldTitle->getTalkPage(), $newTitle->getTalkPage() ); + $store->duplicateAllAssociatedEntries( $oldTitle, $newTitle ); } } diff --git a/includes/WatchedItemStore.php b/includes/WatchedItemStore.php index 83a5856869..1aed8e01f2 100644 --- a/includes/WatchedItemStore.php +++ b/includes/WatchedItemStore.php @@ -1,8 +1,10 @@ loadBalancer = $loadBalancer; + $this->cache = $cache; + $this->deferredUpdatesAddCallableUpdateCallback = [ 'DeferredUpdates', 'addCallableUpdate' ]; + $this->revisionGetTimestampFromIdCallback = [ 'Revision', 'getTimestampFromId' ]; + } + + /** + * Overrides the DeferredUpdates::addCallableUpdate callback + * This is intended for use while testing and will fail if MW_PHPUNIT_TEST is not defined. + * + * @param callable $callback + * @see DeferredUpdates::addCallableUpdate for callback signiture + * + * @throws MWException + */ + public function overrideDeferredUpdatesAddCallableUpdateCallback( $callback ) { + if ( !defined( 'MW_PHPUNIT_TEST' ) ) { + throw new MWException( + 'Cannot override DeferredUpdates::addCallableUpdate callback in operation.' + ); + } + Assert::parameterType( 'callable', $callback, '$callback' ); + $this->deferredUpdatesAddCallableUpdateCallback = $callback; + } + + /** + * Overrides the Revision::getTimestampFromId callback + * This is intended for use while testing and will fail if MW_PHPUNIT_TEST is not defined. + * + * @param callable $callback + * @see Revision::getTimestampFromId for callback signiture + * + * @throws MWException + */ + public function overrideRevisionGetTimestampFromIdCallback( $callback ) { + if ( !defined( 'MW_PHPUNIT_TEST' ) ) { + throw new MWException( + 'Cannot override Revision::getTimestampFromId callback in operation.' + ); + } + Assert::parameterType( 'callable', $callback, '$callback' ); + $this->revisionGetTimestampFromIdCallback = $callback; + } + + /** + * Overrides the default instance of this class + * This is intended for use while testing and will fail if MW_PHPUNIT_TEST is not defined. + * + * @param WatchedItemStore $store + * + * @throws MWException + */ + public static function overrideDefaultInstance( WatchedItemStore $store ) { + if ( !defined( 'MW_PHPUNIT_TEST' ) ) { + throw new MWException( + 'Cannot override ' . __CLASS__ . 'default instance in operation.' + ); + } + self::$instance = $store; } /** * @return self */ public static function getDefaultInstance() { - static $instance; - if ( !$instance ) { - $instance = new self( wfGetLB() ); + if ( !self::$instance ) { + self::$instance = new self( + wfGetLB(), + new HashBagOStuff( [ 'maxKeys' => 100 ] ) + ); + } + return self::$instance; + } + + private function getCacheKey( User $user, LinkTarget $target ) { + return $this->cache->makeKey( + (string)$target->getNamespace(), + $target->getDBkey(), + (string)$user->getId() + ); + } + + private function cache( WatchedItem $item ) { + $this->cache->set( + $this->getCacheKey( $item->getUser(), $item->getLinkTarget() ), + $item + ); + } + + private function uncache( User $user, LinkTarget $target ) { + $this->cache->delete( $this->getCacheKey( $user, $target ) ); + } + + /** + * @param User $user + * @param LinkTarget $target + * + * @return WatchedItem|null + */ + private function getCached( User $user, LinkTarget $target ) { + return $this->cache->get( $this->getCacheKey( $user, $target ) ); + } + + /** + * Return an array of conditions to select or update the appropriate database + * row. + * + * @param User $user + * @param LinkTarget $target + * + * @return array + */ + private function dbCond( User $user, LinkTarget $target ) { + return [ + 'wl_user' => $user->getId(), + 'wl_namespace' => $target->getNamespace(), + 'wl_title' => $target->getDBkey(), + ]; + } + + /** + * Get an item (may be cached) + * + * @param User $user + * @param LinkTarget $target + * + * @return WatchedItem|false + */ + public function getWatchedItem( User $user, LinkTarget $target ) { + $cached = $this->getCached( $user, $target ); + if ( $cached ) { + return $cached; + } + return $this->loadWatchedItem( $user, $target ); + } + + /** + * Loads an item from the db + * + * @param User $user + * @param LinkTarget $target + * + * @return WatchedItem|false + */ + public function loadWatchedItem( User $user, LinkTarget $target ) { + // Only loggedin user can have a watchlist + if ( $user->isAnon() ) { + return false; + } + + $dbr = $this->loadBalancer->getConnection( DB_SLAVE, [ 'watchlist' ] ); + $row = $dbr->selectRow( + 'watchlist', + 'wl_notificationtimestamp', + $this->dbCond( $user, $target ), + __METHOD__ + ); + $this->loadBalancer->reuseConnection( $dbr ); + + if ( !$row ) { + return false; + } + + $item = new WatchedItem( + $user, + $target, + $row->wl_notificationtimestamp + ); + $this->cache( $item ); + + return $item; + } + + /** + * Must be called separately for Subject & Talk namespaces + * + * @param User $user + * @param LinkTarget $target + * + * @return bool + */ + public function isWatched( User $user, LinkTarget $target ) { + return (bool)$this->getWatchedItem( $user, $target ); + } + + /** + * Must be called separately for Subject & Talk namespaces + * + * @param User $user + * @param LinkTarget $target + */ + public function addWatch( User $user, LinkTarget $target ) { + $this->addWatchBatch( [ [ $user, $target ] ] ); + } + + /** + * @param array[] $userTargetCombinations array of arrays containing [0] => User [1] => LinkTarget + * + * @return bool success + */ + public function addWatchBatch( array $userTargetCombinations ) { + if ( $this->loadBalancer->getReadOnlyReason() !== false ) { + return false; + } + + $rows = []; + foreach ( $userTargetCombinations as list( $user, $target ) ) { + /** + * @var User $user + * @var LinkTarget $target + */ + + // Only loggedin user can have a watchlist + if ( $user->isAnon() ) { + continue; + } + $rows[] = [ + 'wl_user' => $user->getId(), + 'wl_namespace' => $target->getNamespace(), + 'wl_title' => $target->getDBkey(), + 'wl_notificationtimestamp' => null, + ]; + $this->uncache( $user, $target ); + } + + if ( !$rows ) { + return false; + } + + $dbw = $this->loadBalancer->getConnection( DB_MASTER, [ 'watchlist' ] ); + foreach ( array_chunk( $rows, 100 ) as $toInsert ) { + // Use INSERT IGNORE to avoid overwriting the notification timestamp + // if there's already an entry for this page + $dbw->insert( 'watchlist', $toInsert, __METHOD__, 'IGNORE' ); + } + $this->loadBalancer->reuseConnection( $dbw ); + + return true; + } + + /** + * Removes the an entry for the User watching the LinkTarget + * Must be called separately for Subject & Talk namespaces + * + * @param User $user + * @param LinkTarget $target + * + * @return bool success + * @throws DBUnexpectedError + * @throws MWException + */ + public function removeWatch( User $user, LinkTarget $target ) { + // Only logged in user can have a watchlist + if ( $this->loadBalancer->getReadOnlyReason() !== false || $user->isAnon() ) { + return false; } - return $instance; + + $this->uncache( $user, $target ); + + $dbw = $this->loadBalancer->getConnection( DB_MASTER, [ 'watchlist' ] ); + $dbw->delete( 'watchlist', + [ + 'wl_user' => $user->getId(), + 'wl_namespace' => $target->getNamespace(), + 'wl_title' => $target->getDBkey(), + ], __METHOD__ + ); + $success = (bool)$dbw->affectedRows(); + $this->loadBalancer->reuseConnection( $dbw ); + + return $success; + } + + /** + * @param User $editor The editor that triggered the update. Their notification + * timestamp will not be updated(they have already seen it) + * @param LinkTarget $target The target to update timestamps for + * @param string $timestamp Set the update timestamp to this value + * + * @return int[] Array of user IDs the timestamp has been updated for + */ + public function updateNotificationTimestamp( User $editor, LinkTarget $target, $timestamp ) { + $dbw = $this->loadBalancer->getConnection( DB_MASTER, [ 'watchlist' ] ); + $res = $dbw->select( [ 'watchlist' ], + [ 'wl_user' ], + [ + 'wl_user != ' . intval( $editor->getId() ), + 'wl_namespace' => $target->getNamespace(), + 'wl_title' => $target->getDBkey(), + 'wl_notificationtimestamp IS NULL', + ], __METHOD__ + ); + + $watchers = []; + foreach ( $res as $row ) { + $watchers[] = intval( $row->wl_user ); + } + + if ( $watchers ) { + // Update wl_notificationtimestamp for all watching users except the editor + $fname = __METHOD__; + $dbw->onTransactionIdle( + function () use ( $dbw, $timestamp, $watchers, $target, $fname ) { + $dbw->update( 'watchlist', + [ /* SET */ + 'wl_notificationtimestamp' => $dbw->timestamp( $timestamp ) + ], [ /* WHERE */ + 'wl_user' => $watchers, + 'wl_namespace' => $target->getNamespace(), + 'wl_title' => $target->getDBkey(), + ], $fname + ); + } + ); + } + + $this->loadBalancer->reuseConnection( $dbw ); + + return $watchers; + } + + /** + * Reset the notification timestamp of this entry + * + * @param User $user + * @param Title $title + * @param string $force Whether to force the write query to be executed even if the + * page is not watched or the notification timestamp is already NULL. + * 'force' in order to force + * @param int $oldid The revision id being viewed. If not given or 0, latest revision is assumed. + * + * @return bool success + */ + public function resetNotificationTimestamp( User $user, Title $title, $force = '', $oldid = 0 ) { + // Only loggedin user can have a watchlist + if ( $this->loadBalancer->getReadOnlyReason() !== false || $user->isAnon() ) { + return false; + } + + $item = null; + if ( $force != 'force' ) { + $item = $this->loadWatchedItem( $user, $title ); + if ( !$item || $item->getNotificationTimestamp() === null ) { + return false; + } + } + + // If the page is watched by the user (or may be watched), update the timestamp + $job = new ActivityUpdateJob( + $title, + [ + 'type' => 'updateWatchlistNotification', + 'userid' => $user->getId(), + 'notifTime' => $this->getNotificationTimestamp( $user, $title, $item, $force, $oldid ), + 'curTime' => time() + ] + ); + + // Try to run this post-send + // Calls DeferredUpdates::addCallableUpdate in normal operation + call_user_func( + $this->deferredUpdatesAddCallableUpdateCallback, + function() use ( $job ) { + $job->run(); + } + ); + + $this->uncache( $user, $title ); + + return true; + } + + private function getNotificationTimestamp( User $user, Title $title, $item, $force, $oldid ) { + if ( !$oldid ) { + // No oldid given, assuming latest revision; clear the timestamp. + return null; + } + + if ( !$title->getNextRevisionID( $oldid ) ) { + // Oldid given and is the latest revision for this title; clear the timestamp. + return null; + } + + if ( $item === null ) { + $item = $this->loadWatchedItem( $user, $title ); + } + + if ( !$item ) { + // This can only happen if $force is enabled. + return null; + } + + // Oldid given and isn't the latest; update the timestamp. + // This will result in no further notification emails being sent! + // Calls Revision::getTimestampFromId in normal operation + $notificationTimestamp = call_user_func( + $this->revisionGetTimestampFromIdCallback, + $title, + $oldid + ); + + // We need to go one second to the future because of various strict comparisons + // throughout the codebase + $ts = new MWTimestamp( $notificationTimestamp ); + $ts->timestamp->add( new DateInterval( 'PT1S' ) ); + $notificationTimestamp = $ts->getTimestamp( TS_MW ); + + if ( $notificationTimestamp < $item->getNotificationTimestamp() ) { + if ( $force != 'force' ) { + return false; + } else { + // This is a little silly… + return $item->getNotificationTimestamp(); + } + } + + return $notificationTimestamp; + } + + /** + * Check if the given title already is watched by the user, and if so + * add a watch for the new title. + * + * To be used for page renames and such. + * + * @param LinkTarget $oldTarget + * @param LinkTarget $newTarget + */ + public function duplicateAllAssociatedEntries( LinkTarget $oldTarget, LinkTarget $newTarget ) { + if ( !$oldTarget instanceof Title ) { + $oldTarget = Title::newFromLinkTarget( $oldTarget ); + } + if ( !$newTarget instanceof Title ) { + $newTarget = Title::newFromLinkTarget( $newTarget ); + } + + $this->duplicateEntry( $oldTarget->getSubjectPage(), $newTarget->getSubjectPage() ); + $this->duplicateEntry( $oldTarget->getTalkPage(), $newTarget->getTalkPage() ); } /** diff --git a/includes/WebRequest.php b/includes/WebRequest.php index ce5cb96190..812a320f91 100644 --- a/includes/WebRequest.php +++ b/includes/WebRequest.php @@ -719,13 +719,13 @@ class WebRequest { } /** - * Return the path and query string portion of the request URI. + * Return the path and query string portion of the main request URI. * This will be suitable for use as a relative link in HTML output. * * @throws MWException * @return string */ - public function getRequestURL() { + public static function getGlobalRequestURL() { if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) { $base = $_SERVER['REQUEST_URI']; } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) @@ -762,6 +762,17 @@ class WebRequest { } } + /** + * Return the path and query string portion of the request URI. + * This will be suitable for use as a relative link in HTML output. + * + * @throws MWException + * @return string + */ + public function getRequestURL() { + return self::getGlobalRequestURL(); + } + /** * Return the request URI with the canonical service and hostname, path, * and query string. This will be suitable for use as an absolute link diff --git a/includes/WebResponse.php b/includes/WebResponse.php index c7d0a5bea8..458c2079e4 100644 --- a/includes/WebResponse.php +++ b/includes/WebResponse.php @@ -179,6 +179,16 @@ class WebResponse { public function clearCookie( $name, $options = [] ) { $this->setCookie( $name, '', time() - 31536000 /* 1 year */, $options ); } + + /** + * Checks whether this request is performing cookie operations + * + * @return bool + * @since 1.27 + */ + public function hasCookies() { + return (bool)self::$setCookies; + } } /** diff --git a/includes/actions/HistoryAction.php b/includes/actions/HistoryAction.php index 6f1f3e81b4..5ec10e6aff 100644 --- a/includes/actions/HistoryAction.php +++ b/includes/actions/HistoryAction.php @@ -682,7 +682,7 @@ class HistoryPager extends ReverseChronologicalPager { $s .= $dirmark; if ( $rev->isMinor() ) { - $s .= ' ' . ChangesList::flag( 'minor' ); + $s .= ' ' . ChangesList::flag( 'minor', $this->getContext() ); } # Sometimes rev_len isn't populated diff --git a/includes/actions/RollbackAction.php b/includes/actions/RollbackAction.php index db8c82d13c..d002da8e89 100644 --- a/includes/actions/RollbackAction.php +++ b/includes/actions/RollbackAction.php @@ -103,7 +103,7 @@ class RollbackAction extends FormlessAction { ->parseAsBlock() ); if ( $user->getBoolOption( 'watchrollback' ) ) { - $user->addWatch( $this->page->getTitle(), WatchedItem::IGNORE_USER_RIGHTS ); + $user->addWatch( $this->page->getTitle(), User::IGNORE_USER_RIGHTS ); } $this->getOutput()->returnToMain( false, $this->getTitle() ); diff --git a/includes/actions/WatchAction.php b/includes/actions/WatchAction.php index 8f13456bc9..890740fdd5 100644 --- a/includes/actions/WatchAction.php +++ b/includes/actions/WatchAction.php @@ -82,12 +82,12 @@ class WatchAction extends FormAction { */ public static function doWatchOrUnwatch( $watch, Title $title, User $user ) { if ( $user->isLoggedIn() && - $user->isWatched( $title, WatchedItem::IGNORE_USER_RIGHTS ) != $watch + $user->isWatched( $title, User::IGNORE_USER_RIGHTS ) != $watch ) { // If the user doesn't have 'editmywatchlist', we still want to // allow them to add but not remove items via edits and such. if ( $watch ) { - return self::doWatch( $title, $user, WatchedItem::IGNORE_USER_RIGHTS ); + return self::doWatch( $title, $user, User::IGNORE_USER_RIGHTS ); } else { return self::doUnwatch( $title, $user ); } @@ -101,15 +101,16 @@ class WatchAction extends FormAction { * @since 1.22 Returns Status, $checkRights parameter added * @param Title $title Page to watch/unwatch * @param User $user User who is watching/unwatching - * @param int $checkRights Passed through to $user->addWatch() + * @param bool $checkRights Passed through to $user->addWatch() + * Pass User::CHECK_USER_RIGHTS or User::IGNORE_USER_RIGHTS. * @return Status */ - public static function doWatch( Title $title, User $user, - $checkRights = WatchedItem::CHECK_USER_RIGHTS + public static function doWatch( + Title $title, + User $user, + $checkRights = User::CHECK_USER_RIGHTS ) { - if ( $checkRights !== WatchedItem::IGNORE_USER_RIGHTS && - !$user->isAllowed( 'editmywatchlist' ) - ) { + if ( $checkRights && !$user->isAllowed( 'editmywatchlist' ) ) { return User::newFatalPermissionDeniedStatus( 'editmywatchlist' ); } diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index 76fae6bbc8..79441673c4 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -302,7 +302,7 @@ abstract class ApiBase extends ContextSource { $qs = $k; $msg = self::escapeWikiText( $v ); if ( is_array( $msg ) ) { - $msg = join( " ", $msg ); + $msg = implode( ' ', $msg ); } } @@ -547,13 +547,13 @@ abstract class ApiBase extends ContextSource { $parent = $module; $manager = $parent->getModuleManager(); if ( $manager === null ) { - $errorPath = join( '+', array_slice( $parts, 0, $i ) ); + $errorPath = implode( '+', array_slice( $parts, 0, $i ) ); $this->dieUsage( "The module \"$errorPath\" has no submodules", 'badmodule' ); } $module = $manager->getModule( $parts[$i] ); if ( $module === null ) { - $errorPath = $i ? join( '+', array_slice( $parts, 0, $i ) ) : $parent->getModuleName(); + $errorPath = $i ? implode( '+', array_slice( $parts, 0, $i ) ) : $parent->getModuleName(); $this->dieUsage( "The module \"$errorPath\" does not have a submodule \"{$parts[$i]}\"", 'badmodule' @@ -711,7 +711,7 @@ abstract class ApiBase extends ContextSource { $p = $this->getModulePrefix(); $intersection = array_intersect( array_keys( array_filter( $params, - [ $this, "parameterNotEmpty" ] ) ), $required ); + [ $this, 'parameterNotEmpty' ] ) ), $required ); if ( count( $intersection ) > 1 ) { $this->dieUsage( @@ -737,7 +737,7 @@ abstract class ApiBase extends ContextSource { $p = $this->getModulePrefix(); $intersection = array_intersect( array_keys( array_filter( $params, - [ $this, "parameterNotEmpty" ] ) ), $required ); + [ $this, 'parameterNotEmpty' ] ) ), $required ); if ( count( $intersection ) > 1 ) { $this->dieUsage( @@ -760,7 +760,7 @@ abstract class ApiBase extends ContextSource { $p = $this->getModulePrefix(); $intersection = array_intersect( - array_keys( array_filter( $params, [ $this, "parameterNotEmpty" ] ) ), + array_keys( array_filter( $params, [ $this, 'parameterNotEmpty' ] ) ), $required ); @@ -830,7 +830,7 @@ abstract class ApiBase extends ContextSource { */ protected function getWatchlistValue( $watchlist, $titleObj, $userOption = null ) { - $userWatching = $this->getUser()->isWatched( $titleObj, WatchedItem::IGNORE_USER_RIGHTS ); + $userWatching = $this->getUser()->isWatched( $titleObj, User::IGNORE_USER_RIGHTS ); switch ( $watchlist ) { case 'watch': @@ -917,7 +917,7 @@ abstract class ApiBase extends ContextSource { ApiBase::dieDebug( __METHOD__, "Boolean param $encParamName's default is set to '$default'. " . - "Boolean parameters must default to false." + 'Boolean parameters must default to false.' ); } @@ -942,8 +942,8 @@ abstract class ApiBase extends ContextSource { if ( $value !== null ) { $this->dieUsage( "File upload param $encParamName is not a file upload; " . - "be sure to use multipart/form-data for your POST and include " . - "a filename in the Content-Disposition header.", + 'be sure to use multipart/form-data for your POST and include ' . + 'a filename in the Content-Disposition header.', "badupload_{$encParamName}" ); } @@ -1157,7 +1157,7 @@ abstract class ApiBase extends ContextSource { if ( count( $unknown ) ) { if ( $allowMultiple ) { $s = count( $unknown ) > 1 ? 's' : ''; - $vals = implode( ", ", $unknown ); + $vals = implode( ', ', $unknown ); $this->setWarning( "Unrecognized value$s for parameter '$valueName': $vals" ); } else { $this->dieUsage( @@ -1615,15 +1615,15 @@ abstract class ApiBase extends ContextSource { ], 'badaccess-group0' => [ 'code' => 'permissiondenied', - 'info' => "Permission denied" + 'info' => 'Permission denied' ], // Generic permission denied message 'badaccess-groups' => [ 'code' => 'permissiondenied', - 'info' => "Permission denied" + 'info' => 'Permission denied' ], 'titleprotected' => [ 'code' => 'protectedtitle', - 'info' => "This title has been protected from creation" + 'info' => 'This title has been protected from creation' ], 'nocreate-loggedin' => [ 'code' => 'cantcreate', @@ -1643,15 +1643,15 @@ abstract class ApiBase extends ContextSource { ], 'confirmedittext' => [ 'code' => 'confirmemail', - 'info' => "You must confirm your email address before you can edit" + 'info' => 'You must confirm your email address before you can edit' ], 'blockedtext' => [ 'code' => 'blocked', - 'info' => "You have been blocked from editing" + 'info' => 'You have been blocked from editing' ], 'autoblockedtext' => [ 'code' => 'autoblocked', - 'info' => "Your IP address has been blocked automatically, because it was used by a blocked user" + 'info' => 'Your IP address has been blocked automatically, because it was used by a blocked user' ], // Miscellaneous interface messages @@ -1661,19 +1661,19 @@ abstract class ApiBase extends ContextSource { ], 'alreadyrolled' => [ 'code' => 'alreadyrolled', - 'info' => "The page you tried to rollback was already rolled back" + 'info' => 'The page you tried to rollback was already rolled back' ], 'cantrollback' => [ 'code' => 'onlyauthor', - 'info' => "The page you tried to rollback only has one author" + 'info' => 'The page you tried to rollback only has one author' ], 'readonlytext' => [ 'code' => 'readonly', - 'info' => "The wiki is currently in read-only mode" + 'info' => 'The wiki is currently in read-only mode' ], 'sessionfailure' => [ 'code' => 'badtoken', - 'info' => "Invalid token" ], + 'info' => 'Invalid token' ], 'cannotdelete' => [ 'code' => 'cantdelete', 'info' => "Couldn't delete \"\$1\". Maybe it was deleted already by someone else" @@ -1686,11 +1686,11 @@ abstract class ApiBase extends ContextSource { ], 'immobile_namespace' => [ 'code' => 'immobilenamespace', - 'info' => "You tried to move pages from or to a namespace that is protected from moving" + 'info' => 'You tried to move pages from or to a namespace that is protected from moving' ], 'articleexists' => [ 'code' => 'articleexists', - 'info' => "The destination article already exists and is not a redirect to the source article" + 'info' => 'The destination article already exists and is not a redirect to the source article' ], 'protectedpage' => [ 'code' => 'protectedpage', @@ -1698,11 +1698,11 @@ abstract class ApiBase extends ContextSource { ], 'hookaborted' => [ 'code' => 'hookaborted', - 'info' => "The modification you tried to make was aborted by an extension hook" + 'info' => 'The modification you tried to make was aborted by an extension hook' ], 'cantmove-titleprotected' => [ 'code' => 'protectedtitle', - 'info' => "The destination article has been protected from creation" + 'info' => 'The destination article has been protected from creation' ], 'imagenocrossnamespace' => [ 'code' => 'nonfilenamespace', @@ -1714,20 +1714,20 @@ abstract class ApiBase extends ContextSource { ], // 'badarticleerror' => shouldn't happen // 'badtitletext' => shouldn't happen - 'ip_range_invalid' => [ 'code' => 'invalidrange', 'info' => "Invalid IP range" ], + 'ip_range_invalid' => [ 'code' => 'invalidrange', 'info' => 'Invalid IP range' ], 'range_block_disabled' => [ 'code' => 'rangedisabled', - 'info' => "Blocking IP ranges has been disabled" + 'info' => 'Blocking IP ranges has been disabled' ], 'nosuchusershort' => [ 'code' => 'nosuchuser', 'info' => "The user you specified doesn't exist" ], - 'badipaddress' => [ 'code' => 'invalidip', 'info' => "Invalid IP address specified" ], - 'ipb_expiry_invalid' => [ 'code' => 'invalidexpiry', 'info' => "Invalid expiry time" ], + 'badipaddress' => [ 'code' => 'invalidip', 'info' => 'Invalid IP address specified' ], + 'ipb_expiry_invalid' => [ 'code' => 'invalidexpiry', 'info' => 'Invalid expiry time' ], 'ipb_already_blocked' => [ 'code' => 'alreadyblocked', - 'info' => "The user you tried to block was already blocked" + 'info' => 'The user you tried to block was already blocked' ], 'ipb_blocked_as_range' => [ 'code' => 'blockedasrange', @@ -1735,11 +1735,11 @@ abstract class ApiBase extends ContextSource { ], 'ipb_cant_unblock' => [ 'code' => 'cantunblock', - 'info' => "The block you specified was not found. It may have been unblocked already" + 'info' => 'The block you specified was not found. It may have been unblocked already' ], 'mailnologin' => [ 'code' => 'cantsend', - 'info' => "You are not logged in, you do not have a confirmed email address, or you are not allowed to send email to other users, so you cannot send email" + 'info' => 'You are not logged in, you do not have a confirmed email address, or you are not allowed to send email to other users, so you cannot send email' ], 'ipbblocked' => [ 'code' => 'ipbblocked', @@ -1751,23 +1751,23 @@ abstract class ApiBase extends ContextSource { ], 'usermaildisabled' => [ 'code' => 'usermaildisabled', - 'info' => "User email has been disabled" + 'info' => 'User email has been disabled' ], 'blockedemailuser' => [ 'code' => 'blockedfrommail', - 'info' => "You have been blocked from sending email" + 'info' => 'You have been blocked from sending email' ], 'notarget' => [ 'code' => 'notarget', - 'info' => "You have not specified a valid target for this action" + 'info' => 'You have not specified a valid target for this action' ], 'noemail' => [ 'code' => 'noemail', - 'info' => "The user has not specified a valid email address, or has chosen not to receive email from other users" + 'info' => 'The user has not specified a valid email address, or has chosen not to receive email from other users' ], 'rcpatroldisabled' => [ 'code' => 'patroldisabled', - 'info' => "Patrolling is disabled on this wiki" + 'info' => 'Patrolling is disabled on this wiki' ], 'markedaspatrollederror-noautopatrol' => [ 'code' => 'noautopatrol', @@ -1804,7 +1804,7 @@ abstract class ApiBase extends ContextSource { // API-specific messages 'readrequired' => [ 'code' => 'readapidenied', - 'info' => "You need read permission to use this module" + 'info' => 'You need read permission to use this module' ], 'writedisabled' => [ 'code' => 'noapiwrite', @@ -1843,7 +1843,7 @@ abstract class ApiBase extends ContextSource { ], 'unblock-notarget' => [ 'code' => 'notarget', - 'info' => "Either the id or the user parameter must be set" + 'info' => 'Either the id or the user parameter must be set' ], 'unblock-idanduser' => [ 'code' => 'idanduser', @@ -1863,7 +1863,7 @@ abstract class ApiBase extends ContextSource { ], 'createonly-exists' => [ 'code' => 'articleexists', - 'info' => "The article you tried to create has been created already" + 'info' => 'The article you tried to create has been created already' ], 'nocreate-missing' => [ 'code' => 'missingtitle', @@ -1992,17 +1992,17 @@ abstract class ApiBase extends ContextSource { 'noedit' => [ 'code' => 'noedit', 'info' => "You don't have permission to edit pages" ], 'wasdeleted' => [ 'code' => 'pagedeleted', - 'info' => "The page has been deleted since you fetched its timestamp" + 'info' => 'The page has been deleted since you fetched its timestamp' ], 'blankpage' => [ 'code' => 'emptypage', - 'info' => "Creating new, empty pages is not allowed" + 'info' => 'Creating new, empty pages is not allowed' ], - 'editconflict' => [ 'code' => 'editconflict', 'info' => "Edit conflict detected" ], - 'hashcheckfailed' => [ 'code' => 'badmd5', 'info' => "The supplied MD5 hash was incorrect" ], + 'editconflict' => [ 'code' => 'editconflict', 'info' => 'Edit conflict detected' ], + 'hashcheckfailed' => [ 'code' => 'badmd5', 'info' => 'The supplied MD5 hash was incorrect' ], 'missingtext' => [ 'code' => 'notext', - 'info' => "One of the text, appendtext, prependtext and undo parameters must be set" + 'info' => 'One of the text, appendtext, prependtext and undo parameters must be set' ], 'emptynewsection' => [ 'code' => 'emptynewsection', @@ -2024,13 +2024,13 @@ abstract class ApiBase extends ContextSource { // Messages from WikiPage::doEit(] 'edit-hook-aborted' => [ 'code' => 'edit-hook-aborted', - 'info' => "Your edit was aborted by an ArticleSave hook" + 'info' => 'Your edit was aborted by an ArticleSave hook' ], 'edit-gone-missing' => [ 'code' => 'edit-gone-missing', 'info' => "The page you tried to edit doesn't seem to exist anymore" ], - 'edit-conflict' => [ 'code' => 'editconflict', 'info' => "Edit conflict detected" ], + 'edit-conflict' => [ 'code' => 'editconflict', 'info' => 'Edit conflict detected' ], 'edit-already-exists' => [ 'code' => 'edit-already-exists', 'info' => 'It seems the page you tried to create already exist' @@ -2223,7 +2223,7 @@ abstract class ApiBase extends ContextSource { Hooks::run( 'APIGetDescription', [ &$this, &$desc ] ); $desc = self::escapeWikiText( $desc ); if ( is_array( $desc ) ) { - $desc = join( "\n", $desc ); + $desc = implode( "\n", $desc ); } else { $desc = (string)$desc; } @@ -2309,7 +2309,7 @@ abstract class ApiBase extends ContextSource { } return $line; }, $d ); - $d = join( ' ', $d ); + $d = implode( ' ', $d ); } if ( isset( $settings[ApiBase::PARAM_HELP_MSG] ) ) { @@ -2323,18 +2323,18 @@ abstract class ApiBase extends ContextSource { $msg = ApiBase::makeMessage( $msg, $this->getContext(), [ $prefix, $param, $name, $path ] ); if ( !$msg ) { - $this->dieDebug( __METHOD__, + self::dieDebug( __METHOD__, 'Value in ApiBase::PARAM_HELP_MSG is not valid' ); } $msgs[$param] = [ $msg ]; if ( isset( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) { if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) { - $this->dieDebug( __METHOD__, + self::dieDebug( __METHOD__, 'ApiBase::PARAM_HELP_MSG_PER_VALUE is not valid' ); } if ( !is_array( $settings[ApiBase::PARAM_TYPE] ) ) { - $this->dieDebug( __METHOD__, + self::dieDebug( __METHOD__, 'ApiBase::PARAM_HELP_MSG_PER_VALUE may only be used when ' . 'ApiBase::PARAM_TYPE is an array' ); } @@ -2356,7 +2356,7 @@ abstract class ApiBase extends ContextSource { ); $msgs[$param][] = $m->setContext( $this->getContext() ); } else { - $this->dieDebug( __METHOD__, + self::dieDebug( __METHOD__, "Value in ApiBase::PARAM_HELP_MSG_PER_VALUE for $value is not valid" ); } } @@ -2364,7 +2364,7 @@ abstract class ApiBase extends ContextSource { if ( isset( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) { if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) { - $this->dieDebug( __METHOD__, + self::dieDebug( __METHOD__, 'Value for ApiBase::PARAM_HELP_MSG_APPEND is not an array' ); } foreach ( $settings[ApiBase::PARAM_HELP_MSG_APPEND] as $m ) { @@ -2373,7 +2373,7 @@ abstract class ApiBase extends ContextSource { if ( $m ) { $msgs[$param][] = $m; } else { - $this->dieDebug( __METHOD__, + self::dieDebug( __METHOD__, 'Value in ApiBase::PARAM_HELP_MSG_APPEND is not valid' ); } } @@ -2524,123 +2524,6 @@ abstract class ApiBase extends ContextSource { * @{ */ - /// @deprecated since 1.24 - const PROP_ROOT = 'ROOT'; - /// @deprecated since 1.24 - const PROP_LIST = 'LIST'; - /// @deprecated since 1.24 - const PROP_TYPE = 0; - /// @deprecated since 1.24 - const PROP_NULLABLE = 1; - - /** - * Formerly used to fetch a list of possible properites in the result, - * somehow organized with respect to the prop parameter that causes them to - * be returned. The specific semantics of the return value was never - * specified. Since this was never possible to be accurately updated, it - * has been removed. - * - * @deprecated since 1.24 - * @return array|bool - */ - protected function getResultProperties() { - wfDeprecated( __METHOD__, '1.24' ); - return false; - } - - /** - * @see self::getResultProperties() - * @deprecated since 1.24 - * @return array|bool - */ - public function getFinalResultProperties() { - wfDeprecated( __METHOD__, '1.24' ); - return []; - } - - /** - * @see self::getResultProperties() - * @deprecated since 1.24 - */ - protected static function addTokenProperties( &$props, $tokenFunctions ) { - wfDeprecated( __METHOD__, '1.24' ); - } - - /** - * @see self::getPossibleErrors() - * @deprecated since 1.24 - * @return array - */ - public function getRequireOnlyOneParameterErrorMessages( $params ) { - wfDeprecated( __METHOD__, '1.24' ); - return []; - } - - /** - * @see self::getPossibleErrors() - * @deprecated since 1.24 - * @return array - */ - public function getRequireMaxOneParameterErrorMessages( $params ) { - wfDeprecated( __METHOD__, '1.24' ); - return []; - } - - /** - * @see self::getPossibleErrors() - * @deprecated since 1.24 - * @return array - */ - public function getRequireAtLeastOneParameterErrorMessages( $params ) { - wfDeprecated( __METHOD__, '1.24' ); - return []; - } - - /** - * @see self::getPossibleErrors() - * @deprecated since 1.24 - * @return array - */ - public function getTitleOrPageIdErrorMessage() { - wfDeprecated( __METHOD__, '1.24' ); - return []; - } - - /** - * This formerly attempted to return a list of all possible errors returned - * by the module. However, this was impossible to maintain in many cases - * since errors could come from other areas of MediaWiki and in some cases - * from arbitrary extension hooks. Since a partial list claiming to be - * comprehensive is unlikely to be useful, it was removed. - * - * @deprecated since 1.24 - * @return array - */ - public function getPossibleErrors() { - wfDeprecated( __METHOD__, '1.24' ); - return []; - } - - /** - * @see self::getPossibleErrors() - * @deprecated since 1.24 - * @return array - */ - public function getFinalPossibleErrors() { - wfDeprecated( __METHOD__, '1.24' ); - return []; - } - - /** - * @see self::getPossibleErrors() - * @deprecated since 1.24 - * @return array - */ - public function parseErrors( $errors ) { - wfDeprecated( __METHOD__, '1.24' ); - return []; - } - /** * Returns the description string for this module * @@ -2740,7 +2623,7 @@ abstract class ApiBase extends ContextSource { $examples ]; } - $msg .= "Example" . ( count( $examples ) > 1 ? 's' : '' ) . ":\n"; + $msg .= 'Example' . ( count( $examples ) > 1 ? 's' : '' ) . ":\n"; foreach ( $examples as $k => $v ) { if ( is_numeric( $k ) ) { $msg .= " $v\n"; @@ -2750,7 +2633,7 @@ abstract class ApiBase extends ContextSource { } else { $msgExample = " $v"; } - $msgExample .= ":"; + $msgExample .= ':'; $msg .= wordwrap( $msgExample, 100, "\n" ) . "\n $k\n"; } } @@ -2766,7 +2649,7 @@ abstract class ApiBase extends ContextSource { * @return string */ private function indentExampleText( $item ) { - return " " . $item; + return ' ' . $item; } /** @@ -2849,7 +2732,7 @@ abstract class ApiBase extends ContextSource { if ( isset( $paramSettings[self::PARAM_REQUIRED] ) && $paramSettings[self::PARAM_REQUIRED] ) { - $desc .= $paramPrefix . "This parameter is required"; + $desc .= $paramPrefix . 'This parameter is required'; } $type = isset( $paramSettings[self::PARAM_TYPE] ) @@ -2925,7 +2808,7 @@ abstract class ApiBase extends ContextSource { } break; case 'upload': - $desc .= $paramPrefix . "Must be posted as a file upload using multipart/form-data"; + $desc .= $paramPrefix . 'Must be posted as a file upload using multipart/form-data'; break; } } @@ -2939,8 +2822,8 @@ abstract class ApiBase extends ContextSource { if ( !$isArray || $isArray && count( $type ) > self::LIMIT_SML1 ) { - $desc .= $paramPrefix . "Maximum number of values " . - self::LIMIT_SML1 . " (" . self::LIMIT_SML2 . " for bots)"; + $desc .= $paramPrefix . 'Maximum number of values ' . + self::LIMIT_SML1 . ' (' . self::LIMIT_SML2 . ' for bots)'; } } } diff --git a/includes/api/ApiContinuationManager.php b/includes/api/ApiContinuationManager.php index 25407bfe96..8f1bd19191 100644 --- a/includes/api/ApiContinuationManager.php +++ b/includes/api/ApiContinuationManager.php @@ -137,7 +137,7 @@ class ApiContinuationManager { } $paramName = $module->encodeParamName( $paramName ); if ( is_array( $paramValue ) ) { - $paramValue = join( '|', $paramValue ); + $paramValue = implode( '|', $paramValue ); } $this->continuationData[$name][$paramName] = $paramValue; } @@ -152,7 +152,7 @@ class ApiContinuationManager { $name = $module->getModuleName(); $paramName = $module->encodeParamName( $paramName ); if ( is_array( $paramValue ) ) { - $paramValue = join( '|', $paramValue ); + $paramValue = implode( '|', $paramValue ); } $this->generatorContinuationData[$name][$paramName] = $paramValue; } @@ -193,7 +193,7 @@ class ApiContinuationManager { $data += $kvp; } $data += $this->generatorParams; - $generatorKeys = join( '|', array_keys( $this->generatorParams ) ); + $generatorKeys = implode( '|', array_keys( $this->generatorParams ) ); } elseif ( $this->generatorContinuationData ) { // All the generator-using modules are complete, but the // generator isn't. Continue the generator and restart the @@ -204,7 +204,7 @@ class ApiContinuationManager { } $data += $generatorParams; $finishedModules = array_diff( $finishedModules, $this->generatedModules ); - $generatorKeys = join( '|', array_keys( $generatorParams ) ); + $generatorKeys = implode( '|', array_keys( $generatorParams ) ); $batchcomplete = true; } else { // Generator and prop modules are all done. Mark it so. @@ -215,7 +215,7 @@ class ApiContinuationManager { // Set 'continue' if any continuation data is set or if the generator // still needs to run if ( $data || $generatorKeys !== '-' ) { - $data['continue'] = $generatorKeys . '||' . join( '|', $finishedModules ); + $data['continue'] = $generatorKeys . '||' . implode( '|', $finishedModules ); } return [ $data, $batchcomplete ]; diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php index f32bab0fcd..08aba9482e 100644 --- a/includes/api/ApiEditPage.php +++ b/includes/api/ApiEditPage.php @@ -335,7 +335,7 @@ class ApiEditPage extends ApiBase { $section = $params['section']; if ( !preg_match( '/^((T-)?\d+|new)$/', $section ) ) { $this->dieUsage( "The section parameter must be a valid section id or 'new'", - "invalidsection" ); + 'invalidsection' ); } $content = $pageObj->getContent(); if ( $section !== '0' && $section != 'new' diff --git a/includes/api/ApiExpandTemplates.php b/includes/api/ApiExpandTemplates.php index 6611a09d0b..286fe887c3 100644 --- a/includes/api/ApiExpandTemplates.php +++ b/includes/api/ApiExpandTemplates.php @@ -158,7 +158,7 @@ class ApiExpandTemplates extends ApiBase { !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." ); + 'for proper module usage.' ); } } } diff --git a/includes/api/ApiFeedContributions.php b/includes/api/ApiFeedContributions.php index dacf828f25..e28b0684d5 100644 --- a/includes/api/ApiFeedContributions.php +++ b/includes/api/ApiFeedContributions.php @@ -173,7 +173,7 @@ class ApiFeedContributions extends ApiBase { return '

' . htmlspecialchars( $revision->getUserText() ) . $msg . htmlspecialchars( FeedItem::stripComment( $revision->getComment() ) ) . - "

\n
\n
" . $html . "
"; + "

\n
\n
" . $html . '
'; } return ''; diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php index 7c203d9f9d..c826bbad95 100644 --- a/includes/api/ApiFormatBase.php +++ b/includes/api/ApiFormatBase.php @@ -151,7 +151,7 @@ abstract class ApiFormatBase extends ApiBase { * Initialize the printer function and prepare the output headers. * @param bool $unused Always false since 1.25 */ - function initPrinter( $unused = false ) { + public function initPrinter( $unused = false ) { if ( $this->mDisabled ) { return; } diff --git a/includes/api/ApiFormatXml.php b/includes/api/ApiFormatXml.php index be9b6d0b4b..a45dbebfb5 100644 --- a/includes/api/ApiFormatXml.php +++ b/includes/api/ApiFormatXml.php @@ -266,7 +266,7 @@ class ApiFormatXml extends ApiFormatBase { ); } - function addXslt() { + protected function addXslt() { $nt = Title::newFromText( $this->mXslt ); if ( is_null( $nt ) || !$nt->exists() ) { $this->setWarning( 'Invalid or non-existent stylesheet specified' ); diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php index 349a34da92..f2d6329ee5 100644 --- a/includes/api/ApiHelp.php +++ b/includes/api/ApiHelp.php @@ -268,7 +268,7 @@ class ApiHelp extends ApiBase { 'level' => $level, 'anchor' => $anchor, 'line' => $header, - 'number' => join( '.', $tocnumber ), + 'number' => implode( '.', $tocnumber ), 'index' => false, ]; if ( empty( $options['noheader'] ) ) { @@ -618,7 +618,7 @@ class ApiHelp extends ApiBase { ->parse(); } if ( $extra ) { - $info[] = join( ' ', $extra ); + $info[] = implode( ' ', $extra ); } } } @@ -655,7 +655,7 @@ class ApiHelp extends ApiBase { } if ( $description ) { - $description = join( '', $description ); + $description = implode( '', $description ); $description = preg_replace( '!\s*\s*<\1>\s*!', "\n", $description ); $help['parameters'] .= Html::rawElement( 'dd', [ 'class' => 'description' ], $description ); @@ -744,7 +744,7 @@ class ApiHelp extends ApiBase { Hooks::run( 'APIHelpModifyOutput', [ $module, &$help, $suboptions, &$haveModules ] ); - $out .= join( "\n", $help ); + $out .= implode( "\n", $help ); } return $out; diff --git a/includes/api/ApiImageRotate.php b/includes/api/ApiImageRotate.php index b309149eac..2b9935341b 100644 --- a/includes/api/ApiImageRotate.php +++ b/includes/api/ApiImageRotate.php @@ -111,9 +111,9 @@ class ApiImageRotate extends ApiBase { $tmpFile = TempFSFile::factory( 'rotate_', $ext ); $dstPath = $tmpFile->getPath(); $err = $handler->rotate( $file, [ - "srcPath" => $srcPath, - "dstPath" => $dstPath, - "rotation" => $rotation + 'srcPath' => $srcPath, + 'dstPath' => $dstPath, + 'rotation' => $rotation ] ); if ( !$err ) { $comment = wfMessage( diff --git a/includes/api/ApiImport.php b/includes/api/ApiImport.php index 8574dceb4f..10106ff02b 100644 --- a/includes/api/ApiImport.php +++ b/includes/api/ApiImport.php @@ -175,7 +175,7 @@ class ApiImportReporter extends ImportReporter { * @param array $pageInfo * @return void */ - function reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ) { + public function reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ) { // Add a result entry $r = []; @@ -194,7 +194,7 @@ class ApiImportReporter extends ImportReporter { parent::reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ); } - function getData() { + public function getData() { return $this->mResultArr; } } diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php index a6e6c49d79..02aae06e80 100644 --- a/includes/api/ApiLogin.php +++ b/includes/api/ApiLogin.php @@ -208,7 +208,6 @@ class ApiLogin extends ApiBase { case LoginForm::THROTTLED: $result['result'] = 'Throttled'; - $throttle = $this->getConfig()->get( 'PasswordAttemptThrottle' ); $result['wait'] = intval( $loginForm->mThrottleWait ); break; diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index 9e5681984c..f09c6f29fb 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -978,7 +978,7 @@ class ApiMain extends ApiBase { if ( $module->needsToken() === true ) { throw new MWException( "Module '{$module->getModuleName()}' must be updated for the new token handling. " . - "See documentation for ApiBase::needsToken for details." + 'See documentation for ApiBase::needsToken for details.' ); } if ( $module->needsToken() ) { @@ -1174,7 +1174,7 @@ class ApiMain extends ApiBase { $this->dieUsageMsg( 'writerequired' ); } elseif ( $this->getRequest()->getHeader( 'Promise-Non-Write-API-Action' ) ) { $this->dieUsage( - "Promise-Non-Write-API-Action HTTP header cannot be sent to write API modules", + 'Promise-Non-Write-API-Action HTTP header cannot be sent to write API modules', 'promised-nonwrite-api' ); } @@ -1225,7 +1225,7 @@ class ApiMain extends ApiBase { // If a majority of slaves are too lagged then disallow writes $slaveCount = wfGetLB()->getServerCount() - 1; if ( $numLagged >= ceil( $slaveCount / 2 ) ) { - $laggedServers = join( ', ', $laggedServers ); + $laggedServers = implode( ', ', $laggedServers ); wfDebugLog( 'api-readonly', "Api request failed as read only because the following DBs are lagged: $laggedServers" @@ -1443,7 +1443,7 @@ class ApiMain extends ApiBase { $ret = $this->getRequest()->getVal( $name ); if ( $ret === null ) { if ( $this->getRequest()->getArray( $name ) !== null ) { - // See bug 10262 for why we don't just join( '|', ... ) the + // See bug 10262 for why we don't just implode( '|', ... ) the // array. $this->setWarning( "Parameter '$name' uses unsupported PHP array syntax" @@ -1637,7 +1637,7 @@ class ApiMain extends ApiBase { 'level' => $level, 'anchor' => 'main/datatypes', 'line' => $header, - 'number' => join( '.', $tocnumber ), + 'number' => implode( '.', $tocnumber ), 'index' => false, ]; } @@ -1656,7 +1656,7 @@ class ApiMain extends ApiBase { 'level' => $level, 'anchor' => 'main/credits', 'line' => $header, - 'number' => join( '.', $tocnumber ), + 'number' => implode( '.', $tocnumber ), 'index' => false, ]; } @@ -1771,7 +1771,7 @@ class ApiMain extends ApiBase { ->inLanguage( 'en' ) ->text(); $groups = User::getGroupsWithPermission( $right ); - $msg .= "* " . $right . " *\n $rightsMsg" . + $msg .= '* ' . $right . " *\n $rightsMsg" . "\nGranted to:\n " . str_replace( '*', 'all', implode( ', ', $groups ) ) . "\n\n"; } diff --git a/includes/api/ApiOpenSearch.php b/includes/api/ApiOpenSearch.php index 304b2d6ecf..effa520a2d 100644 --- a/includes/api/ApiOpenSearch.php +++ b/includes/api/ApiOpenSearch.php @@ -358,7 +358,7 @@ class ApiOpenSearch extends ApiBase { $ns = implode( '|', SearchEngine::defaultNamespaces() ); if ( !$ns ) { - $ns = "0"; + $ns = '0'; } switch ( $type ) { diff --git a/includes/api/ApiOptions.php b/includes/api/ApiOptions.php index 1dde9c2226..e51d46d91a 100644 --- a/includes/api/ApiOptions.php +++ b/includes/api/ApiOptions.php @@ -99,19 +99,19 @@ class ApiOptions extends ApiBase { case 'userjs': // Allow non-default preferences prefixed with 'userjs-', to be set by user scripts if ( strlen( $key ) > 255 ) { - $validation = "key too long (no more than 255 bytes allowed)"; - } elseif ( preg_match( "/[^a-zA-Z0-9_-]/", $key ) !== 0 ) { - $validation = "invalid key (only a-z, A-Z, 0-9, _, - allowed)"; + $validation = 'key too long (no more than 255 bytes allowed)'; + } elseif ( preg_match( '/[^a-zA-Z0-9_-]/', $key ) !== 0 ) { + $validation = 'invalid key (only a-z, A-Z, 0-9, _, - allowed)'; } else { $validation = true; } break; case 'special': - $validation = "cannot be set by this module"; + $validation = 'cannot be set by this module'; break; case 'unused': default: - $validation = "not a valid preference"; + $validation = 'not a valid preference'; break; } if ( $validation === true ) { diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php index 1441a45eef..6bab762aff 100644 --- a/includes/api/ApiPageSet.php +++ b/includes/api/ApiPageSet.php @@ -595,22 +595,22 @@ class ApiPageSet extends ApiBase { 'special', 'missingIds', 'missingRevIds', 'missingTitles', 'interwikiTitles' ] ) { $result = []; - if ( in_array( "invalidTitles", $invalidChecks ) ) { + if ( in_array( 'invalidTitles', $invalidChecks ) ) { self::addValues( $result, $this->getInvalidTitlesAndReasons(), 'invalid' ); } - if ( in_array( "special", $invalidChecks ) ) { + if ( in_array( 'special', $invalidChecks ) ) { self::addValues( $result, $this->getSpecialTitles(), 'special', 'title' ); } - if ( in_array( "missingIds", $invalidChecks ) ) { + if ( in_array( 'missingIds', $invalidChecks ) ) { self::addValues( $result, $this->getMissingPageIDs(), 'missing', 'pageid' ); } - if ( in_array( "missingRevIds", $invalidChecks ) ) { + if ( in_array( 'missingRevIds', $invalidChecks ) ) { self::addValues( $result, $this->getMissingRevisionIDs(), 'missing', 'revid' ); } - if ( in_array( "missingTitles", $invalidChecks ) ) { + if ( in_array( 'missingTitles', $invalidChecks ) ) { self::addValues( $result, $this->getMissingTitles(), 'missing' ); } - if ( in_array( "interwikiTitles", $invalidChecks ) ) { + if ( in_array( 'interwikiTitles', $invalidChecks ) ) { self::addValues( $result, $this->getInterwikiTitlesAsResult() ); } diff --git a/includes/api/ApiParamInfo.php b/includes/api/ApiParamInfo.php index 6e44f82e80..c3c9e2176c 100644 --- a/includes/api/ApiParamInfo.php +++ b/includes/api/ApiParamInfo.php @@ -137,7 +137,7 @@ class ApiParamInfo extends ApiBase { foreach ( $msgs as $m ) { $ret[] = $m->setContext( $this->context )->text(); } - $res[$key] = join( "\n\n", $ret ); + $res[$key] = implode( "\n\n", $ret ); if ( $joinLists ) { $res[$key] = preg_replace( '!^(([*#:;])[^\n]*)\n\n(?=\2)!m', "$1\n", $res[$key] ); } @@ -148,7 +148,7 @@ class ApiParamInfo extends ApiBase { foreach ( $msgs as $m ) { $ret[] = $m->setContext( $this->context )->parseAsBlock(); } - $ret = join( "\n", $ret ); + $ret = implode( "\n", $ret ); if ( $joinLists ) { $ret = preg_replace( '!\s*\s*<\1>\s*!', "\n", $ret ); } diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php index 872876dfcc..fe418e3b0b 100644 --- a/includes/api/ApiParse.php +++ b/includes/api/ApiParse.php @@ -72,7 +72,7 @@ class ApiParse extends ApiBase { $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" + 'The section parameter must be a valid section id or "new"', 'invalidsection' ); } } else { @@ -275,7 +275,7 @@ class ApiParse extends ApiBase { $result_array = []; $result_array['title'] = $titleObj->getPrefixedText(); - $result_array['pageid'] = $pageid ? $pageid : $pageObj->getId(); + $result_array['pageid'] = $pageid ?: $pageObj->getId(); if ( !is_null( $oldid ) ) { $result_array['revid'] = intval( $oldid ); @@ -341,8 +341,7 @@ class ApiParse extends ApiBase { } if ( isset( $prop['displaytitle'] ) ) { - $result_array['displaytitle'] = $p_result->getDisplayTitle() ? - $p_result->getDisplayTitle() : + $result_array['displaytitle'] = $p_result->getDisplayTitle() ?: $titleObj->getPrefixedText(); } @@ -390,9 +389,9 @@ 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->setWarning( 'Property "modules" was set but not "jsconfigvars" ' . + 'or "encodedjsconfigvars". Configuration variables are necessary ' . + 'for proper module usage.' ); } if ( isset( $prop['indicators'] ) ) { @@ -428,7 +427,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->dieUsage( 'parsetree is only supported for wikitext content', 'notwikitext' ); } $wgParser->startExternalParse( $titleObj, $popts, Parser::OT_PREPROCESS ); @@ -545,10 +544,10 @@ class ApiParse extends ApiBase { // 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->dieUsage( "There is no section {$this->section} in $what", 'nosuchsection' ); } if ( $section === null ) { - $this->dieUsage( "Sections are not supported by " . $what, 'nosuchsection' ); + $this->dieUsage( "Sections are not supported by $what", 'nosuchsection' ); $section = false; } diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php index 58b670a2b4..43369074ea 100644 --- a/includes/api/ApiQuery.php +++ b/includes/api/ApiQuery.php @@ -185,33 +185,6 @@ class ApiQuery extends ApiBase { return $this->mPageSet; } - /** - * Get the generators array mapping module names to class names - * @deprecated since 1.21, list of generators is maintained by ApiPageSet - * @return array Array(modulename => classname) - */ - public function getGenerators() { - wfDeprecated( __METHOD__, '1.21' ); - $gens = []; - foreach ( $this->mModuleMgr->getNamesWithClasses() as $name => $class ) { - if ( is_subclass_of( $class, 'ApiQueryGeneratorBase' ) ) { - $gens[$name] = $class; - } - } - - return $gens; - } - - /** - * Get whether the specified module is a prop, list or a meta query module - * @deprecated since 1.21, use getModuleManager()->getModuleGroup() - * @param string $moduleName Name of the module to find type for - * @return string|null - */ - function getModuleType( $moduleName ) { - return $this->getModuleManager()->getModuleGroup( $moduleName ); - } - /** * @return ApiFormatRaw|null */ @@ -451,22 +424,6 @@ class ApiQuery extends ApiBase { } } - /** - * This method is called by the generator base when generator in the smart-continue - * mode tries to set 'query-continue' value. ApiQuery stores those values separately - * until the post-processing when it is known if the generation should continue or repeat. - * @deprecated since 1.24 - * @param ApiQueryGeneratorBase $module Generator module - * @param string $paramName - * @param mixed $paramValue - * @return bool True if processed, false if this is a legacy continue - */ - public function setGeneratorContinue( $module, $paramName, $paramValue ) { - wfDeprecated( __METHOD__, '1.24' ); - $this->getContinuationManager()->addGeneratorContinueParam( $module, $paramName, $paramValue ); - return !$this->getParameter( 'rawcontinue' ); - } - /** * @param ApiPageSet $pageSet Pages to be exported * @param ApiResult $result Result to output to diff --git a/includes/api/ApiQueryAllLinks.php b/includes/api/ApiQueryAllLinks.php index 94707da21c..ac906056a3 100644 --- a/includes/api/ApiQueryAllLinks.php +++ b/includes/api/ApiQueryAllLinks.php @@ -117,7 +117,7 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase { if ( $matches ) { $p = $this->getModulePrefix(); $this->dieUsage( - "Cannot use {$p}prop=" . join( '|', array_keys( $matches ) ) . " with {$p}unique", + "Cannot use {$p}prop=" . implode( '|', array_keys( $matches ) ) . " with {$p}unique", 'params' ); } diff --git a/includes/api/ApiQueryBacklinks.php b/includes/api/ApiQueryBacklinks.php index 97b122accd..fb502e40e7 100644 --- a/includes/api/ApiQueryBacklinks.php +++ b/includes/api/ApiQueryBacklinks.php @@ -296,7 +296,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { // Note we must keep the parameters for the first query constant // This may be overridden at a later step $title = $row->{$this->bl_title}; - $this->continueStr = join( '|', array_slice( $this->cont, 0, 2 ) ) . + $this->continueStr = implode( '|', array_slice( $this->cont, 0, 2 ) ) . "|$ns|$title|{$row->from_ns}|{$row->page_id}"; break; } @@ -451,7 +451,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { [ 'query', $this->getModuleName() ], $idx, array_diff_key( $arr, [ 'redirlinks' => '' ] ) ); if ( !$fit ) { - $this->continueStr = join( '|', array_slice( $this->cont, 0, 6 ) ) . + $this->continueStr = implode( '|', array_slice( $this->cont, 0, 6 ) ) . "|$pageID"; break; } @@ -474,7 +474,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { [ 'query', $this->getModuleName(), $idx, 'redirlinks' ], null, $redir ); if ( !$fit ) { - $this->continueStr = join( '|', array_slice( $this->cont, 0, 6 ) ) . + $this->continueStr = implode( '|', array_slice( $this->cont, 0, 6 ) ) . "|$pageID|$key"; break; } diff --git a/includes/api/ApiQueryBacklinksprop.php b/includes/api/ApiQueryBacklinksprop.php index 17b51da432..3810e90f13 100644 --- a/includes/api/ApiQueryBacklinksprop.php +++ b/includes/api/ApiQueryBacklinksprop.php @@ -164,22 +164,14 @@ class ApiQueryBacklinksprop extends ApiQueryGeneratorBase { $this->dieContinueUsageIf( count( $cont ) != count( $sortby ) ); $where = ''; $i = count( $sortby ) - 1; - $cont_ns = 0; - $cont_title = ''; foreach ( array_reverse( $sortby, true ) as $field => $type ) { $v = $cont[$i]; switch ( $type ) { case 'ns': - $cont_ns = (int)$v; - /* fall through */ case 'int': $v = (int)$v; $this->dieContinueUsageIf( $v != $cont[$i] ); break; - - case 'title': - $cont_title = $v; - /* fall through */ default: $v = $db->addQuotes( $v ); break; @@ -321,7 +313,7 @@ class ApiQueryBacklinksprop extends ApiQueryGeneratorBase { foreach ( $sortby as $field => $v ) { $cont[] = $row->$field; } - $this->setContinueEnumParameter( 'continue', join( '|', $cont ) ); + $this->setContinueEnumParameter( 'continue', implode( '|', $cont ) ); } public function getCacheMode( $params ) { diff --git a/includes/api/ApiQueryFileRepoInfo.php b/includes/api/ApiQueryFileRepoInfo.php index 7848bc8dbc..c4912366c4 100644 --- a/includes/api/ApiQueryFileRepoInfo.php +++ b/includes/api/ApiQueryFileRepoInfo.php @@ -78,7 +78,7 @@ class ApiQueryFileRepoInfo extends ApiQueryBase { return [ 'prop' => [ - ApiBase::PARAM_DFLT => join( '|', $props ), + ApiBase::PARAM_DFLT => implode( '|', $props ), ApiBase::PARAM_ISMULTI => true, ApiBase::PARAM_TYPE => $props, ], diff --git a/includes/api/ApiQueryImageInfo.php b/includes/api/ApiQueryImageInfo.php index 6890046c97..ab94574065 100644 --- a/includes/api/ApiQueryImageInfo.php +++ b/includes/api/ApiQueryImageInfo.php @@ -308,7 +308,7 @@ class ApiQueryImageInfo extends ApiQueryBase { foreach ( $paramList as $name => $value ) { if ( !$h->validateParam( $name, $value ) ) { - $this->dieUsage( "Invalid value for {$p}urlparam ($name=$value)", "urlparam" ); + $this->dieUsage( "Invalid value for {$p}urlparam ($name=$value)", 'urlparam' ); } } @@ -357,7 +357,7 @@ class ApiQueryImageInfo extends ApiQueryBase { * 'revdelUser': User to use when checking whether to show revision-deleted fields. * @return array Result array */ - static function getInfo( $file, $prop, $result, $thumbParams = null, $opts = false ) { + public static function getInfo( $file, $prop, $result, $thumbParams = null, $opts = false ) { global $wgContLang; $anyHidden = false; diff --git a/includes/api/ApiQueryRevisionsBase.php b/includes/api/ApiQueryRevisionsBase.php index c12393da94..266d6999ba 100644 --- a/includes/api/ApiQueryRevisionsBase.php +++ b/includes/api/ApiQueryRevisionsBase.php @@ -294,9 +294,9 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase { $vals['parsetree'] = $xml; } else { $vals['badcontentformatforparsetree'] = true; - $this->setWarning( "Conversion to XML is supported for wikitext only, " . + $this->setWarning( 'Conversion to XML is supported for wikitext only, ' . $title->getPrefixedDBkey() . - " uses content model " . $content->getModel() ); + ' uses content model ' . $content->getModel() ); } } } @@ -315,9 +315,9 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase { ParserOptions::newFromContext( $this->getContext() ) ); } else { - $this->setWarning( "Template expansion is supported for wikitext only, " . + $this->setWarning( 'Template expansion is supported for wikitext only, ' . $title->getPrefixedDBkey() . - " uses content model " . $content->getModel() ); + ' uses content model ' . $content->getModel() ); $vals['badcontentformat'] = true; $text = false; } @@ -332,7 +332,7 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase { } if ( $text === null ) { - $format = $this->contentFormat ? $this->contentFormat : $content->getDefaultFormat(); + $format = $this->contentFormat ?: $content->getDefaultFormat(); $model = $content->getModel(); if ( !$content->isSupportedFormat( $format ) ) { diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php index 4befad66c7..f05556eca3 100644 --- a/includes/api/ApiQuerySiteinfo.php +++ b/includes/api/ApiQuerySiteinfo.php @@ -652,8 +652,8 @@ class ApiQuerySiteinfo extends ApiQueryBase { } $data = [ - 'url' => $url ? $url : '', - 'text' => $text ? $text : '' + 'url' => $url ?: '', + 'text' => $text ?: '' ]; return $this->getResult()->addValue( 'query', $property, $data ); diff --git a/includes/api/ApiQueryStashImageInfo.php b/includes/api/ApiQueryStashImageInfo.php index 51f486295c..6d1540b72e 100644 --- a/includes/api/ApiQueryStashImageInfo.php +++ b/includes/api/ApiQueryStashImageInfo.php @@ -42,7 +42,7 @@ class ApiQueryStashImageInfo extends ApiQueryImageInfo { $result = $this->getResult(); if ( !$params['filekey'] && !$params['sessionkey'] ) { - $this->dieUsage( "One of filekey or sessionkey must be supplied", 'nofilekey' ); + $this->dieUsage( 'One of filekey or sessionkey must be supplied', 'nofilekey' ); } // Alias sessionkey to filekey, but give an existing filekey precedence. @@ -62,9 +62,9 @@ class ApiQueryStashImageInfo extends ApiQueryImageInfo { } // @todo Update exception handling here to understand current getFile exceptions } catch ( UploadStashFileNotFoundException $e ) { - $this->dieUsage( "File not found: " . $e->getMessage(), "invalidsessiondata" ); + $this->dieUsage( 'File not found: ' . $e->getMessage(), 'invalidsessiondata' ); } catch ( UploadStashBadPathException $e ) { - $this->dieUsage( "Bad path: " . $e->getMessage(), "invalidsessiondata" ); + $this->dieUsage( 'Bad path: ' . $e->getMessage(), 'invalidsessiondata' ); } } diff --git a/includes/api/ApiResult.php b/includes/api/ApiResult.php index f70bbe7fef..3436320133 100644 --- a/includes/api/ApiResult.php +++ b/includes/api/ApiResult.php @@ -312,7 +312,7 @@ class ApiResult implements ApiSerializable { if ( !$conflicts ) { $arr[$name] += $value; } else { - $keys = join( ', ', array_keys( $conflicts ) ); + $keys = implode( ', ', array_keys( $conflicts ) ); throw new RuntimeException( "Conflicting keys ($keys) when attempting to merge element $name" ); @@ -340,7 +340,7 @@ class ApiResult implements ApiSerializable { $value = $value->serializeForApiResult(); if ( is_object( $value ) ) { throw new UnexpectedValueException( - get_class( $oldValue ) . "::serializeForApiResult() returned an object of class " . + get_class( $oldValue ) . '::serializeForApiResult() returned an object of class ' . get_class( $value ) ); } @@ -351,7 +351,7 @@ class ApiResult implements ApiSerializable { return self::validateValue( $value ); } catch ( Exception $ex ) { throw new UnexpectedValueException( - get_class( $oldValue ) . "::serializeForApiResult() returned an invalid value: " . + get_class( $oldValue ) . '::serializeForApiResult() returned an invalid value: ' . $ex->getMessage(), 0, $ex @@ -372,7 +372,7 @@ class ApiResult implements ApiSerializable { } $value = $tmp; } elseif ( is_float( $value ) && !is_finite( $value ) ) { - throw new InvalidArgumentException( "Cannot add non-finite floats to ApiResult" ); + throw new InvalidArgumentException( 'Cannot add non-finite floats to ApiResult' ); } elseif ( is_string( $value ) ) { $value = $wgContLang->normalize( $value ); } elseif ( $value !== null && !is_scalar( $value ) ) { @@ -538,7 +538,7 @@ class ApiResult implements ApiSerializable { ) { throw new RuntimeException( "Attempting to set content element as $name when " . $arr[self::META_CONTENT] . - " is already set as the content element" + ' is already set as the content element' ); } $arr[self::META_CONTENT] = $name; @@ -1132,12 +1132,12 @@ class ApiResult implements ApiSerializable { $tmp = []; return $tmp; default: - $fail = join( '.', array_slice( $path, 0, $i + 1 ) ); + $fail = implode( '.', array_slice( $path, 0, $i + 1 ) ); throw new InvalidArgumentException( "Path $fail does not exist" ); } } if ( !is_array( $ret[$k] ) ) { - $fail = join( '.', array_slice( $path, 0, $i + 1 ) ); + $fail = implode( '.', array_slice( $path, 0, $i + 1 ) ); throw new InvalidArgumentException( "Path $fail is not an array" ); } $ret = &$ret[$k]; diff --git a/includes/api/ApiStashEdit.php b/includes/api/ApiStashEdit.php index d8562b0d33..3c02c9ce63 100644 --- a/includes/api/ApiStashEdit.php +++ b/includes/api/ApiStashEdit.php @@ -50,7 +50,7 @@ class ApiStashEdit extends ApiBase { if ( !ContentHandler::getForModelID( $params['contentmodel'] ) ->isSupportedFormat( $params['contentformat'] ) ) { - $this->dieUsage( "Unsupported content model/format", 'badmodelformat' ); + $this->dieUsage( 'Unsupported content model/format', 'badmodelformat' ); } // Trim and fix newlines so the key SHA1's match (see RequestContext::getText()) @@ -77,7 +77,7 @@ class ApiStashEdit extends ApiBase { $baseRev->getId() ); if ( !$editContent ) { - $this->dieUsage( "Could not merge updated section.", 'replacefailed' ); + $this->dieUsage( 'Could not merge updated section.', 'replacefailed' ); } if ( $currentRev->getId() == $baseRev->getId() ) { // Base revision was still the latest; nothing to merge @@ -433,19 +433,19 @@ class ApiStashEdit extends ApiBase { ]; } - function needsToken() { + public function needsToken() { return 'csrf'; } - function mustBePosted() { + public function mustBePosted() { return true; } - function isWriteMode() { + public function isWriteMode() { return true; } - function isInternal() { + public function isInternal() { return true; } } diff --git a/includes/api/ApiTokens.php b/includes/api/ApiTokens.php index 63bae9d42b..4940394fe8 100644 --- a/includes/api/ApiTokens.php +++ b/includes/api/ApiTokens.php @@ -32,9 +32,9 @@ class ApiTokens extends ApiBase { public function execute() { $this->setWarning( - "action=tokens has been deprecated. Please use action=query&meta=tokens instead." + 'action=tokens has been deprecated. Please use action=query&meta=tokens instead.' ); - $this->logFeatureUsage( "action=tokens" ); + $this->logFeatureUsage( 'action=tokens' ); $params = $this->extractRequestParams(); $res = [ diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php index 79e88c6ca4..326f8ba112 100644 --- a/includes/api/ApiUpload.php +++ b/includes/api/ApiUpload.php @@ -542,9 +542,9 @@ class ApiUpload extends ApiBase { ]; ApiResult::setIndexedTagName( $extradata['allowed'], 'ext' ); - $msg = "Filetype not permitted: "; + $msg = 'Filetype not permitted: '; if ( isset( $verification['blacklistedExt'] ) ) { - $msg .= join( ', ', $verification['blacklistedExt'] ); + $msg .= implode( ', ', $verification['blacklistedExt'] ); $extradata['blacklisted'] = array_values( $verification['blacklistedExt'] ); ApiResult::setIndexedTagName( $extradata['blacklisted'], 'ext' ); } else { @@ -664,7 +664,7 @@ class ApiUpload extends ApiBase { $this->dieUsage( 'No such filekey: ' . $e->getMessage(), 'stashnosuchfilekey' ); break; default: - $this->dieUsage( $exceptionType . ": " . $e->getMessage(), 'stasherror' ); + $this->dieUsage( $exceptionType . ': ' . $e->getMessage(), 'stasherror' ); break; } } @@ -714,7 +714,7 @@ class ApiUpload extends ApiBase { if ( $this->mParams['async'] ) { $progress = UploadBase::getSessionStatus( $this->getUser(), $this->mParams['filekey'] ); if ( $progress && $progress['result'] === 'Poll' ) { - $this->dieUsage( "Upload from stash already in progress.", 'publishfailed' ); + $this->dieUsage( 'Upload from stash already in progress.', 'publishfailed' ); } UploadBase::setSessionStatus( $this->getUser(), diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php index 4e5e000115..f09fdcb063 100644 --- a/includes/api/ApiWatch.php +++ b/includes/api/ApiWatch.php @@ -80,7 +80,7 @@ class ApiWatch extends ApiBase { if ( $extraParams ) { $p = $this->getModulePrefix(); $this->dieUsage( - "The parameter {$p}title can not be used with " . implode( ", ", $extraParams ), + "The parameter {$p}title can not be used with " . implode( ', ', $extraParams ), 'invalidparammix' ); } diff --git a/includes/api/i18n/ar.json b/includes/api/i18n/ar.json index 79fe9cf3e0..5072c66a1a 100644 --- a/includes/api/i18n/ar.json +++ b/includes/api/i18n/ar.json @@ -45,6 +45,7 @@ "apihelp-emailuser-description": "مراسلة المستخدم", "apihelp-expandtemplates-param-title": "عنوان الصفحة.", "apihelp-feedrecentchanges-param-tagfilter": "فلتر بالوسم.", + "apihelp-feedrecentchanges-example-simple": " اظهر التغييرات الحديثة", "apihelp-feedrecentchanges-example-30days": "أظهر التغييرات الأخيرة في 30 يوم.", "apihelp-feedwatchlist-example-all6hrs": "اظهر كل التغييرات في اخر 6 ساعات", "apihelp-filerevert-param-comment": "تعليق الرفع.", diff --git a/includes/api/i18n/bn.json b/includes/api/i18n/bn.json index b8be085083..c93d8bae4a 100644 --- a/includes/api/i18n/bn.json +++ b/includes/api/i18n/bn.json @@ -1,10 +1,14 @@ { "@metadata": { "authors": [ - "Aftabuzzaman" + "Aftabuzzaman", + "Bodhisattwa" ] }, + "apihelp-block-description": "ব্যবহারকারীকে বাধা দিন।", "apihelp-createaccount-param-name": "ব্যবহারকারী নাম।", + "apihelp-delete-description": "একটি পাতা মুছে ফেলুন।", + "apihelp-delete-example-simple": "প্রধান পাতা মুছে ফেলুন।", "apihelp-edit-param-minor": "অনুল্লেখ্য সম্পাদনা।", "apihelp-login-example-login": "প্রবেশ" } diff --git a/includes/api/i18n/cs.json b/includes/api/i18n/cs.json index b6ddc888b1..e62cc30028 100644 --- a/includes/api/i18n/cs.json +++ b/includes/api/i18n/cs.json @@ -8,10 +8,11 @@ "Cvanca", "Utar", "Macofe", - "Danny B." + "Danny B.", + "LordMsz" ] }, - "apihelp-main-description": "
\n* [[mw:API:Main_page|Dokumentace]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api E-mailová konference]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Oznámení k API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Chyby a požadavky]\n
\nStav: Všechny funkce uvedené na této stránce by měly fungovat, ale API se stále aktivně vyvíjí a může se kdykoli změnit. Upozornění na změny získáte přihlášením se k [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ e-mailové konferenci mediawiki-api-announce].\n\nChybné požadavky: Pokud jsou do API zaslány chybné požadavky, bude vrácena HTTP hlavička s klíčem „MediaWiki-API-Error“ a hodnota této hlavičky a chybový kód budou nastaveny na stejnou hodnotu. Více informací najdete [[mw:API:Errors_and_warnings|v dokumentaci]].", + "apihelp-main-description": "
\n* [[mw:API:Main_page|Dokumentace]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api E-mailová konference]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Oznámení k API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Chyby a požadavky]\n
\nStav: Všechny funkce uvedené na této stránce by měly fungovat, ale API se stále aktivně vyvíjí a může se kdykoli změnit. Upozornění na změny získáte přihlášením se k [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ e-mailové konferenci mediawiki-api-announce].\n\nChybné požadavky: Pokud jsou do API zaslány chybné požadavky, bude vrácena HTTP hlavička s klíčem „MediaWiki-API-Error“ a hodnota této hlavičky a chybový kód budou nastaveny na stejnou hodnotu. Více informací najdete [[mw:API:Errors_and_warnings|v dokumentaci]].\n\nTestování: Pro více informací o usnadnění testování požadavků na API viz [[Special:ApiSandbox]].", "apihelp-main-param-action": "Která akce se má provést.", "apihelp-main-param-format": "Formát výstupu.", "apihelp-main-param-maxlag": "Maximální zpoždění lze použít, když je MediaWiki nainstalováno na cluster s replikovanou databází. Abyste se vyhnuli zhoršování už tak špatného replikačního zpoždění, můžete tímto parametrem nechat klienta čekat, dokud replikační zpoždění neklesne pod uvedenou hodnotu. V případě příliš vysokého zpoždění se vrátí chybový kód „maxlag“ s hlášením typu „Waiting for $host: $lag seconds lagged“.
Více informací najdete v [[mw:Manual:Maxlag_parameter|příručce]].", diff --git a/includes/api/i18n/el.json b/includes/api/i18n/el.json index 44d750a5ed..2c5c0db95a 100644 --- a/includes/api/i18n/el.json +++ b/includes/api/i18n/el.json @@ -99,7 +99,7 @@ "apihelp-opensearch-param-format": "Η μορφή των δεδομένων εξόδου.", "apihelp-options-example-reset": "Επαναφορά όλων των προτιμήσεων.", "apihelp-paraminfo-param-helpformat": "Μορφή των συμβολοσειρών βοήθειας.", - "apihelp-patrol-example-revid": "Περιπολία αναθεώρησης.", + "apihelp-patrol-example-revid": "Έλεγχος αναθεώρησης.", "apihelp-protect-example-protect": "Προστασία σελίδας.", "apihelp-query+users-paramvalue-prop-gender": "Επισημαίνει το φύλο του χρήστη. Επιστρέφει «αρσενικό», «θηλυκό» ή «άγνωστο»", "api-help-param-type-limit": "Τύπος: ακέραιος ή max", diff --git a/includes/api/i18n/fa.json b/includes/api/i18n/fa.json index f8708874f2..a4295833d0 100644 --- a/includes/api/i18n/fa.json +++ b/includes/api/i18n/fa.json @@ -12,7 +12,8 @@ "Ebraminio", "Macofe", "Huji", - "Ladsgroup" + "Ladsgroup", + "Freshman404" ] }, "apihelp-main-description": "
\n* [[mw:API:Main_page|مستندات]]\n* [[mw:API:FAQ|پرسش‌های متداول]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api فهرست پست الکترونیکی]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce اعلانات رابط برنامه‌نویسی کاربردی]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R ایرادها و درخواست‌ها]\n
\n\nوضعیت: تمام ویژگی‌هایی که در این صفحه نمایش یافته‌اند باید کار بکنند، ولی رابط برنامه‌نویسی کاربردی کماکان در حال توسعه است، و ممکن است در هر زمان تغییر بکند. به عضویت [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ فهرست پست الکترونیکی mediawiki-api-announce] در بیایید تا از تغییرات باخبر شوید.\n\nدرخواست‌های معیوب: وقتی درخواست‌های معیوب به رابط برنامه‌نویسی کاربردی فرستاده شوند، یک سرایند اچ‌تی‌تی‌پی با کلید «MediaWiki-API-Erorr» فرستاده می‌شود و بعد هم مقدار سرایند و هم کد خطای بازگردانده شده هر دو به یک مقدار نسبت داده می‌شوند. برای اطلاعات بیشتر [[mw:API:Errors_and_warnings|API: Errors and warnings]] را ببینید.\n\nآزمایش: برای انجام درخواست‌های API آزمایشی [[Special:ApiSandbox]] را ببینید.", @@ -177,6 +178,7 @@ "apihelp-opensearch-param-suggest": "کاری نکنید اگر [[mw:Manual:$wgEnableOpenSearchSuggest|$wgEnableOpenSearchSuggest]] false است.", "apihelp-opensearch-param-format": "فرمت خروجی.", "apihelp-opensearch-example-te": "یافتن صفحه‌هایی که با Te آغاز می‌شوند", + "apihelp-options-param-reset": "ترجیحات را به مقادیر پیش فرض سایت بازمی گرداند.", "apihelp-options-example-reset": "بازنشانی همه تنظیمات.", "apihelp-paraminfo-param-helpformat": "ساختار راهنمای رشته‌ها", "apihelp-parse-example-page": "تجزیه یک صفحه.", diff --git a/includes/api/i18n/ja.json b/includes/api/i18n/ja.json index 08d087ac6a..979f0239f2 100644 --- a/includes/api/i18n/ja.json +++ b/includes/api/i18n/ja.json @@ -223,6 +223,7 @@ "apihelp-paraminfo-param-querymodules": "クエリモジュール名のリスト (prop, meta or list パラメータの値)。$1querymodules=foo の代わりに $1modules=query+foo を使用してください。", "apihelp-paraminfo-example-1": "[[Special:ApiHelp/parse|action=parse]], [[Special:ApiHelp/jsonfm|format=jsonfm]], [[Special:ApiHelp/query+allpages|action=query&list=allpages]], and [[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]] に関する情報を表示する。", "apihelp-parse-param-summary": "構文解析のための要約", + "apihelp-parse-param-page": "このページの内容を構文解析します。$1text および $1title とは同時に使用できません。", "apihelp-parse-param-redirects": "もし $1page や $1pageid に転送ページが指定された場合、それを解決する。", "apihelp-parse-param-prop": "どの情報を取得するか:", "apihelp-parse-paramvalue-prop-text": "ウィキテキストの解析されたテキストを提供します。", @@ -397,6 +398,9 @@ "apihelp-query+allrevisions-param-generatetitles": "ジェネレーターとして使用する場合、版IDではなくページ名を生成します。", "apihelp-query+allrevisions-example-user": "利用者 Example による直近の50版を一覧表示する。", "apihelp-query+allrevisions-example-ns-main": "標準名前空間にある最初の50版を一覧表示する。", + "apihelp-query+mystashedfiles-param-prop": "ファイルのどのプロパティを取得するか。", + "apihelp-query+mystashedfiles-paramvalue-prop-size": "ファイルサイズと画像の大きさを取得します。", + "apihelp-query+mystashedfiles-paramvalue-prop-type": "ファイルの MIME タイプとメディアタイプを取得します。", "apihelp-query+mystashedfiles-param-limit": "取得するファイルの数。", "apihelp-query+alltransclusions-param-prefix": "この値で始まるすべてのトランスクルードされているページを検索する。", "apihelp-query+alltransclusions-param-prop": "どの情報を結果に含めるか:", diff --git a/includes/api/i18n/ksh.json b/includes/api/i18n/ksh.json index 7b5361d73e..28cef6b6e7 100644 --- a/includes/api/i18n/ksh.json +++ b/includes/api/i18n/ksh.json @@ -145,9 +145,12 @@ "apihelp-feedrecentchanges-param-hideliu": "Änderonge ußschlehße, di vun aanjemälldete Metmaacher jemaht wohde.", "apihelp-feedrecentchanges-param-hidepatrolled": "Nohjelohrte Änderonge övverjonn.", "apihelp-feedrecentchanges-param-hidemyself": "Änderonge vun heh dämm Metmaacher övverjonn.", + "apihelp-feedrecentchanges-param-hidecategorization": "Donn Änderonge aan de Zohjehüreshkeit zoh Saachjroppe veschteijsche.", "apihelp-feedrecentchanges-param-tagfilter": "Noh Makkehronge beschängke.", "apihelp-feedrecentchanges-param-target": "Zeijsch Änderonge aan Sigge, op di vun heh dä Sigg ene Lengk jeihd.", "apihelp-feedrecentchanges-param-showlinkedto": "Zeijsch Änderonge aan Sigge, op di vun dä ußjesöhk Sigg ene Lengk jeihd.", + "apihelp-feedrecentchanges-param-categories": "Donn blohß de Änderonge aan de Zohjehüreshkeit för all heh di Saachjroppe zeije.", + "apihelp-feedrecentchanges-param-categories_any": "Donn deföhr blohß de Änderonge aan de Zohjehüreshkeit för öhndseijn fun heh dä Saachjroppe zeije.", "apihelp-feedrecentchanges-example-simple": "Zeijsch de {{LCFIRST:{{int:recentchanges}}}}", "apihelp-feedrecentchanges-example-30days": "Zeijsch de {{LCFIRST:{{int:recentchanges}}}} vun de läzde 30 Dähsch.", "apihelp-feedwatchlist-description": "Donn ene Kannahl met dä Oppaßleß zerökjävve.", @@ -404,6 +407,7 @@ "apihelp-query+allrevisions-param-generatetitles": "Wann als ene Jenerahtor enjesaz, brängk dat Övverschreffte un kein Kännonge vun Väsjohne.", "apihelp-query+allrevisions-example-user": "Donn de läzde fuffzisch Beijdrähsch vum Metmaacher „Example“ opleßte.", "apihelp-query+allrevisions-example-ns-main": "Donn de eezde fuffzisch Väsjohne em Houp-Appachemang opleßte.", + "apihelp-query+mystashedfiles-param-prop": "Wat för en Aanjahbe holle för di Datteije.", "apihelp-query+mystashedfiles-param-limit": "Wi vill Datteije holle?", "apihelp-query+alltransclusions-param-from": "De Övverschreff vun dä ennjeföhschte Sigg, woh de Leß medd aanfange sull.", "apihelp-query+alltransclusions-param-to": "De Övverschreff vun dä ennjeföhschte Sigg, woh et Zälle ophühre sull.", @@ -701,6 +705,7 @@ "apihelp-query+random-param-namespace": "Jiff blohß sigge en heh dä Appachtemangs uß.", "apihelp-query+random-param-limit": "Wi vill zohfälleje Sigge sulle ußjejovve wähde?", "apihelp-query+random-param-redirect": "Nemm $1filterredir=redirects schtatt dämm.", + "apihelp-query+random-param-filterredir": "Wi de Ömleijdonge ußzottehre?", "apihelp-query+random-example-simple": "Donn zwai zohfälleje Sigge vum Houb_Appachtemang ußjävve.", "apihelp-query+random-example-generator": "Donn Ennfommazjuhne övver zwai zohfälleje Sigge vum Houb_Appachtemang ußjävve.", "apihelp-query+recentchanges-description": "Donn de neußte Änderonge opleßte.", diff --git a/includes/api/i18n/lt.json b/includes/api/i18n/lt.json index 5ad108b2ab..da676a14f1 100644 --- a/includes/api/i18n/lt.json +++ b/includes/api/i18n/lt.json @@ -4,6 +4,13 @@ "Zygimantus" ] }, + "apihelp-createaccount-param-name": "Naudotojo vardas.", + "apihelp-createaccount-param-realname": "Vardas (nebÅ«tina).", + "apihelp-delete-description": "IÅ¡trinti puslapį.", + "apihelp-edit-param-text": "Puslapio turinys.", + "apihelp-emailuser-description": "Siųsti el. laiÅ¡ką naudotojui.", + "apihelp-expandtemplates-param-title": "Puslapio pavadinimas.", + "apihelp-feedrecentchanges-example-simple": "Parodyti naujausius keitimus.", "apihelp-query+alldeletedrevisions-example-user": "SąraÅ¡as paskutinių 50 iÅ¡trintų indėlių pagal vartotoją\nPavyzdys.", "apihelp-query+allrevisions-param-namespace": "Rodyti puslapius tik Å¡ioje vardų srityje.", "apihelp-query+backlinks-example-simple": "Rodyti nuorodas Pagrindinis puslapis.", diff --git a/includes/api/i18n/nap.json b/includes/api/i18n/nap.json index b5cba60ca3..6151f11742 100644 --- a/includes/api/i18n/nap.json +++ b/includes/api/i18n/nap.json @@ -65,7 +65,22 @@ "apihelp-edit-param-title": "Titolo d' 'a paggena a cagnà. Nun se pò ausà nziem'a $1pageid.", "apihelp-edit-param-pageid": "ID d' 'a paggena a cagnà. Nun se pò ausà nziem'a $1title.", "apihelp-edit-param-section": "Nummero 'e sezione. 0 p' 'a sezione ncoppa, new pe' na seziona nova.", + "apihelp-edit-param-sectiontitle": "'O titolo pe' na seziona nova.", "apihelp-edit-param-text": "Cuntenuto 'e paggena.", + "apihelp-edit-param-summary": "Oggetto d' 'a modifica. Pure 'o titolo ra sezione quanno $1sezione=new e $1sectiontitle nun è mpustato.", + "apihelp-edit-param-tags": "Cagna 'e tag ca s'avesser'applicà 'a verziona.", + "apihelp-edit-param-minor": "Cagnamiento piccerillo.", + "apihelp-edit-param-notminor": "Cagnamiento nun-piccerillo.", + "apihelp-edit-param-bot": "Nzegna stu cagnamiento comm' 'e bot.", + "apihelp-edit-param-basetimestamp": "Nzegna 'o tiempo d' 'a verzione bbase, ausato pe' puté ffà scummiglià cunflitte 'edizione. Se putesse piglià pe' bbìa 'e [[Special:ApiHelp/query+revisions|action=query&prop=revisions&rvprop=timestamp]].", + "apihelp-edit-param-starttimestamp": "Nzegna 'o tiempo d' 'a verzione bbase, ausato pe' puté ffà scummiglià cunflitte 'edizione. Nu valore buono se putess'arrepiglià pe' bbìa 'e [[Special:ApiHelp/main|curtimestamp]] quann'accummencia 'o prucess' 'edizione (e.g. quanno se stà a carrecà 'o contenuto 'e na paggena p' 'a cagnà).", + "apihelp-edit-param-recreate": "Scrive ncopp'a cocch'errore ncopp'a paggena avenno scancellato chesto a nu certo punto.", + "apihelp-edit-param-createonly": "Nun cagnà 'a paggena si esiste già.", + "apihelp-edit-param-nocreate": "Ietta 'errore si 'a paggena nun esiste.", + "apihelp-edit-param-watch": "Azzecc' 'a paggena â lista 'e paggene cuntrullate.", + "apihelp-edit-param-unwatch": "Liev' 'a paggena â lista 'e paggene cuntrullate.", + "apihelp-edit-param-watchlist": "Senza condizione, azzeccà o luvà 'a paggena 'a l'elenco 'e paggene cuntrullate 'e ll'utente, ausà mpustaziune o nun 'o cagnà l'elenco.", + "apihelp-edit-param-md5": "'O hash MD5 d' 'o parammetro 'e $1text, o chill' 'e $1prependtext e $1appendtext concatenate. Si mpustato, 'o cagnamiento nun fosse fatto... 'o cuntrario succeresse si 'o hash fosse curretto.", "apihelp-edit-param-prependtext": "Azzecca stu testo addò 'o cap' 'e paggena. Se mettesse ncuoll'a $1text.", "apihelp-edit-param-appendtext": "Azzecca stu testo addò 'o cap' 'e paggena. Se mettesse ncuoll'a $1text.\n\nAusate $1section=new pe' ne puté appennere na seziona nova, ato che ausà stu parammetro.", "apihelp-edit-param-undo": "Torna arrèto sta verziona. Miette ncuollo 'o $1text, $1prependtext e $1appendtext.", @@ -93,6 +108,27 @@ "apihelp-expandtemplates-paramvalue-prop-properties": "'E pruprietà 'e pagena definite p' 'e parole magiche spannute dint' 'o wikitesto.", "apihelp-expandtemplates-paramvalue-prop-volatile": "Si l'output fosse volatile e nun s'avess'ausà n'atavota addò servesse dint' 'a paggena.", "apihelp-expandtemplates-paramvalue-prop-ttl": "'O tiempo massimo aropp' 'o quale 'e caches d' 'o risultato s'avessero a nzegnà invalide.", + "apihelp-expandtemplates-paramvalue-prop-modules": "Ogne modulo ResourceLoader ch' 'e funzione parser addimannajero a s'azzeccà a ll'output. Fosse jsconfigvars o pure encodedjsconfigvars s'avesser'addimannà tutte 'nzieme pe' bbìa d' 'e modules.", + "apihelp-expandtemplates-paramvalue-prop-jsconfigvars": "Dà nfurmaziune 'e variabbele 'e mpustaziona JavaScript specifiche 'a paggena.", + "apihelp-expandtemplates-paramvalue-prop-encodedjsconfigvars": "Dà 'e variabbele 'e mpustaziona 'e JavaScript specifiche 'a na paggena comm'a na stringa JSON.", + "apihelp-expandtemplates-paramvalue-prop-parsetree": "L'albero 'e parse XML 'a ll'input.", + "apihelp-expandtemplates-param-includecomments": "Si s'avess'azzeccà cocche cummento HTML dint'a ll'output.", + "apihelp-expandtemplates-param-generatexml": "Generà ll'albero XML (scagnato 'a $1prop=parsetree).", + "apihelp-expandtemplates-example-simple": "Spanne 'o wikitesto {{Project:Sandbox}}.", + "apihelp-feedcontributions-description": "Tuorna nu feed 'e cuntribbute 'utente.", + "apihelp-feedcontributions-param-feedformat": "'O furmato d' 'o feed.", + "apihelp-feedcontributions-param-user": "'A quale 'utente nc'avimm'a piglià cuntribbute.", + "apihelp-feedcontributions-param-namespace": "'A qualu namespace s'avesser'a filtrà 'e cuntribbute.", + "apihelp-feedcontributions-param-year": "'E ll'anno (e primma).", + "apihelp-feedcontributions-param-month": "D' 'o mese (e pure cchiù primma).", + "apihelp-feedcontributions-param-tagfilter": "Filtrà cuntribbute ca teneno sti ttag.", + "apihelp-feedcontributions-param-deletedonly": "Mmusta surtant' 'e cuntribbute scancellate.", + "apihelp-feedcontributions-param-toponly": "Fà vedé sulamente 'e contribbute 'e l'urdeme verziune.", + "apihelp-feedcontributions-param-newonly": "Fà vedé sulamente 'e contribbute ca songo criazione 'e paggene.", + "apihelp-feedcontributions-param-showsizediff": "Fà vedé 'a differenza nfra verziune.", + "apihelp-feedcontributions-example-simple": "Tuòrna cuntribbute 'a ll'utente Esempio.", + "apihelp-feedrecentchanges-description": "Tuorna 'o blocco 'e nutizie 'e ll'urdeme cagnamiente.", + "apihelp-feedrecentchanges-param-feedformat": "'O furmato d' 'o feed.", "apihelp-feedwatchlist-param-feedformat": "'O furmato d' 'o feed.", "apihelp-login-example-login": "Tràse.", "apihelp-move-description": "Mòve paggena.", diff --git a/includes/api/i18n/vi.json b/includes/api/i18n/vi.json index 0bb5f63d66..6e2ff5a656 100644 --- a/includes/api/i18n/vi.json +++ b/includes/api/i18n/vi.json @@ -61,6 +61,7 @@ "apihelp-edit-param-unwatch": "Bỏ trang này khỏi danh sách theo dõi của người dùng hiện tại.", "apihelp-edit-param-undo": "Hoàn tác sá»­a đổi này. Ghi đè $1text, $1prependtext và $ 1appendtext.", "apihelp-edit-param-undoafter": "Hoàn tác tất cả các sá»­a đổi từ $1undo cho tới sá»­a đổi này. Nếu không được thiết lập, chỉ cần lùi lại một sá»­a đổi.", + "apihelp-edit-param-redirect": "Tá»± động giải quyết các chuyển hướng.", "apihelp-edit-example-edit": "Sá»­a đổi trang", "apihelp-edit-example-prepend": "Đưa __NOTOC__ vào đầu trang", "apihelp-edit-example-undo": "Lùi sá»­a các thay đổi 13579–13585 và tá»± động tóm lược", @@ -73,12 +74,15 @@ "apihelp-expandtemplates-description": "Bung tất cả bản mẫu trong văn bản wiki.", "apihelp-expandtemplates-param-title": "Tên trang.", "apihelp-expandtemplates-param-text": "Văn bản wiki để bung.", + "apihelp-expandtemplates-paramvalue-prop-wikitext": "Wikitext mở rộng.", "apihelp-expandtemplates-paramvalue-prop-parsetree": "Cây phân tích XML của đầu vào.", "apihelp-feedcontributions-description": "Trả về nguồn cấp đóng góp người dùng.", "apihelp-feedcontributions-param-feedformat": "Định dạng nguồn cấp.", "apihelp-feedcontributions-param-user": "Người dùng nhận được những đóng góp gì.", + "apihelp-feedcontributions-param-namespace": "Không gian tên để lọc các khoản đóng góp của.", "apihelp-feedcontributions-param-year": "Từ năm (trở về trước).", "apihelp-feedcontributions-param-month": "Từ tháng (trở về trước).", + "apihelp-feedcontributions-param-tagfilter": "Lọc đóng góp có những thẻ này.", "apihelp-feedcontributions-param-deletedonly": "Chỉ hiện các đóng góp đã xóa.", "apihelp-feedcontributions-param-toponly": "Chỉ hiện các phiên bản mới nhất.", "apihelp-feedcontributions-param-newonly": "Chỉ hiện các sá»­a đổi tạo trang.", @@ -113,6 +117,8 @@ "apihelp-imagerotate-param-rotation": "Độ xoay hình ảnh theo chiều kim đồng hồ.", "apihelp-imagerotate-example-simple": "Xoay Tập tin:Ví dụ.jpg 90 độ.", "apihelp-imagerotate-example-generator": "Xoay tất cả các hình ảnh trong Thể loại:Búng 180 độ.", + "apihelp-import-param-summary": "Nhập tóm lược.", + "apihelp-import-param-xml": "Tập tin XML đã được tải lên.", "apihelp-import-param-interwikisource": "Dành cho các nhập khẩu interwiki: wiki để nhập từ.", "apihelp-login-param-name": "Tên người dùng.", "apihelp-login-param-password": "Mật khẩu.", @@ -121,8 +127,11 @@ "apihelp-login-example-gettoken": "Lấy dấu hiệu đăng nhập", "apihelp-login-example-login": "Đăng nhập", "apihelp-logout-example-logout": "Đăng xuất người dùng hiện tại", + "apihelp-mergehistory-description": "Hợp nhất lịch sá»­ trang.", + "apihelp-mergehistory-param-reason": "Lý do hợp nhất lịch sá»­.", "apihelp-move-description": "Di chuyển trang.", "apihelp-move-param-reason": "Lý do đổi tên.", + "apihelp-move-param-movesubpages": "Đổi tên trang con, nếu có thể áp dụng.", "apihelp-move-param-noredirect": "Không tạo trang đổi hướng.", "apihelp-move-param-ignorewarnings": "Bỏ qua tất cả các cảnh báo.", "apihelp-opensearch-description": "Tìm kiếm trong wiki qua giao thức OpenSearch.", @@ -132,6 +141,7 @@ "apihelp-opensearch-param-format": "Định dạng kết quả được cho ra.", "apihelp-opensearch-example-te": "Tìm trang bắt đầu với Te.", "apihelp-options-example-reset": "Mặc định lại các tùy chọn", + "apihelp-paraminfo-description": "Lấy thông tin về các module API.", "apihelp-paraminfo-param-helpformat": "Định dạng chuỗi trợ giúp.", "apihelp-parse-param-summary": "Lời tóm lược để phân tích.", "apihelp-parse-param-section": "Chỉ phân tích nội dung của số phần này.\n\nNếu có new thì phân tích $1text và $1sectiontitle nhÆ° thể thêm phần mới vào trang.\n\nPhần new chỉ được chấp nhận khi định rõ text.", diff --git a/includes/cache/MessageCache.php b/includes/cache/MessageCache.php index b058d1f818..b26dc8da2f 100644 --- a/includes/cache/MessageCache.php +++ b/includes/cache/MessageCache.php @@ -1054,7 +1054,7 @@ class MessageCache { * @param Title $title * @param bool $linestart Whether or not this is at the start of a line * @param bool $interface Whether this is an interface message - * @param string $language Language code + * @param Language|string $language Language code * @return ParserOutput|string */ public function parse( $text, $title = null, $linestart = true, @@ -1067,6 +1067,10 @@ class MessageCache { $parser = $this->getParser(); $popts = $this->getParserOptions(); $popts->setInterfaceMessage( $interface ); + + if ( is_string( $language ) ) { + $language = Language::factory( $language ); + } $popts->setTargetLanguage( $language ); if ( !$title || !$title instanceof Title ) { diff --git a/includes/changes/ChangesList.php b/includes/changes/ChangesList.php index 15432da2b7..637eb88b09 100644 --- a/includes/changes/ChangesList.php +++ b/includes/changes/ChangesList.php @@ -132,7 +132,7 @@ class ChangesList extends ContextSource { $f = ''; foreach ( array_keys( $this->getConfig()->get( 'RecentChangesFlags' ) ) as $flag ) { $f .= isset( $flags[$flag] ) && $flags[$flag] - ? self::flag( $flag ) + ? self::flag( $flag, $this->getContext() ) : $nothing; } @@ -168,40 +168,40 @@ class ChangesList extends ContextSource { } /** - * Provide the "" element appropriate to a given abbreviated flag, - * namely the flag indicating a new page, a minor edit, a bot edit, or an - * unpatrolled edit. By default in English it will contain "N", "m", "b", - * "!" respectively, plus it will have an appropriate title and class. + * Make an "" element for a given change flag. The flag indicating a new page, minor edit, + * bot edit, or unpatrolled edit. In English it typically contains "N", "m", "b", or "!". * * @param string $flag One key of $wgRecentChangesFlags - * @return string Raw HTML + * @param IContextSource $context + * @return string HTML */ - public static function flag( $flag ) { + public static function flag( $flag, IContextSource $context = null ) { + static $map = [ 'minoredit' => 'minor', 'botedit' => 'bot' ]; static $flagInfos = null; + if ( is_null( $flagInfos ) ) { global $wgRecentChangesFlags; $flagInfos = []; foreach ( $wgRecentChangesFlags as $key => $value ) { - $flagInfos[$key]['letter'] = wfMessage( $value['letter'] )->escaped(); - $flagInfos[$key]['title'] = wfMessage( $value['title'] )->escaped(); + $flagInfos[$key]['letter'] = $value['letter']; + $flagInfos[$key]['title'] = $value['title']; // Allow customized class name, fall back to flag name - $flagInfos[$key]['class'] = Sanitizer::escapeClass( - isset( $value['class'] ) ? $value['class'] : $key ); + $flagInfos[$key]['class'] = isset( $value['class'] ) ? $value['class'] : $key; } } - // Inconsistent naming, bleh, kepted for b/c - $map = [ - 'minoredit' => 'minor', - 'botedit' => 'bot', - ]; + $context = $context ?: RequestContext::getMain(); + + // Inconsistent naming, kepted for b/c if ( isset( $map[$flag] ) ) { $flag = $map[$flag]; } - return "" . $flagInfos[$flag]['letter'] . - ''; + $info = $flagInfos[$flag]; + return Html::element( 'abbr', [ + 'class' => $info['class'], + 'title' => wfMessage( $info['title'] )->setContext( $context )->text(), + ], wfMessage( $info['letter'] )->setContext( $context )->text() ); } /** @@ -337,7 +337,7 @@ class ChangesList extends ContextSource { */ public function insertLog( &$s, $title, $logtype ) { $page = new LogPage( $logtype ); - $logname = $page->getName()->escaped(); + $logname = $page->getName()->setContext( $this->getContext() )->escaped(); $s .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $title, $logname ) )->escaped(); } diff --git a/includes/content/JsonContent.php b/includes/content/JsonContent.php index b9215fce85..40d9277470 100644 --- a/includes/content/JsonContent.php +++ b/includes/content/JsonContent.php @@ -161,7 +161,7 @@ class JsonContent extends TextContent { ); } return Html::rawElement( 'table', [ 'class' => 'mw-json' ], - Html::rawElement( 'tbody', [], join( '', $rows ) ) + Html::rawElement( 'tbody', [], implode( '', $rows ) ) ); } @@ -200,7 +200,7 @@ class JsonContent extends TextContent { ); } return Html::rawElement( 'table', [ 'class' => 'mw-json' ], - Html::rawElement( 'tbody', [], join( "\n", $rows ) ) + Html::rawElement( 'tbody', [], implode( "\n", $rows ) ) ); } diff --git a/includes/context/RequestContext.php b/includes/context/RequestContext.php index 35ee1b7ec4..c8b8108830 100644 --- a/includes/context/RequestContext.php +++ b/includes/context/RequestContext.php @@ -167,7 +167,7 @@ class RequestContext implements IContextSource, MutableContext { * * @param Title $title */ - public function setTitle( Title $title ) { + public function setTitle( Title $title = null ) { $this->title = $title; // Erase the WikiPage so a new one with the new title gets created. $this->wikipage = null; diff --git a/includes/db/DatabaseMysqlBase.php b/includes/db/DatabaseMysqlBase.php index 7058061f8e..1e2720504c 100644 --- a/includes/db/DatabaseMysqlBase.php +++ b/includes/db/DatabaseMysqlBase.php @@ -757,19 +757,13 @@ abstract class DatabaseMysqlBase extends Database { return $approxLag; } - /** - * Wait for the slave to catch up to a given master position. - * @todo Return values for this and base class are rubbish - * - * @param DBMasterPos|MySQLMasterPos $pos - * @param int $timeout The maximum number of seconds to wait for synchronisation - * @return int Zero if the slave was past that position already, - * greater than zero if we waited for some period of time, less than - * zero if we timed out. - */ function masterPosWait( DBMasterPos $pos, $timeout ) { + if ( !( $pos instanceof MySQLMasterPos ) ) { + throw new InvalidArgumentException( "Position not an instance of MySQLMasterPos" ); + } + if ( $this->lastKnownSlavePos && $this->lastKnownSlavePos->hasReached( $pos ) ) { - return '0'; // http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html + return 0; } # Commit any open transactions @@ -778,18 +772,28 @@ abstract class DatabaseMysqlBase extends Database { # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set $encFile = $this->addQuotes( $pos->file ); $encPos = intval( $pos->pos ); - $sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)"; - $res = $this->doQuery( $sql ); - - $status = false; - if ( $res ) { - $row = $this->fetchRow( $res ); - if ( $row ) { - $status = $row[0]; // can be NULL, -1, or 0+ per the MySQL manual - if ( ctype_digit( $status ) ) { // success - $this->lastKnownSlavePos = $pos; - } + $res = $this->doQuery( "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)" ); + + $row = $res ? $this->fetchRow( $res ) : false; + if ( !$row ) { + throw new DBExpectedError( $this, "Failed to query MASTER_POS_WAIT()" ); + } + + // Result can be NULL (error), -1 (timeout), or 0+ per the MySQL manual + $status = ( $row[0] !== null ) ? intval( $row[0] ) : null; + if ( $status === null ) { + // T126436: jobs programmed to wait on master positions might be referencing binlogs + // with an old master hostname. Such calls make MASTER_POS_WAIT() return null. Try + // to detect this and treat the slave as having reached the position; a proper master + // switchover already requires that the new master be caught up before the switch. + $slavePos = $this->getSlavePos(); + if ( $slavePos && !$slavePos->channelsMatch( $pos ) ) { + $this->lastKnownSlavePos = $slavePos; + $status = 0; } + } elseif ( $status >= 0 ) { + // Remember that this position was reached to save queries next time + $this->lastKnownSlavePos = $pos; } return $status; @@ -1446,11 +1450,34 @@ class MySQLMasterPos implements DBMasterPos { return ( $thisPos && $thatPos && $thisPos >= $thatPos ); } + function channelsMatch( DBMasterPos $pos ) { + if ( !( $pos instanceof self ) ) { + throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ ); + } + + $thisBinlog = $this->getBinlogName(); + $thatBinlog = $pos->getBinlogName(); + + return ( $thisBinlog !== false && $thisBinlog === $thatBinlog ); + } + function __toString() { // e.g db1034-bin.000976/843431247 return "{$this->file}/{$this->pos}"; } + /** + * @return string|bool + */ + protected function getBinlogName() { + $m = []; + if ( preg_match( '!^(.+)\.(\d+)/(\d+)$!', (string)$this, $m ) ) { + return $m[1]; + } + + return false; + } + /** * @return array|bool (int, int) */ diff --git a/includes/db/DatabaseOracle.php b/includes/db/DatabaseOracle.php index 9b301a9440..9e53653a76 100644 --- a/includes/db/DatabaseOracle.php +++ b/includes/db/DatabaseOracle.php @@ -618,7 +618,7 @@ class DatabaseOracle extends Database { $table = $this->tableName( $table ); // "INSERT INTO tables (a, b, c)" - $sql = "INSERT INTO " . $table . " (" . join( ',', array_keys( $row ) ) . ')'; + $sql = "INSERT INTO " . $table . " (" . implode( ',', array_keys( $row ) ) . ')'; $sql .= " VALUES ("; // for each value, append ":key" diff --git a/includes/db/DatabaseUtility.php b/includes/db/DatabaseUtility.php index 6fa8bf09f3..b6c37ee7b6 100644 --- a/includes/db/DatabaseUtility.php +++ b/includes/db/DatabaseUtility.php @@ -332,6 +332,13 @@ interface DBMasterPos { */ public function hasReached( DBMasterPos $pos ); + /** + * @param DBMasterPos $pos + * @return bool Whether this position appears to be for the same channel as another + * @since 1.27 + */ + public function channelsMatch( DBMasterPos $pos ); + /** * @return string * @since 1.27 diff --git a/includes/db/IDatabase.php b/includes/db/IDatabase.php index 7855861281..8b1c3dff22 100644 --- a/includes/db/IDatabase.php +++ b/includes/db/IDatabase.php @@ -1183,14 +1183,13 @@ interface IDatabase { public function wasReadOnlyError(); /** - * Wait for the slave to catch up to a given master position. + * Wait for the slave to catch up to a given master position * * @param DBMasterPos $pos - * @param int $timeout The maximum number of seconds to wait for - * synchronisation - * @return int Zero if the slave was past that position already, + * @param int $timeout The maximum number of seconds to wait for synchronisation + * @return int|null Zero if the slave was past that position already, * greater than zero if we waited for some period of time, less than - * zero if we timed out. + * zero if it timed out, and null on error */ public function masterPosWait( DBMasterPos $pos, $timeout ); diff --git a/includes/db/loadbalancer/LoadMonitor.php b/includes/db/loadbalancer/LoadMonitor.php index d5cd01701e..e68cf1a518 100644 --- a/includes/db/loadbalancer/LoadMonitor.php +++ b/includes/db/loadbalancer/LoadMonitor.php @@ -36,7 +36,7 @@ interface LoadMonitor { /** * Perform pre-connection load ratio adjustment. - * @param array $loads + * @param array &$loads * @param string|bool $group The selected query group. Default: false * @param string|bool $wiki Default: false */ diff --git a/includes/diff/DairikiDiff.php b/includes/diff/DairikiDiff.php index bc57c93592..6272e7e992 100644 --- a/includes/diff/DairikiDiff.php +++ b/includes/diff/DairikiDiff.php @@ -249,8 +249,8 @@ class DiffEngine { $edits = []; $xi = $yi = 0; while ( $xi < $n_from || $yi < $n_to ) { - assert( '$yi < $n_to || $this->xchanged[$xi]' ); - assert( '$xi < $n_from || $this->ychanged[$yi]' ); + assert( $yi < $n_to || $this->xchanged[$xi] ); + assert( $xi < $n_from || $this->ychanged[$yi] ); // Skip matching "snake". $copy = []; @@ -448,7 +448,7 @@ class DiffEngine { while ( list( , $y ) = each( $matches ) ) { if ( empty( $this->in_seq[$y] ) ) { $k = $this->lcsPos( $y ); - assert( '$k > 0' ); + assert( $k > 0 ); $ymids[$k] = $ymids[$k - 1]; break; } @@ -456,7 +456,7 @@ class DiffEngine { while ( list( , $y ) = each( $matches ) ) { if ( $y > $this->seq[$k - 1] ) { - assert( '$y < $this->seq[$k]' ); + assert( $y < $this->seq[$k] ); // Optimization: this is a common case: // next match is just replacing previous match. $this->in_seq[$this->seq[$k]] = false; @@ -464,7 +464,7 @@ class DiffEngine { $this->in_seq[$y] = 1; } elseif ( empty( $this->in_seq[$y] ) ) { $k = $this->lcsPos( $y ); - assert( '$k > 0' ); + assert( $k > 0 ); $ymids[$k] = $ymids[$k - 1]; } } @@ -507,7 +507,7 @@ class DiffEngine { } } - assert( '$ypos != $this->seq[$end]' ); + assert( $ypos != $this->seq[$end] ); $this->in_seq[$this->seq[$end]] = false; $this->seq[$end] = $ypos; @@ -595,7 +595,7 @@ class DiffEngine { $i = 0; $j = 0; - assert( 'count($lines) == count($changed)' ); + assert( count( $lines ) == count( $changed ) ); $len = count( $lines ); $other_len = count( $other_changed ); @@ -616,7 +616,7 @@ class DiffEngine { } while ( $i < $len && !$changed[$i] ) { - assert( '$j < $other_len && ! $other_changed[$j]' ); + assert( $j < $other_len && ! $other_changed[$j] ); $i++; $j++; while ( $j < $other_len && $other_changed[$j] ) { @@ -653,11 +653,11 @@ class DiffEngine { while ( $start > 0 && $changed[$start - 1] ) { $start--; } - assert( '$j > 0' ); + assert( $j > 0 ); while ( $other_changed[--$j] ) { continue; } - assert( '$j >= 0 && !$other_changed[$j]' ); + assert( $j >= 0 && !$other_changed[$j] ); } /* @@ -681,7 +681,7 @@ class DiffEngine { $i++; } - assert( '$j < $other_len && ! $other_changed[$j]' ); + assert( $j < $other_len && ! $other_changed[$j] ); $j++; if ( $j < $other_len && $other_changed[$j] ) { $corresponding = $i; @@ -699,11 +699,11 @@ class DiffEngine { while ( $corresponding < $i ) { $changed[--$start] = 1; $changed[--$i] = 0; - assert( '$j > 0' ); + assert( $j > 0 ); while ( $other_changed[--$j] ) { continue; } - assert( '$j >= 0 && !$other_changed[$j]' ); + assert( $j >= 0 && !$other_changed[$j] ); } } } @@ -867,8 +867,8 @@ class MappedDiff extends Diff { public function __construct( $from_lines, $to_lines, $mapped_from_lines, $mapped_to_lines ) { - assert( 'count( $from_lines ) == count( $mapped_from_lines )' ); - assert( 'count( $to_lines ) == count( $mapped_to_lines )' ); + assert( count( $from_lines ) == count( $mapped_from_lines ) ); + assert( count( $to_lines ) == count( $mapped_to_lines ) ); parent::__construct( $mapped_from_lines, $mapped_to_lines ); @@ -959,7 +959,7 @@ class HWLDFWordAccumulator { $this->flushLine( $tag ); $word = substr( $word, 1 ); } - assert( '!strstr( $word, "\n" )' ); + assert( !strstr( $word, "\n" ) ); $this->group .= $word; } } diff --git a/includes/diff/DifferenceEngine.php b/includes/diff/DifferenceEngine.php index 1cf3918a45..a1a1bb0497 100644 --- a/includes/diff/DifferenceEngine.php +++ b/includes/diff/DifferenceEngine.php @@ -847,7 +847,7 @@ class DifferenceEngine extends ContextSource { $result = $this->textDiff( $otext, $ntext ); $time = microtime( true ) - $time; - $this->getStats()->timing( 'diff_time', $time ); + $this->getStats()->timing( 'diff_time', $time * 1000 ); return $result; } diff --git a/includes/filebackend/FileBackendMultiWrite.php b/includes/filebackend/FileBackendMultiWrite.php index 5a103c647b..6f40bda945 100644 --- a/includes/filebackend/FileBackendMultiWrite.php +++ b/includes/filebackend/FileBackendMultiWrite.php @@ -202,11 +202,17 @@ class FileBackendMultiWrite extends FileBackend { if ( $this->asyncWrites && !$this->hasVolatileSources( $ops ) ) { // Bind $scopeLock to the callback to preserve locks DeferredUpdates::addCallableUpdate( - function() use ( $backend, $realOps, $opts, $scopeLock ) { + function() use ( $backend, $realOps, $opts, $scopeLock, $relevantPaths ) { + wfDebugLog( 'FileOperationReplication', + "'{$backend->getName()}' async replication; paths: " . + FormatJson::encode( $relevantPaths ) ); $backend->doOperations( $realOps, $opts ); } ); } else { + wfDebugLog( 'FileOperationReplication', + "'{$backend->getName()}' sync replication; paths: " . + FormatJson::encode( $relevantPaths ) ); $status->merge( $backend->doOperations( $realOps, $opts ) ); } } diff --git a/includes/filerepo/FileRepo.php b/includes/filerepo/FileRepo.php index 789803f616..1565b49d21 100644 --- a/includes/filerepo/FileRepo.php +++ b/includes/filerepo/FileRepo.php @@ -1115,7 +1115,7 @@ class FileRepo { * @param array $srcPaths Ordered list of source virtual URLs/storage paths * @param string $dstPath Target file system path * @param int $flags Bitwise combination of the following flags: - * self::DELETE_SOURCE Delete the source files + * self::DELETE_SOURCE Delete the source files on success * @return FileRepoStatus */ public function concatenate( array $srcPaths, $dstPath, $flags = 0 ) { @@ -1158,7 +1158,7 @@ class FileRepo { * Options to $options include: * - headers : name/value map of HTTP headers to use in response to GET/HEAD requests * - * @param string $srcPath The source file system path, storage path, or URL + * @param string|FSFile $src The source file system path, storage path, or URL * @param string $dstRel The destination relative path * @param string $archiveRel The relative path where the existing file is to * be archived, if there is one. Relative to the public zone root. @@ -1168,12 +1168,12 @@ class FileRepo { * @return FileRepoStatus */ public function publish( - $srcPath, $dstRel, $archiveRel, $flags = 0, array $options = [] + $src, $dstRel, $archiveRel, $flags = 0, array $options = [] ) { $this->assertWritableRepo(); // fail out if read-only $status = $this->publishBatch( - [ [ $srcPath, $dstRel, $archiveRel, $options ] ], $flags ); + [ [ $src, $dstRel, $archiveRel, $options ] ], $flags ); if ( $status->successCount == 0 ) { $status->ok = false; } @@ -1212,7 +1212,9 @@ class FileRepo { $sourceFSFilesToDelete = []; // cleanup for disk source files // Validate each triplet and get the store operation... foreach ( $ntuples as $ntuple ) { - list( $srcPath, $dstRel, $archiveRel ) = $ntuple; + list( $src, $dstRel, $archiveRel ) = $ntuple; + $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src; + $options = isset( $ntuple[3] ) ? $ntuple[3] : []; // Resolve source to a storage path if virtual $srcPath = $this->resolveToStoragePath( $srcPath ); @@ -1275,7 +1277,7 @@ class FileRepo { } else { // FS source path $operations[] = [ 'op' => 'store', - 'src' => $srcPath, + 'src' => $src, // prefer FSFile objects 'dst' => $dstPath, 'overwrite' => true, // replace current 'headers' => $headers diff --git a/includes/filerepo/ForeignAPIRepo.php b/includes/filerepo/ForeignAPIRepo.php index d29cd7d7d2..cc9099c6c0 100644 --- a/includes/filerepo/ForeignAPIRepo.php +++ b/includes/filerepo/ForeignAPIRepo.php @@ -537,7 +537,7 @@ class ForeignAPIRepo extends FileRepo { * @since 1.23 */ protected static function getIIProps() { - return join( '|', self::$imageInfoProps ); + return implode( '|', self::$imageInfoProps ); } /** diff --git a/includes/filerepo/LocalRepo.php b/includes/filerepo/LocalRepo.php index 5e9a4a9c73..d24aa24cba 100644 --- a/includes/filerepo/LocalRepo.php +++ b/includes/filerepo/LocalRepo.php @@ -532,7 +532,7 @@ class LocalRepo extends FileRepo { } public function publish( - $srcPath, + $src, $dstRel, $archiveRel, $flags = 0, diff --git a/includes/filerepo/file/File.php b/includes/filerepo/file/File.php index 6a1bb925fd..433e39548e 100644 --- a/includes/filerepo/file/File.php +++ b/includes/filerepo/file/File.php @@ -1802,7 +1802,7 @@ abstract class File implements IDBAccessObject { * Options to $options include: * - headers : name/value map of HTTP headers to use in response to GET/HEAD requests * - * @param string $srcPath Local filesystem path to the source image + * @param string|FSFile $src Local filesystem path to the source image * @param int $flags A bitwise combination of: * File::DELETE_SOURCE Delete the source file, i.e. move rather than copy * @param array $options Optional additional parameters @@ -1812,7 +1812,7 @@ abstract class File implements IDBAccessObject { * STUB * Overridden by LocalFile */ - function publish( $srcPath, $flags = 0, array $options = [] ) { + function publish( $src, $flags = 0, array $options = [] ) { $this->readOnlyError(); } diff --git a/includes/filerepo/file/LocalFile.php b/includes/filerepo/file/LocalFile.php index c97f38f771..0a3ded748f 100644 --- a/includes/filerepo/file/LocalFile.php +++ b/includes/filerepo/file/LocalFile.php @@ -1108,7 +1108,7 @@ class LocalFile extends File { /** * Upload a file and record it in the DB - * @param string $srcPath Source storage path, virtual URL, or filesystem path + * @param string|FSFile $src Source storage path, virtual URL, or filesystem path * @param string $comment Upload description * @param string $pageText Text to use for the new description page, * if a new description page is created @@ -1124,7 +1124,7 @@ class LocalFile extends File { * @return FileRepoStatus On success, the value member contains the * archive name, or an empty string if it was a new file. */ - function upload( $srcPath, $comment, $pageText, $flags = 0, $props = false, + function upload( $src, $comment, $pageText, $flags = 0, $props = false, $timestamp = false, $user = null, $tags = [] ) { global $wgContLang; @@ -1133,6 +1133,7 @@ class LocalFile extends File { return $this->readOnlyFatalStatus(); } + $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src; if ( !$props ) { if ( $this->repo->isVirtualUrl( $srcPath ) || FileBackend::isStoragePath( $srcPath ) @@ -1158,7 +1159,7 @@ class LocalFile extends File { // non-nicely (dangling multi-byte chars, non-truncated version in cache). $comment = $wgContLang->truncate( $comment, 255 ); $this->lock(); // begin - $status = $this->publish( $srcPath, $flags, $options ); + $status = $this->publish( $src, $flags, $options ); if ( $status->successCount >= 2 ) { // There will be a copy+(one of move,copy,store). @@ -1523,15 +1524,15 @@ class LocalFile extends File { * The archive name should be passed through to recordUpload for database * registration. * - * @param string $srcPath Local filesystem path or virtual URL to the source image + * @param string|FSFile $src Local filesystem path or virtual URL to the source image * @param int $flags A bitwise combination of: * File::DELETE_SOURCE Delete the source file, i.e. move rather than copy * @param array $options Optional additional parameters * @return FileRepoStatus On success, the value member contains the * archive name, or an empty string if it was a new file. */ - function publish( $srcPath, $flags = 0, array $options = [] ) { - return $this->publishTo( $srcPath, $this->getRel(), $flags, $options ); + function publish( $src, $flags = 0, array $options = [] ) { + return $this->publishTo( $src, $this->getRel(), $flags, $options ); } /** @@ -1541,7 +1542,7 @@ class LocalFile extends File { * The archive name should be passed through to recordUpload for database * registration. * - * @param string $srcPath Local filesystem path or virtual URL to the source image + * @param string|FSFile $src Local filesystem path or virtual URL to the source image * @param string $dstRel Target relative path * @param int $flags A bitwise combination of: * File::DELETE_SOURCE Delete the source file, i.e. move rather than copy @@ -1549,7 +1550,9 @@ class LocalFile extends File { * @return FileRepoStatus On success, the value member contains the * archive name, or an empty string if it was a new file. */ - function publishTo( $srcPath, $dstRel, $flags = 0, array $options = [] ) { + function publishTo( $src, $dstRel, $flags = 0, array $options = [] ) { + $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src; + $repo = $this->getRepo(); if ( $repo->getReadOnlyReason() !== false ) { return $this->readOnlyFatalStatus(); @@ -1563,9 +1566,9 @@ class LocalFile extends File { if ( $repo->hasSha1Storage() ) { $sha1 = $repo->isVirtualUrl( $srcPath ) ? $repo->getFileSha1( $srcPath ) - : File::sha1Base36( $srcPath ); + : FSFile::getSha1Base36FromPath( $srcPath ); $dst = $repo->getBackend()->getPathForSHA1( $sha1 ); - $status = $repo->quickImport( $srcPath, $dst ); + $status = $repo->quickImport( $src, $dst ); if ( $flags & File::DELETE_SOURCE ) { unlink( $srcPath ); } diff --git a/includes/htmlform/HTMLAutoCompleteSelectField.php b/includes/htmlform/HTMLAutoCompleteSelectField.php index 1f077a4318..6606ca3833 100644 --- a/includes/htmlform/HTMLAutoCompleteSelectField.php +++ b/includes/htmlform/HTMLAutoCompleteSelectField.php @@ -101,11 +101,11 @@ class HTMLAutoCompleteSelectField extends HTMLTextField { } // FIXME Ewww, this shouldn't be adding any attributes not requested in $list :( - public function getAttributes( array $list, array $mappings = null ) { + public function getAttributes( array $list ) { $attribs = [ 'type' => 'text', 'data-autocomplete' => FormatJson::encode( array_keys( $this->autocomplete ) ), - ] + parent::getAttributes( $list, $mappings ); + ] + parent::getAttributes( $list ); if ( $this->getOptions() ) { $attribs['data-hide-if'] = FormatJson::encode( diff --git a/includes/htmlform/HTMLButtonField.php b/includes/htmlform/HTMLButtonField.php index 211089f765..0b07765931 100644 --- a/includes/htmlform/HTMLButtonField.php +++ b/includes/htmlform/HTMLButtonField.php @@ -104,7 +104,9 @@ class HTMLButtonField extends HTMLFormField { 'id' => $this->mID, 'flags' => $this->mFlags, 'useInputTag' => $this->isBadIE(), - ] + $this->getAttributes( [ 'disabled', 'tabindex' ], [ 'tabindex' => 'tabIndex' ] ) ); + ] + OOUI\Element::configFromHtmlAttributes( + $this->getAttributes( [ 'disabled', 'tabindex' ] ) + ) ); } protected function needsLabel() { diff --git a/includes/htmlform/HTMLCheckField.php b/includes/htmlform/HTMLCheckField.php index 13f30c2d06..a59b15e828 100644 --- a/includes/htmlform/HTMLCheckField.php +++ b/includes/htmlform/HTMLCheckField.php @@ -56,9 +56,8 @@ class HTMLCheckField extends HTMLFormField { $attr['id'] = $this->mID; $attr['name'] = $this->mName; - $attr += $this->getAttributes( - [ 'disabled', 'tabindex' ], - [ 'tabindex' => 'tabIndex' ] + $attr += OOUI\Element::configFromHtmlAttributes( + $this->getAttributes( [ 'disabled', 'tabindex' ] ) ); if ( $this->mClass !== '' ) { diff --git a/includes/htmlform/HTMLCheckMatrix.php b/includes/htmlform/HTMLCheckMatrix.php index 6a4bec8370..9f67233641 100644 --- a/includes/htmlform/HTMLCheckMatrix.php +++ b/includes/htmlform/HTMLCheckMatrix.php @@ -85,13 +85,7 @@ class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable { $rows = $this->mParams['rows']; $columns = $this->mParams['columns']; - $mappings = []; - - if ( $this->mParent instanceof OOUIHTMLForm ) { - $mappings['tabindex'] = 'tabIndex'; - } - - $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ], $mappings ); + $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ] ); // Build the column headers $headerContents = Html::rawElement( 'td', [], ' ' ); @@ -156,8 +150,9 @@ class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable { return new OOUI\CheckboxInputWidget( [ 'name' => "{$this->mName}[]", 'selected' => $checked, - 'value' => $attribs['value'], - ] + $attribs ); + ] + OOUI\Element::configFromHtmlAttributes( + $attribs + ) ); } else { $checkbox = Xml::check( "{$this->mName}[]", $checked, $attribs ); if ( $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) { diff --git a/includes/htmlform/HTMLComboboxField.php b/includes/htmlform/HTMLComboboxField.php index a0f248fdea..778aedbc8a 100644 --- a/includes/htmlform/HTMLComboboxField.php +++ b/includes/htmlform/HTMLComboboxField.php @@ -16,11 +16,11 @@ */ class HTMLComboboxField extends HTMLTextField { // FIXME Ewww, this shouldn't be adding any attributes not requested in $list :( - public function getAttributes( array $list, array $mappings = null ) { + public function getAttributes( array $list ) { $attribs = [ 'type' => 'text', 'list' => $this->mName . '-datalist', - ] + parent::getAttributes( $list, $mappings ); + ] + parent::getAttributes( $list ); return $attribs; } @@ -36,7 +36,9 @@ class HTMLComboboxField extends HTMLTextField { function getInputOOUI( $value ) { $disabled = false; $allowedParams = [ 'tabindex' ]; - $attribs = $this->getAttributes( $allowedParams, [ 'tabindex' => 'tabIndex' ] ); + $attribs = OOUI\Element::configFromHtmlAttributes( + $this->getAttributes( $allowedParams ) + ); if ( $this->mClass !== '' ) { $attribs['classes'] = [ $this->mClass ]; diff --git a/includes/htmlform/HTMLFormField.php b/includes/htmlform/HTMLFormField.php index ebbe3233ee..6e5d6569f5 100644 --- a/includes/htmlform/HTMLFormField.php +++ b/includes/htmlform/HTMLFormField.php @@ -912,45 +912,23 @@ abstract class HTMLFormField { return Linker::tooltipAndAccesskeyAttribs( $this->mParams['tooltip'] ); } - /** - * Get a translated key if necessary. - * @param array|null $mappings Array of mappings, 'original' => 'translated' - * @param string $key - * @return string - */ - protected function getMappedKey( $mappings, $key ) { - if ( !is_array( $mappings ) ) { - return $key; - } - - if ( !empty( $mappings[$key] ) ) { - return $mappings[$key]; - } - - return $key; - } - /** * Returns the given attributes from the parameters * * @param array $list List of attributes to get - * @param array $mappings Optional - Key/value map of attribute names to use - * instead of the ones passed in. * @return array Attributes */ - public function getAttributes( array $list, array $mappings = null ) { + public function getAttributes( array $list ) { static $boolAttribs = [ 'disabled', 'required', 'autofocus', 'multiple', 'readonly' ]; $ret = []; foreach ( $list as $key ) { - $mappedKey = $this->getMappedKey( $mappings, $key ); - if ( in_array( $key, $boolAttribs ) ) { if ( !empty( $this->mParams[$key] ) ) { - $ret[$mappedKey] = $mappedKey; + $ret[$key] = ''; } } elseif ( isset( $this->mParams[$key] ) ) { - $ret[$mappedKey] = $this->mParams[$key]; + $ret[$key] = $this->mParams[$key]; } } diff --git a/includes/htmlform/HTMLFormFieldWithButton.php b/includes/htmlform/HTMLFormFieldWithButton.php index 272af15479..bcb07bd1c2 100644 --- a/includes/htmlform/HTMLFormFieldWithButton.php +++ b/includes/htmlform/HTMLFormFieldWithButton.php @@ -19,7 +19,7 @@ class HTMLFormFieldWithButton extends HTMLFormField { protected $mButtonValue; /** @var string $mButtonType Value for the button in this field */ - protected $mButtonFlags = [ 'primary', 'progressive' ]; + protected $mButtonFlags = [ 'progressive' ]; public function __construct( $info ) { if ( isset( $info['buttonclass'] ) ) { @@ -59,7 +59,9 @@ class HTMLFormFieldWithButton extends HTMLFormField { 'type' => $this->mButtonType, 'label' => $this->mButtonValue, 'flags' => $this->mButtonFlags, - ] ); + ] + OOUI\Element::configFromHtmlAttributes( + $this->getAttributes( [ 'disabled', 'tabindex' ] ) + ) ); } /** diff --git a/includes/htmlform/HTMLMultiSelectField.php b/includes/htmlform/HTMLMultiSelectField.php index 251bb051a8..1aaa3e88fa 100644 --- a/includes/htmlform/HTMLMultiSelectField.php +++ b/includes/htmlform/HTMLMultiSelectField.php @@ -72,8 +72,9 @@ class HTMLMultiSelectField extends HTMLFormField implements HTMLNestedFilterable new OOUI\CheckboxInputWidget( [ 'name' => "{$this->mName}[]", 'selected' => $checked, - 'value' => $attribs['value'], - ] + $attribs ), + ] + OOUI\Element::configFromHtmlAttributes( + $attribs + ) ), [ 'label' => $label, 'align' => 'inline', diff --git a/includes/htmlform/HTMLRadioField.php b/includes/htmlform/HTMLRadioField.php index ecca6ff807..12a8a1fd37 100644 --- a/includes/htmlform/HTMLRadioField.php +++ b/includes/htmlform/HTMLRadioField.php @@ -53,7 +53,9 @@ class HTMLRadioField extends HTMLFormField { 'value' => $value, 'options' => $options, 'classes' => 'mw-htmlform-flatlist-item', - ] + $this->getAttributes( [ 'disabled', 'tabindex' ], [ 'tabindex' => 'tabIndex' ] ) ); + ] + OOUI\Element::configFromHtmlAttributes( + $this->getAttributes( [ 'disabled', 'tabindex' ] ) + ) ); } function formatOptions( $options, $value ) { diff --git a/includes/htmlform/HTMLSelectField.php b/includes/htmlform/HTMLSelectField.php index 6191665903..b6ad46c5ae 100644 --- a/includes/htmlform/HTMLSelectField.php +++ b/includes/htmlform/HTMLSelectField.php @@ -45,7 +45,9 @@ class HTMLSelectField extends HTMLFormField { function getInputOOUI( $value ) { $disabled = false; $allowedParams = [ 'tabindex' ]; - $attribs = $this->getAttributes( $allowedParams, [ 'tabindex' => 'tabIndex' ] ); + $attribs = OOUI\Element::configFromHtmlAttributes( + $this->getAttributes( $allowedParams ) + ); if ( $this->mClass !== '' ) { $attribs['classes'] = [ $this->mClass ]; diff --git a/includes/htmlform/HTMLTextAreaField.php b/includes/htmlform/HTMLTextAreaField.php index b05fd7c79a..973f1cb8d8 100644 --- a/includes/htmlform/HTMLTextAreaField.php +++ b/includes/htmlform/HTMLTextAreaField.php @@ -66,10 +66,9 @@ class HTMLTextAreaField extends HTMLFormField { 'autofocus', ]; - $attribs += $this->getAttributes( $allowedParams, [ - 'tabindex' => 'tabIndex', - 'readonly' => 'readOnly', - ] ); + $attribs += OOUI\Element::configFromHtmlAttributes( + $this->getAttributes( $allowedParams ) + ); return new OOUI\TextInputWidget( [ 'id' => $this->mID, diff --git a/includes/htmlform/HTMLTextField.php b/includes/htmlform/HTMLTextField.php index fb7584b7d1..4d5bcabbc8 100644 --- a/includes/htmlform/HTMLTextField.php +++ b/includes/htmlform/HTMLTextField.php @@ -107,11 +107,9 @@ class HTMLTextField extends HTMLFormField { 'type', ]; - $attribs += $this->getAttributes( $allowedParams, [ - 'maxlength' => 'maxLength', - 'readonly' => 'readOnly', - 'tabindex' => 'tabIndex', - ] ); + $attribs += OOUI\Element::configFromHtmlAttributes( + $this->getAttributes( $allowedParams ) + ); $type = $this->getType( $attribs ); diff --git a/includes/installer/MysqlUpdater.php b/includes/installer/MysqlUpdater.php index 57eaffed26..154f7c3498 100644 --- a/includes/installer/MysqlUpdater.php +++ b/includes/installer/MysqlUpdater.php @@ -516,7 +516,7 @@ class MysqlUpdater extends DatabaseUpdater { $prev_title = $row->cur_title; $prev_namespace = $row->cur_namespace; } - $sql = "DELETE FROM $cur WHERE cur_id IN ( " . join( ',', $deleteId ) . ')'; + $sql = "DELETE FROM $cur WHERE cur_id IN ( " . implode( ',', $deleteId ) . ')'; $this->db->query( $sql, __METHOD__ ); $this->output( wfTimestamp( TS_DB ) ); $this->output( "......Deleted " . $this->db->affectedRows() . " records.\n" ); diff --git a/includes/installer/i18n/ar.json b/includes/installer/i18n/ar.json index b6bc521566..4d16d45925 100644 --- a/includes/installer/i18n/ar.json +++ b/includes/installer/i18n/ar.json @@ -155,6 +155,6 @@ "config-help": "مساعدة", "config-help-tooltip": "اضغط للتوسيع", "config-nofile": "لا يمكن العثور على الملف \"$1\". هل حُذف؟", - "mainpagetext": "'''تم تثبيت ميدياويكي بنجاح.'''", + "mainpagetext": "تم تثبيت ميدياويكي بنجاح.", "mainpagedocfooter": "استشر [//meta.wikimedia.org/wiki/Help:Contents دليل المستخدم] لمعلومات حول استخدام برنامج الويكي.\n\n== البداية ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings قائمة إعدادات الضبط]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ أسئلة متكررة حول ميدياويكي]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce القائمة البريدية الخاصة بإصدار ميدياويكي]" } diff --git a/includes/installer/i18n/bn.json b/includes/installer/i18n/bn.json index b3ab3bfecf..c9e96ed1e5 100644 --- a/includes/installer/i18n/bn.json +++ b/includes/installer/i18n/bn.json @@ -128,6 +128,6 @@ "config-install-tables": "টেবিল তৈরি", "config-install-keys": "গোপন কি তৈরি", "config-help": "সাহায্য", - "mainpagetext": "'''মিডিয়াউইকি সফলভাবে ইন্সটল করা হয়েছে।'''", + "mainpagetext": "মিডিয়াউইকি ইনস্টল করা হয়েছে।", "mainpagedocfooter": "কীভাবে উইকি সফটওয়্যারটি ব্যবহারকার করবেন, তা জানতে [//meta.wikimedia.org/wiki/Help:Contents ব্যবহারকারী সহায়িকা] দেখুন।\n\n== কোথা থেকে শুরু করবেন ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings কনফিগারেশন সেটিংস তালিকা]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ প্রশ্নোত্তরে মিডিয়াউইকি]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce মিডিয়াউইকি মুক্তির মেইলিং লিস্ট]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources আপনার ভাষার জন্য মিডিয়াউইকি স্থানীয়করণ করুন]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam আপনার উইকিতে স্প্যামের সাথে লড়াই করার উপায় সম্পর্কে জানুন]" } diff --git a/includes/installer/i18n/cs.json b/includes/installer/i18n/cs.json index de1f03f0ee..ddbaa19675 100644 --- a/includes/installer/i18n/cs.json +++ b/includes/installer/i18n/cs.json @@ -7,7 +7,8 @@ "아라", "Matěj Grabovský", "Paxt", - "Matěj Suchánek" + "Matěj Suchánek", + "LordMsz" ] }, "config-desc": "Instalační program pro MediaWiki", @@ -315,12 +316,12 @@ "config-install-mainpage": "Vytváří se počáteční obsah hlavní strany", "config-install-extension-tables": "Vytvářejí se tabulky pro zapnutá rozšíření", "config-install-mainpage-failed": "Nepodařilo se vložit hlavní stranu: $1", - "config-install-done": "'''Gratulujeme!'''\nÚspěšně jste nainstalovali MediaWiki.\n\nInstalátor vytvořil soubor LocalSettings.php.\nTen obsahuje veÅ¡kerou vaÅ¡i konfiguraci.\n\nBudete si ho muset stáhnout a uložit do základního adresáře vaší instalace wiki (do stejného adresáře jako soubor index.php). Stažení souboru se mělo spustit automaticky.\n\nPokud se vám stažení nenabídlo nebo jste ho zruÅ¡ili, můžete ho spustit znovu kliknutím na následující odkaz:\n\n$3\n\n'''Poznámka''': Pokud to neuděláte hned, tento vygenerovaný konfigurační soubor nebude později dostupný, pokud instalaci opustíte, aniž byste si ho stáhli.\n\nAž to dokončíte, můžete '''[$2 vstoupit do své wiki]'''.", + "config-install-done": "Gratulujeme!\nNainstalovali jste MediaWiki.\n\nInstalátor vytvořil soubor LocalSettings.php.\nTen obsahuje veÅ¡kerou vaÅ¡i konfiguraci.\n\nBudete si ho muset stáhnout a uložit do základního adresáře vaší instalace wiki (do stejného adresáře jako soubor index.php). Stažení souboru se mělo spustit automaticky.\n\nPokud se vám stažení nenabídlo nebo jste ho zruÅ¡ili, můžete ho spustit znovu kliknutím na následující odkaz:\n\n$3\n\nPoznámka: Pokud to neuděláte hned, tento vygenerovaný konfigurační soubor nebude později dostupný, pokud instalaci opustíte, aniž byste si ho stáhli.\n\nAž to dokončíte, můžete [$2 vstoupit do své wiki].", "config-download-localsettings": "Stáhnout LocalSettings.php", "config-help": "nápověda", "config-help-tooltip": "rozbalíte kliknutím", "config-nofile": "Soubor „$1“ nelze nalézt. Byl smazán?", "config-extension-link": "Věděli jste, že vaÅ¡e wiki podporuje [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions rozšíření]?\n\nMůžete si prohlédnout [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category seznam rozšíření po kategoriích].", - "mainpagetext": "'''MediaWiki byla úspěšně nainstalována.'''", + "mainpagetext": "MediaWiki byla úspěšně nainstalována.", "mainpagedocfooter": "[//meta.wikimedia.org/wiki/Help:Contents Uživatelská příručka] vám napoví, jak používat MediaWiki.\n\n== Začínáme ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Nastavení konfigurace]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Často kladené otázky o MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce E-mailová konference oznámení MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Překlad MediaWiki do vaÅ¡eho jazyka]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Naučte se bojovat se spamem na vaší wiki]" } diff --git a/includes/installer/i18n/de.json b/includes/installer/i18n/de.json index 419b98658a..2516a16e2d 100644 --- a/includes/installer/i18n/de.json +++ b/includes/installer/i18n/de.json @@ -329,6 +329,6 @@ "config-help-tooltip": "Zum Expandieren klicken", "config-nofile": "Die Datei „$1“ konnte nicht gefunden werden. Wurde sie gelöscht?", "config-extension-link": "Wusstest du, dass dein Wiki die Nutzung von [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions Erweiterungen] unterstützt?\n\nDu kannst [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category Erweiterungen nach Kategorie] durchsuchen.", - "mainpagetext": "'''MediaWiki wurde erfolgreich installiert.'''", + "mainpagetext": "MediaWiki wurde installiert.", "mainpagedocfooter": "Hilfe zur Benutzung und Konfiguration der Wiki-Software findest du im [//meta.wikimedia.org/wiki/Help:Contents Benutzerhandbuch].\n\n== Starthilfen ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsvariablen]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki-FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Mailingliste neuer MediaWiki-Versionen]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Lokalisiere MediaWiki für deine Sprache]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Erfahre, wie du Spam auf deinem Wiki bekämpfen kannst]" } diff --git a/includes/installer/i18n/en.json b/includes/installer/i18n/en.json index 865447ae71..6fa59306b4 100644 --- a/includes/installer/i18n/en.json +++ b/includes/installer/i18n/en.json @@ -307,12 +307,12 @@ "config-install-mainpage": "Creating main page with default content", "config-install-extension-tables": "Creating tables for enabled extensions", "config-install-mainpage-failed": "Could not insert main page: $1", - "config-install-done": "Congratulations!\nYou have successfully installed MediaWiki.\n\nThe installer has generated a LocalSettings.php file.\nIt contains all your configuration.\n\nYou will need to download it and put it in the base of your wiki installation (the same directory as index.php). The download should have started automatically.\n\nIf the download was not offered, or if you cancelled it, you can restart the download by clicking the link below:\n\n$3\n\nNote: If you do not do this now, this generated configuration file will not be available to you later if you exit the installation without downloading it.\n\nWhen that has been done, you can [$2 enter your wiki].", + "config-install-done": "Congratulations!\nYou have installed MediaWiki.\n\nThe installer has generated a LocalSettings.php file.\nIt contains all your configuration.\n\nYou will need to download it and put it in the base of your wiki installation (the same directory as index.php). The download should have started automatically.\n\nIf the download was not offered, or if you cancelled it, you can restart the download by clicking the link below:\n\n$3\n\nNote: If you do not do this now, this generated configuration file will not be available to you later if you exit the installation without downloading it.\n\nWhen that has been done, you can [$2 enter your wiki].", "config-download-localsettings": "Download LocalSettings.php", "config-help": "help", "config-help-tooltip": "click to expand", "config-nofile": "File \"$1\" could not be found. Has it been deleted?", "config-extension-link": "Did you know that your wiki supports [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions extensions]?\n\nYou can browse [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensions by category] or the [//www.mediawiki.org/wiki/Extension_Matrix Extension Matrix] to see the full list of extensions.", - "mainpagetext": "MediaWiki has been successfully installed.", + "mainpagetext": "MediaWiki has been installed.", "mainpagedocfooter": "Consult the [//meta.wikimedia.org/wiki/Help:Contents User's Guide] for information on using the wiki software.\n\n== Getting started ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Configuration settings list]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki]" } diff --git a/includes/installer/i18n/fi.json b/includes/installer/i18n/fi.json index 223955d4d3..a083210f31 100644 --- a/includes/installer/i18n/fi.json +++ b/includes/installer/i18n/fi.json @@ -17,7 +17,8 @@ "SMAUG", "SuperPete", "McSalama", - "Jaakkoh" + "Jaakkoh", + "Mikahama" ] }, "config-desc": "MediaWiki-asennin", @@ -121,6 +122,12 @@ "config-type-sqlite": "SQLite", "config-type-oracle": "Oracle", "config-type-mssql": "Microsoft SQL Server", + "config-support-info": "MediaWiki tukee seuraavia tietokantajärjestelmiä:\n\n$1\n\nJos et näe tietokantajärjestelmää, jota yrität käyttää, listattuna alhaalla, seuraa yläpuolella olevia ohjeita tuen aktivoimiseksi.", + "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] on MediaWikin ensisijainen kohde ja se on myös parhaiten tuettu. MediaWiki voi myös käyttää [{{int:version-db-mariadb-url}} MariaDB]- sekä [{{int:version-db-percona-url}} Percona Server]-järjestelmiä, jotka ovat MySQL-yhteensopivia. ([http://www.php.net/manual/en/mysqli.installation.php Miten käännetään PHP MySQL-tuella])", + "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] on suosittu avoimen lähdekoodin tietokantajärjestelmä vaihtoehtona MySQL:lle. Tuessa saattaa olla pieniä puutteita, eikä sitä suositella käytettäväksi tuotantoympäristössä. ([http://www.php.net/manual/en/pgsql.installation.php Kuinka käännetään PHP PostgreSQL-tuella])", + "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] on kevyt tietokantajärjestelmä, jota tuetaan hyvin. ([http://www.php.net/manual/en/pdo.installation.php Miten käännetään PHP SQLite-tuella], käyttää PDO:ta)", + "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] on kaupallinen yritystietokanta. ([http://www.php.net/manual/en/oci8.installation.php Kuinka käännetään PHP OCI8-tuella])", + "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] on kaupallinen yritystietokanta Windowsille. ([http://www.php.net/manual/en/sqlsrv.installation.php Miten käännetään PHP SQLSRV-tuella])", "config-header-mysql": "MySQL-asetukset", "config-header-postgres": "PostgreSQL-asetukset", "config-header-sqlite": "SQLite-asetukset", @@ -130,9 +137,12 @@ "config-missing-db-name": "\"{{int:config-db-name}}\" on pakollinen.", "config-missing-db-host": "\"{{int:config-db-host}}\" on pakollinen.", "config-missing-db-server-oracle": "\"{{int:config-db-host-oracle}}\" on pakollinen.", + "config-invalid-db-server-oracle": "Virheellinen tietokanta TNS \"$1\".\nKäytä joko \"TNS Name\"- tai \"Easy Connect\" -tekstiä\n([http://docs.oracle.com/cd/E11882_01/network.112/e10836/naming.htm Oracle metodien nimeäminen]).", "config-invalid-db-name": "”$1” ei kelpaa tietokannan nimeksi.\nKäytä ainoastaan kirjaimia (a-z, A-Z), numeroita (0-9), alaviivoja (_) ja tavuviivoja (-).", "config-invalid-db-prefix": "”$1” ei kelpaa tietokannan etuliitteeksi.\nKäytä ainoastaan kirjaimia (a-z, A-Z), numeroita (0-9), alaviivoja (_) ja tavuviivoja (-).", "config-connection-error": "$1.\n\nTarkista isäntä, käyttäjänimi, salasana ja yritä uudestaan.", + "config-invalid-schema": "Virheellinen skeema MediaWikille \"$1\".\nKäytä pelkkiä ASCII-kirjaimia (a-z, A-Z), numeroita (0-9) ja alaviivoja (_).", + "config-db-sys-create-oracle": "Asennusohjelma tukee ainoastaan SYSDBA-tunnuksen käyttämistä uuden tunnuksen luonnissa.", "config-postgres-old": "MediaWiki tarvitsee PostgreSQL:n version $1 tai uudemman. Nykyinen versio on $2.", "config-mssql-old": "Vaaditaan Microsoft SQL Server $1 tai uudempi. Sinulla on käytössä $2.", "config-sqlite-name-help": "Valitse nimi, joka yksilöi tämän wikin.\nÄlä käytä välilyöntejä tai viivoja.\nNimeä käytetään SQLite-tietokannan tiedostonimessä.", diff --git a/includes/installer/i18n/fr.json b/includes/installer/i18n/fr.json index 5455cb725f..60fe204a5b 100644 --- a/includes/installer/i18n/fr.json +++ b/includes/installer/i18n/fr.json @@ -22,7 +22,8 @@ "Wladek92", "Scoopfinder", "Seb35", - "Linedwell" + "Linedwell", + "Orlodrim" ] }, "config-desc": "Le programme d’installation de MediaWiki", @@ -330,12 +331,12 @@ "config-install-mainpage": "Création de la page principale avec un contenu par défaut", "config-install-extension-tables": "Création de tables pour les extensions activées", "config-install-mainpage-failed": "Impossible d’insérer la page principale : $1", - "config-install-done": "'''Félicitations!'''\nVous avez réussi à installer MediaWiki.\n\nLe programme d'installation a généré un fichier LocalSettings.php. Il contient tous les paramètres de votre configuration.\n\nVous devrez le télécharger et le mettre à la racine de votre installation wiki (dans le même répertoire que index.php). Le téléchargement démarre automatiquement.\n\nSi le téléchargement n'a pas été offert, ou que vous l'avez annulé, vous pouvez démarrer à nouveau le téléchargement en cliquant ce lien :\n\n$3\n\n'''Note''': Si vous ne le faites pas maintenant, ce fichier de configuration généré ne sera pas disponible plus tard si vous quittez l'installation sans le télécharger.\n\nLorsque c'est fait, vous pouvez '''[$2 accéder à votre wiki]'''.", + "config-install-done": "'''Félicitations!'''\nVous avez installé MediaWiki.\n\nLe programme d'installation a généré un fichier LocalSettings.php. Il contient tous les paramètres de votre configuration.\n\nVous devrez le télécharger et le mettre à la racine de votre installation wiki (dans le même répertoire que index.php). Le téléchargement démarre automatiquement.\n\nSi le téléchargement n'a pas été offert, ou que vous l'avez annulé, vous pouvez démarrer à nouveau le téléchargement en cliquant ce lien :\n\n$3\n\n'''Note''': Si vous ne le faites pas maintenant, ce fichier de configuration généré ne sera pas disponible plus tard si vous quittez l'installation sans le télécharger.\n\nLorsque c'est fait, vous pouvez '''[$2 accéder à votre wiki]'''.", "config-download-localsettings": "Télécharger LocalSettings.php", "config-help": "aide", "config-help-tooltip": "cliquer pour agrandir", "config-nofile": "Le fichier « $1 » est introuvable. A-t-il été supprimé ?", "config-extension-link": "Saviez-vous que votre wiki supporte [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions des extensions] ?\n\nVous pouvez consulter les [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensions par catégorie].", - "mainpagetext": "MediaWiki a été installé avec succès.", + "mainpagetext": "MediaWiki a été installé.", "mainpagedocfooter": "Consultez le [//meta.wikimedia.org/wiki/Help:Contents/fr Guide de l’utilisateur] pour plus d’informations sur l’utilisation de ce logiciel de wiki.\n\n== Pour démarrer ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste des paramètres de configuration]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/fr Questions courantes sur MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Liste de discussion sur les distributions de MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Adaptez MediaWiki dans votre langue]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Apprendre comment lutter contre le pourriel dans votre wiki]" } diff --git a/includes/installer/i18n/gl.json b/includes/installer/i18n/gl.json index 37b1b0af41..7b9abdbc01 100644 --- a/includes/installer/i18n/gl.json +++ b/includes/installer/i18n/gl.json @@ -313,12 +313,12 @@ "config-install-mainpage": "Creando a páxina principal co contido por defecto", "config-install-extension-tables": "Creando as táboas para as extensións activadas", "config-install-mainpage-failed": "Non se puido inserir a páxina principal: $1", - "config-install-done": "Parabéns!\nInstalou correctamente MediaWiki.\n\nO programa de instalación xerou un ficheiro LocalSettings.php.\nEste ficheiro contén toda a súa configuración.\n\nTerá que descargalo e poñelo na base da instalación do seu wiki (no mesmo directorio ca index.php). A descarga debería comezar automaticamente.\n\nSe non comezou a descarga ou se a cancelou, pode facer que comece de novo premendo na ligazón que aparece a continuación:\n\n$3\n\nNota: Se non fai iso agora, este ficheiro de configuración xerado non estará dispoñible máis adiante se sae da instalación sen descargalo.\n\nCando faga todo isto, xa poderá [$2 entrar no seu wiki].", + "config-install-done": "Parabéns!\nInstalou MediaWiki.\n\nO programa de instalación xerou un ficheiro LocalSettings.php.\nEste ficheiro contén toda a súa configuración.\n\nTerá que descargalo e poñelo na base da instalación do seu wiki (no mesmo directorio ca index.php). A descarga debería comezar automaticamente.\n\nSe non comezou a descarga ou se a cancelou, pode facer que comece de novo premendo na ligazón que aparece a continuación:\n\n$3\n\nNota: Se non fai iso agora, este ficheiro de configuración xerado non estará dispoñible máis adiante se sae da instalación sen descargalo.\n\nCando faga todo isto, xa poderá [$2 entrar no seu wiki].", "config-download-localsettings": "Descargar o LocalSettings.php", "config-help": "axuda", "config-help-tooltip": "prema para expandir", "config-nofile": "Non se puido atopar o ficheiro \"$1\". Se cadra, foi borrado.", "config-extension-link": "Sabía que o seu wiki soporta [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions extensións]?\n\nPode explorar as [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensións por categoría] ou a [//www.mediawiki.org/wiki/Extension_Matrix matriz de extensións] para ollar a lista completa de extensións.", - "mainpagetext": "MediaWiki instalouse correctamente.", + "mainpagetext": "Instalouse MediaWiki.", "mainpagedocfooter": "Consulte a [//meta.wikimedia.org/wiki/Help:Contents guía de usuario] para obter máis información sobre como usar o software wiki.\n\n== Primeiros pasos ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista das opcións de configuración]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Preguntas máis frecuentes sobre MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Lista de correo dos lanzamentos de MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localice MediaWiki á súa lingua]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Aprenda como combater a publicidade na súa wiki]" } diff --git a/includes/installer/i18n/is.json b/includes/installer/i18n/is.json index f524b3276f..2909e52649 100644 --- a/includes/installer/i18n/is.json +++ b/includes/installer/i18n/is.json @@ -22,17 +22,47 @@ "config-page-releasenotes": "Athugasemdir með útgáfu", "config-page-copying": "Afritun", "config-page-upgradedoc": "Uppfærsla", + "config-page-existingwiki": "Fyrirliggjandi wiki", + "config-restart": "Já, endurræsa", "config-env-php": "PHP $1 er uppsett.", "config-env-hhvm": "HHVM $1 er uppsett.", + "config-diff3-bad": "GNU diff3 fannst ekki.", + "config-db-type": "Tegund gagnagrunns:", + "config-db-host": "Netþjónn gagnagrunns:", + "config-db-name": "Heiti gagnagrunns:", + "config-db-name-oracle": "Gagnagrunnsskema:", + "config-db-username": "Notandanafn á gagnagrunni:", + "config-db-password": "Lykilorð gagnagrunns:", + "config-type-mysql": "MySQL (eða samhæft)", + "config-type-postgres": "PostgreSQL", + "config-type-sqlite": "SQLite", + "config-type-oracle": "Oracle", + "config-type-mssql": "Microsoft SQL Server", + "config-regenerate": "Endurgera LocalSettings.php →", + "config-show-table-status": "SHOW TABLE STATUS beiðni mistókst!", + "config-db-web-account": "Gagnagrunnsreikningur fyrir vefaðgang", + "config-mysql-engine": "Gagnagrunnshýsing:", + "config-mysql-innodb": "InnoDB", + "config-mysql-myisam": "MyISAM", + "config-mysql-charset": "Stafatafla gagnagrunns:", + "config-mysql-binary": "Tvíundakerfis", "config-mysql-utf8": "UTF-8", + "config-mssql-auth": "Tegund auðkenningar:", + "config-mssql-sqlauth": "SQL Server auðkenning", + "config-mssql-windowsauth": "Windows auðkenning", "config-ns-generic": "Verkefni", "config-admin-name": "Notandanafnið þitt:", "config-admin-password": "Lykilorð:", "config-admin-password-confirm": "Lykilorðið aftur:", "config-admin-email": "Tölvupóstfang:", + "config-license": "Höfundaréttur og leyfi", "config-license-pd": "Almenningseign", + "config-extensions": "Viðbætur", + "config-skins": "Skinn", "config-install-step-done": "lokið", "config-install-step-failed": "mistókst", + "config-install-pg-commit": "Virkja breytingar...", + "config-help": "_Hjálp", "mainpagetext": "'''Uppsetning á MediaWiki heppnaðist.'''", "mainpagedocfooter": "Ráðfærðu þig við [//meta.wikimedia.org/wiki/Help:Contents Notandahandbókina] fyrir frekari upplýsingar um notkun wiki-hugbúnaðarins.\n\n== Fyrir byrjendur ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Listi yfir uppsetningarstillingar]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki Algengar spurningar MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Póstlisti MediaWiki-útgáfa]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Læra hvernig á að berjast við amapóst á þínum wiki]" } diff --git a/includes/installer/i18n/it.json b/includes/installer/i18n/it.json index bc707db090..79a1f56c66 100644 --- a/includes/installer/i18n/it.json +++ b/includes/installer/i18n/it.json @@ -321,12 +321,12 @@ "config-install-mainpage": "Creazione della pagina principale con contenuto predefinito", "config-install-extension-tables": "Creazione delle tabelle per le estensioni attivate", "config-install-mainpage-failed": "Impossibile inserire la pagina principale: $1", - "config-install-done": "Complimenti!\nHai installato correttamente MediaWiki.\n\nIl programma di installazione ha generato un file LocalSettings.php che contiene tutte le impostazioni.\n\nDevi scaricarlo ed inserirlo nella directory base del tuo wiki (la stessa dove è presente index.php). Il download dovrebbe partire automaticamente.\n\nSe il download non si avvia, o se è stato annullato, puoi riavviarlo cliccando sul collegamento di seguito:\n\n$3\n\nNota: se esci ora dall'installazione senza scaricare il file di configurazione che è stato generato, questo poi non sarà più disponibile in seguito.\n\nQuando hai fatto, puoi [$2 entrare nel tuo wiki].", + "config-install-done": "Complimenti!\nHai installato MediaWiki.\n\nIl programma di installazione ha generato un file LocalSettings.php che contiene tutte le impostazioni.\n\nDevi scaricarlo ed inserirlo nella directory base del tuo wiki (la stessa dove è presente index.php). Il download dovrebbe partire automaticamente.\n\nSe il download non si avvia, o se è stato annullato, puoi riavviarlo cliccando sul collegamento di seguito:\n\n$3\n\nNota: se esci ora dall'installazione senza scaricare il file di configurazione che è stato generato, questo poi non sarà più disponibile in seguito.\n\nQuando hai fatto, puoi [$2 entrare nel tuo wiki].", "config-download-localsettings": "Scarica LocalSettings.php", "config-help": "aiuto", "config-help-tooltip": "fai clic per espandere", "config-nofile": "Il file \"$1\" non può essere trovato. È stato eliminato?", "config-extension-link": "Sapevi che il tuo wiki supporta le [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions estensioni]?\n\nPuoi navigare tra le [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category estensioni per categoria].", - "mainpagetext": "'''Installazione di MediaWiki completata correttamente.'''", + "mainpagetext": "MediaWiki è stato installato.", "mainpagedocfooter": "Consulta la [//meta.wikimedia.org/wiki/Special:MyLanguage/Help:Contents Guida utente] per maggiori informazioni sull'uso di questo software wiki.\n\n== Per iniziare ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Impostazioni di configurazione]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Domande frequenti su MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Mailing list annunci MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localizza MediaWiki nella tua lingua]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Imparare a combattere lo spam sul tuo wiki]" } diff --git a/includes/installer/i18n/jbo.json b/includes/installer/i18n/jbo.json new file mode 100644 index 0000000000..e52919fbab --- /dev/null +++ b/includes/installer/i18n/jbo.json @@ -0,0 +1,12 @@ +{ + "@metadata": { + "authors": [ + "Xbony2" + ] + }, + "config-information": "lo datni", + "config-page-name": "lo cmene", + "config-page-options": "lo cuxna", + "config-page-install": "lo instale", + "config-page-copying": "nu lo fukpi" +} diff --git a/includes/installer/i18n/ka.json b/includes/installer/i18n/ka.json index 13750e8be0..2163c6f1c8 100644 --- a/includes/installer/i18n/ka.json +++ b/includes/installer/i18n/ka.json @@ -41,6 +41,7 @@ "config-charset-mysql5": "MySQL 4.1/5.0 UTF-8", "config-db-port": "მონაცემთა ბაზის პორტი:", "config-db-schema": "მედიავიკის სქემა:", + "config-type-mssql": "Microsoft SQL Server", "config-header-mysql": "MySQL-ის პარამეტრები", "config-header-postgres": "PostgreSQL-ის პარამეტრები", "config-header-sqlite": "SQLite-ის პარამეტრები", diff --git a/includes/installer/i18n/ksh.json b/includes/installer/i18n/ksh.json index 721e81eafd..b16addda66 100644 --- a/includes/installer/i18n/ksh.json +++ b/includes/installer/i18n/ksh.json @@ -311,12 +311,12 @@ "config-install-mainpage": "Ben de Houpsigg med enem shtandatmääßeje Enhald aam aanlääje", "config-install-extension-tables": "Ben Datebangk-Tabälle för de Zohsazprojramme aam ennreschte", "config-install-mainpage-failed": "Kunnt de Houpsigg nit afshpeishere: $1", - "config-install-done": "'''Jlöckwonsch!'''\nMediaWiki es jetz enstalleet.\n\nEt Projramm zom Enreeschte hät en Dattei LocalSettings.php aanjelaat.\nDoh sin de Enstellunge vum Wiki dren.\n\nDo weeß se eronge laade möße un dann en dem Wiki sing Aanfangsverzeishnes donn möße, et sellve Verzeisneß, woh di Dattei index.php dren litt. Dat Erongerlaade sullt automattesch aanjefange han.\n\nWann domet jet nit jeflupp hät, udder De di Dattei norr_ens han wells, donn op dä Lengk heh dronger klecke:\n\n$3\n\n'''Opjepaß''': Wann De dat jez nit deihß, es alles verschött, wat De bes jöz enjejovve häs, weil di Dattei fott es en däm Momang, woh heh dat Projamm aam Engk es.\n\nWann De mem Ronger- un widder Huhlaade fäädesh bes, kanns De '''[$2 en Ding Wiki jonn]'''.", + "config-install-done": "Jlöckwonsch!\nMediaWiki es jetz enstalleet.\n\nEt Projramm zom Enreeschte hät en Dattei LocalSettings.php aanjelaat.\nDoh sin de Enstellunge vum Wiki dren.\n\nDo weeß se eronge laade möße un dann en dem Wiki sing Aanfangsverzeishnes donn möße, et sellve Verzeisneß, woh di Dattei index.php dren litt. Dat Erongerlaade sullt automattesch aanjefange han.\n\nWann domet jet nit jeflupp hät, udder De di Dattei norr_ens han wells, donn op dä Lengk heh dronger klecke:\n\n$3\n\nOpjepaß: Wann De dat jez nit deihß, es alles verschött, wat De bes jöz enjejovve häs, weil di Dattei fott es en däm Momang, woh heh dat Projamm aam Engk es.\n\nWann De mem Ronger- un widder Huhlaade fäädesh bes, kanns De [$2 en Ding Wiki jonn].", "config-download-localsettings": "Donn di Dattei LocalSettings.php eronger lahde", "config-help": "Hölp", "config-help-tooltip": "Donn Hölp heh aan däm Plaaz enblände.", "config-nofile": "De Dattei „$1“ ham_mer nit jefonge. Es di fottjeschmeße?", "config-extension-link": "Häs De jewoß, dat et Wiki [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions Zohsazprojramme] hann kann?\n\nDo kanns [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category Zohsazprojramme noh Saachjroppe] söhke udder en de [//www.mediawiki.org/wiki/Extension_Matrix Tabäll met de Zohsazprojramme] kike, öm de kumplätte Leß met de Zohsazprojramme ze krijje.", - "mainpagetext": "'''MehdijaWikki es jäz enschtalleht.'''", + "mainpagetext": "MehdijaWikki es jäz enschtalleht.", "mainpagedocfooter": "Luur en et (änglesche) [//meta.wikimedia.org/wiki/Help:Contents Handbohch] wann De weße wells wi de Wikki-ẞoffwähr jebruch un bedehnt wähde moß.\n\n== För der Aanfang ==\nDat es och all op Änglesch:\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings En leß met müjjelesche Enschtällonge för et MehdijaWikki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Öff jefrooch övver et Mehdijawikki …]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce De Meilengleß met Annköndijonge övver neuje Ußjahbe vum MehdijaWikki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Donn MediaWiki op Ding Schprohch aanpaße]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Loor, wi der der SPAM em Wikki klein hälls]\n\n=== Jrammatek ===\nJeh nohdämm, ovv_et „di {{SITENAME}}“, „dä {{SITENAME}}“ udder „dat {{SITENAME}}“ heiß, moß mer velleijsch en Datteij änndere. Wann „{{SITENAME}}“ med „wikki“ ov „wiki“ ophürt, moß mer nix donn. Bei „dä {{SITENAME}}“ och nit. Söns kütt en di Datteij languages/classes/LanguageKsh.php vör udder henger dä Reihj met „No need add neuter wikis having names ending in -wiki.“ en neuje Reihj eren:\n* för „di {{SITENAME}}“ heijß di:\n*: '{{SITENAME}}' => 'f',\n* för „dat {{SITENAME}}“ heijs et:\n*: '{{SITENAME}}' => 'n',\n\n== Un dann ==\nDonn heh di Sigg ömbenänne un/udder jähje en ääschte Aanfangssigg för heh dat Wikki ußtuusche!\n\nAlles Johde!" } diff --git a/includes/installer/i18n/lt.json b/includes/installer/i18n/lt.json index 71c5231a0a..e7bed0e662 100644 --- a/includes/installer/i18n/lt.json +++ b/includes/installer/i18n/lt.json @@ -13,7 +13,7 @@ "config-localsettings-upgrade": "Aptiktas failas LocalSettings.php. Norėdami patobulinti Å¡ią instaliaciją, praÅ¡ome įvesti reikÅ¡mę $wgUpgradeKey į dėžutę žemiau. JÅ«s rasite ją LocalSettings.php.", "config-localsettings-cli-upgrade": "Aptiktas failas LocalSettings.php. Tam kad patobulinti Å¡ią instaliaciją, praÅ¡ome paleisti update.php.", "config-localsettings-key": "Naujinimo raktas:", - "config-localsettings-badkey": "Raktas, kurį pateikėte, yra neteisingas.", + "config-localsettings-badkey": "Atnaujinimo raktas, kurį pateikėte, yra neteisingas.", "config-upgrade-key-missing": "Aptikta esama MediaWiki instaliacija. Tam kad atnaujinti Å¡ią instaliaciją, praÅ¡ome įraÅ¡yti Å¡ią eilutę LocalSettings.php failo pabaigoje:\n\n$1", "config-localsettings-incomplete": "Esamas failas LocalSettings.php yra nepilnas. Nenustatytas kintamasis $1.\nPraÅ¡ome pakeisti failą LocalSettings.php tai, kad kintamasis bÅ«tų nustatytas ir spauskite „{{int:Config-continue}}“.", "config-localsettings-connection-error": "Buvo susidurta su klaida, kai jungtasi prie duomenų bazės naudojantis nustatymais iÅ¡ LocalSettings.php. PraÅ¡ome pataisyti Å¡iuos nustatymus ir bandyti dar kartą.\n\n$1", @@ -54,6 +54,7 @@ "config-apc": "[http://www.php.net/apc APC] yra įdiegtas", "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] yra įdiegtas", "config-diff3-bad": "GNU diff3 nerastas.", + "config-using-server": "Naudojant serverio pavadinimas „$1“.", "config-using-uri": "Naudojamas serverio URL „$1$2“.", "config-db-type": "Duomenų bazės tipas:", "config-db-host": "Duomenų bazės serveris:", @@ -83,9 +84,11 @@ "config-header-oracle": "Oracle nustatymai", "config-header-mssql": "„Microsoft“ SQL serverio nustatymai", "config-invalid-db-type": "Neteisingas duomenų bazės tipas", + "config-missing-db-name": "Privalote įvesti „{{int:config-db-name}}“ reikÅ¡mę.", "config-missing-db-host": "Privalote įvesti „{{int:config-db-host}}“ reikÅ¡mę.", "config-missing-db-server-oracle": "Privalote įvesti „{{int:config-db-host-oracle}}“ reikÅ¡mę.", "config-postgres-old": "PostgreSQL $1 ar vėlesnė yra reikalinga. JÅ«s turite $2.", + "config-sqlite-cant-create-db": "Nepavyko sukurti duomenų bazės failo $1.", "config-regenerate": "Pergeneruoti LocalSettings.php →", "config-db-web-account-same": "Naudoti tą pačią paskyrą kaip ir įdiegimui", "config-db-web-create": "Sukurti paskyrą, jeigu jos nėra", diff --git a/includes/installer/i18n/mg.json b/includes/installer/i18n/mg.json index 391623367b..e576e2ff54 100644 --- a/includes/installer/i18n/mg.json +++ b/includes/installer/i18n/mg.json @@ -13,12 +13,15 @@ "config-localsettings-badkey": "Diso ilay lakile fanavaozana natsofokao.", "config-session-error": "Hadisoana teo am-panombohana ny fidirana : $1", "config-your-language": "Ny fiteninao :", + "config-your-language-help": "Hifidy ny teny ilaina amin'ny piraosesy fametrahana.", "config-wiki-language": "Fiteny ho ampiasain'ny wiki :", + "config-wiki-language-help": "Hifidy ny teny hanoratana ny votoatin'ny wiki.", "config-back": "← Miverina", "config-continue": "Manohy →", "config-page-language": "Fiteny", "config-page-welcome": "Tonga soa eto amin'i MediaWiki !", "config-page-dbconnect": "Hiditra eo amin'i banky angona", + "config-page-upgrade": "Hanavao ny fametrahana efa misy", "config-page-dbsettings": "Parametatry ny banky angona", "config-page-name": "Anarana", "config-page-options": "Safidy", @@ -33,6 +36,8 @@ "config-help-restart": "Tianao hofafana avokoa ve ny data voaangona natsofokao ary hamerina ny fizotran'ny fametrahana ?", "config-restart": "Eny, avereno atao", "config-welcome": "=== Fanamarinana mikasika ny tontolo ===\nNy fanamarihana tsotsotra dia atao hijerena raha mety ho ana rindrankajy Mediawiki ny tontolo.\nTadidio ny mametraka ireto torohay ireo raha mitady fanohanana mikasika ny fomba famaranana ny fametrahana ianao.", + "config-copyright": "== Zom-pamorona ary fepetra ==\n\n$1\n\n\nIo fandaharana dia rindrambaiko maimaim-poana; dia afaka zarazarain ary ovaina araka ny fepetra ao amin'ny GNU General Public License navoakan'ny Free Software Foundation; na versiona 2 ao amin'ny lisansa, na (araka ny safidinao) versiona tatỳ aoriana.\n\nIo fandaharaa io dia zaraina amin'ny fanantenana fa ho ilaina, anefa kosa dia tsy misy fiantohana; tsy misy fiantohana mikasika ny fivarotana azy na famendrehana ho azo ampiasaina amin'ny tranga iray manokana.\nJereo ny GNU General Public License hahazoana zavatra amin'ny antsipiriany.\n\nIanao dia tokony nandray kôpian'nyGNU General Public License miaraka amin'ny fandaharana ity; raha tsy izany, manorata any amin'ny Free Software Foundation, Inc., 51 Franklin Street, Fahadimy Floor, Boston, MA 02110-1301, USA, na [http://www.gnu.org/copyleft/gpl.html vakio ao amin'ny Internet izany].", + "config-sidebar": "* [//www.mediawiki.org MediaWiki fandraisana]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Torolalan'ny mampiasa]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Torolalan'ny mpandrindra]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Fanontaniana mipetraka matetika]\n----\n* Vakio aho\n* Naoty famoahana\n* Fandikàna\n* Fampihatsaràna", "config-env-good": "Voamarina ny tontolo.\nAfaka apetrakao i MediaWiki.", "config-env-bad": "Voamarina ny tontolo.\nTsy afaka mametraka an'i MediaWiki ianao.", "config-env-php": "Misy ato PHP $1.", @@ -41,13 +46,17 @@ "config-unicode-pure-php-warning": "Fampitandremana: Ny [http://pecl.php.net/intl itatra PECL intl] dia tsy misy mba hahazakana ny fampifenerana Unicode, ka mitontona amin'ny implementasiona PHP ranoray noho ny tsifisiany.\nRaha hametraka tranonkala be mpamangy ianao dia tokony mamaky ny [//www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations ''Unicode normalization''] (amin'ny teny anglisy)", "config-db-username": "Anaram-pikamban'ny banky angona :", "config-db-password": "Tenimiafin'ny banky angona :", + "config-db-prefix": "Tovom-banky angona:", "config-charset-mysql5-binary": "roafototra MySQL 4.1/5.0", "config-charset-mysql5": "MySQL 4.1/5.0 UTF-8", "config-charset-mysql4": "MySQL 4.0 miverinjaka UTF-8", "config-db-port": "Seranam-banky angona:", "config-header-mysql": "Parametatr'i MySQL", + "config-header-postgres": "Parametatra PostgreSQL", "config-header-sqlite": "Parametatr'i SQLite", "config-header-oracle": "Parametatr'i Oracle", + "config-header-mssql": "Parametatry ny lohamilina Microsoft SQL Server", + "config-invalid-db-type": "Karazana banky angona tsy ekena.", "config-mysql-innodb": "innoDB", "config-mysql-myisam": "MyISAM", "config-ns-generic": "Tetikasa", diff --git a/includes/installer/i18n/pl.json b/includes/installer/i18n/pl.json index cd75013746..6251c03eb2 100644 --- a/includes/installer/i18n/pl.json +++ b/includes/installer/i18n/pl.json @@ -84,7 +84,6 @@ "config-xcache": "[Http://trac.lighttpd.net/xcache/ XCache] jest zainstalowany", "config-apc": "[Http://www.php.net/apc APC] jest zainstalowany", "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] jest zainstalowany", - "config-no-cache": "'''Uwaga:''' Pamięć podręczna dla kodu MediaWiki nie będzie uruchomiona., gdyż nie ma żadnego z następujących narzędzi: [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] lub [http://www.iis.net/download/WinCacheForPhp WinCache].", "config-mod-security": "''' Ostrzeżenie ''': Serwer sieci web ma włączone [http://modsecurity.org/ mod_security]. Jeśli jest niepoprawnie skonfigurowane, może być przyczyną problemów MediaWiki lub innego oprogramowania, które pozwala użytkownikom na wysyłanie dowolnej zawartości.\nSprawdź w [http://modsecurity.org/documentation/ dokumentacji mod_security] lub skontaktuj się z obsługa hosta, jeśli wystąpią losowe błędy.", "config-diff3-bad": "Nie znaleziono GNU diff3.", "config-git": "Znaleziono oprogramowanie kontroli wersji Git: $1.", @@ -331,6 +330,6 @@ "config-help-tooltip": "kliknij, aby rozwinąć", "config-nofile": "Nie udało się odnaleźć pliku \"$1\". Czy nie został usunięty?", "config-extension-link": "Czy wiesz, że twoja wiki obsługuje [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions/pl rozszerzenia]?\n\nMożesz przejrzeć [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category rozszerzenia według kategorii] lub [//www.mediawiki.org/wiki/Extension_Matrix Extension Matrix] aby zobaczyć pełną listę rozszerzeń.", - "mainpagetext": "'''Instalacja MediaWiki powiodła się.'''", + "mainpagetext": "Instalacja MediaWiki powiodła się.", "mainpagedocfooter": "Zobacz [//meta.wikimedia.org/wiki/Help:Contents przewodnik użytkownika], aby uzyskać informacje o działaniu oprogramowania wiki.\n\n== Na początek ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings/pl Lista ustawień konfiguracyjnych]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/pl MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Komunikaty o nowych wersjach MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Przetłumacz MediaWiki na swój język]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam \nDowiedz się, jak walczyć ze spamem na swojej wiki]" } diff --git a/includes/installer/i18n/pt.json b/includes/installer/i18n/pt.json index 896b94b3c3..39ce4b16b5 100644 --- a/includes/installer/i18n/pt.json +++ b/includes/installer/i18n/pt.json @@ -319,12 +319,12 @@ "config-install-mainpage": "A criar a página principal com o conteúdo padrão.", "config-install-extension-tables": "A criar as tabelas das extensões ativadas", "config-install-mainpage-failed": "Não foi possível inserir a página principal: $1", - "config-install-done": "'''Parabéns!'''\nTerminou a instalação do MediaWiki.\n\nO instalador gerou um ficheiro LocalSettings.php.\nEste ficheiro contém todas as configurações.\n\nPrecisa de fazer o download do ficheiro e colocá-lo no diretório de raiz da sua instalação (o mesmo diretório onde está o ficheiro index.php). Este download deverá ter sido iniciado automaticamente.\n\nSe o download não foi iniciado, ou se o cancelou, pode recomeçá-lo clicando o link abaixo:\n\n$3\n\n'''Nota''': Se não fizer isto agora, o ficheiro que foi gerado deixará de estar disponível quando sair do processo de instalação.\n\nDepois de terminar o passo anterior, pode '''[$2 entrar na wiki]'''.", + "config-install-done": "Parabéns!\nTerminou a instalação do MediaWiki.\n\nO instalador gerou um ficheiro LocalSettings.php.\nEste ficheiro contém todas as configurações.\n\nPrecisa de fazer a descarga do ficheiro e colocá-lo no diretório de raiz da sua instalação (o mesmo diretório onde está o ficheiro index.php). Esta descarga deverá ter sido iniciada automaticamente.\n\nSe a descarga não foi iniciada, ou se o cancelou, pode recomeçá-la ao clicar na ligação abaixo:\n\n$3\n\nNota: Se não fizer isto agora, o ficheiro que foi gerado deixará de estar disponível quando sair do processo de instalação.\n\nDepois de terminar o passo anterior, pode [$2 entrar na wiki].", "config-download-localsettings": "Descarga do LocalSettings.php", "config-help": "ajuda", "config-help-tooltip": "clique para expandir", "config-nofile": "Não foi possível encontrar o ficheiro \"$1\". Terá sido apagado?", "config-extension-link": "Sabia que a sua wiki suporta [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions extensões]?\n\nPode procurar [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensões por categoria].", - "mainpagetext": "'''MediaWiki instalado com sucesso.'''", + "mainpagetext": "MediaWiki instalado.", "mainpagedocfooter": "Consulte o [//meta.wikimedia.org/wiki/Help:Contents Guia de Utilizadores] para informações sobre o uso do software wiki.\n\n== Onde começar ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista de opções de configuração]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Perguntas e respostas frequentes sobre o MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Subscreva a lista de divulgação de novas versões do MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Regionalize MediaWiki para seu idioma]" } diff --git a/includes/installer/i18n/sl.json b/includes/installer/i18n/sl.json index 542a38d925..25febaf455 100644 --- a/includes/installer/i18n/sl.json +++ b/includes/installer/i18n/sl.json @@ -182,6 +182,6 @@ "config-install-tables": "Ustvarjanje tabel", "config-download-localsettings": "Prenesi LocalSettings.php", "config-help": "pomoč", - "mainpagetext": "'''Programje MediaWiki je bilo uspeÅ¡no nameščeno.'''", + "mainpagetext": "Programje MediaWiki je bilo nameščeno.", "mainpagedocfooter": "Oglejte si [//meta.wikimedia.org/wiki/Help:Contents UporabniÅ¡ki priročnik] za informacije o uporabi programja wiki.\n\n== Kako začeti ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Seznam konfiguracijskih nastavitev]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Poogsto zastavljena vpraÅ¡anja MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce PoÅ¡tni seznam izdaj MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Prevedite MediaWiki v svoj jezik]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Izvedite, kako se boriti proti smetju na svojem wikiju]" } diff --git a/includes/installer/i18n/uk.json b/includes/installer/i18n/uk.json index edb061b888..07939189e9 100644 --- a/includes/installer/i18n/uk.json +++ b/includes/installer/i18n/uk.json @@ -317,12 +317,12 @@ "config-install-mainpage": "Створення головної сторінки із вмістом за замовчуванням", "config-install-extension-tables": "Створення таблиць для увімкнених розширень", "config-install-mainpage-failed": "Не вдається вставити головну сторінку: $1", - "config-install-done": "'''Вітаємо!'''\nВи успішно встановили MediaWiki.\n\nІнсталятор згенерував файл LocalSettings.php, який містить усі Ваші налаштування.\n\nВам необхідно завантажити його і помістити у кореневу папку Вашої вікі (туди ж, де index.php). Завантаження мало початись автоматично.\n\nЯкщо завантаження не почалось або Ви його скасували, можете заново його почати, натиснувши на посилання внизу:\n\n$3\n\n'''Примітка''': Якщо Ви не зробите цього зараз, цей файл не буде доступним пізніше, коли Ви вийдете з встановлення, не скачавши його.\n\nПісля виконання дій, описаних вище, Ви зможете '''[$2 увійти у свою вікі]'''.", + "config-install-done": "Вітаємо!\nВи успішно встановили MediaWiki.\n\nІнсталятор згенерував файл LocalSettings.php, який містить усі Ваші налаштування.\n\nВам необхідно завантажити його і помістити у кореневу папку Вашої вікі (туди ж, де index.php). Завантаження мало початись автоматично.\n\nЯкщо завантаження не почалось або Ви його скасували, можете заново його почати, натиснувши на посилання внизу:\n\n$3\n\nПримітка: Якщо Ви не зробите цього зараз, цей файл не буде доступним пізніше, коли Ви вийдете з встановлення, не скачавши його.\n\nПісля виконання дій, описаних вище, Ви зможете [$2 увійти у свою вікі].", "config-download-localsettings": "Завантажити LocalSettings.php", "config-help": "допомога", "config-help-tooltip": "натисніть, щоб розгорнути", "config-nofile": "Файл \"$1\" не знайдено. Його видалено?", "config-extension-link": "Чи знаєте ви, що ваше вікі підтримує [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions розширення]?\n\nВи можете переглядати [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category розширення по категорії] або в [//www.mediawiki.org/wiki/Extension_Matrix матрицю розширень] щоб побачити повний список розширень.", - "mainpagetext": "Програмне забезпечення «MediaWiki» успішно встановлене.", + "mainpagetext": "Програмне забезпечення «MediaWiki» встановлено.", "mainpagedocfooter": "Інформацію про роботу з цією вікі можна знайти в [//meta.wikimedia.org/wiki/Help:Contents посібнику користувача].\n\n== Деякі корисні ресурси ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Список налаштувань];\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Часті питання з приводу MediaWiki];\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Розсилка повідомлень про появу нових версій MediaWiki];\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Локалізуйте MediaWiki своєю мовою]" } diff --git a/includes/installer/i18n/zh-hans.json b/includes/installer/i18n/zh-hans.json index adaae3506c..8c6988b757 100644 --- a/includes/installer/i18n/zh-hans.json +++ b/includes/installer/i18n/zh-hans.json @@ -327,12 +327,12 @@ "config-install-mainpage": "正在创建显示默认内容的首页", "config-install-extension-tables": "正在创建已启用扩展程序表", "config-install-mainpage-failed": "无法插入首页:$1", - "config-install-done": "'''恭喜!'''\n您已经成功地安装了MediaWiki。\n\n安装程序已经生成了LocalSettings.php文件,其中包含了您所有的配置。\n\n您需要下载该文件,并将其放在您wiki的根目录(index.php的同级目录)中。稍后下载将自动开始。\n\n如果浏览器没有提示您下载,或者您取消了下载,您可以点击下面的链接重新开始下载:\n\n$3\n\n'''注意''':如果您现在不完成本步骤,而是没有下载便退出了安装过程,此后您将无法获得自动生成的配置文件。\n\n当本步骤完成后,您可以 '''[$2 进入您的wiki]'''。", + "config-install-done": "恭喜!\n您已经安装了MediaWiki。\n\n安装程序已经生成了LocalSettings.php文件,其中包含了您所有的配置。\n\n您需要下载该文件,并将其放在您wiki的根目录(index.php的同级目录)中。稍后下载将自动开始。\n\n如果浏览器没有提示您下载,或者您取消了下载,您可以点击下面的链接重新开始下载:\n\n$3\n\n注意:如果您现在不完成本步骤,而是没有下载便退出了安装过程,此后您将无法获得自动生成的配置文件。\n\n当本步骤完成后,您可以[$2 进入您的wiki]。", "config-download-localsettings": "下载LocalSettings.php", "config-help": "帮助", "config-help-tooltip": "单击展开", "config-nofile": "找不到文件“$1”。它是否已被删除?", "config-extension-link": "您是否知道您的wiki支持[//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions 拓展]?\n您可浏览[//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category 拓展分类]。", - "mainpagetext": "'''已成功安装MediaWiki。'''", + "mainpagetext": "已安装MediaWiki。", "mainpagedocfooter": "请查阅[//meta.wikimedia.org/wiki/Help:Contents 用户指南]以获取使用本wiki软件的信息!\n\n== 入门 ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings MediaWiki配置设置列表]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/zh-hans MediaWiki常见问题]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki发布邮件列表]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources 本地化MediaWiki到您的语言]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam 了解如何在您的wiki上打击破坏]" } diff --git a/includes/jobqueue/JobQueueDB.php b/includes/jobqueue/JobQueueDB.php index aaf9fb048d..479ec3289c 100644 --- a/includes/jobqueue/JobQueueDB.php +++ b/includes/jobqueue/JobQueueDB.php @@ -452,7 +452,6 @@ class JobQueueDB extends JobQueue { * @see JobQueue::doAck() * @param Job $job * @throws MWException - * @return Job|bool */ protected function doAck( Job $job ) { if ( !isset( $job->metadata['id'] ) ) { @@ -476,8 +475,6 @@ class JobQueueDB extends JobQueue { } catch ( DBError $e ) { $this->throwDBException( $e ); } - - return true; } /** diff --git a/includes/jobqueue/JobQueueFederated.php b/includes/jobqueue/JobQueueFederated.php index c127239360..bd832dbcd6 100644 --- a/includes/jobqueue/JobQueueFederated.php +++ b/includes/jobqueue/JobQueueFederated.php @@ -49,7 +49,7 @@ class JobQueueFederated extends JobQueue { /** @var HashRing */ protected $partitionRing; - /** @var array (partition name => JobQueue) reverse sorted by weight */ + /** @var JobQueue[] (partition name => JobQueue) reverse sorted by weight */ protected $partitionQueues = []; /** @var int Maximum number of partitions to try */ @@ -311,7 +311,7 @@ class JobQueueFederated extends JobQueue { throw new MWException( "The given job has no defined partition name." ); } - return $this->partitionQueues[$job->metadata['QueuePartition']]->ack( $job ); + $this->partitionQueues[$job->metadata['QueuePartition']]->ack( $job ); } protected function doIsRootJobOldDuplicate( Job $job ) { diff --git a/includes/mail/EmailNotification.php b/includes/mail/EmailNotification.php index 30907e636c..4f8f6b3da1 100644 --- a/includes/mail/EmailNotification.php +++ b/includes/mail/EmailNotification.php @@ -72,10 +72,13 @@ class EmailNotification { protected $editor; /** + * @deprecated since 1.27 use WatchedItemStore::updateNotificationTimestamp directly + * * @param User $editor The editor that triggered the update. Their notification * timestamp will not be updated(they have already seen it) * @param LinkTarget $linkTarget The link target of the title to update timestamps for * @param string $timestamp Set the update timestamp to this value + * * @return int[] Array of user IDs */ public static function updateWatchlistTimestamp( @@ -83,47 +86,16 @@ class EmailNotification { LinkTarget $linkTarget, $timestamp ) { - global $wgEnotifWatchlist, $wgShowUpdatedMarker; - - if ( !$wgEnotifWatchlist && !$wgShowUpdatedMarker ) { + // wfDeprecated( __METHOD__, '1.27' ); + $config = RequestContext::getMain()->getConfig(); + if ( !$config->get( 'EnotifWatchlist' ) && !$config->get( 'ShowUpdatedMarker' ) ) { return []; } - - $dbw = wfGetDB( DB_MASTER ); - $res = $dbw->select( [ 'watchlist' ], - [ 'wl_user' ], - [ - 'wl_user != ' . intval( $editor->getID() ), - 'wl_namespace' => $linkTarget->getNamespace(), - 'wl_title' => $linkTarget->getDBkey(), - 'wl_notificationtimestamp IS NULL', - ], __METHOD__ + return WatchedItemStore::getDefaultInstance()->updateNotificationTimestamp( + $editor, + $linkTarget, + $timestamp ); - - $watchers = []; - foreach ( $res as $row ) { - $watchers[] = intval( $row->wl_user ); - } - - if ( $watchers ) { - // Update wl_notificationtimestamp for all watching users except the editor - $fname = __METHOD__; - $dbw->onTransactionIdle( - function () use ( $dbw, $timestamp, $watchers, $linkTarget, $fname ) { - $dbw->update( 'watchlist', - [ /* SET */ - 'wl_notificationtimestamp' => $dbw->timestamp( $timestamp ) - ], [ /* WHERE */ - 'wl_user' => $watchers, - 'wl_namespace' => $linkTarget->getNamespace(), - 'wl_title' => $linkTarget->getDBkey(), - ], $fname - ); - } - ); - } - - return $watchers; } /** @@ -149,7 +121,15 @@ class EmailNotification { } // update wl_notificationtimestamp for watchers - $watchers = self::updateWatchlistTimestamp( $editor, $title, $timestamp ); + $config = RequestContext::getMain()->getConfig(); + $watchers = []; + if ( $config->get( 'EnotifWatchlist' ) || $config->get( 'ShowUpdatedMarker' ) ) { + $watchers = WatchedItemStore::getDefaultInstance()->updateNotificationTimestamp( + $editor, + $title, + $timestamp + ); + } $sendEmail = true; // $watchers deals with $wgEnotifWatchlist. diff --git a/includes/media/XCF.php b/includes/media/XCF.php index f8fa2521d3..526b45ef6a 100644 --- a/includes/media/XCF.php +++ b/includes/media/XCF.php @@ -68,21 +68,15 @@ class XCFHandler extends BitmapHandler { # Forge a return array containing metadata information just like getimagesize() # See PHP documentation at: http://www.php.net/getimagesize - $metadata = []; - $metadata[0] = $header['width']; - $metadata[1] = $header['height']; - $metadata[2] = null; # IMAGETYPE constant, none exist for XCF. - $metadata[3] = sprintf( - 'height="%s" width="%s"', $header['height'], $header['width'] - ); - $metadata['mime'] = 'image/x-xcf'; - $metadata['channels'] = null; - $metadata['bits'] = 8; # Always 8-bits per color - - assert( '7 == count($metadata); ' . - '# return array must contains 7 elements just like getimagesize() return' ); - - return $metadata; + return [ + 0 => $header['width'], + 1 => $header['height'], + 2 => null, # IMAGETYPE constant, none exist for XCF. + 3 => "height=\"{$header['height']}\" width=\"{$header['width']}\"", + 'mime' => 'image/x-xcf', + 'channels' => null, + 'bits' => 8, # Always 8-bits per color + ]; } /** diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php index d65e8be59f..d7ba266fea 100644 --- a/includes/parser/Parser.php +++ b/includes/parser/Parser.php @@ -4636,7 +4636,7 @@ class Parser { $anchor = $safeHeadline; $legacyAnchor = $legacyHeadline; if ( isset( $refers[$arrayKey] ) ) { - // @codingStandardsIgnoreStart + // @codingStandardsIgnoreStart for ( $i = 2; isset( $refers["${arrayKey}_$i"] ); ++$i ); // @codingStandardsIgnoreEnd $anchor .= "_$i"; @@ -4645,7 +4645,7 @@ class Parser { $refers[$arrayKey] = true; } if ( $legacyHeadline !== false && isset( $refers[$legacyArrayKey] ) ) { - // @codingStandardsIgnoreStart + // @codingStandardsIgnoreStart for ( $i = 2; isset( $refers["${legacyArrayKey}_$i"] ); ++$i ); // @codingStandardsIgnoreEnd $legacyAnchor .= "_$i"; @@ -4793,7 +4793,7 @@ class Parser { $sections[0] = $sections[0] . $toc . "\n"; } - $full .= join( '', $sections ); + $full .= implode( '', $sections ); if ( $this->mForceTocPosition ) { return str_replace( '', $toc, $full ); diff --git a/includes/registration/ExtensionProcessor.php b/includes/registration/ExtensionProcessor.php index fe9304fb61..7c60aa5af8 100644 --- a/includes/registration/ExtensionProcessor.php +++ b/includes/registration/ExtensionProcessor.php @@ -168,12 +168,12 @@ class ExtensionProcessor implements Processor { $this->extractCredits( $path, $info ); foreach ( $info as $key => $val ) { if ( in_array( $key, self::$globalSettings ) ) { - $this->storeToArray( "wg$key", $val, $this->globals ); + $this->storeToArray( $path, "wg$key", $val, $this->globals ); // Ignore anything that starts with a @ } elseif ( $key[0] !== '@' && !in_array( $key, self::$notAttributes ) && !in_array( $key, self::$creditsAttributes ) ) { - $this->storeToArray( $key, $val, $this->attributes ); + $this->storeToArray( $path, $key, $val, $this->attributes ); } } } @@ -367,14 +367,15 @@ class ExtensionProcessor implements Processor { } /** + * @param string $path * @param string $name * @param array $value * @param array &$array * @throws InvalidArgumentException */ - protected function storeToArray( $name, $value, &$array ) { + protected function storeToArray( $path, $name, $value, &$array ) { if ( !is_array( $value ) ) { - throw new InvalidArgumentException( "The value for '$name' should be an array" ); + throw new InvalidArgumentException( "The value for '$name' should be an array (from $path)" ); } if ( isset( $array[$name] ) ) { $array[$name] = array_merge_recursive( $array[$name], $value ); diff --git a/includes/resourceloader/ResourceLoaderContext.php b/includes/resourceloader/ResourceLoaderContext.php index 6458e71e7e..8e0239a530 100644 --- a/includes/resourceloader/ResourceLoaderContext.php +++ b/includes/resourceloader/ResourceLoaderContext.php @@ -211,6 +211,18 @@ class ResourceLoaderContext { return $this->user; } + /** + * Get a Message object with context set. See wfMessage for parameters. + * + * @since 1.27 + * @param mixed ... + * @return Message + */ + public function msg() { + return call_user_func_array( 'wfMessage', func_get_args() ) + ->inLanguage( $this->getLanguage() ); + } + /** * Get the possibly-cached User object for the specified username * diff --git a/includes/search/SearchMssql.php b/includes/search/SearchMssql.php index 598702d1ed..5e8fb044b6 100644 --- a/includes/search/SearchMssql.php +++ b/includes/search/SearchMssql.php @@ -160,7 +160,7 @@ class SearchMssql extends SearchDatabase { } } - $searchon = $this->db->addQuotes( join( ',', $q ) ); + $searchon = $this->db->addQuotes( implode( ',', $q ) ); $field = $this->getIndexField( $fulltext ); return "$field, $searchon"; } diff --git a/includes/session/BotPasswordSessionProvider.php b/includes/session/BotPasswordSessionProvider.php index 70c771dc31..bbdfdc3f26 100644 --- a/includes/session/BotPasswordSessionProvider.php +++ b/includes/session/BotPasswordSessionProvider.php @@ -120,7 +120,7 @@ class BotPasswordSessionProvider extends ImmutableSessionProviderWithCookie { if ( $missingKeys ) { $this->logger->info( 'Session "{session}": Missing metadata: {missing}', [ 'session' => $info, - 'missing' => join( ', ', $missingKeys ), + 'missing' => implode( ', ', $missingKeys ), ] ); return false; } diff --git a/includes/session/Session.php b/includes/session/Session.php index 21db609090..0fd8fa8a31 100644 --- a/includes/session/Session.php +++ b/includes/session/Session.php @@ -352,7 +352,7 @@ final class Session implements \Countable, \Iterator, \ArrayAccess { $new = true; } if ( is_array( $salt ) ) { - $salt = join( '|', $salt ); + $salt = implode( '|', $salt ); } return new Token( $secret, (string)$salt, $new ); } diff --git a/includes/session/SessionManager.php b/includes/session/SessionManager.php index 81f82439e2..0a304a99b2 100644 --- a/includes/session/SessionManager.php +++ b/includes/session/SessionManager.php @@ -287,7 +287,7 @@ final class SessionManager implements SessionManagerInterface { // Make sure there's exactly one if ( count( $infos ) > 1 ) { throw new \UnexpectedValueException( - 'Multiple empty sessions tied for top priority: ' . join( ', ', $infos ) + 'Multiple empty sessions tied for top priority: ' . implode( ', ', $infos ) ); } elseif ( count( $infos ) < 1 ) { throw new \UnexpectedValueException( 'No provider could provide an empty session!' ); @@ -537,7 +537,7 @@ final class SessionManager implements SessionManagerInterface { \DeferredUpdates::addUpdate( new \SiteStatsUpdate( 0, 0, 0, 0, 1 ) ); # Watch user's userpage and talk page - $user->addWatch( $user->getUserPage(), \WatchedItem::IGNORE_USER_RIGHTS ); + $user->addWatch( $user->getUserPage(), User::IGNORE_USER_RIGHTS ); return true; } @@ -677,7 +677,7 @@ final class SessionManager implements SessionManagerInterface { if ( count( $retInfos ) > 1 ) { $ex = new \OverflowException( - 'Multiple sessions for this request tied for top priority: ' . join( ', ', $retInfos ) + 'Multiple sessions for this request tied for top priority: ' . implode( ', ', $retInfos ) ); $ex->sessionInfos = $retInfos; throw $ex; @@ -974,7 +974,9 @@ final class SessionManager implements SessionManagerInterface { if ( defined( 'MW_NO_SESSION' ) ) { if ( MW_NO_SESSION === 'warn' ) { // Undocumented safety case for converting existing entry points - $this->logger->error( 'Sessions are supposed to be disabled for this entry point' ); + $this->logger->error( 'Sessions are supposed to be disabled for this entry point', [ + 'exception' => new \BadMethodCallException( 'Sessions are disabled for this entry point' ), + ] ); } else { throw new \BadMethodCallException( 'Sessions are disabled for this entry point' ); } diff --git a/includes/skins/SkinTemplate.php b/includes/skins/SkinTemplate.php index d6e0377480..419c4b4653 100644 --- a/includes/skins/SkinTemplate.php +++ b/includes/skins/SkinTemplate.php @@ -248,6 +248,30 @@ class SkinTemplate extends Skin { } + /** + * Wrap the body text with language information and identifiable element + * + * @param Title $title + * @return string html + */ + protected function wrapHTML( $title, $html ) { + # An ID that includes the actual body text; without categories, contentSub, ... + $realBodyAttribs = [ 'id' => 'mw-content-text' ]; + + # Add a mw-content-ltr/rtl class to be able to style based on text direction + # when the content is different from the UI language, i.e.: + # not for special pages or file pages AND only when viewing + if ( !in_array( $title->getNamespace(), [ NS_SPECIAL, NS_FILE ] ) && + Action::getActionName( $this ) === 'view' ) { + $pageLang = $title->getPageViewLanguage(); + $realBodyAttribs['lang'] = $pageLang->getHtmlCode(); + $realBodyAttribs['dir'] = $pageLang->getDir(); + $realBodyAttribs['class'] = 'mw-content-' . $pageLang->getDir(); + } + + return Html::rawElement( 'div', $realBodyAttribs, $html ); + } + /** * initialize various variables and generate the template * @@ -424,22 +448,8 @@ class SkinTemplate extends Skin { $tpl->set( 'sitenotice', $this->getSiteNotice() ); $tpl->set( 'bottomscripts', $this->bottomScripts() ); $tpl->set( 'printfooter', $this->printSource() ); - - # An ID that includes the actual body text; without categories, contentSub, ... - $realBodyAttribs = [ 'id' => 'mw-content-text' ]; - - # Add a mw-content-ltr/rtl class to be able to style based on text direction - # when the content is different from the UI language, i.e.: - # not for special pages or file pages AND only when viewing - if ( !in_array( $title->getNamespace(), [ NS_SPECIAL, NS_FILE ] ) && - Action::getActionName( $this ) === 'view' ) { - $pageLang = $title->getPageViewLanguage(); - $realBodyAttribs['lang'] = $pageLang->getHtmlCode(); - $realBodyAttribs['dir'] = $pageLang->getDir(); - $realBodyAttribs['class'] = 'mw-content-' . $pageLang->getDir(); - } - - $out->mBodytext = Html::rawElement( 'div', $realBodyAttribs, $out->mBodytext ); + // Wrap the bodyText with #mw-content-text element + $out->mBodytext = $this->wrapHTML( $title, $out->mBodytext ); $tpl->setRef( 'bodytext', $out->mBodytext ); $language_urls = $this->getLanguages(); @@ -640,42 +650,33 @@ class SkinTemplate extends Skin { 'active' => false ]; } else { - $useCombinedLoginLink = $this->useCombinedLoginLink(); - $loginlink = $this->getUser()->isAllowed( 'createaccount' ) && $useCombinedLoginLink - ? 'nav-login-createaccount' - : 'pt-login'; - $is_signup = $request->getText( 'type' ) == 'signup'; - - $login_url = [ - 'text' => $this->msg( $loginlink )->text(), - 'href' => self::makeSpecialUrl( 'Userlogin', $returnto ), - 'active' => $title->isSpecial( 'Userlogin' ) - && ( $loginlink == 'nav-login-createaccount' || !$is_signup ), - ]; - $createaccount_url = [ - 'text' => $this->msg( 'pt-createaccount' )->text(), - 'href' => self::makeSpecialUrl( 'Userlogin', "$returnto&type=signup" ), - 'active' => $title->isSpecial( 'Userlogin' ) && $is_signup, - ]; - // No need to show Talk and Contributions to anons if they can't contribute! if ( User::groupHasPermission( '*', 'edit' ) ) { - // Show the text "Not logged in" - $personal_urls['anonuserpage'] = [ - 'text' => $this->msg( 'notloggedin' )->text() - ]; - // Because of caching, we can't link directly to the IP talk and - // contributions pages. Instead we use the special page shortcuts - // (which work correctly regardless of caching). This means we can't - // determine whether these links are active or not, but since major - // skins (MonoBook, Vector) don't use this information, it's not a - // huge loss. + // Because of caching, we can't link directly to the anonymous + // user page (for example [[User:127.0.0.1]]), talk page, and + // contributions pages. Instead we use the special page + // shortcuts (which work correctly regardless of caching). This + // means we can't determine whether these links are active or + // not, but since major skins (MonoBook, Vector) don't use this + // information, it's not a huge loss. + + // Only show (red) link to anon user page if anon users are + // allowed to create that page + if ( User::groupHasPermission( '*', 'createpage' ) ) { + $personal_urls[ 'anonuserpage' ] = [ + 'text' => $this->msg( 'anonuserpage' )->text(), + 'href' => self::makeSpecialUrlSubpage( 'Mypage', false ), + 'active' => false + ]; + } + $personal_urls['anontalk'] = [ 'text' => $this->msg( 'anontalk' )->text(), 'href' => self::makeSpecialUrlSubpage( 'Mytalk', false ), 'active' => false ]; + $personal_urls['anoncontribs'] = [ 'text' => $this->msg( 'anoncontribs' )->text(), 'href' => self::makeSpecialUrlSubpage( 'Mycontributions', false ), @@ -683,11 +684,21 @@ class SkinTemplate extends Skin { ]; } - if ( $this->getUser()->isAllowed( 'createaccount' ) && !$useCombinedLoginLink ) { - $personal_urls['createaccount'] = $createaccount_url; + $is_signup = $request->getText( 'type' ) === 'signup'; + + if ( $this->getUser()->isAllowed( 'createaccount' ) && !( $this->useCombinedLoginLink() ) ) { + $personal_urls[ 'createaccount' ] = [ + 'text' => $this->msg( 'pt-createaccount' )->text(), + 'href' => self::makeSpecialUrl( 'Userlogin', "$returnto&type=signup" ), + 'active' => $title->isSpecial( 'Userlogin' ) && $is_signup, + ]; } - $personal_urls['login'] = $login_url; + $personal_urls['login'] = [ + 'text' => $this->msg( 'pt-login' )->text(), + 'href' => self::makeSpecialUrl( 'Userlogin', $returnto ), + 'active' => $title->isSpecial( 'Userlogin' ) && !$is_signup, + ]; } Hooks::run( 'PersonalUrls', [ &$personal_urls, &$title, $this ] ); diff --git a/includes/specialpage/SpecialPageFactory.php b/includes/specialpage/SpecialPageFactory.php index bc2bb31a6f..8ce480e123 100644 --- a/includes/specialpage/SpecialPageFactory.php +++ b/includes/specialpage/SpecialPageFactory.php @@ -582,31 +582,51 @@ class SpecialPageFactory { * @return string HTML fragment */ public static function capturePath( Title $title, IContextSource $context ) { - global $wgOut, $wgTitle, $wgRequest, $wgUser, $wgLang; - - // Save current globals - $oldTitle = $wgTitle; - $oldOut = $wgOut; - $oldRequest = $wgRequest; - $oldUser = $wgUser; - $oldLang = $wgLang; - - // Set the globals to the current context + global $wgTitle, $wgOut, $wgRequest, $wgUser, $wgLang; + $main = RequestContext::getMain(); + + // Save current globals and main context + $glob = [ + 'title' => $wgTitle, + 'output' => $wgOut, + 'request' => $wgRequest, + 'user' => $wgUser, + 'language' => $wgLang, + ]; + $ctx = [ + 'title' => $main->getTitle(), + 'output' => $main->getOutput(), + 'request' => $main->getRequest(), + 'user' => $main->getUser(), + 'language' => $main->getLanguage(), + ]; + + // Override $wgTitle = $title; $wgOut = $context->getOutput(); $wgRequest = $context->getRequest(); $wgUser = $context->getUser(); $wgLang = $context->getLanguage(); + $main->setTitle( $title ); + $main->setOutput( $context->getOutput() ); + $main->setRequest( $context->getRequest() ); + $main->setUser( $context->getUser() ); + $main->setLanguage( $context->getLanguage() ); // The useful part $ret = self::executePath( $title, $context, true ); - // And restore the old globals - $wgTitle = $oldTitle; - $wgOut = $oldOut; - $wgRequest = $oldRequest; - $wgUser = $oldUser; - $wgLang = $oldLang; + // Restore old globals and context + $wgTitle = $glob['title']; + $wgOut = $glob['output']; + $wgRequest = $glob['request']; + $wgUser = $glob['user']; + $wgLang = $glob['language']; + $main->setTitle( $ctx['title'] ); + $main->setOutput( $ctx['output'] ); + $main->setRequest( $ctx['request'] ); + $main->setUser( $ctx['user'] ); + $main->setLanguage( $ctx['language'] ); return $ret; } diff --git a/includes/specials/SpecialBlock.php b/includes/specials/SpecialBlock.php index 993065586d..625e4aa946 100644 --- a/includes/specials/SpecialBlock.php +++ b/includes/specials/SpecialBlock.php @@ -794,7 +794,7 @@ class SpecialBlock extends FormSpecialPage { WatchAction::doWatch( Title::makeTitle( NS_USER, $target ), $performer, - WatchedItem::IGNORE_USER_RIGHTS + User::IGNORE_USER_RIGHTS ); } diff --git a/includes/specials/SpecialBotPasswords.php b/includes/specials/SpecialBotPasswords.php index 11357fbd0b..bcba190570 100644 --- a/includes/specials/SpecialBotPasswords.php +++ b/includes/specials/SpecialBotPasswords.php @@ -192,6 +192,7 @@ class SpecialBotPasswords extends FormSpecialPage { 'type' => 'textwithbutton', 'label-message' => 'botpasswords-label-appid', 'buttondefault' => $this->msg( 'botpasswords-label-create' )->text(), + 'buttonflags' => [ 'progressive', 'primary' ], 'required' => true, 'size' => BotPassword::APPID_MAXLENGTH, 'maxlength' => BotPassword::APPID_MAXLENGTH, @@ -315,20 +316,21 @@ class SpecialBotPasswords extends FormSpecialPage { public function onSuccess() { $out = $this->getOutput(); + $username = $this->getUser()->getName(); switch ( $this->operation ) { case 'insert': $out->setPageTitle( $this->msg( 'botpasswords-created-title' )->text() ); - $out->addWikiMsg( 'botpasswords-created-body', $this->par ); + $out->addWikiMsg( 'botpasswords-created-body', $this->par, $username ); break; case 'update': $out->setPageTitle( $this->msg( 'botpasswords-updated-title' )->text() ); - $out->addWikiMsg( 'botpasswords-updated-body', $this->par ); + $out->addWikiMsg( 'botpasswords-updated-body', $this->par, $username ); break; case 'delete': $out->setPageTitle( $this->msg( 'botpasswords-deleted-title' )->text() ); - $out->addWikiMsg( 'botpasswords-deleted-body', $this->par ); + $out->addWikiMsg( 'botpasswords-deleted-body', $this->par, $username ); $this->password = null; break; } @@ -337,7 +339,7 @@ class SpecialBotPasswords extends FormSpecialPage { $sep = BotPassword::getSeparator(); $out->addWikiMsg( 'botpasswords-newpassword', - htmlspecialchars( $this->getUser()->getName() . $sep . $this->par ), + htmlspecialchars( $username . $sep . $this->par ), htmlspecialchars( $this->password ) ); $this->password = null; diff --git a/includes/specials/SpecialChangeContentModel.php b/includes/specials/SpecialChangeContentModel.php index 57c6fec5b5..ee9f665bbd 100644 --- a/includes/specials/SpecialChangeContentModel.php +++ b/includes/specials/SpecialChangeContentModel.php @@ -43,6 +43,9 @@ class SpecialChangeContentModel extends FormSpecialPage { } $this->addHelpLink( 'Help:ChangeContentModel' ); + + // T120576 + $form->setSubmitTextMsg( 'changecontentmodel-submit' ); } public function validateTitle( $title ) { diff --git a/includes/specials/SpecialImport.php b/includes/specials/SpecialImport.php index 72f8cca28f..fe1dd985d7 100644 --- a/includes/specials/SpecialImport.php +++ b/includes/specials/SpecialImport.php @@ -581,7 +581,7 @@ class ImportReporter extends ContextSource { * @param array $pageInfo * @return void */ - function reportPage( $title, $foreignTitle, $revisionCount, + public function reportPage( $title, $foreignTitle, $revisionCount, $successCount, $pageInfo ) { $args = func_get_args(); call_user_func_array( $this->mOriginalPageOutCallback, $args ); diff --git a/includes/specials/SpecialLinkSearch.php b/includes/specials/SpecialLinkSearch.php index bc33b8e053..2a7046ecbd 100644 --- a/includes/specials/SpecialLinkSearch.php +++ b/includes/specials/SpecialLinkSearch.php @@ -158,7 +158,7 @@ class LinkSearchPage extends QueryPage { if ( $target != '' ) { $this->setParams( [ - 'query' => $target2, + 'query' => Parser::normalizeLinkUrl( $target2 ), 'namespace' => $namespace, 'protocol' => $protocol ] ); parent::execute( $par ); diff --git a/includes/specials/SpecialUserlogin.php b/includes/specials/SpecialUserlogin.php index 6b61ef99e4..8d45468092 100644 --- a/includes/specials/SpecialUserlogin.php +++ b/includes/specials/SpecialUserlogin.php @@ -714,7 +714,7 @@ class LoginForm extends SpecialPage { DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 0, 0, 0, 1 ) ); // Watch user's userpage and talk page - $u->addWatch( $u->getUserPage(), WatchedItem::IGNORE_USER_RIGHTS ); + $u->addWatch( $u->getUserPage(), User::IGNORE_USER_RIGHTS ); return Status::newGood( $u ); } diff --git a/includes/templates/Userlogin.php b/includes/templates/Userlogin.php index 4a0b413f43..f19c0f2b75 100644 --- a/includes/templates/Userlogin.php +++ b/includes/templates/Userlogin.php @@ -1,7 +1,7 @@ data['formheader'] ) { ?>
- html( 'formheader' ); /* extensions such as MobileFrontend add html here */ ?> + html( 'formheader' ); /* extensions such as MobileFrontend add HTML here */ ?>
-
-
-