'ExternalStoreHttp' => __DIR__ . '/includes/externalstore/ExternalStoreHttp.php',
'ExternalStoreMedium' => __DIR__ . '/includes/externalstore/ExternalStoreMedium.php',
'ExternalStoreMwstore' => __DIR__ . '/includes/externalstore/ExternalStoreMwstore.php',
- 'FSFile' => __DIR__ . '/includes/libs/filebackend/FSFile.php',
+ 'FSFile' => __DIR__ . '/includes/libs/filebackend/fsfile/FSFile.php',
'FSFileBackend' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php',
'FSFileBackendDirList' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php',
'FSFileBackendFileList' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php',
'TableDiffFormatter' => __DIR__ . '/includes/diff/TableDiffFormatter.php',
'TablePager' => __DIR__ . '/includes/pager/TablePager.php',
'TagLogFormatter' => __DIR__ . '/includes/logging/TagLogFormatter.php',
- 'TempFSFile' => __DIR__ . '/includes/libs/filebackend/TempFSFile.php',
+ 'TempFSFile' => __DIR__ . '/includes/libs/filebackend/fsfile/TempFSFile.php',
'TempFileRepo' => __DIR__ . '/includes/filerepo/FileRepo.php',
'TemplateParser' => __DIR__ . '/includes/TemplateParser.php',
'TemplatesOnThisPageFormatter' => __DIR__ . '/includes/TemplatesOnThisPageFormatter.php',
$updates = $content->getSecondaryDataUpdates(
$title, null, $forceRecursiveLinkUpdate, $p_result );
foreach ( $updates as $update ) {
- # Some extensions, like EventBus, need to know the user
- # that performed the purge action, so set it here
- if ( $update instanceof LinksUpdate ) {
- $update->setTriggeringUser( $user );
- }
DeferredUpdates::addUpdate( $update, DeferredUpdates::PRESEND );
}
"apihelp-query+exturlusage-param-prop": "Qué piezas de información incluir:",
"apihelp-query+exturlusage-paramvalue-prop-ids": "Añade el identificado de la página.",
"apihelp-query+exturlusage-paramvalue-prop-title": "Agrega el título y el identificador del espacio de nombres de la página.",
+ "apihelp-query+exturlusage-paramvalue-prop-url": "Añade el URL utilizado en la página.",
"apihelp-query+exturlusage-param-protocol": "Protocolo del URL. Si está vacío y se establece <var>$1query</var>, el protocolo es <kbd>http</kbd>. Deja vacío esto y <var>$1query</var> para listar todos los enlaces externos.",
"apihelp-query+exturlusage-param-limit": "Cuántas páginas se devolverán.",
"apihelp-query+exturlusage-example-simple": "Mostrar páginas que enlacen con <kbd>http://www.mediawiki.org</kbd>.",
"apihelp-emailuser-param-text": "메일 본문.",
"apihelp-emailuser-param-ccme": "자신에게 메일의 복사본을 보냅니다.",
"apihelp-emailuser-example-email": "<kbd>WikiSysop</kbd> 사용자에게 텍스트 <kbd>Content</kbd>로 이메일을 보냅니다.",
- "apihelp-expandtemplates-description": "모든 틀을 위키텍스트로 확장.",
+ "apihelp-expandtemplates-description": "위키텍스트 안에 모든 틀을 확장합니다.",
"apihelp-expandtemplates-param-title": "문서 제목",
"apihelp-expandtemplates-param-text": "변환할 위키텍스트.",
"apihelp-expandtemplates-paramvalue-prop-wikitext": "확장된 위키텍스트.",
"apihelp-query+revisions+base-paramvalue-prop-contentmodel": "판의 콘텐츠 모델 ID.",
"apihelp-query+revisions+base-paramvalue-prop-content": "판의 텍스트.",
"apihelp-query+revisions+base-paramvalue-prop-tags": "판의 태그.",
+ "apihelp-query+search-description": "전문 검색을 수행합니다.",
"apihelp-query+search-param-qiprofile": "쿼리 독립적인 프로파일 사용(순위 알고리즘에 영향있음)",
"apihelp-query+search-paramvalue-prop-size": "바이트 단위로 문서의 크기를 추가합니다.",
"apihelp-query+search-paramvalue-prop-wordcount": "문서의 낱말 수를 추가합니다.",
"apihelp-delete-description": "पान वगळा",
"apihelp-edit-param-minor": "छोटे संपादन",
"apihelp-edit-param-notminor": "छोटे नसलेले संपादन",
+ "apihelp-edit-param-bot": "या संपादनावर सांगकाम्याचे संपादन म्हणून खूण करा.",
"apihelp-edit-example-edit": "पान संपादा",
+ "apihelp-expandtemplates-description": "विकिमजकूरात सर्व साच्यांचा विस्तार करा.",
+ "apihelp-feedcontributions-param-toponly": "केवळ नवीनतम आवर्तने असलेलीच संपादने दाखवा.",
"apihelp-feedrecentchanges-param-categories": "या सर्व वर्गात असलेल्या पानांमधील बदलच फक्त दाखवा.",
"apihelp-feedrecentchanges-param-categories_any": "त्यापेक्षा,या कोणत्याही वर्गांमधील,पानांना झालेले बदलच फक्त दाखवा.",
"apihelp-login-param-name": "सदस्य नाव.",
"PhiLiP",
"Arthur2e5",
"損齋",
- "Myy730"
+ "Myy730",
+ "D41D8CD98F"
]
},
"apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\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 API公告]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 程序错误与功能请求]\n</div>\n<strong>状态信息:</strong>本页所展示的所有特性都应正常工作,但是API仍在开发当中,将会随时变化。请订阅[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 邮件列表]以便获得更新通知。\n\n<strong>错误请求:</strong>当API收到错误请求时,HTTP header将会返回一个包含\"MediaWiki-API-Error\"的值,随后header的值与error code将会送回并设置为相同的值。详细信息请参阅[[mw:API:Errors_and_warnings|API: 错误与警告]]。\n\n<strong>测试中:</strong>测试API请求的易用性,请参见[[Special:ApiSandbox]]。",
"apihelp-query+logevents-paramvalue-prop-ids": "添加日志活动的ID。",
"apihelp-query+logevents-paramvalue-prop-title": "为日志事件添加页面标题。",
"apihelp-query+logevents-paramvalue-prop-type": "添加日志活动的类型。",
- "apihelp-query+logevents-paramvalue-prop-user": "为日志事件添加用户责任。",
- "apihelp-query+logevents-paramvalue-prop-userid": "为日志事件添加对此负责的用户ID。",
+ "apihelp-query+logevents-paramvalue-prop-user": "添加对此日志事件负责的用户。",
+ "apihelp-query+logevents-paramvalue-prop-userid": "添加对此日志事件负责的用户的ID。",
"apihelp-query+logevents-paramvalue-prop-timestamp": "为日志活动添加时间戳。",
"apihelp-query+logevents-paramvalue-prop-comment": "添加日志活动的摘要。",
"apihelp-query+logevents-paramvalue-prop-parsedcomment": "添加被解析的日志活动的摘要。",
"apihelp-query+recentchanges-param-excludeuser": "不要列出此用户的更改。",
"apihelp-query+recentchanges-param-tag": "只列出带此标签的更改。",
"apihelp-query+recentchanges-param-prop": "包含的额外信息束:",
- "apihelp-query+recentchanges-paramvalue-prop-user": "为编辑和标签添加用户责任,如果它们是IP的话。",
+ "apihelp-query+recentchanges-paramvalue-prop-user": "添加造成编辑的用户,并标出它们是否是IP。",
"apihelp-query+recentchanges-paramvalue-prop-userid": "为编辑添加用户ID责任。",
"apihelp-query+recentchanges-paramvalue-prop-comment": "为编辑添加摘要。",
"apihelp-query+recentchanges-paramvalue-prop-parsedcomment": "为编辑添加解析的摘要。",
* or false if populating empty cache fails. Also returns true if MessageCache
* is disabled.
*
- * @param bool|string $code Language to which load messages
- * @param integer $mode Use MessageCache::FOR_UPDATE to skip process cache
+ * @param string $code Language to which load messages
+ * @param integer $mode Use MessageCache::FOR_UPDATE to skip process cache [optional]
* @throws MWException
* @return bool
*/
- function load( $code = false, $mode = null ) {
+ protected function load( $code, $mode = null ) {
if ( !is_string( $code ) ) {
- # This isn't really nice, so at least make a note about it and try to
- # fall back
- wfDebug( __METHOD__ . " called without providing a language code\n" );
- $code = 'en';
+ throw new InvalidArgumentException( "Missing language code" );
}
# Don't do double loading...
}
$alreadyTried[ $langcode ] = true;
}
+ } else {
+ $uckey = null;
}
// Check the CDB cache
continue;
}
- $message = $this->getMsgFromNamespace( $this->getMessagePageName( $code, $uckey ), $code );
+ $message = $this->getMsgFromNamespace(
+ $this->getMessagePageName( $code, $uckey ), $code );
if ( $message !== false ) {
return $message;
return false;
}
- # Try the individual message cache
+ // Try the individual message cache
$titleKey = wfMemcKey( 'messages', 'individual', $title );
- $entry = $this->wanCache->get( $titleKey );
+
+ $curTTL = null;
+ $entry = $this->wanCache->get(
+ $titleKey,
+ $curTTL,
+ [ wfMemcKey( 'messages', $code ) ]
+ );
+ $entry = ( $curTTL >= 0 ) ? $entry : false;
+
if ( $entry ) {
if ( substr( $entry, 0, 1 ) === ' ' ) {
$this->mCache[$code][$title] = $entry;
-
- // The message exists, so make sure a string
- // is returned.
+ // The message exists, so make sure a string is returned
return (string)substr( $entry, 1 );
} elseif ( $entry === '!NONEXISTENT' ) {
$this->mCache[$code][$title] = '!NONEXISTENT';
return false;
} else {
- # Corrupt/obsolete entry, delete it
+ // Corrupt/obsolete entry, delete it
$this->wanCache->delete( $titleKey );
}
}
- # Try loading it from the database
- $revision = Revision::newFromTitle( Title::makeTitle( NS_MEDIAWIKI, $title ) );
+ // Try loading it from the database
+ $dbr = wfGetDB( DB_REPLICA );
+ $cacheOpts = Database::getCacheSetOptions( $dbr );
+ // Use newKnownCurrent() to avoid querying revision/user tables
+ $titleObj = Title::makeTitle( NS_MEDIAWIKI, $title );
+ if ( $titleObj->getLatestRevID() ) {
+ $revision = Revision::newKnownCurrent(
+ $dbr,
+ $titleObj->getArticleID(),
+ $titleObj->getLatestRevID()
+ );
+ } else {
+ $revision = false;
+ }
+
if ( $revision ) {
$content = $revision->getContent();
if ( !$content ) {
$message = false; // negative caching
} else {
$this->mCache[$code][$title] = ' ' . $message;
- $this->wanCache->set( $titleKey, ' ' . $message, $this->mExpiry );
+ $this->wanCache->set( $titleKey, ' ' . $message, $this->mExpiry, $cacheOpts );
}
}
} else {
if ( $message === false ) { // negative caching
$this->mCache[$code][$title] = '!NONEXISTENT';
- $this->wanCache->set( $titleKey, '!NONEXISTENT', $this->mExpiry );
+ $this->wanCache->set( $titleKey, '!NONEXISTENT', $this->mExpiry, $cacheOpts );
}
return $message;
* @param Exception|Throwable $e
*/
public static function rollbackMasterChangesAndLog( $e ) {
- $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ $services = MediaWikiServices::getInstance();
+ if ( $services->isServiceDisabled( 'DBLoadBalancerFactory' ) ) {
+ return; // T147599
+ }
+
+ $lbFactory = $services->getDBLoadBalancerFactory();
if ( $lbFactory->hasMasterChanges() ) {
$logger = LoggerFactory::getInstance( 'Bug56269' );
$logger->warning(
* @return OOUI\CheckboxMultiselectInputWidget
*/
public function getInputOOUI( $value ) {
+ $this->mParent->getOutput()->addModules( 'oojs-ui-widgets' );
+
$attr = $this->getTooltipAndAccessKey();
$attr['id'] = $this->mID;
$attr['name'] = "{$this->mName}[]";
return $status;
}
- /**
- * @var $conn Database
- */
+ /** @var $conn DatabasePostgres */
$conn = $status->value;
if ( $conn->tableExists( 'archive' ) ) {
return $status;
}
- $error = $conn->sourceFile( $conn->getSchemaPath() );
+ $error = $conn->sourceFile( $this->getSchemaPath( $conn ) );
if ( $error !== true ) {
$conn->reportQueryError( $error, 0, '', __METHOD__ );
$conn->rollback( __METHOD__ );
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki supports the following database systems:\n\n$1\n\nIf you do not see the database system you are trying to use listed below, then follow the instructions linked above to enable support.",
"config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] is the primary target for MediaWiki and is best supported. MediaWiki also works with [{{int:version-db-mariadb-url}} MariaDB] and [{{int:version-db-percona-url}} Percona Server], which are MySQL compatible. ([http://www.php.net/manual/en/mysqli.installation.php How to compile PHP with MySQL support])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] is a popular open source database system as an alternative to MySQL. There may be some minor outstanding bugs, and it is not recommended for use in a production environment. ([http://www.php.net/manual/en/pgsql.installation.php How to compile PHP with PostgreSQL support])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] is a popular open source database system as an alternative to MySQL. ([http://www.php.net/manual/en/pgsql.installation.php How to compile PHP with PostgreSQL support])",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] is a lightweight database system that is very well supported. ([http://www.php.net/manual/en/pdo.installation.php How to compile PHP with SQLite support], uses PDO)",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] is a commercial enterprise database. ([http://www.php.net/manual/en/oci8.installation.php How to compile PHP with OCI8 support])",
"config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] is a commercial enterprise database for Windows. ([http://www.php.net/manual/en/sqlsrv.installation.php How to compile PHP with SQLSRV support])",
"config-db-install-account": "Vartotojo paskyra diegimui",
"config-db-username": "Duomenų bazės vartotojo vardas:",
"config-db-password": "Duomenų bazės slaptažodis:",
- "config-db-wiki-account": "Vartotojo paskyra normaliai operacijai",
+ "config-db-wiki-account": "Naudotojo paskyra įprastai operacijai",
"config-db-prefix": "Duomenų bazės lentelės priešdėlis:",
"config-mysql-old": "MySQL $1 ar vėlesnė yra reikalinga. Jūs turite $2.",
"config-db-port": "Duomenų bazės prievadas:",
+++ /dev/null
-<?php
-/**
- * Non-directory file on the file system.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup FileBackend
- */
-
-/**
- * Class representing a non-directory file on the file system
- *
- * @ingroup FileBackend
- */
-class FSFile {
- /** @var string Path to file */
- protected $path;
-
- /** @var string File SHA-1 in base 36 */
- protected $sha1Base36;
-
- /**
- * Sets up the file object
- *
- * @param string $path Path to temporary file on local disk
- */
- public function __construct( $path ) {
- $this->path = $path;
- }
-
- /**
- * Returns the file system path
- *
- * @return string
- */
- public function getPath() {
- return $this->path;
- }
-
- /**
- * Checks if the file exists
- *
- * @return bool
- */
- public function exists() {
- return is_file( $this->path );
- }
-
- /**
- * Get the file size in bytes
- *
- * @return int|bool
- */
- public function getSize() {
- return filesize( $this->path );
- }
-
- /**
- * Get the file's last-modified timestamp
- *
- * @return string|bool TS_MW timestamp or false on failure
- */
- public function getTimestamp() {
- MediaWiki\suppressWarnings();
- $timestamp = filemtime( $this->path );
- MediaWiki\restoreWarnings();
- if ( $timestamp !== false ) {
- $timestamp = wfTimestamp( TS_MW, $timestamp );
- }
-
- return $timestamp;
- }
-
- /**
- * Get an associative array containing information about
- * a file with the given storage path.
- *
- * Resulting array fields include:
- * - fileExists
- * - size (filesize in bytes)
- * - mime (as major/minor)
- * - file-mime (as major/minor)
- * - sha1 (in base 36)
- * - major_mime
- * - minor_mime
- *
- * @param string|bool $ext The file extension, or true to extract it from the filename.
- * Set it to false to ignore the extension. Currently unused.
- * @return array
- */
- public function getProps( $ext = true ) {
- $info = self::placeholderProps();
- $info['fileExists'] = $this->exists();
-
- if ( $info['fileExists'] ) {
- $info['size'] = $this->getSize(); // bytes
- $info['sha1'] = $this->getSha1Base36();
-
- $mime = mime_content_type( $this->path );
- # MIME type according to file contents
- $info['file-mime'] = ( $mime === false ) ? 'unknown/unknown' : $mime;
- # logical MIME type
- $info['mime'] = $mime;
-
- if ( strpos( $mime, '/' ) !== false ) {
- list( $info['major_mime'], $info['minor_mime'] ) = explode( '/', $mime, 2 );
- } else {
- list( $info['major_mime'], $info['minor_mime'] ) = [ $mime, 'unknown' ];
- }
- }
-
- return $info;
- }
-
- /**
- * Placeholder file properties to use for files that don't exist
- *
- * Resulting array fields include:
- * - fileExists
- * - size (filesize in bytes)
- * - mime (as major/minor)
- * - file-mime (as major/minor)
- * - sha1 (in base 36)
- * - major_mime
- * - minor_mime
- *
- * @return array
- */
- public static function placeholderProps() {
- $info = [];
- $info['fileExists'] = false;
- $info['size'] = 0;
- $info['file-mime'] = null;
- $info['major_mime'] = null;
- $info['minor_mime'] = null;
- $info['mime'] = null;
- $info['sha1'] = '';
-
- return $info;
- }
-
- /**
- * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case
- * encoding, zero padded to 31 digits.
- *
- * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36
- * fairly neatly.
- *
- * @param bool $recache
- * @return bool|string False on failure
- */
- public function getSha1Base36( $recache = false ) {
- if ( $this->sha1Base36 !== null && !$recache ) {
- return $this->sha1Base36;
- }
-
- MediaWiki\suppressWarnings();
- $this->sha1Base36 = sha1_file( $this->path );
- MediaWiki\restoreWarnings();
-
- if ( $this->sha1Base36 !== false ) {
- $this->sha1Base36 = Wikimedia\base_convert( $this->sha1Base36, 16, 36, 31 );
- }
-
- return $this->sha1Base36;
- }
-
- /**
- * Get the final file extension from a file system path
- *
- * @param string $path
- * @return string
- */
- public static function extensionFromPath( $path ) {
- $i = strrpos( $path, '.' );
-
- return strtolower( $i ? substr( $path, $i + 1 ) : '' );
- }
-
- /**
- * Get an associative array containing information about a file in the local filesystem.
- *
- * @param string $path Absolute local filesystem path
- * @param string|bool $ext The file extension, or true to extract it from the filename.
- * Set it to false to ignore the extension.
- * @return array
- */
- public static function getPropsFromPath( $path, $ext = true ) {
- $fsFile = new self( $path );
-
- return $fsFile->getProps( $ext );
- }
-
- /**
- * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case
- * encoding, zero padded to 31 digits.
- *
- * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36
- * fairly neatly.
- *
- * @param string $path
- * @return bool|string False on failure
- */
- public static function getSha1Base36FromPath( $path ) {
- $fsFile = new self( $path );
-
- return $fsFile->getSha1Base36();
- }
-}
+++ /dev/null
-<?php
-/**
- * Location holder of files stored temporarily
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup FileBackend
- */
-
-/**
- * This class is used to hold the location and do limited manipulation
- * of files stored temporarily (this will be whatever wfTempDir() returns)
- *
- * @ingroup FileBackend
- */
-class TempFSFile extends FSFile {
- /** @var bool Garbage collect the temp file */
- protected $canDelete = false;
-
- /** @var array Map of (path => 1) for paths to delete on shutdown */
- protected static $pathsCollect = null;
-
- public function __construct( $path ) {
- parent::__construct( $path );
-
- if ( self::$pathsCollect === null ) {
- self::$pathsCollect = [];
- register_shutdown_function( [ __CLASS__, 'purgeAllOnShutdown' ] );
- }
- }
-
- /**
- * Make a new temporary file on the file system.
- * Temporary files may be purged when the file object falls out of scope.
- *
- * @param string $prefix
- * @param string $extension Optional file extension
- * @param string|null $tmpDirectory Optional parent directory
- * @return TempFSFile|null
- */
- public static function factory( $prefix, $extension = '', $tmpDirectory = null ) {
- $ext = ( $extension != '' ) ? ".{$extension}" : '';
-
- $attempts = 5;
- while ( $attempts-- ) {
- $hex = sprintf( '%06x%06x', mt_rand( 0, 0xffffff ), mt_rand( 0, 0xffffff ) );
- if ( !is_string( $tmpDirectory ) ) {
- $tmpDirectory = self::getUsableTempDirectory();
- }
- $path = wfTempDir() . '/' . $prefix . $hex . $ext;
- MediaWiki\suppressWarnings();
- $newFileHandle = fopen( $path, 'x' );
- MediaWiki\restoreWarnings();
- if ( $newFileHandle ) {
- fclose( $newFileHandle );
- $tmpFile = new self( $path );
- $tmpFile->autocollect();
- // Safely instantiated, end loop.
- return $tmpFile;
- }
- }
-
- // Give up
- return null;
- }
-
- /**
- * @return string Filesystem path to a temporary directory
- * @throws RuntimeException
- */
- public static function getUsableTempDirectory() {
- $tmpDir = array_map( 'getenv', [ 'TMPDIR', 'TMP', 'TEMP' ] );
- $tmpDir[] = sys_get_temp_dir();
- $tmpDir[] = ini_get( 'upload_tmp_dir' );
- foreach ( $tmpDir as $tmp ) {
- if ( $tmp != '' && is_dir( $tmp ) && is_writable( $tmp ) ) {
- return $tmp;
- }
- }
-
- // PHP on Windows will detect C:\Windows\Temp as not writable even though PHP can write to
- // it so create a directory within that called 'mwtmp' with a suffix of the user running
- // the current process.
- // The user is included as if various scripts are run by different users they will likely
- // not be able to access each others temporary files.
- if ( strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN' ) {
- $tmp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mwtmp-' . get_current_user();
- if ( !file_exists( $tmp ) ) {
- mkdir( $tmp );
- }
- if ( is_dir( $tmp ) && is_writable( $tmp ) ) {
- return $tmp;
- }
- }
-
- throw new RuntimeException(
- 'No writable temporary directory could be found. ' .
- 'Please explicitly specify a writable directory in configuration.' );
- }
-
- /**
- * Purge this file off the file system
- *
- * @return bool Success
- */
- public function purge() {
- $this->canDelete = false; // done
- MediaWiki\suppressWarnings();
- $ok = unlink( $this->path );
- MediaWiki\restoreWarnings();
-
- unset( self::$pathsCollect[$this->path] );
-
- return $ok;
- }
-
- /**
- * Clean up the temporary file only after an object goes out of scope
- *
- * @param object $object
- * @return TempFSFile This object
- */
- public function bind( $object ) {
- if ( is_object( $object ) ) {
- if ( !isset( $object->tempFSFileReferences ) ) {
- // Init first since $object might use __get() and return only a copy variable
- $object->tempFSFileReferences = [];
- }
- $object->tempFSFileReferences[] = $this;
- }
-
- return $this;
- }
-
- /**
- * Set flag to not clean up after the temporary file
- *
- * @return TempFSFile This object
- */
- public function preserve() {
- $this->canDelete = false;
-
- unset( self::$pathsCollect[$this->path] );
-
- return $this;
- }
-
- /**
- * Set flag clean up after the temporary file
- *
- * @return TempFSFile This object
- */
- public function autocollect() {
- $this->canDelete = true;
-
- self::$pathsCollect[$this->path] = 1;
-
- return $this;
- }
-
- /**
- * Try to make sure that all files are purged on error
- *
- * This method should only be called internally
- */
- public static function purgeAllOnShutdown() {
- foreach ( self::$pathsCollect as $path ) {
- MediaWiki\suppressWarnings();
- unlink( $path );
- MediaWiki\restoreWarnings();
- }
- }
-
- /**
- * Cleans up after the temporary file by deleting it
- */
- function __destruct() {
- if ( $this->canDelete ) {
- $this->purge();
- }
- }
-}
--- /dev/null
+<?php
+/**
+ * Non-directory file on the file system.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+/**
+ * Class representing a non-directory file on the file system
+ *
+ * @ingroup FileBackend
+ */
+class FSFile {
+ /** @var string Path to file */
+ protected $path;
+
+ /** @var string File SHA-1 in base 36 */
+ protected $sha1Base36;
+
+ /**
+ * Sets up the file object
+ *
+ * @param string $path Path to temporary file on local disk
+ */
+ public function __construct( $path ) {
+ $this->path = $path;
+ }
+
+ /**
+ * Returns the file system path
+ *
+ * @return string
+ */
+ public function getPath() {
+ return $this->path;
+ }
+
+ /**
+ * Checks if the file exists
+ *
+ * @return bool
+ */
+ public function exists() {
+ return is_file( $this->path );
+ }
+
+ /**
+ * Get the file size in bytes
+ *
+ * @return int|bool
+ */
+ public function getSize() {
+ return filesize( $this->path );
+ }
+
+ /**
+ * Get the file's last-modified timestamp
+ *
+ * @return string|bool TS_MW timestamp or false on failure
+ */
+ public function getTimestamp() {
+ MediaWiki\suppressWarnings();
+ $timestamp = filemtime( $this->path );
+ MediaWiki\restoreWarnings();
+ if ( $timestamp !== false ) {
+ $timestamp = wfTimestamp( TS_MW, $timestamp );
+ }
+
+ return $timestamp;
+ }
+
+ /**
+ * Get an associative array containing information about
+ * a file with the given storage path.
+ *
+ * Resulting array fields include:
+ * - fileExists
+ * - size (filesize in bytes)
+ * - mime (as major/minor)
+ * - file-mime (as major/minor)
+ * - sha1 (in base 36)
+ * - major_mime
+ * - minor_mime
+ *
+ * @param string|bool $ext The file extension, or true to extract it from the filename.
+ * Set it to false to ignore the extension. Currently unused.
+ * @return array
+ */
+ public function getProps( $ext = true ) {
+ $info = self::placeholderProps();
+ $info['fileExists'] = $this->exists();
+
+ if ( $info['fileExists'] ) {
+ $info['size'] = $this->getSize(); // bytes
+ $info['sha1'] = $this->getSha1Base36();
+
+ $mime = mime_content_type( $this->path );
+ # MIME type according to file contents
+ $info['file-mime'] = ( $mime === false ) ? 'unknown/unknown' : $mime;
+ # logical MIME type
+ $info['mime'] = $mime;
+
+ if ( strpos( $mime, '/' ) !== false ) {
+ list( $info['major_mime'], $info['minor_mime'] ) = explode( '/', $mime, 2 );
+ } else {
+ list( $info['major_mime'], $info['minor_mime'] ) = [ $mime, 'unknown' ];
+ }
+ }
+
+ return $info;
+ }
+
+ /**
+ * Placeholder file properties to use for files that don't exist
+ *
+ * Resulting array fields include:
+ * - fileExists
+ * - size (filesize in bytes)
+ * - mime (as major/minor)
+ * - file-mime (as major/minor)
+ * - sha1 (in base 36)
+ * - major_mime
+ * - minor_mime
+ *
+ * @return array
+ */
+ public static function placeholderProps() {
+ $info = [];
+ $info['fileExists'] = false;
+ $info['size'] = 0;
+ $info['file-mime'] = null;
+ $info['major_mime'] = null;
+ $info['minor_mime'] = null;
+ $info['mime'] = null;
+ $info['sha1'] = '';
+
+ return $info;
+ }
+
+ /**
+ * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case
+ * encoding, zero padded to 31 digits.
+ *
+ * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36
+ * fairly neatly.
+ *
+ * @param bool $recache
+ * @return bool|string False on failure
+ */
+ public function getSha1Base36( $recache = false ) {
+ if ( $this->sha1Base36 !== null && !$recache ) {
+ return $this->sha1Base36;
+ }
+
+ MediaWiki\suppressWarnings();
+ $this->sha1Base36 = sha1_file( $this->path );
+ MediaWiki\restoreWarnings();
+
+ if ( $this->sha1Base36 !== false ) {
+ $this->sha1Base36 = Wikimedia\base_convert( $this->sha1Base36, 16, 36, 31 );
+ }
+
+ return $this->sha1Base36;
+ }
+
+ /**
+ * Get the final file extension from a file system path
+ *
+ * @param string $path
+ * @return string
+ */
+ public static function extensionFromPath( $path ) {
+ $i = strrpos( $path, '.' );
+
+ return strtolower( $i ? substr( $path, $i + 1 ) : '' );
+ }
+
+ /**
+ * Get an associative array containing information about a file in the local filesystem.
+ *
+ * @param string $path Absolute local filesystem path
+ * @param string|bool $ext The file extension, or true to extract it from the filename.
+ * Set it to false to ignore the extension.
+ * @return array
+ */
+ public static function getPropsFromPath( $path, $ext = true ) {
+ $fsFile = new self( $path );
+
+ return $fsFile->getProps( $ext );
+ }
+
+ /**
+ * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case
+ * encoding, zero padded to 31 digits.
+ *
+ * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36
+ * fairly neatly.
+ *
+ * @param string $path
+ * @return bool|string False on failure
+ */
+ public static function getSha1Base36FromPath( $path ) {
+ $fsFile = new self( $path );
+
+ return $fsFile->getSha1Base36();
+ }
+}
--- /dev/null
+<?php
+/**
+ * Location holder of files stored temporarily
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+/**
+ * This class is used to hold the location and do limited manipulation
+ * of files stored temporarily (this will be whatever wfTempDir() returns)
+ *
+ * @ingroup FileBackend
+ */
+class TempFSFile extends FSFile {
+ /** @var bool Garbage collect the temp file */
+ protected $canDelete = false;
+
+ /** @var array Map of (path => 1) for paths to delete on shutdown */
+ protected static $pathsCollect = null;
+
+ public function __construct( $path ) {
+ parent::__construct( $path );
+
+ if ( self::$pathsCollect === null ) {
+ self::$pathsCollect = [];
+ register_shutdown_function( [ __CLASS__, 'purgeAllOnShutdown' ] );
+ }
+ }
+
+ /**
+ * Make a new temporary file on the file system.
+ * Temporary files may be purged when the file object falls out of scope.
+ *
+ * @param string $prefix
+ * @param string $extension Optional file extension
+ * @param string|null $tmpDirectory Optional parent directory
+ * @return TempFSFile|null
+ */
+ public static function factory( $prefix, $extension = '', $tmpDirectory = null ) {
+ $ext = ( $extension != '' ) ? ".{$extension}" : '';
+
+ $attempts = 5;
+ while ( $attempts-- ) {
+ $hex = sprintf( '%06x%06x', mt_rand( 0, 0xffffff ), mt_rand( 0, 0xffffff ) );
+ if ( !is_string( $tmpDirectory ) ) {
+ $tmpDirectory = self::getUsableTempDirectory();
+ }
+ $path = wfTempDir() . '/' . $prefix . $hex . $ext;
+ MediaWiki\suppressWarnings();
+ $newFileHandle = fopen( $path, 'x' );
+ MediaWiki\restoreWarnings();
+ if ( $newFileHandle ) {
+ fclose( $newFileHandle );
+ $tmpFile = new self( $path );
+ $tmpFile->autocollect();
+ // Safely instantiated, end loop.
+ return $tmpFile;
+ }
+ }
+
+ // Give up
+ return null;
+ }
+
+ /**
+ * @return string Filesystem path to a temporary directory
+ * @throws RuntimeException
+ */
+ public static function getUsableTempDirectory() {
+ $tmpDir = array_map( 'getenv', [ 'TMPDIR', 'TMP', 'TEMP' ] );
+ $tmpDir[] = sys_get_temp_dir();
+ $tmpDir[] = ini_get( 'upload_tmp_dir' );
+ foreach ( $tmpDir as $tmp ) {
+ if ( $tmp != '' && is_dir( $tmp ) && is_writable( $tmp ) ) {
+ return $tmp;
+ }
+ }
+
+ // PHP on Windows will detect C:\Windows\Temp as not writable even though PHP can write to
+ // it so create a directory within that called 'mwtmp' with a suffix of the user running
+ // the current process.
+ // The user is included as if various scripts are run by different users they will likely
+ // not be able to access each others temporary files.
+ if ( strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN' ) {
+ $tmp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mwtmp-' . get_current_user();
+ if ( !file_exists( $tmp ) ) {
+ mkdir( $tmp );
+ }
+ if ( is_dir( $tmp ) && is_writable( $tmp ) ) {
+ return $tmp;
+ }
+ }
+
+ throw new RuntimeException(
+ 'No writable temporary directory could be found. ' .
+ 'Please explicitly specify a writable directory in configuration.' );
+ }
+
+ /**
+ * Purge this file off the file system
+ *
+ * @return bool Success
+ */
+ public function purge() {
+ $this->canDelete = false; // done
+ MediaWiki\suppressWarnings();
+ $ok = unlink( $this->path );
+ MediaWiki\restoreWarnings();
+
+ unset( self::$pathsCollect[$this->path] );
+
+ return $ok;
+ }
+
+ /**
+ * Clean up the temporary file only after an object goes out of scope
+ *
+ * @param object $object
+ * @return TempFSFile This object
+ */
+ public function bind( $object ) {
+ if ( is_object( $object ) ) {
+ if ( !isset( $object->tempFSFileReferences ) ) {
+ // Init first since $object might use __get() and return only a copy variable
+ $object->tempFSFileReferences = [];
+ }
+ $object->tempFSFileReferences[] = $this;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set flag to not clean up after the temporary file
+ *
+ * @return TempFSFile This object
+ */
+ public function preserve() {
+ $this->canDelete = false;
+
+ unset( self::$pathsCollect[$this->path] );
+
+ return $this;
+ }
+
+ /**
+ * Set flag clean up after the temporary file
+ *
+ * @return TempFSFile This object
+ */
+ public function autocollect() {
+ $this->canDelete = true;
+
+ self::$pathsCollect[$this->path] = 1;
+
+ return $this;
+ }
+
+ /**
+ * Try to make sure that all files are purged on error
+ *
+ * This method should only be called internally
+ */
+ public static function purgeAllOnShutdown() {
+ foreach ( self::$pathsCollect as $path ) {
+ MediaWiki\suppressWarnings();
+ unlink( $path );
+ MediaWiki\restoreWarnings();
+ }
+ }
+
+ /**
+ * Cleans up after the temporary file by deleting it
+ */
+ function __destruct() {
+ if ( $this->canDelete ) {
+ $this->purge();
+ }
+ }
+}
/** @var callback Error logging callback */
protected $errorLogger;
- /** @var resource Database connection */
+ /** @var resource|null Database connection */
protected $mConn = null;
/** @var bool */
protected $mOpened = false;
}
if ( !isset( $p['errorLogger'] ) ) {
$p['errorLogger'] = function ( Exception $e ) {
- trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_WARNING );
+ trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
};
}
return !!( $this->mFlags & $flag );
}
+ /**
+ * @param string $name Class field name
+ * @return mixed
+ * @deprecated Since 1.28
+ */
public function getProperty( $name ) {
return $this->$name;
}
if ( $this->htmlErrors !== false ) {
ini_set( 'html_errors', $this->htmlErrors );
}
+
+ return $this->getLastPHPError();
+ }
+
+ /**
+ * @return string|bool Last PHP error for this DB (typically connection errors)
+ */
+ protected function getLastPHPError() {
if ( $this->mPHPError ) {
$error = preg_replace( '!\[<a.*</a>\]!', '', $this->mPHPError );
$error = preg_replace( '!^.*?:\s?(.*)$!', '$1', $error );
return $error;
- } else {
- return false;
}
+
+ return false;
}
/**
* @return bool
*/
protected function isTransactableQuery( $sql ) {
- $verb = $this->getQueryVerb( $sql );
- return !in_array( $verb, [ 'BEGIN', 'COMMIT', 'ROLLBACK', 'SHOW', 'SET' ], true );
+ return !in_array(
+ $this->getQueryVerb( $sql ),
+ [ 'BEGIN', 'COMMIT', 'ROLLBACK', 'SHOW', 'SET', 'CREATE', 'ALTER' ],
+ true
+ );
}
/**
$fnames = implode( ', ', $this->pendingWriteAndCallbackCallers() );
throw new DBUnexpectedError(
$this,
- "$fname: Cannot COMMIT to clear snapshot because writes are pending ($fnames)."
+ "$fname: Cannot flush snapshot because writes are pending ($fnames)."
);
}
$fnames = implode( ', ', $this->pendingWriteAndCallbackCallers() );
throw new DBUnexpectedError(
$this,
- "$fname: Cannot COMMIT to clear snapshot because writes are pending ($fnames)."
+ "$fname: Cannot flush pre-lock snapshot because writes are pending ($fnames)."
);
}
return true;
}
+ /**
+ * Get the underlying binding handle, mConn
+ *
+ * Makes sure that mConn is set (disconnects and ping() failure can unset it).
+ * This catches broken callers than catch and ignore disconnection exceptions.
+ * Unlike checking isOpen(), this is safe to call inside of open().
+ *
+ * @return resource|object
+ * @throws DBUnexpectedError
+ * @since 1.26
+ */
+ protected function getBindingHandle() {
+ if ( !$this->mConn ) {
+ throw new DBUnexpectedError(
+ $this,
+ 'DB connection was already closed or the connection dropped.'
+ );
+ }
+
+ return $this->mConn;
+ }
+
/**
* @since 1.19
* @return string
}
if ( $this->mConn ) {
- // Avoid connection leaks for sanity
+ // Avoid connection leaks for sanity. Normally, resources close at script completion.
+ // The connection might already be closed in zend/hhvm by now, so suppress warnings.
+ \MediaWiki\suppressWarnings();
$this->closeConnection();
+ \MediaWiki\restoreWarnings();
$this->mConn = false;
$this->mOpened = false;
}
return $errno == 2013 || $errno == 2006;
}
- /**
- * Get the underlying binding handle, mConn
- *
- * Makes sure that mConn is set (disconnects and ping() failure can unset it).
- * This catches broken callers than catch and ignore disconnection exceptions.
- * Unlike checking isOpen(), this is safe to call inside of open().
- *
- * @return resource|object
- * @throws DBUnexpectedError
- * @since 1.26
- */
- protected function getBindingHandle() {
- if ( !$this->mConn ) {
- throw new DBUnexpectedError(
- $this,
- 'DB connection was already closed or the connection dropped.'
- );
- }
-
- return $this->mConn;
- }
-
/**
* @param string $oldName
* @param string $newName
* @see Database
*/
class DatabaseMysqli extends DatabaseMysqlBase {
- /** @var mysqli */
- protected $mConn;
+ /** @var $mConn mysqli */
/**
* @param string $sql
}
function hasConstraint( $name ) {
+ $conn = $this->getBindingHandle();
+
$sql = "SELECT 1 FROM pg_catalog.pg_constraint c, pg_catalog.pg_namespace n " .
"WHERE c.connamespace = n.oid AND conname = '" .
- pg_escape_string( $this->mConn, $name ) . "' AND n.nspname = '" .
- pg_escape_string( $this->mConn, $this->getCoreSchema() ) . "'";
+ pg_escape_string( $conn, $name ) . "' AND n.nspname = '" .
+ pg_escape_string( $conn, $this->getCoreSchema() ) . "'";
$res = $this->doQuery( $sql );
return $this->numRows( $res );
);
}
- if ( !strlen( $user ) ) { # e.g. the class is being loaded
- return null;
- }
-
$this->mServer = $server;
$this->mUser = $user;
$this->mPassword = $password;
$this->installErrorHandler();
try {
- $this->mConn = pg_connect( $this->connectString );
+ // Use new connections to let LoadBalancer/LBFactory handle reuse
+ $this->mConn = pg_connect( $this->connectString, PGSQL_CONNECT_FORCE_NEW );
} catch ( Exception $ex ) {
$this->restoreErrorHandler();
throw $ex;
$phpError = $this->restoreErrorHandler();
if ( !$this->mConn ) {
- $this->queryLogger->debug( "DB connection error\n" );
$this->queryLogger->debug(
+ "DB connection error\n" .
"Server: $server, Database: $dbName, User: $user, Password: " .
- substr( $password, 0, 3 ) . "...\n" );
+ substr( $password, 0, 3 ) . "...\n"
+ );
$this->queryLogger->debug( $this->lastError() . "\n" );
throw new DBConnectionError( $this, str_replace( "\n", ' ', $phpError ) );
}
* @return bool
*/
protected function closeConnection() {
- return pg_close( $this->mConn );
+ return $this->mConn ? pg_close( $this->mConn ) : true;
}
public function doQuery( $sql ) {
+ $conn = $this->getBindingHandle();
+
$sql = mb_convert_encoding( $sql, 'UTF-8' );
// Clear previously left over PQresult
- while ( $res = pg_get_result( $this->mConn ) ) {
+ while ( $res = pg_get_result( $conn ) ) {
pg_free_result( $res );
}
- if ( pg_send_query( $this->mConn, $sql ) === false ) {
+ if ( pg_send_query( $conn, $sql ) === false ) {
throw new DBUnexpectedError( $this, "Unable to post new query to PostgreSQL\n" );
}
- $this->mLastResult = pg_get_result( $this->mConn );
+ $this->mLastResult = pg_get_result( $conn );
$this->mAffectedRows = null;
if ( pg_result_error( $this->mLastResult ) ) {
return false;
# @todo hashar: not sure if the following test really trigger if the object
# fetching failed.
- if ( pg_last_error( $this->mConn ) ) {
+ $conn = $this->getBindingHandle();
+ if ( pg_last_error( $conn ) ) {
throw new DBUnexpectedError(
$this,
- 'SQL error: ' . htmlspecialchars( pg_last_error( $this->mConn ) )
+ 'SQL error: ' . htmlspecialchars( pg_last_error( $conn ) )
);
}
MediaWiki\suppressWarnings();
$row = pg_fetch_array( $res );
MediaWiki\restoreWarnings();
- if ( pg_last_error( $this->mConn ) ) {
+
+ $conn = $this->getBindingHandle();
+ if ( pg_last_error( $conn ) ) {
throw new DBUnexpectedError(
$this,
- 'SQL error: ' . htmlspecialchars( pg_last_error( $this->mConn ) )
+ 'SQL error: ' . htmlspecialchars( pg_last_error( $conn ) )
);
}
MediaWiki\suppressWarnings();
$n = pg_num_rows( $res );
MediaWiki\restoreWarnings();
- if ( pg_last_error( $this->mConn ) ) {
+
+ $conn = $this->getBindingHandle();
+ if ( pg_last_error( $conn ) ) {
throw new DBUnexpectedError(
$this,
- 'SQL error: ' . htmlspecialchars( pg_last_error( $this->mConn ) )
+ 'SQL error: ' . htmlspecialchars( pg_last_error( $conn ) )
);
}
} else {
return pg_last_error();
}
- } else {
- return 'No database connection';
}
+
+ return $this->getLastPHPError() ?: 'No database connection';
}
function lastErrno() {
$this->mCoreSchema . "\"\n" );
}
/* Commit SET otherwise it will be rollbacked on error or IGNORE SELECT */
- $this->commit( __METHOD__ );
+ $this->commit( __METHOD__, self::FLUSHING_INTERNAL );
}
/**
*/
function getServerVersion() {
if ( !isset( $this->numericVersion ) ) {
- $versionInfo = pg_version( $this->mConn );
+ $conn = $this->getBindingHandle();
+ $versionInfo = pg_version( $conn );
if ( version_compare( $versionInfo['client'], '7.4.0', 'lt' ) ) {
// Old client, abort install
$this->numericVersion = '7.3 or earlier';
$this->numericVersion = $versionInfo['server'];
} else {
// Bug 16937: broken pgsql extension from PHP<5.3
- $this->numericVersion = pg_parameter_status( $this->mConn, 'server_version' );
+ $this->numericVersion = pg_parameter_status( $conn, 'server_version' );
}
}
function strencode( $s ) {
// Should not be called by us
- return pg_escape_string( $this->mConn, $s );
+ return pg_escape_string( $this->getBindingHandle(), $s );
}
/**
* @return string|int
*/
function addQuotes( $s ) {
+ $conn = $this->getBindingHandle();
+
if ( is_null( $s ) ) {
return 'NULL';
} elseif ( is_bool( $s ) ) {
if ( $s instanceof PostgresBlob ) {
$s = $s->fetch();
} else {
- $s = pg_escape_bytea( $this->mConn, $s->fetch() );
+ $s = pg_escape_bytea( $conn, $s->fetch() );
}
return "'$s'";
}
- return "'" . pg_escape_string( $this->mConn, $s ) . "'";
+ return "'" . pg_escape_string( $conn, $s ) . "'";
}
/**
/** @var resource */
protected $mLastResult;
- /** @var PDO */
- protected $mConn;
+ /** @var $mConn PDO */
/** @var FSLockManager (hopefully on the same server as the DB) */
protected $lockMgr;
*/
public function getFlag( $flag );
- /**
- * General read-only accessor
- *
- * @param string $name
- * @return string
- */
- public function getProperty( $name );
-
/**
* @return string
*/
$this->errorLogger = isset( $conf['errorLogger'] )
? $conf['errorLogger']
: function ( Exception $e ) {
- trigger_error( E_WARNING, get_class( $e ) . ': ' . $e->getMessage() );
+ trigger_error( E_USER_WARNING, get_class( $e ) . ': ' . $e->getMessage() );
};
$this->profiler = isset( $params['profiler'] ) ? $params['profiler'] : null;
$this->errorLogger = isset( $params['errorLogger'] )
? $params['errorLogger']
: function ( Exception $e ) {
- trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_WARNING );
+ trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
};
foreach ( [ 'replLogger', 'connLogger', 'queryLogger', 'perfLogger' ] as $key ) {
if ( $i == self::DB_REPLICA ) {
$this->mLastError = 'Unknown error'; // reset error string
# Try the general server pool if $groups are unavailable.
- $i = in_array( false, $groups, true )
+ $i = ( $groups === [ false ] )
? false // don't bother with this if that is what was tried above
: $this->getReaderIndex( false, $domain );
# Couldn't find a working server in getReaderIndex()?
// If all servers were busy, mLastError will contain something sensible
throw new DBConnectionError( null, $this->mLastError );
} else {
- $context['db_server'] = $conn->getProperty( 'mServer' );
+ $context['db_server'] = $conn->getServer();
$this->connLogger->warning(
"Connection error: {last_error} ({db_server})",
$context
* @return string
*/
public function __toString() {
- return $this->getName();
+ return (string)$this->getName();
}
/**
*/
private $key;
+ /**
+ * @var array Additional query options
+ */
+ protected $options = [];
+
/**
* @param IDatabase $db The database to read from
* @param string|array $table The name or names of the table to read from
$this->conditions = array_merge( $this->conditions, $conditions );
}
+ /**
+ * @param array $options Query options suitable for use with
+ * IDatabase::select
+ */
+ public function addOptions( array $options ) {
+ $this->options = array_merge( $this->options, $options );
+ }
+
/**
* @param array $conditions Query join conditions suitable for use
* with IDatabase::select
[
'LIMIT' => $this->batchSize,
'ORDER BY' => $this->orderBy,
- ],
+ ] + $this->options,
$this->joinConditions
);
];
public $mMainLanguageCode;
- public $mVariants, $mVariantFallbacks, $mVariantNames;
+ public $mVariants;
+ public $mVariantFallbacks;
+ public $mVariantNames;
public $mTablesLoaded = false;
public $mTables;
// 'bidirectional' 'unidirectional' 'disable' for each variant
"movelogpagetext": "Esta ye la llista de páxines treslladaes.",
"movesubpage": "{{PLURAL:$1|Subpáxina|Subpáxines}}",
"movesubpagetext": "Esta páxina tien $1 {{PLURAL:$1|subpáxina|subpáxines}} que s'amuesen darréu.",
+ "movesubpagetalktext": "La páxina d'alderique correspondiente tien $1 {{PLURAL:$1|subpáxina|subpáxines}} que {{PLURAL:$1|s'amuesa|s'amuesen}} darréu.",
"movenosubpage": "Esta páxina nun tien subpáxines.",
"movereason": "Motivu:",
"revertmove": "revertir",
"newimages-showbots": "Ver les xubíes de los bots",
"newimages-hidepatrolled": "Despintar les entraes patrullaes",
"noimages": "Nun hai nada que ver.",
+ "gallery-slideshow-toggle": "Intercambiar les miniatures",
"ilsubmit": "Guetar",
"bydate": "por fecha",
"sp-newimages-showfrom": "Amosar los archivos nuevos emprimando dende'l $1 a les $2",
"feedback-thanks": "¡Gracies! La to opinión s'espublizó na páxina «[$2 $1]».",
"feedback-thanks-title": "¡Gracies!",
"feedback-useragent": "Axente d'usuariu:",
- "searchsuggest-search": "Buscar",
+ "searchsuggest-search": "Buscar en {{SITENAME}}",
"searchsuggest-containing": "que contien...",
"api-error-autoblocked": "La to dirección IP bloquióse automáticamente porque la usó un usuariu bloquiáu.",
"api-error-badaccess-groups": "Nun tienes permisu pa xubir ficheros a esta wiki.",
"usercssispublic": "Atención: les subpáxines CSS nun tendríen de contener datos acutaos porque son visibles pa otros usuarios.",
"restrictionsfield-badip": "Direición o rangu IP inválidu: $1",
"restrictionsfield-label": "Rangos d'IP permitíos:",
- "restrictionsfield-help": "Una única direición IP o rangu CIDR per llinia. P'activar toos, utiliza<br><code>0.0.0.0/0</code><br><code>::/0</code>"
+ "restrictionsfield-help": "Una única direición IP o rangu CIDR per llinia. P'activar toos, utiliza<br><code>0.0.0.0/0</code><br><code>::/0</code>",
+ "edit-error-short": "Error: $1",
+ "edit-error-long": "Errores:\n\n$1"
}
"specialpage": "Pela xısusiye",
"personaltools": "Hacetê şexsiy",
"articlepage": "Pera zerreki bıvin",
- "talk": "Vacenayış",
+ "talk": "Werênayış",
"views": "Asayışi",
"toolbox": "Haceti",
"tool-link-userrights": "Grubanê {{GENDER:$1|karberi}} bıvırnë",
"passwordreset-emailelement": "Nombre de {{GENDER:$1|usuario|usuaria}}: \n$1\n\nContraseña temporal: \n$2",
"passwordreset-emailsentemail": "Si esta dirección de correo electrónico está asociada a tu cuenta, entonces se enviará un correo electrónico para restablecer la contraseña.",
"passwordreset-emailsentusername": "Si existe una dirección de correo electrónico asociada a este nombre de usuario, entonces se enviará un correo para restablecer la contraseña.",
- "passwordreset-emailsent-capture2": "{{PLURAL:$1|El e-mail de restablecimiento de contraseña ha sido enviado|Los e-mails de restablecimiento de contraseña han sido enviados}}. {{PLURAL:$1|El nombre de usuario y la contraseña se muestra|La lista de nombres de usuarios y contraseñas se muestra}} aquí.",
+ "passwordreset-emailsent-capture2": "{{PLURAL:$1|Se ha enviado el mensaje de restablecimiento de contraseña|Se han enviado los mensajes de restablecimiento de contraseña}}. {{PLURAL:$1|El nombre de usuario y la contraseña|La lista de nombres de usuarios y contraseñas}} se muestra aquí.",
"passwordreset-emailerror-capture2": "No fue posible mandar un correo electrónico {{GENDER:$2|al usuario|a la usuaria}}: $1 {{PLURAL:$3|El nombre de usuario y la contraseña|La lista de nombres de usuarios y contraseñas}} se muestra aquí.",
"passwordreset-nocaller": "Debe de proporcionarse un interlocutor",
"passwordreset-nosuchcaller": "La persona que llama no existe: $1",
"feedback-thanks": "¡Gracias! Tus comentarios se han publicado en la página \"[$2 $1]\".",
"feedback-thanks-title": "¡Muchas gracias!",
"feedback-useragent": "Agente de usuario:",
- "searchsuggest-search": "Buscar",
+ "searchsuggest-search": "Buscar en {{SITENAME}}",
"searchsuggest-containing": "que contiene...",
"api-error-autoblocked": "Tu dirección IP ha sido bloqueada automáticamente porque fue utilizada por un usuario bloqueado.",
"api-error-badaccess-groups": "No puedes cargar archivos en este wiki.",
"authenticationdatachange-ignored": "El cambio den los datos de autentificacion no fue realizado. ¿Tal vez, no se configuró un proveedor?",
"userjsispublic": "Recuerda: las subpáginas JavaScript no deberían contener datos confidenciales, pues otros usuarios los pueden ver.",
"usercssispublic": "Recuerda: las subpáginas CSS no deberían contener datos confidenciales, pues otros usuarios los pueden ver.",
- "restrictionsfield-badip": "Dirección IP o intervalo inválido: $1",
+ "restrictionsfield-badip": "Dirección o intervalo IP no válidos: $1",
"restrictionsfield-label": "Intervalos de IP permitidos:"
}
"undeletehistorynoadmin": "See lehekülg on kustutatud.\nKustutamise põhjus ning selle lehekülje kustutamiseelne redigeerimislugu on näha allolevas kokkuvõttes.\nLehekülje kustutamiseelsed redaktsioonid on kättesaadavad ainult administraatoritele.",
"undelete-revision": "Lehekülje $1 kustutatud redaktsioon, mille autor on $3, seisuga $4, kell $5.",
"undeleterevision-missing": "Vigane või puuduv redaktsioon.\nLink võib olla kõlbmatu või redaktsioon võib olla taastatud või arhiivist eemaldatud.",
+ "undeleterevision-duplicate-revid": "{{PLURAL:$1|Üht|$1}} redaktsiooni ei saanud taastada, sest {{PLURAL:$1|selle|nende}} <code>rev_id</code> oli juba kasutuses.",
"undelete-nodiff": "Varasemat redaktsiooni ei leidunud.",
"undeletebtn": "Taasta",
"undeletelink": "vaata/taasta",
"ipb-unblock": "Kasutaja või IP-aadressi vabastamine blokeerimisest",
"ipb-blocklist": "Vaata kehtivaid blokeeringuid",
"ipb-blocklist-contribs": "Kasutaja $1 kaastöö",
+ "ipb-blocklist-duration-left": "$1 järel",
"unblockip": "Blokeerimise eemaldamine",
"unblockiptext": "Kasuta allpool olevat vormi varem blokeeritud IP-aadressi või kasutaja redigeerimisõiguse taastamiseks.",
"ipusubmit": "Eemalda see blokeering",
"block-log-flags-hiddenname": "kasutajanimi peidetud",
"range_block_disabled": "Administraatori õigus blokeerida IP-aadresside vahemik on ära võetud.",
"ipb_expiry_invalid": "Vigane aegumise tähtaeg.",
+ "ipb_expiry_old": "Aegumistähtaeg on minevikus.",
"ipb_expiry_temp": "Peidetud kasutajanime blokeeringud peavad olema alalised.",
"ipb_hide_invalid": "Seda kontot ei saa varjata, sest sellega on tehtud üle {{PLURAL:$1|ühe|$1}} muudatuse.",
"ipb_already_blocked": "\"$1\" on juba blokeeritud.",
"pageinfo-category-files": "Faile",
"markaspatrolleddiff": "Märgi kontrollituks",
"markaspatrolledtext": "Märgi see leht kontrollituks",
+ "markaspatrolledtext-file": "Märgi see failiversioon kontrollituks",
"markedaspatrolled": "Kontrollituks märgitud",
"markedaspatrolledtext": "Valitud redaktsioon leheküljel [[:$1]] on kontrollituks märgitud.",
"rcpatroldisabled": "Viimaste muudatuste kontroll ei toimi",
"confirm-watch-top": "Kas lisad selle lehekülje oma jälgimisloendisse?",
"confirm-unwatch-button": "Sobib",
"confirm-unwatch-top": "Kas eemaldad selle lehekülje oma jälgimisloendist?",
+ "confirm-rollback-button": "Sobib",
"quotation-marks": "\"$1\"",
"imgmultipageprev": "← eelmine lehekülg",
"imgmultipagenext": "järgmine lehekülg →",
"editpage-cannot-use-custom-model": "Ennek a lapnak a tartalommodellje nem változtatható.",
"longpageerror": "'''HIBA: Az általad beküldött szöveg {{PLURAL:$1|egy kilobájt|$1 kilobájt}} hosszú, ami több az engedélyezett {{PLURAL:$2|egy kilobájtnál|$2 kilobájtnál}}.\nA szerkesztést nem lehet elmenteni.'''",
"readonlywarning": "<strong>FIGYELMEZTETÉS: A wiki adatbázisát karbantartás miatt zárolták, ezért most nem fogod tudni elmenteni a szerkesztéseidet!</strong>\nA lap szövegét másold egy szövegfájlba, amit később felhasználhatsz!\n\nAz adatbázist lezáró rendszeradminisztrátor az alábbi magyarázatot adta: $1",
- "protectedpagewarning": "'''Figyelem: Ez a lap le van védve, így csak adminisztrátori jogosultságokkal rendelkező szerkesztők módosíthatják.'''\nA legutolsó ide vonatkozó naplóbejegyzés alább látható:",
+ "protectedpagewarning": "<strong>Figyelem: Ez a lap védett, így csak adminisztrátori jogosultságokkal rendelkező szerkesztők módosíthatják.</strong>\nA legutolsó ide vonatkozó naplóbejegyzés alább látható:",
"semiprotectedpagewarning": "'''Megjegyzés:''' ez a lap védett, így regisztrálatlan vagy újonnan regisztrált szerkesztők nem módosíthatják.",
"cascadeprotectedwarning": "<strong>Figyelem:</strong> ez a lap le van zárva, csak adminisztrátorok szerkeszthetik, mert a következő kaszkádvédelemmel ellátott {{PLURAL:$1|lapon|lapokon}} be van illesztve:",
"titleprotectedwarning": "'''Figyelem: Ez a lap le van védve, így csak a [[Special:ListGroupRights|megfelelő jogosultságokkal]] rendelkező szerkesztők hozhatják létre.'''\nA legutolsó ide vonatkozó naplóbejegyzés alább látható:",
"feedback-external-bug-report-button": "A fájl egy technikai feladat",
"feedback-dialog-title": "Visszajelzés küldése",
"feedback-dialog-intro": "A visszajelzésedre az alábbi egyszerű űrlapot használhatod. A hozzászólásod a felhasználó neveddel együtt a „$1” oldalon fog megjelenni.",
- "feedback-error-title": "Hiba",
"feedback-error1": "Hiba: az API ismeretlen eredménnyel tért vissza",
"feedback-error2": "Hiba: a szerkesztés nem sikerült",
"feedback-error3": "Hiba: nem érkezett válasz az API-tól",
"prefs-personal": "사용자 정보",
"prefs-rc": "최근 바뀜",
"prefs-watchlist": "주시문서 목록",
- "prefs-editwatchlist": "주시목록 편집",
+ "prefs-editwatchlist": "주ì\8b\9c문ì\84\9c 목ë¡\9d í\8e¸ì§\91",
"prefs-editwatchlist-label": "주시문서 목록의 항목을 편집합니다:",
"prefs-editwatchlist-edit": "주시문서의 제목을 보고 지우기",
"prefs-editwatchlist-raw": "주시문서 목록 직접 편집하기",
"usercssispublic": "주목해 주십시오: CSS의 하위 문서들은 다른 사용자들이 볼 수 있기 때문에 기밀 데이터를 포함해서는 안 됩니다.",
"restrictionsfield-badip": "유효하지 않은 IP 주소나 대역: $1",
"restrictionsfield-label": "허용된 IP 대역:",
- "restrictionsfield-help": "줄 단위의 하나의 IP 주소 또는 CIDR 대역입니다. 모든 곳에 적용하려면, 다음을 사용하세요<br><code>0.0.0.0/0</code><br><code>::/0</code>"
+ "restrictionsfield-help": "줄 단위의 하나의 IP 주소 또는 CIDR 대역입니다. 모든 곳에 적용하려면, 다음을 사용하세요<br><code>0.0.0.0/0</code><br><code>::/0</code>",
+ "edit-error-short": "오류: $1",
+ "edit-error-long": "오류:\n\n$1"
}
"subject-preview": "Temos peržiūra:",
"previewerrortext": "Įvyko klaida bandant peržiūrėti jūsų pakeitimus.",
"blockedtitle": "Naudotojas yra užblokuotas",
- "blockedtext": "'''Jūsų naudotojo vardas arba IP adresas yra užblokuotas.'''\n\nUžblokavo $1. Nurodyta priežastis yra ''$2''.\n\n* Blokavimo pradžia: $8\n* Blokavimo pabaiga: $6\n* Numatytas blokuojamasis: $7\n\nJūs galite susisiekti su $1 arba kuriuo nors kitu [[{{MediaWiki:Grouppage-sysop}}|administratoriumi]] ir aptarti neaiškumus dėl blokavimo.\nAtkreipkite dėmesį, kad negalėsite naudotis funkcija „Rašyti laišką šiam naudotojui“, jei nesate užsiregistravę ir pateikę realaus savo el. pašto adreso naudotojo [[Special:Preferences|nustatymuose]], arba, jei jums užblokuotas šios funkcijos naudojimas.\nJūsų IP adresas yra $3, o blokavimo ID yra #$5.\nPrašome nurodyti vieną iš jų ar abu, kai kreipiatės dėl blokavimo.",
+ "blockedtext": "'''Jūsų naudotojo vardas arba IP adresas yra užblokuotas.'''\n\nUžblokavo $1. Nurodyta priežastis yra ''$2''.\n\n* Blokavimo pradžia: $8\n* Blokavimo pabaiga: $6\n* Užblokuotasis: $7\n\nJūs galite susisiekti su $1 arba kuriuo nors kitu [[{{MediaWiki:Grouppage-sysop}}|administratoriumi]] ir aptarti neaiškumus dėl blokavimo.\nAtkreipkite dėmesį, kad negalėsite naudotis funkcija „Rašyti laišką šiam naudotojui“, jei nesate užsiregistravę ir pateikę galiojančio el. pašto adreso naudotojo paskyros [[Special:Preferences|nustatymuose]], arba, jei jums užblokuotas šios funkcijos naudojimas.\nJūsų IP adresas yra $3, o blokavimo ID yra #$5.\nPrašome nurodyti vieną iš jų ar abu, kai kreipiatės dėl blokavimo.",
"autoblockedtext": "Jūsų IP adresas buvo automatiškai užblokuotas, nes jį naudojo kitas naudotojas, kurį užblokavo $1.\nNurodyta priežastis yra ši:\n\n:''$2''\n\n* Blokavimo pradžia: $8\n* Blokavimo pabaiga: $6\n* Numatomas blokavimo laikas: $7\n\nJūs galite susisiekti su $1 arba kitu [[{{MediaWiki:Grouppage-sysop}}|administratoriumi]], kad aptartumėte neaiškumus dėl blokavimo.\n\nJūs negalite naudotis funkcija „Rašyti laišką šiam naudotojui“, jei nesate nurodę tikro el. pašto adreso savo [[Special:Preferences|naudotojo nustatymuose]]. Taip pat Jūs negalite naudotis šia funkcija, jei Jums užblokuotas jos naudojimas.\n\nJūsų IP adresas yra $3, blokavimo ID yra $5.\nPrašome nurodyti šiuos duomenis visais atvejais, kai kreipiatės dėl blokavimo.",
"blockednoreason": "priežastis nenurodyta",
"whitelistedittext": "Jūs turite $1, kad redaguotumėte puslapius.",
"listfiles-delete": "trinti",
"listfiles-summary": "Šiame specialiame puslapyje rodomos visos įkeltos rinkmenos.",
"listfiles_search_for": "Ieškoti failo pavadinimo:",
- "listfiles-userdoesnotexist": "Vartotojo paskyrą „$1“ nėra registruota.",
+ "listfiles-userdoesnotexist": "Naudotojo paskyra „$1“ nėra užregistruota.",
"imgfile": "rinkmena",
"listfiles": "Failų sąrašas",
"listfiles_thumb": "Miniatiūra",
"logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|pervadino}} puslapį $3 į $4, nesukurdamas nukreipimo",
"logentry-patrol-patrol": "$1 {{GENDER:$2|pažymėjo}} kaip patikrintą puslapio $3 versiją $4",
"logentry-patrol-patrol-auto": "$1 automatiškai {{GENDER:$2|pažymėjo}} kaip patikrintą puslapio $3 versiją $4",
- "logentry-newusers-newusers": "Vartotojo paskyra $1 buvo {{GENDER:$2|sukurta}}",
+ "logentry-newusers-newusers": "Naudotojo paskyrą $1 {{GENDER:$2|sukūrė}}",
"logentry-newusers-create": "$1 sukūrė naudotojo paskyrą",
- "logentry-newusers-create2": "Vartotojo paskyra $3 buvo {{GENDER:$2|sukurta}} $1",
- "logentry-newusers-byemail": "Vartotojo paskyra $3 buvo {{GENDER:$2|sukurta}} $1 ir slaptažodis išsiųstas el. paštu",
- "logentry-newusers-autocreate": "Vartotojo paskyra $1 buvo {{GENDER:$2|sukurta}} automatiškai",
+ "logentry-newusers-create2": "$1 sukūrė naudotojo paskyrą $3",
+ "logentry-newusers-byemail": "Naudotojo paskyrą $3 {{GENDER:$2|sukūrė}} $1, slaptažodis išsiųstas el. paštu",
+ "logentry-newusers-autocreate": "Naudotojo paskyra $1 buvo {{GENDER:$2|sukurta}} automatiškai",
"logentry-protect-move_prot": "$1 {{GENDER:$2|perkėlė}} apsaugos nustatymus iš $4 į $3",
"logentry-protect-unprotect": "$1 {{GENDER:$2|atrakino}} $3",
"logentry-protect-protect": "$1 {{GENDER:$2|užrakino}} $3 $4",
"log-action-filter-delete": "Trynimo tipas:",
"log-action-filter-import": "Importo tipas:",
"log-action-filter-managetags": "Žymės tvarkymo veiksmo tipas:",
- "log-action-filter-move": "Kėlimo tipas:",
+ "log-action-filter-move": "Perkėlimo tipas:",
"log-action-filter-newusers": "Paskyros kūrimo tipas:",
+ "log-action-filter-patrol": "Patikrinimo tipas:",
"log-action-filter-protect": "Apsaugos tipas:",
"log-action-filter-rights": "Teisių tipo keitimas:",
"log-action-filter-upload": "Įkėlimo tipas:",
"log-action-filter-managetags-delete": "Žymės trynimas",
"log-action-filter-managetags-activate": "Žymės aktyvavimas",
"log-action-filter-managetags-deactivate": "Žymės deaktyvavimas",
+ "log-action-filter-move-move": "Perkėlimai, nepakeičiant nukreipimų",
+ "log-action-filter-move-move_redir": "Perkėlimai, pakeičiant buvusius nukreipimus",
"log-action-filter-newusers-autocreate": "Automatinis kūrimas",
+ "log-action-filter-patrol-patrol": "„Rankinis“ patikrinimas",
+ "log-action-filter-patrol-autopatrol": "Automatinis patikrinimas",
"log-action-filter-protect-protect": "Apsauga",
"log-action-filter-protect-modify": "Apsaugos keitimas",
"log-action-filter-protect-move_prot": "Apsauga perkelta",
"talk": "Diskusija",
"views": "Apskates",
"toolbox": "Rīki",
+ "tool-link-emailuser": "Nosūtīt e-pastu {{GENDER:$1|šim dalībniekam|šai dalībniecei}}",
"userpage": "Skatīt dalībnieka lapu",
"projectpage": "Skatīt projekta lapu",
"imagepage": "Skatīt faila lapu",
"category-file-count-limited": "खालील {{PLURAL:$1|संचिका|$1 संचिका}} या वर्गात आहेत.",
"listingcontinuesabbrev": "पुढे चला",
"index-category": "अनुक्रमित पाने",
- "noindex-category": "विना-à¤\85नà¥\81à¤\95à¥\8dरमित पाने",
+ "noindex-category": "à¤\85नà¥\81à¤\95à¥\8dरमित नसलà¥\87लà¥\80 पाने",
"broken-file-category": "तुटलेल्या संचिका दुव्यांसह असलेली पाने",
"about": "च्या विषयी",
"article": "आशयाचे पान",
"movelogpagetext": "स्थानांतरित केलेल्या पानांची यादी.",
"movesubpage": "{{PLURAL:$1|उपपान|उपपाने}}",
"movesubpagetext": "या पानास $1 {{PLURAL:$1|उपपान|उपपाने}} असून ती पुढे दर्शवली आहेत:",
+ "movesubpagetalktext": "संबंधित चर्चा पानाची $1 {{PLURAL:$1|उपपान|उपपाने}} खाली दर्शविली आहेत.",
"movenosubpage": "या पानात उपपाने नाहीत.",
"movereason": "कारण:",
"revertmove": "पूर्वपदास न्या",
"feedback-thanks": " \"[$2 $1]\" या पानात आपला पश्चप्रदाय (फिडबॅक) टाकत आहोत.",
"feedback-thanks-title": "आपणास धन्यवाद!",
"feedback-useragent": "सदस्य प्रतिनीधी:",
- "searchsuggest-search": "शोधा",
+ "searchsuggest-search": "शोधा {{SITENAME}}",
"searchsuggest-containing": ".......हे असलेले",
"api-error-badaccess-groups": "आपणास ह्या विकिवर संचिका चढवण्याची परवानगी नाही",
"api-error-badtoken": "अंतर्गत चूक: अयोग्य टोकन",
"randomrootpage": "अविशिष्ट मूळ पान",
"log-action-filter-suppress-block": "रोधामार्फत सदस्य दाबणे",
"changecredentials": "अधिकारपत्रे (क्रेडेंटियल्स)बदला",
- "removecredentials": "अधिकारपत्रे (क्रेडेंटियल्स) हटवा"
+ "removecredentials": "अधिकारपत्रे (क्रेडेंटियल्स) हटवा",
+ "edit-error-short": "त्रुटी: $1",
+ "edit-error-long": "त्रुटी:$1"
}
"tag-list-wrapper": "([[Special:Tags|$1 ê piau-chhiam]]: $2)",
"logentry-move-move": "$1 {{GENDER:$2|sóa}} $3 chit ia̍h khì $4",
"logentry-newusers-create": "已經{{GENDER:$2|開好}}用者口座 $1",
- "searchsuggest-search": "Chhoē",
+ "searchsuggest-search": "Chhoē {{SITENAME}}",
"expandtemplates": "Khok-chhiong pang-bô͘",
"expand_templates_input": "Su-ji̍p bûn-jī:",
"expand_templates_output": "Kiat-kó:",
"revdelete-confirm": "Por favor, confirme que pretende executar esta operação, que compreende as suas consequências e que o faz em concordância com as [[{{MediaWiki:Policy-url}}|políticas e recomendações]].",
"revdelete-suppress-text": "A supressão '''só''' deverá ser usada nos seguintes casos:\n* Informação potencialmente caluniosa, difamatória ou injuriosa\n* Informação pessoal imprópria\n*: ''endereços de domicílio e números de telefone, números de identificação nacional, etc''",
"revdelete-legend": "Definir restrições de visibilidade",
- "revdelete-hide-text": "Revisão do texto",
+ "revdelete-hide-text": "Texto da revisão",
"revdelete-hide-image": "Ocultar conteúdo do ficheiro",
"revdelete-hide-name": "Ocultar destino e parâmetros",
"revdelete-hide-comment": "Resumo da edição",
- "revdelete-hide-user": "Nome de utilizador/endereço de IP",
+ "revdelete-hide-user": "Nome de utilizador/endereço IP",
"revdelete-hide-restricted": "Ocultar dados dos administradores e de todos os outros",
"revdelete-radio-same": "(manter)",
"revdelete-radio-set": "Oculto",
"version-libraries-description": "Descrição",
"version-libraries-authors": "Autores",
"redirect": "Redirecionar por ficheiro, utilizador, página, revisão, ou ID de registo",
- "redirect-summary": "Esta página especial redireciona para um ficheiro (dado o nome do ficheiro), para uma página (dado um ID de revisão ou página) ou para uma página de utilizador (dado um ID numérico do utilizador), ou para uma entrada do registo (dado o ID do registo). Utilização: [[{{#Special:Redirect}}/file/Example.jpg]], \n[[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], ou [[{{#Special:Redirect}}/logid/186]].",
+ "redirect-summary": "Esta página especial redireciona para um ficheiro (dado o nome do ficheiro), para uma página (dado um ID de revisão ou página), para uma página de utilizador (dado um ID numérico do utilizador) ou para uma entrada do registo (dado o ID do registo). Utilização: [[{{#Special:Redirect}}/file/Example.jpg]], \n[[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], ou [[{{#Special:Redirect}}/logid/186]].",
"redirect-submit": "Ir",
"redirect-lookup": "Pesquisa:",
"redirect-value": "Valor:",
"tog-enotifrevealaddr": "پڌراين ۾ منهنجو برق ٽپال پتو ظاهر ڪريو.",
"tog-shownumberswatching": "ڏسندڙ يوزرس جو انگ ڏيکاريو",
"tog-oldsig": "توھان جو موجوده دستخط:",
+ "tog-fancysig": "صحيح کي وڪيٽيڪسٽ سمجھو (ڪنھن خوڪار ڳنڍڻي کانسواءِ)",
"tog-uselivepreview": "سڌي سنئين پيش نگاھہ استعمال ڪريو",
"tog-watchlisthideown": "زير نظر فهرست مان منهنجون ڪيل ترميمون لڪايو",
"tog-watchlisthidebots": "ٽيٽ فهرست تان بوٽ جون ترميمون لڪايو",
"tog-watchlisthideminor": "ٽيٽ فهرست تان معمولي ترميمون لڪايو",
"tog-watchlisthideliu": "لاگ اِن ٿيل يوزرس جون ڪيل ترميمون زيرنظر فهرست ۾ نہ ڏيکاريو",
"tog-watchlisthideanons": "ٽيٽ فهرست تان اڻڄاتل يوزر جون ترميمون لڪايو",
+ "tog-watchlisthidepatrolled": "نظرھيٺ فھرست مان گشت ڪيل ترميمون لڪايو",
"tog-watchlisthidecategorization": "صفحن جا زمرا لڪايو",
"tog-ccmeonemails": "ٻين يوزرس ڏانهن منهنجي موڪليل برق ٽپال جو پرت مون کي اماڻيو",
"tog-diffonly": "تفاوت هيٺان صفحي جو مواد نہ ڏيکاريو",
"talk": "بحث",
"views": "ڏيٺون",
"toolbox": "اوزارَ",
+ "tool-link-userrights": "{{GENDER:$1|يوزر}} گروھ تبديل ڪريو",
+ "tool-link-emailuser": "ھن {{GENDER:$1|يوزر}} ڏانھن برقٽپال موڪليو",
"userpage": "يوزر صفحو ڏسو",
"projectpage": "رٿائي صفحو ڏسو",
"imagepage": "ذريعاتي صفحو ڏسو",
"jumpto": "ڏانھن ٽپ ڏيو:",
"jumptonavigation": "رهنمائي",
"jumptosearch": "ڳولا",
+ "view-pool-error": "معذرت سان سرور هاڻي تمام گھڻو سُڪ آهي.\nتمام گھڻا يوزر ھن صفحي کي ڏسڻ جي ڪوشش ڪري رھيا آھن.\nمهرباني ڪري ٿورو ترسو انکان اڳ جو توھان ھن صفحي تائين رسڻ لاءِ ٻيھر ڪوشش ڪريو.\n\n$1",
"generic-pool-error": "معذرت سان سرور هاڻي تمام گھڻو سُڪ آهي.\nتمام گھڻا يوزر هتي موجود آهن.\nمهرباني ڪري ٿورو ترسي پوءِ ڪوشش ڪريو.",
"pool-errorunknown": "اڻ ڄاتل چُڪَ",
"poolcounter-usage-error": "استعمال جي خرابي: $1",
"retrievedfrom": "\"$1\" تان ورتل",
"youhavenewmessages": "توهان لاءِ $1 ($2) آهن.",
"youhavenewmessagesmanyusers": "توهان لاءِ ڪيترن ئي يُوزرس ($2) طرفان $1 آيل آهن.",
+ "newmessageslinkplural": "{{PLURAL:$1|ھڪ نئون پيغام|999=نوان پيغام}}",
+ "newmessagesdifflinkplural": "آخري {{PLURAL:$1|تبديلي|999=تبديليون}}",
"youhavenewmessagesmulti": "$1 تي توهان لاءِ نوان نياپا آهن",
"editsection": "سنواريو",
"editold": "سنواريو",
"databaseerror-query": "استفسار: $1",
"databaseerror-function": "ڪاڄ: $1",
"databaseerror-error": "چُڪَ: $1",
+ "laggedslavemode": "<strong>چتاءُ:</strong> صفحي ۾ ھاڻوڪيون تبديليون نه ھجڻ جو امڪان آھي.",
"readonly": "اعدادخانو بنديل",
"missingarticle-rev": "(ڀيرو#: $1)",
"missingarticle-diff": "(تفاوت: $1، $2)",
"createacct-yourpasswordagain-ph": "ٻيھر ڳجھولفظ داخل ڪريو",
"userlogin-remembermypassword": "مون کي داخل ٿيل رکو",
"userlogin-signwithsecure": "محفوظ ڳانڍاپو استعمال ڪريو",
+ "cannotlogin-title": "داخل نٿو ٿي سگھجي",
+ "cannotlogin-text": "داخل ٿيڻ ممڪن نه آھي",
"cannotloginnow-title": "ھاڻي داخل نٿو ٿي سگھجي",
"cannotloginnow-text": "$1 استعمال ڪرڻ دوران داخل ٿيڻ ممڪن نہ آھي.",
+ "cannotcreateaccount-title": "کاتا نٿو کولي سگھي",
"yourdomainname": "توهان جو ميدان:",
"password-change-forbidden": "هن وڪِي تي توهان ڳجھالفظ بدلائي نٿا سگھو.",
"login": "داخل ٿيو",
+ "login-security": "پنھنجي سڃاڻپ جي خاطري ڪريو",
"nav-login-createaccount": "داخل ٿيو / کاتو کوليو",
"userlogin": "داخل ٿيو / کاتو کوليو",
"userloginnocreate": "داخل ٿيو",
"userlogin-resetlink": "پنهنجي داخل ٿيڻ جا تفصيل وساري ويٺا؟",
"userlogin-resetpassword-link": "ڳجھولفظ وساري ويٺا آهيو؟",
"userlogin-helplink2": "داخل ٿيڻ ۾ مدد",
+ "userlogin-reauth": "اھو پڪ ڪرڻ لاءِ ته توھان {{GENDER:$1|$1}} آھيو توھان کي ٻيھر داخل ٿيڻو پوندو.",
"userlogin-createanother": "ٻيو کاتو کوليو",
"createacct-emailrequired": "برق ٽپال پتو",
"createacct-emailoptional": "برق ٽپال پتو (مرضيءَ موجب)",
"createacct-reason-ph": "توهان ٻيو کاتو ڇو کولي رهيا آهيو",
"createacct-submit": "پنهنجو کاتو کوليو",
"createacct-another-submit": "کاتو کوليو",
+ "createacct-continue-submit": "کاتو کولڻ جاري رکو",
+ "createacct-another-continue-submit": "کاتو کولڻ جاري رکو",
"createacct-benefit-heading": "{{SITENAME}} توهان جهڙن سڄڻن ٺاهيو آهي.",
"createacct-benefit-body1": "{{PLURAL:$1|ترميم|ترميمون}}",
"createacct-benefit-body2": "{{PLURAL:$1|صفحو|صفحا}}",
"createacct-another-realname-tip": "اصل نالو ڄاڻائڻ اختياري آهي. جيڪڏهن توهان اصل نالو ڄاڻايو ٿا، تہ اهو توهان کي توهان جي ڪم جي مڃتا ڏيڻ لاءِ ڪم آندو ويندو.",
"pt-login": "داخل ٿيو",
"pt-login-button": "داخل ٿيو",
+ "pt-login-continue-button": "داخل ٿيڻ جاري رکو",
"pt-createaccount": "کاتو کوليو",
"pt-userlogout": "ٻاھر نڪرو",
"php-mail-error-unknown": "پي ايڇ پي جي ڪاڄ اندر اڻڄاتل چُڪَ.",
"minoredit": "هيءَ هڪ معمولي ترميم آهي",
"watchthis": "هيءُ صفحو سانڍيو",
"savearticle": "صفحو سانڍيو",
+ "savechanges": "تبديليون سانڍيو",
+ "publishpage": "صفحو ڇاپيو",
+ "publishchanges": "تبديليون ڇاپيو",
"preview": "پيش نگاھ",
"showpreview": "پيش نگاھ",
"showdiff": "تبديليون ڏيکاريو",
"enotif_subject_created": "Страницу $1 на {{SITENAME}} {{GENDER:$2|направио је|направила је|направио је}} $2",
"enotif_subject_moved": "Страницу $1 на {{SITENAME}} {{GENDER:$2|преместио је|преместила је}} $2",
"enotif_subject_restored": "Страницу $1 на {{SITENAME}} {{GENDER:$2|вратио је|вратила је|вратио је}} $2",
- "enotif_subject_changed": "Страницу $1 на {{SITENAME}} {{GENDER:$2|променио је|променила је|променио је}} $2",
+ "enotif_subject_changed": "Страницу $1 на {{SITENAME}} {{GENDER:$2|променио|променила}} је $2",
"enotif_body_intro_deleted": "Страницу $1 на {{SITENAME}} {{GENDER:$2|обрисао|обрисала}} је $2 дана $PAGEEDITDATE Погледајте $3.",
"enotif_body_intro_created": "Страницу $1 на {{SITENAME}} {{GENDER:$2|направио|направила}} је $2 дана $PAGEEDITDATE Тренутна измена налази се на $3.",
"enotif_body_intro_moved": "Страницу $1 на {{SITENAME}} {{GENDER:$2|преместио|преместила}} је $2 дана $PAGEEDITDATE Тренутна измена налази се на $3.",
"enotif_subject_created": "Stranicu $1 na {{SITENAME}} {{GENDER:$2|napravio je|napravila je}} $2",
"enotif_subject_moved": "Stranicu $1 na {{SITENAME}} {{GENDER:$2|premestio je|premestila je}} $2",
"enotif_subject_restored": "Stranicu $1 na {{SITENAME}} {{GENDER:$2|vratio je|vratila je}} $2",
- "enotif_subject_changed": "Stranicu $1 na {{SITENAME}} {{GENDER:$2|promenio je|promenila je}} $2",
+ "enotif_subject_changed": "Stranicu $1 na {{SITENAME}} {{GENDER:$2|promenio|promenila}} je $2",
"enotif_body_intro_deleted": "Stranicu $1 na {{SITENAME}} {{GENDER:$2|obrisao|obrisala}} je $2 dana $PAGEEDITDATE Pogledajte $3.",
"enotif_body_intro_created": "Stranicu $1 na {{SITENAME}} {{GENDER:$2|napravio|napravila}} je $2 dana $PAGEEDITDATE Trenutna izmena nalazi se na $3.",
"enotif_body_intro_moved": "Stranicu $1 na {{SITENAME}} {{GENDER:$2|premestio|premestila}} je $2 dana $PAGEEDITDATE Trenutna izmena nalazi se na $3.",
"Cosine02",
"Arthur2e5",
"Myy730",
- "SolidBlock"
+ "SolidBlock",
+ "D41D8CD98F"
]
},
"tog-underline": "链接下划线:",
"lockedbyandtime": "(由 {{GENDER:$1|$1}} 于$2 $3执行)",
"move-page": "移动$1",
"move-page-legend": "移动页面",
- "movepagetext": "您可以使用下面的表单来重命名一个页面,同时将其版本历史移动到新页面。同时老的条目将会被重定向到新条目。您可以自动地将重定向更新到原条目。如果您不选择这样做的话,请检查[[Special:DoubleRedirects|双重]]或[[Special:BrokenRedirects|损坏重定向]]链接。您有责任确保链接会被正确指向他们应该被指向的地方。\n\n注意:即使新条目已经有对应页面,此页面也<strong>不会</strong>被移动,除非新页面无任何编辑历史或是重定向页。这意味着您可在误操作后将页面移回原处,同时,您也无法覆盖现有页面。\n\n<strong>注意:</strong>对这样一个经常被访问的页面而言这可能是一个重大且唐突的更改;请在行动前先了解您的修改可能带来的一切后果。",
+ "movepagetext": "您可以使用下面的表单来重命名一个页面,同时将其版本历史移动到新页面。同时老的条目将会被重定向到新条目。您可以自动地将指向老的条目的重定向更新为指向新条目。如果您不选择这样做的话,请检查[[Special:DoubleRedirects|双重]]或[[Special:BrokenRedirects|损坏重定向]]链接。您有责任确保链接会被正确指向他们应该被指向的地方。\n\n注意:如果新条目已经有对应页面,此页面将<strong>不会</strong>被移动,除非新页面是重定向页并且无任何修订历史。这意味着您可在误操作后将页面移回原处,同时,您无法覆盖现有页面。\n\n<strong>注意:</strong>对这样一个经常被访问的页面而言这可能是一个重大且唐突的更改;请在行动前先了解您的修改可能带来的一切后果。",
"movepagetext-noredirectfixer": "用下面的表单来重命名一个页面,并将其版本历史同时移动到新页面。老的页面将成为新页面的重定向页。请检查[[Special:DoubleRedirects|双重重定向]]或[[Special:BrokenRedirects|损坏重定向]]链接。您应当负责确定所有链接依然会链到指定的页面。\n\n注意如果新页面已经有内容的话,页面将<strong>不会</strong>被移动,除非新页面无内容或是重定向页,而且没有版本历史。这意味着您再必要时可以在移动到新页面后再移回老的页面,同时您也无法覆盖现有页面。\n\n<strong>注意:</strong>对一个经常被访问的页面而言这可能是一个重大与唐突的更改;请在行动前先确定您了解其所可能带来的后果。",
"movepagetalktext": "如果您勾选此框,相关联的讨论页将被自动移动到新的标题,除非这里已经有了一个非空讨论页。\n\n在这种情况下,如有需要,您将不得不手动移动或合并页面。",
"moveuserpage-warning": "'''警告:'''你将移动一个用户页面。请注意,只有该页面会被移动,该用户''不''会被更名。",
* @file
* @ingroup Maintenance
*/
+use MediaWiki\MediaWikiServices;
if ( !defined( 'RUN_MAINTENANCE_IF_MAIN' ) ) {
echo "This file must be included after Maintenance.php\n";
$maintenance->globals();
// Perform deferred updates.
+$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+$lbFactory->commitMasterChanges( $maintClass );
DeferredUpdates::doUpdates();
// log profiling info
wfLogProfilingData();
// Commit and close up!
-$factory = wfGetLBFactory();
-$factory->commitMasterChanges( 'doMaintenance' );
-$factory->shutdown( $factory::SHUTDOWN_NO_CHRONPROT );
+$lbFactory->commitMasterChanges( 'doMaintenance' );
+$lbFactory->shutdown( $lbFactory::SHUTDOWN_NO_CHRONPROT );
function convertCheckboxesToMulti( $oldContainer, type ) {
var $fieldLabel = $( '<td>' ),
- $td = $( '<td>' ),
- $fieldLabelText = $( '<label>' ),
- $container;
+ $td = $( '<td>' ),
+ $fieldLabelText = $( '<label>' ),
+ $container;
if ( type === 'tr' ) {
addMulti( $oldContainer, $td );
$container = $( '<tr>' );
return $container;
}
+ function convertCheckboxesWidgetToCapsules( fieldLayout ) {
+ var checkboxesWidget, checkboxesOptions, capsulesOptions, capsulesWidget;
+
+ checkboxesWidget = fieldLayout.fieldWidget;
+ checkboxesOptions = checkboxesWidget.checkboxMultiselectWidget.getItems();
+ capsulesOptions = checkboxesOptions.map( function ( option ) {
+ return new OO.ui.MenuOptionWidget( {
+ data: option.getData(),
+ label: option.getLabel()
+ } );
+ } );
+ capsulesWidget = new OO.ui.CapsuleMultiselectWidget( {
+ menu: {
+ items: capsulesOptions
+ }
+ } );
+ capsulesWidget.setItemsFromData( checkboxesWidget.getValue() );
+
+ // Data from CapsuleMultiselectWidget will not be submitted with the form, so keep the original
+ // CheckboxMultiselectInputWidget up-to-date.
+ capsulesWidget.on( 'change', function () {
+ checkboxesWidget.setValue( capsulesWidget.getItemsData() );
+ } );
+
+ // Hide original widget and add new one in its place. This is a bit hacky, since the FieldLayout
+ // still thinks it's connected to the old widget.
+ checkboxesWidget.toggle( false );
+ checkboxesWidget.$element.after( capsulesWidget.$element );
+ }
+
mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
- if ( $root.find( '.mw-htmlform-dropdown' ).length ) {
- mw.loader.using( 'jquery.chosen', function () {
- $root.find( '.mw-htmlform-dropdown' ).each( function () {
- var type = this.nodeName.toLowerCase(),
- $converted = convertCheckboxesToMulti( $( this ), type );
- $converted.find( '.htmlform-chzn-select' ).chosen( { width: 'auto' } );
- } );
+ var $dropdowns = $root.find( '.mw-htmlform-field-HTMLMultiSelectField.mw-htmlform-dropdown' );
+ if ( $dropdowns.length ) {
+ $dropdowns.each( function () {
+ var $el = $( this ),
+ data, modules, extraModules;
+ if ( $el.is( '[data-ooui]' ) ) {
+ // Load 'oojs-ui-widgets' for CapsuleMultiselectWidget
+ modules = [ 'mediawiki.htmlform.ooui', 'oojs-ui-widgets' ];
+ data = $el.data( 'mw-modules' );
+ if ( data ) {
+ // We can trust this value, 'data-mw-*' attributes are banned from user content in Sanitizer
+ extraModules = data.split( ',' );
+ modules.push.apply( modules, extraModules );
+ }
+ mw.loader.using( modules, function () {
+ /*jshint -W024*/
+ convertCheckboxesWidgetToCapsules( OO.ui.FieldLayout.static.infuse( $el ) );
+ } );
+ } else {
+ mw.loader.using( 'jquery.chosen', function () {
+ var type = $el.is( 'tr' ) ? 'tr' : 'div',
+ $converted = convertCheckboxesToMulti( $el, type );
+ $converted.find( '.htmlform-chzn-select' ).chosen( { width: 'auto' } );
+ } );
+ }
} );
}
} );
margin: 2px;
padding: 2px;
display: block;
+ width: -moz-fit-content;
+ width: -webkit-fit-content;
+ width: fit-content;
}
li.gallerycaption {