* (bug 26995) Update filename field on Upload page after having sanitized it.
* (bug 41793) Contribution links to users with 0 edits on Special:ListUsers didn't
show up red.
+* (bug 41899) A PHP notice no longer occurs when using the "rvcontinue" API parameter.
=== API changes in 1.21 ===
* prop=revisions can now report the contentmodel and contentformat, see docs/contenthandler.txt
wfProfileIn( 'img_auth.php' );
# Set action base paths so that WebRequest::getPathInfo()
-# recognizes the "X" as the 'title' in ../image_auth/X urls.
+# recognizes the "X" as the 'title' in ../img_auth.php/X urls.
$wgArticlePath = false; # Don't let a "/*" article path clober our action path
$wgActionPaths = array( "$wgUploadPath/" );
# Already initialized
return true;
}
+
+ wfProfileIn( __METHOD__ );
+
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow(
'category',
__METHOD__
);
+ wfProfileOut( __METHOD__ );
+
if ( !$row ) {
# Okay, there were no contents. Nothing to initialize.
if ( $this->mTitle ) {
}
/** @return mixed DB key name, or false on failure */
- public function getName() { return $this->getX( 'mName' ); }
+ public function getName() {
+ return $this->getX( 'mName' );
+ }
/** @return mixed Category ID, or false on failure */
- public function getID() { return $this->getX( 'mID' ); }
+ public function getID() {
+ return $this->getX( 'mID' );
+ }
/** @return mixed Total number of member pages, or false on failure */
- public function getPageCount() { return $this->getX( 'mPages' ); }
+ public function getPageCount() {
+ return $this->getX( 'mPages' );
+ }
/** @return mixed Number of subcategories, or false on failure */
- public function getSubcatCount() { return $this->getX( 'mSubcats' ); }
+ public function getSubcatCount() {
+ return $this->getX( 'mSubcats' );
+ }
/** @return mixed Number of member files, or false on failure */
- public function getFileCount() { return $this->getX( 'mFiles' ); }
+ public function getFileCount() {
+ return $this->getX( 'mFiles' );
+ }
/**
* @return Title|bool Title for this category, or false on failure.
*/
public function getTitle() {
- if ( $this->mTitle ) return $this->mTitle;
+ if ( $this->mTitle ) {
+ return $this->mTitle;
+ }
if ( !$this->initialize() ) {
return false;
* @return TitleArray object for category members.
*/
public function getMembers( $limit = false, $offset = '' ) {
+ wfProfileIn( __METHOD__ );
+
$dbr = wfGetDB( DB_SLAVE );
$conds = array( 'cl_to' => $this->getName(), 'cl_from = page_id' );
$options = array( 'ORDER BY' => 'cl_sortkey' );
if ( $limit ) {
- $options[ 'LIMIT' ] = $limit;
+ $options['LIMIT'] = $limit;
}
if ( $offset !== '' ) {
$conds[] = 'cl_sortkey > ' . $dbr->addQuotes( $offset );
}
- return TitleArray::newFromResult(
+ $result = TitleArray::newFromResult(
$dbr->select(
array( 'page', 'categorylinks' ),
array( 'page_id', 'page_namespace', 'page_title', 'page_len',
$options
)
);
+
+ wfProfileOut( __METHOD__ );
+
+ return $result;
}
/**
if ( !$this->initialize() ) {
return false;
}
- return $this-> { $key } ;
+ return $this->{$key};
}
/**
}
}
+ wfProfileIn( __METHOD__ );
+
$dbw = wfGetDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $dbw->begin( __METHOD__ );
# Insert the row if it doesn't exist yet (e.g., this is being run via
# update.php from a pre-1.16 schema). TODO: This will cause lots and
$result = $dbw->selectRow(
array( 'categorylinks', 'page' ),
array( 'pages' => 'COUNT(*)',
- 'subcats' => "COUNT($cond1)",
- 'files' => "COUNT($cond2)"
+ 'subcats' => "COUNT($cond1)",
+ 'files' => "COUNT($cond2)"
),
array( 'cl_to' => $this->mName, 'page_id = cl_from' ),
__METHOD__,
);
$dbw->commit( __METHOD__ );
+ wfProfileOut( __METHOD__ );
+
# Now we should update our local counts.
$this->mPages = $result->pages;
$this->mSubcats = $result->subcats;
/**
* Add a subcategory to the internal lists, using a title object
- * @deprecated since 1.17 kept for compatibility, please use addSubcategoryObject instead
+ * @deprecated since 1.17 kept for compatibility, use addSubcategoryObject instead
*/
function addSubcategory( Title $title, $sortkey, $pageLength ) {
wfDeprecated( __METHOD__, '1.17' );
}
/**
- * Get the character to be used for sorting subcategories.
- * If there's a link from Category:A to Category:B, the sortkey of the resulting
- * entry in the categorylinks table is Category:A, not A, which it SHOULD be.
- * Workaround: If sortkey == "Category:".$title, than use $title for sorting,
- * else use sortkey...
- *
- * @param Title $title
- * @param string $sortkey The human-readable sortkey (before transforming to icu or whatever).
+ * Get the character to be used for sorting subcategories.
+ * If there's a link from Category:A to Category:B, the sortkey of the resulting
+ * entry in the categorylinks table is Category:A, not A, which it SHOULD be.
+ * Workaround: If sortkey == "Category:".$title, than use $title for sorting,
+ * else use sortkey...
+ *
+ * @param Title $title
+ * @param string $sortkey The human-readable sortkey (before transforming to icu or whatever).
* @return string
*/
function getSubcategorySortChar( $title, $sortkey ) {
function finaliseCategoryState() {
if ( $this->flip['subcat'] ) {
- $this->children = array_reverse( $this->children );
+ $this->children = array_reverse( $this->children );
$this->children_start_char = array_reverse( $this->children_start_char );
}
if ( $this->flip['page'] ) {
- $this->articles = array_reverse( $this->articles );
+ $this->articles = array_reverse( $this->articles );
$this->articles_start_char = array_reverse( $this->articles_start_char );
}
if ( !$this->showGallery && $this->flip['file'] ) {
- $this->imgsNoGallery = array_reverse( $this->imgsNoGallery );
+ $this->imgsNoGallery = array_reverse( $this->imgsNoGallery );
$this->imgsNoGallery_start_char = array_reverse( $this->imgsNoGallery_start_char );
}
}
'page_is_redirect', 'cl_sortkey', 'cat_id', 'cat_title',
'cat_subcats', 'cat_pages', 'cat_files',
'cl_sortkey_prefix', 'cl_collation' ),
- array_merge( array( 'cl_to' => $this->title->getDBkey() ), $extraConds ),
+ array_merge( array( 'cl_to' => $this->title->getDBkey() ), $extraConds ),
__METHOD__,
array(
'USE INDEX' => array( 'categorylinks' => 'cl_sortkey' ),
'ORDER BY' => $this->flip[$type] ? 'cl_sortkey DESC' : 'cl_sortkey',
),
array(
- 'categorylinks' => array( 'INNER JOIN', 'cl_from = page_id' ),
+ 'categorylinks' => array( 'INNER JOIN', 'cl_from = page_id' ),
'category' => array( 'LEFT JOIN', 'cat_title = page_title AND page_namespace = ' . NS_CATEGORY )
)
);
*/
function formatList( $articles, $articles_start_char, $cutoff = 6 ) {
$list = '';
- if ( count ( $articles ) > $cutoff ) {
+ if ( count( $articles ) > $cutoff ) {
$list = self::columnList( $articles, $articles_start_char );
} elseif ( count( $articles ) > 0 ) {
// for short lists of articles in categories.
$pageLang = $this->title->getPageLanguage();
$attribs = array( 'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(),
- 'class' => 'mw-content-'.$pageLang->getDir() );
+ 'class' => 'mw-content-' . $pageLang->getDir() );
$list = Html::rawElement( 'div', $attribs, $list );
return $list;
);
}
- return $this->msg('categoryviewer-pagedlinks')->rawParams($prevLink, $nextLink)->escaped();
+ return $this->msg( 'categoryviewer-pagedlinks' )->rawParams( $prevLink, $nextLink )->escaped();
}
/**
return Title::makeTitle( $title->getNamespace(),
$title->getDBkey(), $fragment );
}
+
/**
* What to do if the category table conflicts with the number of results
* returned? This function says what. Each type is considered independently
$fromOrUntil = true;
}
- if ( $dbcnt == $rescnt || ( ( $rescnt == $this->limit || $fromOrUntil )
- && $dbcnt > $rescnt ) ) {
+ if ( $dbcnt == $rescnt ||
+ ( ( $rescnt == $this->limit || $fromOrUntil ) && $dbcnt > $rescnt )
+ ) {
# Case 1: seems sane.
$totalcnt = $dbcnt;
} elseif ( $rescnt < $this->limit && !$fromOrUntil ) {
*
* Example use :
* <code>
- * # Determines whether the article with the page_id 12345 is in both
- * # "Category 1" and "Category 2" or their subcategories, respectively
+ * # Determines whether the article with the page_id 12345 is in both
+ * # "Category 1" and "Category 2" or their subcategories, respectively
*
- * $cf = new Categoryfinder;
- * $cf->seed(
- * array( 12345 ),
- * array( 'Category 1', 'Category 2' ),
- * 'AND'
- * );
- * $a = $cf->run();
- * print implode( ',' , $a );
+ * $cf = new Categoryfinder;
+ * $cf->seed(
+ * array( 12345 ),
+ * array( 'Category 1', 'Category 2' ),
+ * 'AND'
+ * );
+ * $a = $cf->run();
+ * print implode( ',' , $a );
* </code>
*
*/
# iterate through the parents
foreach ( $this->parents[$id] as $p ) {
- $pname = $p->cl_to ;
+ $pname = $p->cl_to;
# Is this a condition?
if ( isset( $conds[$pname] ) ) {
* Scans a "parent layer" of the articles/categories in $this->next
*/
function scan_next_layer() {
+ wfProfileIn( __METHOD__ );
+
# Find all parents of the article currently in $this->next
$layer = array();
$res = $this->dbr->select(
foreach ( $layer as $v ) {
$this->deadend[$v] = $v;
}
- }
+ wfProfileOut( __METHOD__ );
+ }
}
* container : backend container name the zone is in
* directory : root path within container for the zone
* url : base URL to the root of the zone
- * handlerUrl : base script handled URL to the root of the zone
+ * urlsByExt : map of file extension types to base URLs
+ * (useful for using a different cache for videos)
+ * handlerUrl : base script-handled URL to the root of the zone
* (see FileRepo::getZoneHandlerUrl() function)
* Zones default to using "<repo name>-<zone name>" as the container name
* and default to using the container root as the zone's root directory.
*/
$wgUseCombinedLoginLink = false;
+/**
+ * Appearance of user page and talk page labels in personal tools.
+ * - true = combine links into a single label
+ * - false = keep links in separate labels
+ */
+$wgVectorCombineUserTalk = false;
+
/**
* Search form look for Vector skin only.
* - true = use an icon search button
*/
public static function newFromConds( $conds, $fname = __METHOD__ ) {
$dbr = wfGetDB( DB_SLAVE );
- $row = $dbr->selectRow( 'recentchanges', '*', $conds, $fname );
+ $row = $dbr->selectRow( 'recentchanges', self::selectFields(), $conds, $fname );
if ( $row !== false ) {
return self::newFromRow( $row );
} else {
}
}
+ /**
+ * Return the list of recentchanges fields that should be selected to create
+ * a new recentchanges object.
+ * @return array
+ */
+ public static function selectFields() {
+ return array(
+ 'rc_id',
+ 'rc_timestamp',
+ 'rc_cur_time',
+ 'rc_user',
+ 'rc_user_text',
+ 'rc_namespace',
+ 'rc_title',
+ 'rc_comment',
+ 'rc_minor',
+ 'rc_bot',
+ 'rc_new',
+ 'rc_cur_id',
+ 'rc_this_oldid',
+ 'rc_last_oldid',
+ 'rc_type',
+ 'rc_patrolled',
+ 'rc_ip',
+ 'rc_old_len',
+ 'rc_new_len',
+ 'rc_deleted',
+ 'rc_logid',
+ 'rc_log_type',
+ 'rc_log_action',
+ 'rc_params',
+ );
+ }
+
# Accessors
/**
* Returns null if no such revision can be found.
*
* $flags include:
- * Revision::READ_LATEST : Select the data from the master
+ * Revision::READ_LATEST : Select the data from the master (since 1.20)
* Revision::READ_LOCKING : Select & lock the data from the master
*
* @param $revId Integer
// rvstart and rvstartid when that is supplied.
if ( !is_null( $params['continue'] ) ) {
$params['startid'] = $params['continue'];
- unset( $params['start'] );
+ $params['start'] = null;
}
// This code makes an assumption that sorting by rev_id and rev_timestamp produces
return $status;
}
- if ( file_exists( $dest ) ) {
- $ok = unlink( $dest );
- if ( !$ok ) {
- $status->fatal( 'backend-fail-delete', $params['dst'] );
- return $status;
- }
- }
-
if ( !empty( $params['async'] ) ) { // deferred
- $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
+ $cmd = implode( ' ', array(
+ wfIsWindows() ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
wfEscapeShellArg( $this->cleanPathSlashes( $params['src'] ) ),
wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
) );
return $status; // do nothing; either OK or bad status
}
- if ( file_exists( $dest ) ) {
- $ok = unlink( $dest );
- if ( !$ok ) {
- $status->fatal( 'backend-fail-delete', $params['dst'] );
- return $status;
- }
- }
-
if ( !empty( $params['async'] ) ) { // deferred
- $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
+ $cmd = implode( ' ', array(
+ wfIsWindows() ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
wfEscapeShellArg( $this->cleanPathSlashes( $source ) ),
wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
) );
return $status; // do nothing; either OK or bad status
}
- if ( file_exists( $dest ) ) {
- // Windows does not support moving over existing files
- if ( wfIsWindows() ) {
- $ok = unlink( $dest );
- if ( !$ok ) {
- $status->fatal( 'backend-fail-delete', $params['dst'] );
- return $status;
- }
- }
- }
-
if ( !empty( $params['async'] ) ) { // deferred
- $cmd = implode( ' ', array( wfIsWindows() ? 'MOVE' : 'mv',
+ $cmd = implode( ' ', array(
+ wfIsWindows() ? 'MOVE /Y' : 'mv', // (overwrite)
wfEscapeShellArg( $this->cleanPathSlashes( $source ) ),
wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
) );
}
if ( !empty( $params['async'] ) ) { // deferred
- $cmd = implode( ' ', array( wfIsWindows() ? 'DEL' : 'unlink',
+ $cmd = implode( ' ', array(
+ wfIsWindows() ? 'DEL' : 'unlink',
wfEscapeShellArg( $this->cleanPathSlashes( $source ) )
) );
$status->value = new FSFileOpHandle( $this, $params, 'Copy', $cmd );
return $status;
}
- if ( file_exists( $dest ) ) {
- $ok = unlink( $dest );
- if ( !$ok ) {
- $status->fatal( 'backend-fail-delete', $params['dst'] );
- return $status;
- }
- }
-
if ( !empty( $params['async'] ) ) { // deferred
$tempFile = TempFSFile::factory( 'create_', 'tmp' );
if ( !$tempFile ) {
$status->fatal( 'backend-fail-create', $params['dst'] );
return $status;
}
- $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
+ $cmd = implode( ' ', array(
+ wfIsWindows() ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
wfEscapeShellArg( $this->cleanPathSlashes( $tempFile->getPath() ) ),
wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
) );
*/
abstract public function getLocalCopyMulti( array $params );
+ /**
+ * Return an HTTP URL to a given file that requires no authentication to use.
+ * The URL may be pre-authenticated (via some token in the URL) and temporary.
+ * This will return null if the backend cannot make an HTTP URL for the file.
+ *
+ * This is useful for key/value stores when using scripts that seek around
+ * large files and those scripts (and the backend) support HTTP Range headers.
+ * Otherwise, one would need to use getLocalReference(), which involves loading
+ * the entire file on to local disk.
+ *
+ * @param $params Array
+ * $params include:
+ * - src : source storage path
+ * @return string|null
+ * @since 1.21
+ */
+ abstract public function getFileHttpUrl( array $params );
+
/**
* Check if a directory exists at a given storage path.
* Backends using key/value stores will check if the path is a
return $tempFiles;
}
+ /**
+ * @see FileBackend::getFileHttpUrl()
+ * @return string|null
+ */
+ public function getFileHttpUrl( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getFileHttpUrl( $realParams );
+ }
+
/**
* @see FileBackend::directoryExists()
* @param $params array
*/
abstract protected function doGetLocalCopyMulti( array $params );
+ /**
+ * @see FileBackend::getFileHttpUrl()
+ * @return string|null
+ */
+ public function getFileHttpUrl( array $params ) {
+ return null; // not supported
+ }
+
/**
* @see FileBackend::streamFile()
* @return Status
/** @var CF_Authentication */
protected $auth; // Swift authentication handler
protected $authTTL; // integer seconds
+ protected $swiftTempUrlKey; // string; shared secret value for making temp urls
protected $swiftAnonUser; // string; username to handle unauthenticated requests
protected $swiftUseCDN; // boolean; whether CloudFiles CDN is enabled
protected $swiftCDNExpiry; // integer; how long to cache things in the CDN
* - swiftUser : Swift user used by MediaWiki (account:username)
* - swiftKey : Swift authentication key for the above user
* - swiftAuthTTL : Swift authentication TTL (seconds)
+ * - swiftTempUrlKey : Swift "X-Account-Meta-Temp-URL-Key" value on the account.
+ * Do not set this until it has been set in the backend.
* - swiftAnonUser : Swift user used for end-user requests (account:username).
* If set, then views of public containers are assumed to go
* through this user. If not set, then public containers are
$this->swiftAnonUser = isset( $config['swiftAnonUser'] )
? $config['swiftAnonUser']
: '';
+ $this->swiftTempUrlKey = isset( $config['swiftTempUrlKey'] )
+ ? $config['swiftTempUrlKey']
+ : '';
$this->shardViaHashLevels = isset( $config['shardViaHashLevels'] )
? $config['shardViaHashLevels']
: '';
return $tmpFiles;
}
+ /**
+ * @see FileBackendStore::getFileHttpUrl()
+ * @return string|null
+ */
+ public function getFileHttpUrl( array $params ) {
+ if ( $this->swiftTempUrlKey != '' ) { // temp urls enabled
+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
+ if ( $srcRel === null ) {
+ return null; // invalid path
+ }
+ try {
+ $sContObj = $this->getContainer( $srcCont );
+ $obj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
+ return $obj->get_temp_url( $this->swiftTempUrlKey, 86400, "GET" );
+ } catch ( NoSuchContainerException $e ) {
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, null, __METHOD__, $params );
+ }
+ }
+ return null;
+ }
+
/**
* @see FileBackendStore::directoriesAreVirtual()
* @return bool
try {
$dbw->insert( 'filejournal', $data, __METHOD__ );
+ if ( mt_rand( 0, 99 ) == 0 ) {
+ $this->purgeOldLogs(); // occasionally delete old logs
+ }
} catch ( DBError $e ) {
$status->fatal( 'filejournal-fail-dbquery', $this->backend );
return $status;
if ( !isset( $this->zones[$zone]['directory'] ) ) {
$this->zones[$zone]['directory'] = '';
}
+ if ( !isset( $this->zones[$zone]['urlsByExt'] ) ) {
+ $this->zones[$zone]['urlsByExt'] = array();
+ }
}
}
/**
* Get the URL corresponding to one of the four basic zones
*
- * @param $zone String: one of: public, deleted, temp, thumb
+ * @param $zone String One of: public, deleted, temp, thumb
+ * @param $ext String|null Optional file extension
* @return String or false
*/
- public function getZoneUrl( $zone ) {
- if ( isset( $this->zones[$zone]['url'] )
- && in_array( $zone, array( 'public', 'temp', 'thumb' ) ) )
- {
- return $this->zones[$zone]['url']; // custom URL
+ public function getZoneUrl( $zone, $ext = null ) {
+ if ( in_array( $zone, array( 'public', 'temp', 'thumb' ) ) ) { // standard public zones
+ if ( $ext !== null && isset( $this->zones[$zone]['urlsByExt'][$ext] ) ) {
+ return $this->zones[$zone]['urlsByExt'][$ext]; // custom URL for extension/zone
+ } elseif ( isset( $this->zones[$zone]['url'] ) ) {
+ return $this->zones[$zone]['url']; // custom URL for zone
+ }
}
switch ( $zone ) {
case 'public':
public function getUrl() {
if ( !isset( $this->url ) ) {
$this->assertRepoDefined();
- $this->url = $this->repo->getZoneUrl( 'public' ) . '/' . $this->getUrlRel();
+ $ext = $this->getExtension();
+ $this->url = $this->repo->getZoneUrl( 'public', $ext ) . '/' . $this->getUrlRel();
}
return $this->url;
}
*/
function getArchiveUrl( $suffix = false ) {
$this->assertRepoDefined();
- $path = $this->repo->getZoneUrl( 'public' ) . '/archive/' . $this->getHashPath();
+ $ext = $this->getExtension();
+ $path = $this->repo->getZoneUrl( 'public', $ext ) . '/archive/' . $this->getHashPath();
if ( $suffix === false ) {
$path = substr( $path, 0, -1 );
} else {
*/
function getArchiveThumbUrl( $archiveName, $suffix = false ) {
$this->assertRepoDefined();
- $path = $this->repo->getZoneUrl( 'thumb' ) . '/archive/' .
+ $ext = $this->getExtension();
+ $path = $this->repo->getZoneUrl( 'thumb', $ext ) . '/archive/' .
$this->getHashPath() . rawurlencode( $archiveName ) . "/";
if ( $suffix === false ) {
$path = substr( $path, 0, -1 );
*/
function getThumbUrl( $suffix = false ) {
$this->assertRepoDefined();
- $path = $this->repo->getZoneUrl( 'thumb' ) . '/' . $this->getUrlRel();
+ $ext = $this->getExtension();
+ $path = $this->repo->getZoneUrl( 'thumb', $ext ) . '/' . $this->getUrlRel();
if ( $suffix !== false ) {
$path .= '/' . rawurlencode( $suffix );
}
* @author Beta16
* @author Darth Kule
* @author F. Cosoleto
+ * @author Gianfranco
* @author Karika
*/
$messages['it'] = array(
'config-using-server' => 'Nome server in uso "<nowiki>$1</nowiki>".',
'config-using-uri' => 'URL del server in uso "<nowiki>$1$2</nowiki>".',
'config-db-type' => 'Tipo di database:',
+ 'config-db-wiki-settings' => 'Identifica questo wiki',
'config-db-name' => 'Nome del database:',
+ 'config-db-name-oracle' => 'Schema del database:',
+ 'config-db-username' => 'Nome utente del database:',
'config-db-password-empty' => 'Inserire una password per il nuovo utente del database: $1.
Anche se può essere possibile creare utenti senza password, questo non è sicuro.',
'config-db-install-help' => "Inserire il nome utente e la password che verranno usate per la connessione al database durante il processo d'installazione.",
+ 'config-db-prefix' => 'Prefisso tabella del database:',
'config-db-charset' => 'Set di caratteri del database',
'config-charset-mysql5' => 'MySQL 4.1/5.0 UTF-8',
'config-charset-mysql4' => 'MySQL 4.0 con compatibilità UTF-8',
'config-header-oracle' => 'Impostazioni Oracle',
'config-header-ibm_db2' => 'Impostazioni IBM DB2',
'config-invalid-db-type' => 'Tipo di database non valido',
+ 'config-db-web-account' => "Account del database per l'accesso web",
'config-db-web-create' => "Crea l'account se non esiste già",
'config-mysql-engine' => 'Storage engine:',
'config-mysql-innodb' => 'InnoDB',
'config-mysql-myisam' => 'MyISAM',
'config-mysql-charset' => 'Set di caratteri del database:',
+ 'config-mysql-binary' => 'Binario',
'config-mysql-utf8' => 'UTF-8',
'config-ibm_db2-low-db-pagesize' => "Il database DB2 in uso ha una tablespace predefinita con un insufficiente pagesize, che dovrebbe essere '''32K''' o maggiore.",
'config-ns-generic' => 'Progetto',
'config-ns-site-name' => 'Stesso nome wiki: $1',
+ 'config-ns-other-default' => 'MyWiki',
'config-admin-box' => 'Account amministratore',
'config-admin-name' => 'Tuo nome:',
'config-admin-password' => 'Password:',
Inserire un indirizzo email se si desidera effettuare l'iscrizione alla mailing list.",
'config-almost-done' => 'Hai quasi finito!
Adesso puoi saltare la rimanente parte della configurazione e semplicemente installare la wiki.',
+ 'config-optional-continue' => 'Fammi altre domande.',
'config-profile-wiki' => 'Wiki tradizionale',
'config-profile-no-anon' => 'Creazione utenza obbligatoria',
'config-profile-fishbowl' => 'Solo editori autorizzati',
In precedenza Wikipedia ha utilizzato la GNU Free Documentation License. La GFDL è una licenza valida, ma è di difficile comprensione e complica il riutilizzo dei contenuti.",
'config-email-settings' => 'Impostazioni email',
+ 'config-email-user' => 'Abilita invio email fra utenti',
'config-email-auth' => 'Abilita autenticazione via email',
+ 'config-upload-enable' => 'Consentire il caricamento di file',
'config-upload-deleted' => 'Directory per i file cancellati:',
'config-logo' => 'URL del logo:',
+ 'config-instantcommons' => 'Abilita Instant Commons',
'config-cc-again' => 'Seleziona di nuovo...',
'config-cc-not-chosen' => 'Scegliere quale licenza Creative Commons si desidera e cliccare su "procedi".',
'config-advanced-settings' => 'Configurazione avanzata',
+ 'config-memcache-needservers' => 'È stato selezionato il tipo di caching Memcached, ma non è stato impostato alcun server.',
'config-memcache-badip' => 'È stato inserito un indirizzo IP non valido per Memcached: $1.',
'config-extensions' => 'Estensioni',
'config-install-step-done' => 'fatto',
'config-install-step-failed' => 'non riuscito',
+ 'config-install-schema' => 'Creazione dello schema',
+ 'config-install-user' => 'Creazione di utente del database',
'config-install-user-alreadyexists' => 'L\'utente "$1" è già presente',
'config-install-user-create-failed' => 'Creazione dell\'utente "$1" non riuscita: $2',
'config-install-user-missing' => 'L\'utente indicato "$1" non esiste.',
'config-install-tables-failed' => "'''Errore''': La creazione della tabella non è riuscita: $1",
+ 'config-install-interwiki' => 'Riempimento della tabella interwiki predefinita',
'config-install-interwiki-list' => 'Impossibile leggere il file <code>interwiki.list</code>.',
'config-install-stats' => 'Inizializzazione delle statistiche',
'config-install-keys' => 'Generazione delle chiavi segrete',
}
/**
- * @return bool Quickly check if the queue is empty
+ * Quickly check if the queue is empty.
+ * Queue classes should use caching if they are any slower without memcached.
+ *
+ * @return bool
*/
final public function isEmpty() {
wfProfileIn( __METHOD__ );
* @since 1.21
*/
class JobQueueDB extends JobQueue {
- const CACHE_TTL = 30; // integer; seconds
+ const CACHE_TTL = 300; // integer; seconds
const MAX_JOB_RANDOM = 2147483647; // 2^31 - 1; used for job_random
/**
$dbw->setFlag( $autoTrx ? DBO_TRX : 0 ); // restore automatic begin()
}
- $wgMemc->set( $key, 'false', $ttl );
+ $wgMemc->set( $key, 'false', $ttl ); // queue is not empty
} );
}
return array_diff( $this->getQueueTypes(), $wgJobTypesExcludedFromDefaultQueue );
}
+
+ /**
+ * @return Array List of job types that have non-empty queues
+ */
+ public function getQueuesWithJobs() {
+ $types = array();
+ foreach ( $this->getQueueTypes() as $type ) {
+ if ( !$this->get( $type )->isEmpty() ) {
+ $types[] = $type;
+ }
+ }
+ return $types;
+ }
}
* @return Mixed
*/
public function get( $key ) {
+ wfProfileIn( __METHOD__ );
$this->debugLog( "get($key)" );
- return $this->checkResult( $key, parent::get( $key ) );
+ $value = $this->checkResult( $key, parent::get( $key ) );
+ wfProfileOut( __METHOD__ );
+ return $value;
}
/**
* @return Array
*/
public function getMulti( array $keys ) {
+ wfProfileIn( __METHOD__ );
$this->debugLog( 'getMulti(' . implode( ', ', $keys ) . ')' );
$callback = array( $this, 'encodeKey' );
$result = $this->client->getMulti( array_map( $callback, $keys ) );
+ wfProfileOut( __METHOD__ );
return $this->checkResult( false, $result );
}
DoubleRedirectJob::fixRedirects( 'move', $ot, $nt );
}
- wfRunHooks( 'SpecialMovepageAfterMove', array( &$this, &$ot, &$nt ) );
-
$out = $this->getOutput();
$out->setPageTitle( $this->msg( 'pagemovedsub' ) );
$newLink )->params( $oldText, $newText )->parseAsBlock() );
$out->addWikiMsg( $msgName );
+ wfRunHooks( 'SpecialMovepageAfterMove', array( &$this, &$ot, &$nt ) );
+
# Now we move extra pages we've been asked to move: subpages and talk
# pages. First, if the old page or the new page is a talk page, we
# can't move any talk pages: cancel that.
$invert = $opts['invert'];
$associated = $opts['associated'];
- $fields = array( $dbr->tableName( 'recentchanges' ) . '.*' ); // all rc columns
+ $fields = RecentChange::selectFields();
// JOIN on watchlist for users
if ( $uid ) {
$tables[] = 'watchlist';
$dbkey = $title->getDBkey();
$tables = array( 'recentchanges' );
- $select = array( $dbr->tableName( 'recentchanges' ) . '.*' );
+ $select = RecentChange::selectFields();
$join_conds = array();
$query_options = array();
$form .= '<hr />';
$tables = array( 'recentchanges', 'watchlist' );
- $fields = array( $dbr->tableName( 'recentchanges' ) . '.*' );
+ $fields = RecentChange::selectFields();
$join_conds = array(
'watchlist' => array(
'INNER JOIN',
*
* @author Cekli829
* @author Don Alessandro
+ * @author E THP
* @author Emperyan
* @author Erdemaslancan
* @author Gulmammad
# Preferences page
'preferences' => 'Nizamlamalar',
-'mypreferences' => 'Nizamlamalarım',
+'mypreferences' => 'Nizamlamalar',
'prefs-edits' => 'Redaktələrin sayı:',
'prefsnologin' => 'Daxil olmamısınız',
'prefsnologintext' => 'Nizamlamaları dəyişmək üçün <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} daxil olmaq]</span> zəruridir.',
'cancel' => 'Kanselaron',
'moredotdotdot' => 'Kadagdagan...',
'mypage' => 'An sakóng pahina',
-'mytalk' => 'An sakóng olay',
+'mytalk' => 'Orolayan',
'anontalk' => 'Olay para kaining IP address',
'navigation' => 'Nabigasyon',
'and' => ' asin',
# Preferences page
'preferences' => 'Mga kabòtan',
-'mypreferences' => 'Mga kabòtan ko',
+'mypreferences' => 'Mga Kamuyahan ko',
'prefs-edits' => 'Bilang kan mga hirá:',
'prefsnologin' => 'Dai nakalaog',
'prefsnologintext' => 'Ika dapat na magin <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} nakalaog na]</span> tanganing tuytuyon an mga kabotan nin paragamit.',
# Watchlist
'watchlist' => 'Pigbabantayan ko',
-'mywatchlist' => 'Babantáyan ko',
+'mywatchlist' => 'Bantay-listahan',
'watchlistfor2' => 'Para ki $1 $2',
'nowatchlist' => 'Mayo ka man na mga bagay saimong lista nin pigbabantayan.',
'watchlistanontext' => 'Mag $1 tabi para mahiling o maghira nin mga bagay saimong lista nin mga pigbabantayan.',
# Contributions
'contributions' => 'Mga kontribusyon kan parágamit',
'contributions-title' => 'Mga kontribusyon kan paragamit para sa $1',
-'mycontris' => 'Mga ambág ko',
+'mycontris' => 'Mga Kaarambagan',
'contribsub2' => 'Para sa $1 ($2)',
'nocontribs' => 'Mayong mga pagbabago na nahanap na kapadis sa ining mga criteria.',
'uctop' => '(alituktok)',
'whatlinkshere-hideredirs' => '$1 mga panukdong otro',
'whatlinkshere-hidetrans' => '$1 kabaling-binalyuhan',
'whatlinkshere-hidelinks' => '$1 mga kasugpon',
-'whatlinkshere-hideimages' => '$1 mga kasugpon kan imahe',
+'whatlinkshere-hideimages' => '$1 mga kasugpon nin mga sagunson',
'whatlinkshere-filters' => 'Mga pansarà',
# Block/unblock
'ipb-confirm' => 'Confirma el blocatge',
'badipaddress' => "L'adreça IP no té el format correcte.",
'blockipsuccesssub' => "S'ha blocat amb èxit",
-'blockipsuccesstext' => "[[Special:Contributions/$1|$1]] ha estat {{GENDER:$1|bloquejat|bloquejada|bloquejat/da}}.<br />
-Vegeu la [[Special:BlockList|llista d'IP blocades]] per revisar els bloqueigs.",
+'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] ha estat {{GENDER:$1|blocat|blocada}}.<br />
+Vegeu la [[Special:BlockList|llista de bloqueigs]] per revisar-los.',
'ipb-blockingself' => 'Esteu a punt de blocar-vos a vós mateix! Esteu segurs de voler-ho fer?',
'ipb-confirmhideuser' => "Esteu a punt de bloquejar un usuari que està marcat amb l'opció «amaga l'usuari». Això suprimirà el seu nom a totes les llistes i registres. Esteu segurs de voler-ho fer?",
'ipb-edit-dropdown' => 'Edita les raons per a blocar',
# Font style option in Special:Preferences
'editfont-style' => 'Cayê vurnayışi de terzê nuştışi:',
-'editfont-default' => 'Cıgeyrayoğo hesıbyaye',
+'editfont-default' => 'Cıgeyrayoğo hesabiyaye',
'editfont-monospace' => 'Terzê nusteyê sabıtcagırewtoği',
'editfont-sansserif' => 'Babetê Sans-serifi',
'editfont-serif' => 'Babetê serifi',
'newwindow' => '(pençereyê newey de beno a)',
'cancel' => 'Bıtexelne',
'moredotdotdot' => 'Vêşi...',
-'mypage' => 'Pela mı',
-'mytalk' => 'Werênayışê mı',
+'mypage' => 'Per',
+'mytalk' => 'Werênayış',
'anontalk' => 'Pela werênayışê nê IPy',
'navigation' => 'Geyrayış',
'and' => ' u',
# Vector skin
'vector-action-addsection' => 'Mesel Vırazê',
'vector-action-delete' => 'Besterne',
-'vector-action-move' => 'Berdış',
+'vector-action-move' => 'Berê',
'vector-action-protect' => 'Bıpawe',
'vector-action-undelete' => 'Esterıtışi peyser bıgê',
'vector-action-unprotect' => 'Starkerdışi bıvurne',
# Preferences page
'preferences' => 'Tercihi',
-'mypreferences' => 'Tercihê mı',
+'mypreferences' => 'Tercihi',
'prefs-edits' => 'Amarê vurnayışan:',
'prefsnologin' => 'Şıma cıkewtış nêvıraşto',
'prefsnologintext' => 'Şıma gani be <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} cikewte]</span> ke tercihanê karberi xo eyar bıkerê.',
# Watchlist
'watchlist' => 'Lista mına seyrkerdışi',
-'mywatchlist' => 'Lista mına seyrkerdışi',
+'mywatchlist' => 'Lista seyr kerdışi',
'watchlistfor2' => 'Qandê $1 ($2)',
'nowatchlist' => 'listeya temaşa kerdıişê şıma de yew madde zi çina.',
'watchlistanontext' => 'qey vurnayişê maddeya listeya temaşakerdişi $1.',
# Contributions
'contributions' => 'İştiraqê karberi',
'contributions-title' => 'Dekerdenê karber de $1',
-'mycontris' => 'Cıkerdışê mı',
+'mycontris' => 'Cıkerdışi',
'contribsub2' => 'Qandê $1 ($2)',
'nocontribs' => 'Ena kriteriya de vurnayîş çini yo.',
'uctop' => '(top)',
'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD', # only translate this message to other languages if you have to change it
-'about' => 'About',
-'article' => 'Content page',
-'newwindow' => '(opens in new window)',
-'cancel' => 'Cancel',
-'moredotdotdot' => 'More...',
-'mypage' => 'Page',
-'mytalk' => 'Talk',
-'anontalk' => 'Talk for this IP address',
-'navigation' => 'Navigation',
-'and' => ' and',
+'about' => 'About',
+'article' => 'Content page',
+'newwindow' => '(opens in new window)',
+'cancel' => 'Cancel',
+'moredotdotdot' => 'More...',
+'mypage' => 'Page',
+'mytalk' => 'Talk',
+'mytalk-parenthetical' => 'talk',
+'anontalk' => 'Talk for this IP address',
+'navigation' => 'Navigation',
+'and' => ' and',
# Cologne Blue skin
'qbfind' => 'Find',
'userpage-userdoesnotexist-view' => 'حساب کاربری «$1» ثبت نشدهاست.',
'blocked-notice-logextract' => 'دسترسی این کاربر در حال حاضر بسته است.
آخرین مورد سیاهه قطع دسترسی در زیر آمدهاست:',
-'clearyourcache' => "''نکته:''' پس از ذخیرهکردن ممکن است برای دیدن تغییرات نیاز باشد که حافظهٔ نهانی مرورگر خود را پاک کنید.
+'clearyourcache' => "'''نکته:''' پس از ذخیرهکردن ممکن است برای دیدن تغییرات نیاز باشد که حافظهٔ نهانی مرورگر خود را پاک کنید.
*'''فایرفاکس / سافاری:''' کلید ''Shift'' را نگه دارید و روی دکمهٔ ''Reload'' کلیک کنید، یا کلیدهای ''Ctrl-F5'' یا ''Ctrl-R'' را با هم فشار دهید (در رایانههای اپل مکینتاش کلیدهای ''⌘-R'')
*'''گوگل کروم:'''کلیدهای ''Ctrl+Shift+R'' را با هم فشار دهید. (در رایانههای اپل مکینتاش کلیدهای ''⌘-Shift-R'')
*'''اینترنت اکسپلورر:''' کلید ''Ctrl'' را نگهدارید و روی دکمهٔ ''Refresh'' کلیک کنید، یا کلیدهای ''Ctrl-F5'' را با هم فشار دهید
'newwindow' => '(새 창으로 열림)',
'cancel' => '취소',
'moredotdotdot' => '더 보기...',
-'mypage' => 'ë\82´ ì\82¬ì\9a©ì\9e\90 문ì\84\9c',
-'mytalk' => '내 사용자 토론',
+'mypage' => '문서',
+'mytalk' => '토론',
'anontalk' => '익명 사용자 토론',
'navigation' => '둘러보기',
'and' => ',',
# Preferences page
'preferences' => '사용자 환경 설정',
-'mypreferences' => '사용자 환경 설정',
+'mypreferences' => '환경 설정',
'prefs-edits' => '편집 횟수:',
'prefsnologin' => '로그인하지 않음',
'prefsnologintext' => '사용자 환경 설정을 바꾸려면 먼저 <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} 로그인]</span>해야 합니다.',
# Watchlist
'watchlist' => '주시문서 목록',
-'mywatchlist' => '내 주시문서 목록',
+'mywatchlist' => '주시문서 목록',
'watchlistfor2' => '사용자:$1 $2',
'nowatchlist' => '주시하는 문서가 아직 없습니다.',
'watchlistanontext' => '주시문서 목록을 보거나 고치려면 $1 하세요.',
# Contributions
'contributions' => '사용자 기여',
'contributions-title' => '$1 사용자의 기여 목록',
-'mycontris' => '내 기여 목록',
+'mycontris' => '기여 목록',
'contribsub2' => '$1($2)의 기여',
'nocontribs' => '이 사용자는 아무 것도 기여하지 않았습니다.',
'uctop' => '(최신)',
'last' => 'berê',
'page_first' => 'ya pêşîn',
'page_last' => 'ya paşîn',
-'histlegend' => 'Rênîşan: (cudahî) = cudahiya nav vê û versiyona niha,
-(berê) = cudahiya nav vê û ya berî vê, B = guhertina biçûk',
+'histlegend' => "Rênîşan: ({{int:cur}}) = cudahiya nav vê û versiyona niha, ({{int:last}}) = cudahiya nav vê û ya berî vê, '''{{int:minoreditletter}}''' = guhertina biçûk",
'history-fieldset-title' => 'Li dîrokê bigere',
'history-show-deleted' => 'Tenê yên jêbirî',
'histfirst' => 'Kevintirîn',
'userinvalidcssjstitle' => "'''Åtvaring:''' Det finst ikkje noka sidedrakt som heiter «$1». Hugs på at vanlege .css- og .js-sider brukar titlar med små bokstavar, til dømes {{ns:user}}:Døme/vector.css, og ikkje {{ns:user}}:Døme/Vector.css.",
'updated' => '(Oppdatert)',
'note' => "'''Merk:'''",
-'previewnote' => "'''Hugs at dette berre er ei førehandsvising.''' Endringane dine er ikkje lagra enno!",
+'previewnote' => "'''Hugsa at dette berre er ei førehandsvising.'''
+Endringane dine er ikkje lagra enno!",
'continue-editing' => 'Gå til endringsområdet',
'previewconflict' => 'Dette er ei førehandsvising av teksten i endringsboksen over, slik han vil sjå ut om du lagrar han',
'session_fail_preview' => "'''Orsak! Endringa di kunne ikkje lagrast. Ver venleg og prøv ein gong til. Dersom det framleis ikkje går, prøv å logge deg ut og inn att.'''",
'delete_and_move' => 'Slett og flytt',
'delete_and_move_text' => '== Sletting påkravd ==
-Målsida «[[:$1]]» finst alt. Vil du sletta henne for å gjeva rom for flytting?',
+Målsida «[[:$1]]» finst allereie. Vil du slette ho for å gje rom for flytting?',
'delete_and_move_confirm' => 'Ja, slett sida',
'delete_and_move_reason' => 'Sletta for å gje rom for flytting frå «[[$1]]»',
'selfmove' => 'Kjelde- og måltitlane er like; kan ikkje flytte sida over seg sjølv.',
'confirmemail_needlogin' => 'A venta $1 për confermé soa adrëssa ëd pòsta eletrònica.',
'confirmemail_success' => "Soa adrëssa a l'é stàita confermà, adess a peul [[Special:UserLogin|rintré ant ël sistema]] e i-j auguroma da fessla bin ant la wiki!",
'confirmemail_loggedin' => "Motobin mersì. Soa adrëssa ëd pòsta eletrònica adess a l'é confermà.",
-'confirmemail_error' => "Cheich-còs a l'é andà mal ën salvand soa conferma.",
+'confirmemail_error' => "Cheicòs a l'é andà mal ën salvand soa conferma.",
'confirmemail_subject' => "Conferma dl'adrëssa postal da 'nt la {{SITENAME}}",
-'confirmemail_body' => "Cheidun, a l'é belfé che a sia stait pròpe chiel (ò chila), da 'nt l'adrëssa IP \$1,
-a l'ha doertà un cont utent \"\$2\" ansima a {{SITENAME}}, lassand-ne st'adrëssa ëd pòsta eletrònica-sì.
+'confirmemail_body' => "Cheidun, a l'é belfé che a sia stàit pròpe chiel, da 'nt l'adrëssa IP $1,
+a l'ha duvertà un cont utent «$2» ansima a {{SITENAME}}, lassand-ne st'adrëssa ëd pòsta eletrònica-sì.
Për confermé che ës cont a l'é da bon sò e për ativé
-le possibilità corelà a la pòsta eletrònica ansima a {{SITENAME}}, che a deurba st'adrëssa-sì andrinta a sò programa ëd navigassion (browser):
+le possibilità gropà a la pòsta eletrònica ansima a {{SITENAME}}, che a deurba st'adrëssa-sì andrinta a sò programa ëd navigassion:
-\$3
+$3
-Se a fussa *nen* stait chiel a deurbe ël cont, anlora che a vada daré a sto colegament-sì
-për scanselé la conferma ëd l'adrëssa e-mail:
+Se a fussa *nen* stàit chiel a deurbe ël cont, anlora che a vada dapress a la liura sì-sota
+për scancelé la conferma ëd l'adrëssa ëd pòsta eletrònica:
-\$5
+$5
-Cost còdes ëd conferma a l'é bon fin-a al \$4.",
+Cost còdes ëd conferma a l'é bon fin-a al $4.",
'confirmemail_body_changed' => "Cheidun, a l'é belfé ch'a sia chiel, da l'adrëssa IP \$1,
a l'ha cangià l'adrëssa ëd pòsta eletrònica dël cont \"\$2\" con st'adrëssa-sì dzora a {{SITENAME}}.
'newwindow' => '(abre numa janela nova)',
'cancel' => 'Cancelar',
'moredotdotdot' => 'Mais...',
-'mypage' => 'Utilizador',
+'mypage' => 'Página',
'mytalk' => 'Discussão',
'anontalk' => 'Discussão para este IP',
'navigation' => 'Navegação',
'newwindow' => '(abre em uma nova janela)',
'cancel' => 'Cancelar',
'moredotdotdot' => 'Mais...',
-'mypage' => 'Minha página',
-'mytalk' => 'Minha discussão',
+'mypage' => 'Página',
+'mytalk' => 'Discussão',
'anontalk' => 'Discussão para este IP',
'navigation' => 'Navegação',
'and' => ' e',
# Preferences page
'preferences' => 'Preferências',
-'mypreferences' => 'Minhas preferências',
+'mypreferences' => 'Preferências',
'prefs-edits' => 'Número de edições:',
'prefsnologin' => 'Não autenticado',
'prefsnologintext' => 'É necessário estar <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} autenticado]</span> para definir as suas preferências.',
# Contributions
'contributions' => 'Contribuições {{GENDER:{{BASEPAGENAME}}|do usuário|da usuária}}',
'contributions-title' => 'Contribuições {{GENDER:$1|do usuário|da usuária}} $1',
-'mycontris' => 'Minhas contribuições',
+'mycontris' => 'Contribuições',
'contribsub2' => 'Para $1 ($2)',
'nocontribs' => 'Não foram encontradas mudanças com este critério.',
'uctop' => '(atual)',
{{Identical|Cancel}}',
'moredotdotdot' => '{{Identical|More...}}',
'mypage' => "A text for the link to the user's user page in the links at the top of the page.",
-'mytalk' => 'In the personal urls page section - right upper corner.
-
-Used as link title in "Personal tools" toolbar.',
+'mytalk' => 'Used as link title in "Personal tools" toolbar.',
+'mytalk-parenthetical' => 'When user page and talk combined into single label, link title for talk label',
'anontalk' => 'Link to the talk page appearing in [[mw:Help:Navigation#User_Links|user links]] for each anonymous users when [[mw:Manual:$wgShowIPinHeader|$wgShowIPinHeader]] is true.',
'navigation' => 'This is shown as a section header in the sidebar of most skins.
'newwindow' => '(se deschide într-o fereastră nouă)',
'cancel' => 'Revocare',
'moredotdotdot' => 'Mai mult…',
-'mypage' => 'Pagina mea',
+'mypage' => 'Pagină',
'mytalk' => 'Discuții',
'anontalk' => 'Discuția pentru această adresă IP',
'navigation' => 'Navigare',
'category-empty' => "''Эта категория в данный момент пуста.''",
'hidden-categories' => '{{PLURAL:$1|Скрытая категория|Скрытые категории}}',
'hidden-category-category' => 'Скрытые категории',
-'category-subcat-count' => '{{PLURAL:$2|Данная категория содержит только следующую подкатегорию.|{{PLURAL:$1|Показана $1 подкатегория|Показано $1 подкатегории|Показано $1 подкатегорий}} из $2.}}',
+'category-subcat-count' => '{{PLURAL:$2|Данная категория содержит только следующую подкатегорию.|{{PLURAL:$1|Показана $1 подкатегория|Показано $1 подкатегории|Показано $1 подкатегорий}} из $2, находящихся в этой категории.}}',
'category-subcat-count-limited' => 'В этой категории {{PLURAL:$1|$1 подкатегория|$1 подкатегории|$1 подкатегорий}}.',
-'category-article-count' => '{{PLURAL:$2|Эта категория содержит только одну страницу.|{{PLURAL:$1|Показана $1 страница|Показано $1 страницы|Показано $1 страниц}} этой категории из $2.}}',
+'category-article-count' => '{{PLURAL:$2|Эта категория содержит только одну страницу.|{{PLURAL:$1|Показана $1 страница|Показано $1 страницы|Показано $1 страниц}} из $2, находящихся в этой категории.}}',
'category-article-count-limited' => 'В этой категории {{PLURAL:$1|$1 страница|$1 страницы|$1 страниц}}.',
-'category-file-count' => '{{PLURAL:$2|Эта категория содержит только один файл.|{{PLURAL:$1|Показан $1 файл|Показано $1 файла|Показано $1 файлов}} этой категории из $2.}}',
+'category-file-count' => '{{PLURAL:$2|Эта категория содержит только один файл.|{{PLURAL:$1|Показан $1 файл|Показано $1 файла|Показано $1 файлов}} из $2, находящихся в этой категории.}}',
'category-file-count-limited' => 'В этой категории {{PLURAL:$1|$1 файл|$1 файла|$1 файлов}}.',
'listingcontinuesabbrev' => '(продолжение)',
'index-category' => 'Индексируемые страницы',
'permalink' => 'Постоянная ссылка',
'print' => 'Печать',
'view' => 'Просмотр',
-'edit' => 'РедакÑ\82иÑ\80овать',
+'edit' => 'Ð\9fÑ\80авить',
'create' => 'Создать',
'editthispage' => 'Править эту страницу',
'create-this-page' => 'Создать эту страницу',
'delete-hook-aborted' => 'Правка отменена процедурой-перехватчиком.
Дополнительных пояснений не приведено.',
'badtitle' => 'Недопустимое название',
-'badtitletext' => 'Ð\97апÑ\80аÑ\88иваемое название Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b непÑ\80авилÑ\8cно, пÑ\83Ñ\81Ñ\82о, либо непÑ\80авилÑ\8cно указано межъязыковое или интервики название. Возможно, в названии используются недопустимые символы.',
+'badtitletext' => 'Ð\97апÑ\80аÑ\88иваемое название Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b непÑ\80авилÑ\8cно, пÑ\83Ñ\81Ñ\82о, либо невеÑ\80но указано межъязыковое или интервики название. Возможно, в названии используются недопустимые символы.',
'perfcached' => 'Следующие данные взяты из кэша и могут не учитывать последних изменений. В кэше хранится не более $1 {{PLURAL:$1|записи|записей|записей}}.',
'perfcachedts' => 'Следующие данные взяты из кэша, последний раз он обновлялся в $1. В кэше хранится не более $4 {{PLURAL:$4|записи|записей|записей}}.',
'querypage-no-updates' => 'Обновление этой страницы сейчас отключено.
'welcomecreation' => '== Добро пожаловать, $1! ==
Ваша учётная запись создана.
Не забудьте провести [[Special:Preferences|персональную настройку]] сайта.',
-'yourname' => 'Имя участника:',
+'yourname' => 'Имя учётной записи:',
'yourpassword' => 'Пароль:',
'yourpasswordagain' => 'Повторный набор пароля:',
'remembermypassword' => 'Помнить мою учётную запись на этом компьютере (не более $1 {{PLURAL:$1|дня|дней|дней}})',
'logout' => 'Завершение сеанса',
'userlogout' => 'Завершение сеанса',
'notloggedin' => 'Вы не представились системе',
-'nologin' => "Нет учётной записи? '''$1'''.",
+'nologin' => 'Нет учётной записи? $1.',
'nologinlink' => 'Создать учётную запись',
-'createaccount' => 'Ð\97аÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80оваÑ\82Ñ\8c нового Ñ\83Ñ\87аÑ\81Ñ\82ника',
+'createaccount' => 'СоздаÑ\82Ñ\8c Ñ\83Ñ\87Ñ\91Ñ\82нÑ\83Ñ\8e запиÑ\81Ñ\8c',
'gotaccount' => "Вы уже зарегистрированы? '''$1'''.",
'gotaccountlink' => 'Представьтесь',
'userlogin-resetlink' => 'Забыли данные для входа?',
'showpreview' => 'Предварительный просмотр',
'showlivepreview' => 'Быстрый предпросмотр',
'showdiff' => 'Внесённые изменения',
-'anoneditwarning' => "'''Внимание:''' Вы не представились системе.
-Ваш IP-адрес будет записан в историю изменений этой страницы.",
+'anoneditwarning' => "'''Внимание!''' Вы не авторизовались на сайте.
+В истории изменений этой страницы будет записан ваш IP-адрес.",
'anonpreviewwarning' => "''Вы не представились системе. Сохранение приведёт к записи вашего IP-адреса в историю изменений страницы.''",
'missingsummary' => "'''Напоминание.''' Вы не дали краткого описания изменений. При повторном нажатии на кнопку «{{int:savearticle}}», ваши изменения будут сохранены без комментария.",
'missingcommenttext' => 'Пожалуйста, введите ниже ваше сообщение.',
'cascadeprotectedwarning' => "'''Предупреждение:''' Данную страницу могут редактировать только участники группы «Администраторы», поскольку она включена {{PLURAL:$1|в следующую страницу, для которой|в следующие страницы, для которых}} включена каскадная защита:",
'titleprotectedwarning' => "'''Предупреждение. Это название защищено. Создать эту страницу могут только участники с [[Special:ListGroupRights|соответствующими правами]].'''
Ниже для справки приведена последняя запись журнала:",
-'templatesused' => '{{PLURAL:$1|Шаблон, иÑ\81полÑ\8cзованнÑ\8bй|ШаблонÑ\8b, иÑ\81полÑ\8cзованнÑ\8bе}} на Ñ\82екÑ\83Ñ\89ей веÑ\80Ñ\81ии Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b:',
+'templatesused' => '{{PLURAL:$1|Шаблон, иÑ\81полÑ\8cзованнÑ\8bй|ШаблонÑ\8b, иÑ\81полÑ\8cзованнÑ\8bе}} на Ñ\8dÑ\82ой Ñ\81Ñ\82Ñ\80аниÑ\86е:',
'templatesusedpreview' => '{{PLURAL:$1|Шаблон, используемый|Шаблоны, используемые}} в предпросматриваемой странице:',
'templatesusedsection' => '{{PLURAL:$1|Шаблон, используемый|Шаблоны, использованные}} в этом разделе:',
'template-protected' => '(защищено)',
'template-semiprotected' => '(частично защищено)',
-'hiddencategories' => 'Эта страница относится к $1 {{PLURAL:$1|скрытой категории|скрытым категориям|скрытым категориям}}:',
+'hiddencategories' => 'Эта страница относится к $1 {{PLURAL:$1|скрытой категории|скрытым категориям}}:',
'edittools' => '<!-- Расположенный здесь текст будет показываться под формой редактирования и формой загрузки. -->',
'nocreatetitle' => 'Создание страниц ограничено',
'nocreatetext' => 'На этом сайте ограничена возможность создания новых страниц.
'content-model-css' => 'CSS',
# Parser/template warnings
-'expensive-parserfunction-warning' => 'Внимание. Эта страница содержит слишком много вызовов ресурсоёмких функций.
+'expensive-parserfunction-warning' => "'''Внимание!''' Эта страница содержит слишком много вызовов ресурсоёмких функций.
-Ð\9eгÑ\80аниÑ\87ение на колиÑ\87еÑ\81Ñ\82во вÑ\8bзовов Ñ\83Ñ\81Ñ\82ановлено на Ñ\83Ñ\80овне $2 {{PLURAL:$2|вÑ\8bзова|вÑ\8bзовов|вÑ\8bзовов}}, в данном Ñ\81лÑ\83Ñ\87ае Ñ\82Ñ\80ебÑ\83еÑ\82Ñ\81Ñ\8f Ñ\81делаÑ\82Ñ\8c $1 {{PLURAL:$1|вÑ\8bзов|вÑ\8bзова|вÑ\8bзовов}}.',
+Ð\94олжно бÑ\8bÑ\82Ñ\8c не более $2 {{PLURAL:$2|вÑ\8bзова|вÑ\8bзовов}}, в Ñ\82о вÑ\80емÑ\8f как Ñ\81ейÑ\87аÑ\81 здеÑ\81Ñ\8c $1 {{PLURAL:$1|вÑ\8bзов|вÑ\8bзова|вÑ\8bзовов}}.",
'expensive-parserfunction-category' => 'Страницы со слишком большим количеством вызовов ресурсоёмких функций',
'post-expand-template-inclusion-warning' => 'Предупреждение: суммарный размер включаемых шаблонов слишком велик.
Некоторые шаблоны не будут включены.',
** Потенциально клеветнические сведения',
'revdelete-otherreason' => 'Другая/дополнительная причина:',
'revdelete-reasonotherlist' => 'Другая причина',
-'revdelete-edit-reasonlist' => 'Ð\9fÑ\80авить список причин',
+'revdelete-edit-reasonlist' => 'РедакÑ\82иÑ\80овать список причин',
'revdelete-offender' => 'Автор версии страницы:',
# Suppression log
'diff-multi-manyusers' => '(не {{PLURAL:$1|показана $1 промежуточная версия|показаны $1 промежуточные версии|показаны $1 промежуточных версий}}, сделанные более чем $2 {{PLURAL:$2|участником|участниками}})',
'difference-missing-revision' => '{{PLURAL:$2|$2 версия|$2 версии|$2 версий}} для этого сравнения ($1) {{PLURAL:$2|не обнаружена|не обнаружены}}.
-ÐÑ\82о обÑ\8bÑ\87но бÑ\8bваеÑ\82, еÑ\81ли поÑ\81ледоваÑ\82Ñ\8c по Ñ\83Ñ\81Ñ\82аÑ\80евÑ\88ей Ñ\81Ñ\81Ñ\8bлке на Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83, которая была удалена.
+ÐÑ\82о обÑ\8bÑ\87но бÑ\8bваеÑ\82, еÑ\81ли пеÑ\80ейÑ\82и по Ñ\83Ñ\81Ñ\82аÑ\80евÑ\88ей Ñ\81Ñ\81Ñ\8bлке Ñ\81Ñ\80авнениÑ\8f веÑ\80Ñ\81ий длÑ\8f Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b, которая была удалена.
Подробности могут быть в [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журнале удалений].',
# Search results
'searchresulttext' => 'Для получения более подробной информации о поиске на страницах проекта, см. [[{{MediaWiki:Helppage}}|справочный раздел]].',
'searchsubtitle' => 'По запросу «[[:$1]]» ([[Special:Prefixindex/$1|страницы, начинающиеся с этого названия]]{{int:pipe-separator}}[[Special:WhatLinksHere/$1|ссылающиеся на это название]])',
'searchsubtitleinvalid' => 'По запросу «$1»',
-'toomanymatches' => 'Найдено слишком много соответствий, пожалуйста, попробуйте другой запрос',
+'toomanymatches' => 'Найдено слишком много соответствий; пожалуйста, попробуйте сформулировать запрос иначе',
'titlematches' => 'Совпадения в названиях страниц',
'notitlematches' => 'Нет совпадений в названиях страниц',
'textmatches' => 'Совпадения в текстах страниц',
'shown-title' => 'Показывать $1 {{PLURAL:$1|запись|записи|записей}} на странице',
'viewprevnext' => 'Просмотреть ($1 {{int:pipe-separator}} $2) ($3)',
'searchmenu-legend' => 'Настройки поиска',
-'searchmenu-exists' => "'''Ð\92 Ñ\8dÑ\82ом вики-пÑ\80оекÑ\82е есть страница «[[:$1]]»'''",
+'searchmenu-exists' => "'''Ð\92 Ñ\8dÑ\82ой вики есть страница «[[:$1]]»'''",
'searchmenu-new' => "'''Создать страницу «[[:$1]]» в этом вики-проекте!'''",
'searchhelp-url' => 'Help:Содержание',
'searchmenu-prefix' => '[[Special:PrefixIndex/$1|Показать страницы с этим префиксом]]',
'searchprofile-articles-tooltip' => 'Поиск в $1',
'searchprofile-project-tooltip' => 'Поиск в $1',
'searchprofile-images-tooltip' => 'Поиск файлов',
-'searchprofile-everything-tooltip' => 'Поиск на всех страницах (включая страницы обсуждения)',
+'searchprofile-everything-tooltip' => 'Поиск на всех страницах (включая страницы обсуждений)',
'searchprofile-advanced-tooltip' => 'Искать в заданных пространствах имён',
'search-result-size' => '$1 ({{PLURAL:$2|$2 слово|$2 слова|$2 слов}})',
-'search-result-category-size' => '$1 {{PLURAL:$1|член|члена|членов}} ($2 {{PLURAL:$2|подкатегория|подкатегории|подкатегорий}}, $3 {{PLURAL:$3|файл|файла|файлов}}).',
+'search-result-category-size' => '$1 {{PLURAL:$1|вхождение|вхождения|вхождений}} ($2 {{PLURAL:$2|подкатегория|подкатегории|подкатегорий}}, $3 {{PLURAL:$3|файл|файла|файлов}}).',
'search-result-score' => 'Релевантность: $1%.',
'search-redirect' => '(перенаправление с $1)',
'search-section' => '(раздел «$1»)',
'timezoneregion-europe' => 'Европа',
'timezoneregion-indian' => 'Индийский океан',
'timezoneregion-pacific' => 'Тихий океан',
-'allowemail' => 'Разрешить приём электронной почты от других участников',
+'allowemail' => 'Разрешить получение электронной почты от других участников',
'prefs-searchoptions' => 'Поиск',
'prefs-namespaces' => 'Пространства имён',
'defaultns' => 'Иначе искать в следующих пространствах имён:',
'prefs-emailconfirm-label' => 'Подтверждение электронной почты:',
'prefs-textboxsize' => 'Размер окна редактирования',
'youremail' => 'Электронная почта:',
-'username' => 'РегиÑ\81Ñ\82Ñ\80аÑ\86ионное имÑ\8f:',
+'username' => 'Ð\98мÑ\8f Ñ\83Ñ\87Ñ\91Ñ\82ной запиÑ\81и:',
'uid' => 'Идентификатор участника:',
'prefs-memberingroups' => 'Член {{PLURAL:$1|группы|групп}}:',
'prefs-registration' => 'Время регистрации:',
# User preference: e-mail validation using jQuery
'email-address-validity-valid' => 'Выглядит корректно',
-'email-address-validity-invalid' => 'ТÑ\80ебÑ\83еÑ\82Ñ\81Ñ\8f коÑ\80Ñ\80екÑ\82нÑ\8bй адÑ\80еÑ\81!',
+'email-address-validity-invalid' => 'Ð\92ведиÑ\82е коÑ\80Ñ\80екÑ\82нÑ\8bй адÑ\80еÑ\81 Ñ\8dлекÑ\82Ñ\80онной поÑ\87Ñ\82Ñ\8b!',
# User rights
'userrights' => 'Управление правами участника',
'userrights-lookup-user' => 'Управление группами участников',
-'userrights-user-editname' => 'Введите имя участника:',
+'userrights-user-editname' => 'Введите имя учётной записи:',
'editusergroup' => 'Изменить членство в группах',
'editinguser' => "Изменение прав {{GENDER:$1|участника|участницы}} '''[[User:$1|$1]]''' $2",
'userrights-editusergroup' => 'Изменение членства в группах',
'saveusergroups' => 'Сохранить группы участника',
-'userrights-groupsmember' => 'Член гÑ\80Ñ\83пп:',
-'userrights-groupsmember-auto' => 'Неявный член:',
+'userrights-groupsmember' => 'СоÑ\81Ñ\82оиÑ\82 в гÑ\80Ñ\83ппаÑ\85:',
+'userrights-groupsmember-auto' => 'Неявно состоит в группах:',
'userrights-groups-help' => 'Вы можете изменить группы, в которые входит этот участник.
* Если около названия группы стоит отметка, значит участник входит в эту группу.
* Если отметка не стоит — участник не относится к соответствующей группе.
-* Знак * отмечает, что вы не можете удалить из группы участника, если добавите его в неё или наоборот.',
+* Знак * отмечает, что вы не сможете удалить участника из группы, если добавите его в неё, или наоборот.',
'userrights-reason' => 'Причина:',
'userrights-no-interwiki' => 'У вас нет разрешения изменять права участников на других вики.',
'userrights-nodatabase' => 'База данных $1 не существует или не является локальной.',
'right-undelete' => 'восстановление страниц',
'right-suppressrevision' => 'просмотр и восстановление скрытых от администраторов версий страниц',
'right-suppressionlog' => 'просмотр частных журналов',
-'right-block' => 'Ñ\83Ñ\81Ñ\82ановка запÑ\80еÑ\82а на Ñ\80едакÑ\82иÑ\80ование дÑ\80Ñ\83гим Ñ\83Ñ\87аÑ\81Ñ\82никам',
+'right-block' => 'Ñ\83Ñ\81Ñ\82ановка огÑ\80аниÑ\87ений на Ñ\80едакÑ\82иÑ\80ование длÑ\8f дÑ\80Ñ\83гиÑ\85 Ñ\83Ñ\87аÑ\81Ñ\82ников',
'right-blockemail' => 'установка запрета на отправку электронной почты',
'right-hideuser' => 'запрет имени участника и его сокрытие',
'right-ipblock-exempt' => 'обход блокировок по IP, автоблокировок и блокировок диапазонов',
'action-undelete' => 'восстановление этой страницы',
'action-suppressrevision' => 'просмотр и восстановление этой скрытой версии страницы',
'action-suppressionlog' => 'просмотр этого частного журнала',
-'action-block' => 'блокиÑ\80овка участника',
+'action-block' => 'огÑ\80аниÑ\87иваÑ\82Ñ\8c возможноÑ\81Ñ\82Ñ\8c Ñ\80едакÑ\82иÑ\80ованиÑ\8f длÑ\8f Ñ\8dÑ\82ого участника',
'action-protect' => 'изменение уровня защиты этой страницы',
-'action-rollback' => 'быстрый откат изменений последнего пользователя, который редактировал страницу',
+'action-rollback' => 'быстрый откат изменений участника, который последним редактировал страницу',
'action-import' => 'импорт этой страницы из другой вики',
'action-importupload' => 'импорт этой страницы из загруженного файла',
'action-patrol' => 'отметка чужих правок как отпатрулированных',
'recentchanges' => 'Свежие правки',
'recentchanges-legend' => 'Настройки свежих правок',
'recentchanges-summary' => 'Ниже в хронологическом порядке перечислены последние изменения на страницах {{grammar:genitive|{{SITENAME}}}}.',
-'recentchanges-feed-description' => 'Ð\9eÑ\82Ñ\81леживаÑ\82Ñ\8c поÑ\81ледние изменениÑ\8f в вики в Ñ\8dÑ\82ом поÑ\82оке.',
+'recentchanges-feed-description' => 'Ð\9eÑ\82Ñ\81леживаÑ\82Ñ\8c в Ñ\8dÑ\82ом поÑ\82оке поÑ\81ледние изменениÑ\8f в вики.',
'recentchanges-label-newpage' => 'Этой правкой была создана новая страница.',
'recentchanges-label-minor' => 'Это незначительное изменение',
'recentchanges-label-bot' => 'Эта правка сделана ботом',
-'recentchanges-label-unpatrolled' => 'Эту правку ещё не отпатрулировали',
+'recentchanges-label-unpatrolled' => 'Эта правку ещё никем не патрулировалась',
'rcnote' => "{{PLURAL:$1|Последнее '''$1''' изменение|Последние '''$1''' изменения|Последние '''$1''' изменений}} за '''$2''' {{PLURAL:$2|день|дня|дней}}, на момент времени $5 $4.",
-'rcnotefrom' => 'Ниже перечислены изменения с <strong>$2</strong> (по <strong>$1</strong>).',
+'rcnotefrom' => "Ниже перечислены изменения с '''$2''' (не более '''$1''').",
'rclistfrom' => 'Показать изменения с $1.',
'rcshowhideminor' => '$1 малые правки',
'rcshowhidebots' => '$1 ботов',
'mycontris' => 'Вклад',
'contribsub2' => 'Вклад $1 ($2)',
'nocontribs' => 'Изменений, соответствующих заданным условиям, найдено не было.',
-'uctop' => ' (последняя)',
+'uctop' => '(последняя)',
'month' => 'С месяца (и ранее):',
'year' => 'С года (и ранее):',
'isredirect' => 'страница-перенаправление',
'istemplate' => 'включение',
'isimage' => 'файловая ссылка',
-'whatlinkshere-prev' => '{{PLURAL:$1|предыдущая|предыдущие|предыдущие}} $1',
-'whatlinkshere-next' => '{{PLURAL:$1|следующая|следующие|следующие}} $1',
+'whatlinkshere-prev' => '{{PLURAL:$1|предыдущая|предыдущие}} $1',
+'whatlinkshere-next' => '{{PLURAL:$1|следующая|следующие}} $1',
'whatlinkshere-links' => '← ссылки',
'whatlinkshere-hideredirs' => '$1 перенаправления',
'whatlinkshere-hidetrans' => '$1 включения',
'hist' => 'história',
'hide' => 'skryť',
'show' => 'zobraziť',
-'minoreditletter' => 'D',
+'minoreditletter' => 'd',
'newpageletter' => 'N',
'boteditletter' => 'b',
'number_of_watching_users_pageview' => '[$1 {{PLURAL:$1|sledujúci používateľ|sledujúci používatelia|sledujúcich používateľov}}]',
'version-license' => 'Licens',
'version-poweredby-credits' => "Den här wikin drivs av '''[//www.mediawiki.org/ MediaWiki]''', copyright © 2001-$1 $2.",
'version-poweredby-others' => 'andra',
+'version-credits-summary' => 'Vi skulle vilja tacka följande personer för deras bidrag till [[Special:Version|MediaWiki]].',
'version-license-info' => 'MediaWiki är fri programvara; du kan distribuera det och/eller modifiera det under villkoren i GNU General Public License, publicerad av Free Software Foundation; antingen version 2 av licensen, eller (om du önskar) någon senare version.
MediaWiki distribueras i hopp om att det ska vara användbart, men UTAN NÅGON GARANTI, även utan underförstådd garanti om SÄLJBARHET eller LÄMPLIGHET FÖR ETT VISST SYFTE. Se GNU General Public License för fler detaljer.
'newwindow' => '(yeni bir pencerede açılır)',
'cancel' => 'İptal',
'moredotdotdot' => 'Daha...',
-'mypage' => 'sayfam',
-'mytalk' => 'Mesaj sayfam',
+'mypage' => 'Sayfa',
+'mytalk' => 'Tartışma',
'anontalk' => "Bu IP'nin iletileri",
'navigation' => 'Gezinti',
'and' => ' ve',
# Preferences page
'preferences' => 'Tercihler',
-'mypreferences' => 'Tercihlerim',
+'mypreferences' => 'Tercihler',
'prefs-edits' => 'Değişiklik sayısı:',
'prefsnologin' => 'Oturum açık değil',
'prefsnologintext' => 'Kullanıcı tercihlerinizi ayarlamak için <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} giriş yapmalısınız]</span>.',
# Watchlist
'watchlist' => 'İzleme listem',
-'mywatchlist' => 'İzleme listem',
+'mywatchlist' => 'İzleme listesi',
'watchlistfor2' => '$1 için $2',
'nowatchlist' => 'İzleme listesinde hiçbir madde bulunmuyor.',
'watchlistanontext' => 'Lütfen izleme listenizdeki maddeleri görmek ya da değiştirmek için $1.',
# Contributions
'contributions' => 'Kullanıcının katkıları',
'contributions-title' => '$1 için kullanıcı katkıları',
-'mycontris' => 'Katkılarım',
+'mycontris' => 'Katkılar',
'contribsub2' => '$1 ($2)',
'nocontribs' => 'Bu kriterlere uyan değişiklik bulunamadı',
'uctop' => '(son)',
'newwindow' => '(mở cửa sổ mới)',
'cancel' => 'Hủy bỏ',
'moredotdotdot' => 'Thêm nữa…',
-'mypage' => 'Trang của tôi',
-'mytalk' => 'Thảo luận với tôi',
+'mypage' => 'Trang cá nhân',
+'mytalk' => 'Thảo luận',
'anontalk' => 'Thảo luận với IP này',
'navigation' => 'Xem nhanh',
'and' => ' và',
# Watchlist
'watchlist' => 'Trang tôi theo dõi',
-'mywatchlist' => 'Trang tôi theo dõi',
+'mywatchlist' => 'Trang theo dõi',
'watchlistfor2' => 'Của $1 $2',
'nowatchlist' => 'Danh sách theo dõi của bạn không có gì.',
'watchlistanontext' => 'Xin hãy $1 để xem hay sửa đổi các trang được theo dõi.',
# Contributions
'contributions' => 'Đóng góp của thành viên',
'contributions-title' => 'Đóng góp của thành viên $1',
-'mycontris' => 'Đóng góp của tôi',
+'mycontris' => 'Đóng góp',
'contribsub2' => 'Của $1 ($2)',
'nocontribs' => 'Không tìm thấy thay đổi nào khớp với yêu cầu.',
'uctop' => '(mới nhất)',
'cancel' => 'זיי מבטל',
'moredotdotdot' => 'נאך…',
'mypage' => 'מײַן בלאט',
-'mytalk' => '×\9eײַ×\9f ש×\9e×\95עס',
+'mytalk' => 'שמועס',
'anontalk' => 'דאס רעדן פון דעם IP',
'navigation' => 'נאַוויגאַציע',
'and' => ' און',
# Watchlist
'watchlist' => 'מיין אויפפַּאסונג ליסטע',
-'mywatchlist' => '×\9e×\99×\99×\9f ×\90×\95×\99פפַּ×\90ס×\95× ×\92 ×\9c×\99ס×\98×¢',
+'mywatchlist' => 'אויפפַּאסונג ליסטע',
'watchlistfor2' => 'פֿאַר $1 $2',
'nowatchlist' => 'איר האט נישט קיין שום בלעטער אין אייער אויפפַּאסונג ליסטע.',
'watchlistanontext' => 'ביטע $1 כדי צו זען אדער ענדערן בלעטער אין אייער אַכטגעבן ליסטע.',
# Contributions
'contributions' => "באניצער'ס בײַשטײַערונגען",
'contributions-title' => 'בײַשטײַערונגען פֿון באַניצער $1',
-'mycontris' => '×\9e×²Ö·× ×¢ ×\91ײַש×\98ײַער×\95× ×\92×¢×\9f',
+'mycontris' => 'בײַשטײַערונגען',
'contribsub2' => 'וועגן $1 ($2)',
'nocontribs' => 'נישט געטראפן קיין ענדערונגען צוזאמעגעפאסט מיט די קריטעריעס.',
'uctop' => '(לעצטע)',
'whatlinkshere-hideredirs' => '$1 ווײַטערפֿירונגען',
'whatlinkshere-hidetrans' => '$1 אַריבערשליסונגען',
'whatlinkshere-hidelinks' => '$1 פֿאַרבינדונגען',
-'whatlinkshere-hideimages' => '$1 ×\91×\99×\9c×\93ער פֿאַרבינדונגען',
+'whatlinkshere-hideimages' => '$1 ×\98עקע פֿאַרבינדונגען',
'whatlinkshere-filters' => 'פֿילטערס',
# Block/unblock
# Info page
'pageinfo-title' => 'אינפֿאָרמאַציע פֿאַר "$1"',
+'pageinfo-not-current' => 'קען ווייזן אינפארמאציע נאר פאר דער לויפיקער רעוויזיע.',
'pageinfo-header-basic' => 'גרונטלעכע אינפֿארמאַציע',
'pageinfo-header-edits' => '!רעדאַקטירן היסטאריע',
'pageinfo-header-restrictions' => 'בלאט באַשיצונג',
'pageinfo-length' => 'בלאט לענג (אין בייטן)',
'pageinfo-article-id' => 'בלאט נומער',
'pageinfo-robot-policy' => 'זוכמאשין סטאטוס',
+'pageinfo-robot-index' => 'אינדעקסירבאר',
+'pageinfo-robot-noindex' => 'נישט אינדעקסירבאר',
'pageinfo-views' => 'צאַל קוקן',
'pageinfo-watchers' => '!צאָל בלאט אויפֿפאַסער',
'pageinfo-redirects-name' => 'ווײַטערפירונגען צו דעם בלאט',
'exif-orientation' => 'אריענטאַציע',
'exif-samplesperpixel' => 'צאל קאמאפאנענטן',
'exif-planarconfiguration' => 'דאטן איינארדנונג',
+'exif-xresolution' => 'האריזאנטאלע רעזאלוציע',
+'exif-yresolution' => 'ווערטיקאלע רעזאלוציע',
+'exif-stripoffsets' => 'בילדדאטן פלאציר',
+'exif-rowsperstrip' => 'צאל שורות אין א שטרייף',
+'exif-stripbytecounts' => 'בייטן אין א קאמפרימירטן שטרייף',
'exif-jpeginterchangeformatlength' => 'בייטן פון JPEG דאטן',
'exif-datetime' => 'טעקע ענדערונג דאטע און צײַט',
'exif-imagedescription' => 'בילד טיטל',
public function execute() {
global $wgMemc;
+
$type = $this->getOption( 'type', false );
- $memcKey = 'jobqueue:dbs:v2';
- $pendingDBs = $wgMemc->get( $memcKey );
+ $memcKey = 'jobqueue:dbs:v3';
+ $pendingDbInfo = $wgMemc->get( $memcKey );
// If the cache entry wasn't present, or in 1% of cases otherwise,
- // regenerate the cache.
- if ( !$pendingDBs || mt_rand( 0, 100 ) == 0 ) {
- $pendingDBs = $this->getPendingDbs();
- $wgMemc->set( $memcKey, $pendingDBs, 300 );
+ // regenerate the cache. Use any available stale cache if another
+ // process is currently regenerating the pending DB information.
+ if ( !$pendingDbInfo || mt_rand( 0, 100 ) == 0 ) {
+ $lock = $wgMemc->add( 'jobqueue:dbs:v3:lock', 1, 1800 ); // lock
+ if ( $lock ) {
+ $pendingDbInfo = array(
+ 'pendingDBs' => $this->getPendingDbs(),
+ 'timestamp' => time()
+ );
+ $wgMemc->set( $memcKey, $pendingDbInfo );
+ $wgMemc->delete( 'jobqueue:dbs:v3:lock' ); // unlock
+ }
}
- if ( !$pendingDBs ) {
- return;
+ if ( !$pendingDbInfo || !$pendingDbInfo['pendingDBs'] ) {
+ return; // no DBs with jobs or cache is both empty and locked
}
+ $pendingDBs = $pendingDbInfo['pendingDBs'];
do {
$again = false;
* @return bool
*/
function checkJob( $type, $dbName ) {
- global $wgJobTypesExcludedFromDefaultQueue;
-
+ $group = JobQueueGroup::singleton( $dbName );
if ( $type === false ) {
- $lb = wfGetLB( $dbName );
- $db = $lb->getConnection( DB_MASTER, array(), $dbName );
- $conds = array();
- if ( count( $wgJobTypesExcludedFromDefaultQueue ) > 0 ) {
- foreach ( $wgJobTypesExcludedFromDefaultQueue as $cmdType ) {
- $conds[] = "job_cmd != " . $db->addQuotes( $cmdType );
+ foreach ( $group->getDefaultQueueTypes() as $type ) {
+ if ( !$group->get( $type )->isEmpty() ) {
+ return true;
}
}
- $exists = (bool)$db->selectField( 'job', '1', $conds, __METHOD__ );
- $lb->reuseConnection( $db );
+ return false;
} else {
- $exists = !JobQueueGroup::singleton( $dbName )->get( $type )->isEmpty();
+ return !$group->get( $type )->isEmpty();
}
-
- return $exists;
}
/**
*/
private function getPendingDbs() {
global $wgLocalDatabases;
- $pendingDBs = array();
- # Cross-reference DBs by master DB server
- $dbsByMaster = array();
- foreach ( $wgLocalDatabases as $db ) {
- $lb = wfGetLB( $db );
- $dbsByMaster[$lb->getServerName( 0 )][] = $db;
- }
-
- foreach ( $dbsByMaster as $dbs ) {
- $dbConn = wfGetDB( DB_MASTER, array(), $dbs[0] );
- # Padding row for MySQL bug
- $pad = str_repeat( '-', 40 );
- $sql = "(SELECT '$pad' as db, '$pad' as job_cmd)";
- foreach ( $dbs as $wikiId ) {
- if ( $sql != '' ) {
- $sql .= ' UNION ';
- }
-
- list( $dbName, $tablePrefix ) = wfSplitWikiID( $wikiId );
- $dbConn->tablePrefix( $tablePrefix );
- $jobTable = $dbConn->tableName( 'job' );
-
- $sql .= "(SELECT DISTINCT '$wikiId' as db, job_cmd FROM $dbName.$jobTable GROUP BY job_cmd)";
- }
- $res = $dbConn->query( $sql, __METHOD__ );
- $first = true;
- foreach ( $res as $row ) {
- if ( $first ) {
- // discard padding row
- $first = false;
- continue;
- }
- $pendingDBs[$row->job_cmd][] = $row->db;
+ $pendingDBs = array(); // (job type => (db list))
+ foreach ( $wgLocalDatabases as $db ) {
+ $types = JobQueueGroup::singleton( $db )->getQueuesWithJobs();
+ foreach ( $types as $type ) {
+ $pendingDBs[$type][] = $db;
}
}
+
return $pendingDBs;
}
}
/**
* jQuery Badge plugin
*
- * Based on Badger plugin by Daniel Raftery (http://thrivingkings.com/badger).
- *
* @license MIT
*/
* This program is distributed WITHOUT ANY WARRANTY.
*/
( function ( $ ) {
-
/**
- * Allows you to put a numeric "badge" on an item on the page.
+ * Allows you to put a "badge" on an item on the page. The badge container
+ * will be appended to the selected element(s).
* See mediawiki.org/wiki/ResourceLoader/Default_modules#jQuery.badge
*
- * @param {string|number} badgeCount An explicit number, or "+n"/ "-n"
- * to modify the existing value. If the new value is equal or lower than 0,
- * any existing badge will be removed. The badge container will be appended
- * to the selected element(s).
- * @param {Object} options Optional parameters specified below
- * type: 'inline' or 'overlay' (default)
- * callback: will be called with the number now shown on the badge as a parameter
+ * @param text The value to display in the badge. If the value is falsey (0,
+ * null, false, '', etc.), any existing badge will be removed.
+ * @param boolean inline True if the badge should be displayed inline, false
+ * if the badge should overlay the parent element (default is inline)
*/
- $.fn.badge = function ( badgeCount, options ) {
- var $badge,
- oldBadgeCount,
- newBadgeCount,
- $existingBadge = this.find( '.mw-badge' );
-
- options = $.extend( { type : 'overlay' }, options );
-
- // If there is no existing badge, this will give an empty string
- oldBadgeCount = Number( $existingBadge.text() );
- if ( isNaN( oldBadgeCount ) ) {
- oldBadgeCount = 0;
- }
+ $.fn.badge = function ( text, inline ) {
+ var div, $badge = this.find( '.mw-badge' );
- // If badgeCount is a number, use that as the new badge
- if ( typeof badgeCount === 'number' ) {
- newBadgeCount = badgeCount;
- } else if ( typeof badgeCount === 'string' ) {
- // If badgeCount is "+x", add x to the old badge
- if ( badgeCount.charAt(0) === '+' ) {
- newBadgeCount = oldBadgeCount + Number( badgeCount.substr(1) );
- // If badgeCount is "-x", subtract x from the old badge
- } else if ( badgeCount.charAt(0) === '-' ) {
- newBadgeCount = oldBadgeCount - Number( badgeCount.substr(1) );
- // If badgeCount can be converted into a number, convert it
- } else if ( !isNaN( Number( badgeCount ) ) ) {
- newBadgeCount = Number( badgeCount );
+ if ( text ) {
+ // If a badge already exists, reuse it
+ if ( $badge.length ) {
+ $badge.find( '.mw-badge-content' ).text( text );
} else {
- newBadgeCount = 0;
+ // Otherwise, create a new badge with the specified text and style
+ div = document.createElement( 'div' );
+ div.className = 'mw-badge mw-badge-' + ( inline ? 'inline' : 'overlay' );
+ div.innerHTML = '<span class="mw-badge-content">' + text + '</span>';
+ $( div ).appendTo( this );
}
- // Other types are not supported, fall back to 0.
- } else {
- newBadgeCount = 0;
- }
-
- // Badge count must be a whole number
- newBadgeCount = Math.round( newBadgeCount );
-
- if ( newBadgeCount <= 0 ) {
- // Badges should only exist for values > 0.
- $existingBadge.remove();
} else {
- // Don't add duplicates
- if ( $existingBadge.length ) {
- $badge = $existingBadge;
- // Insert the new count into the badge
- this.find( '.mw-badge-content' ).text( newBadgeCount );
- } else {
- // Contruct a new badge with the count
- $badge = $( '<div>' )
- .addClass( 'mw-badge' )
- .append(
- $( '<span>' )
- .addClass( 'mw-badge-content' )
- .text( newBadgeCount )
- );
- this.append( $badge );
- }
-
- if ( options.type === 'inline' ) {
- $badge
- .removeClass( 'mw-badge-overlay' )
- .addClass( 'mw-badge-inline' );
- // Default: overlay
- } else {
- $badge
- .removeClass( 'mw-badge-inline' )
- .addClass( 'mw-badge-overlay' );
-
- }
-
- // If a callback was specified, call it with the badge count
- if ( $.isFunction( options.callback ) ) {
- options.callback( newBadgeCount );
- }
+ $badge.remove();
}
+ return this;
};
}( jQuery ) );
* @param $elements array
*/
protected function renderNavigation( $elements ) {
- global $wgVectorUseSimpleSearch;
+ global $wgVectorUseSimpleSearch, $wgVectorCombineUserTalk;
// If only one element was given, wrap it in an array, allowing more
// flexible arguments
<div id="p-personal" class="<?php if ( count( $this->data['personal_urls'] ) == 0 ) echo ' emptyPortlet'; ?>">
<h5><?php $this->msg( 'personaltools' ) ?></h5>
<ul<?php $this->html( 'userlangattributes' ) ?>>
-<?php foreach( $this->getPersonalTools() as $key => $item ) { ?>
- <?php echo $this->makeListItem( $key, $item ); ?>
-
-<?php } ?>
+<?php
+ $personalTools = $this->getPersonalTools();
+ if ( $wgVectorCombineUserTalk && isset( $personalTools['userpage'] ) ) {
+?>
+ <li>
+<?php
+ echo $this->makeListItem( 'userpage', $personalTools['userpage'], array( 'tag' => 'span' ) );
+?> <?php
+ $personalTools['mytalk']['links'][0]['text'] = $this->getMsg( 'mytalk-parenthetical' )->text();
+ $talkItem = $this->makeListItem( 'mytalk', $personalTools['mytalk'], array( 'tag' => 'span' ) );
+ echo $this->getMsg( 'parentheses' )->rawParams( $talkItem )->escaped();
+ unset( $personalTools['userpage'], $personalTools['mytalk'] );
+?>
+ </li>
+<?php
+ }
+ foreach ( $personalTools as $key => $item ) {
+ echo $this->makeListItem( $key, $item );
+ }
+?>
</ul>
</div>
<?php
'RandomImageGenerator' => "$testFolder/phpunit/includes/api/RandomImageGenerator.php",
'UserWrapper' => "$testFolder/phpunit/includes/api/ApiTestCase.php",
+ //db
+ 'ORMTableTest' => "$testFolder/phpunit/includes/db/ORMTableTest.php",
+
//Selenium
'SeleniumTestConstants' => "$testFolder/selenium/SeleniumTestConstants.php",
{{{1}}}="{{{2}}}"
!! endarticle
+!! article
+Template:table_attribs
+!! text
+<noinclude>
+|</noinclude>style="color: red"| Foo
+!! endarticle
+
###
### Basic tests
###
</p>
!! end
+!! test
+</pre> inside nowiki
+!! input
+<nowiki></pre></nowiki>
+!! result
+<p></pre>
+</p>
+!! end
+
!!test
Templates: Pre: 1a. Templates that break a line should suppress <pre>
!!input
</pre>
!!end
+!! test
+Templates: Single-line variant of parameter whitespace stripping test
+!! input
+{{echo| a}}
+
+{{echo|1= a}}
+
+{{echo|{{echo| a}}}}
+
+{{echo|1={{echo| a}}}}
+!! result
+<pre>a
+</pre>
+<p>a
+</p>
+<pre>a
+</pre>
+<p>a
+</p>
+!! end
+
+!! test
+Templates: Strip whitespace from named parameters, but not positional ones
+!! input
+{{echo|
+ foo}}
+
+{{echo|
+* foo}}
+
+{{echo| 1 =
+ foo}}
+
+{{echo| 1 =
+* foo}}
+!! result
+<pre>foo
+</pre>
+<p><br />
+</p>
+<ul><li> foo
+</li></ul>
+<p>foo
+</p>
+<ul><li> foo
+</li></ul>
+
+!! end
+
###
### Parsoid-centric tests for testing RT edge cases for pre
###
</p>
!! end
+!! test
+Bracketed external links with template-generated invalid target
+!! input
+[{{echo|http:/example.com}} title]
+!! result
+<p>[http:/example.com title]
+</p>
+!! end
+
!! test
Bug 2702: Mismatched <i>, <b> and <a> tags are invalid
!! input
!! end
+!! test
+Template-generated table cell attributes and cell content
+!! input
+{|
+|{{table_attribs}}
+|}
+!! result
+<table>
+<tr>
+<td style="color: red"> Foo
+</td></tr></table>
+
+!! end
###
### Internal links
</p>
!! end
+!! test
+Broken br tag sanitization
+!! input
+</br>
+!! result
+<p></br>
+</p>
+!! end
+
!! test
Incorrecly removing closing slashes from correctly formed XHTML
!! input
!! end
+# Plain MediaWiki does not remove empty lists, but tidy actually does.
+# Templates in Wikipedia rely on this behavior, as tidy has always been
+# enabled there. These tests are normally run *without* tidy, so specify the
+# full output here.
+# To test realistic parsing behavior, apply a tidy-like transformation to both
+# the expected output and your parser's output.
+!! test
+Bug 529: Uncovered bullet leaving empty list, normally removed by tidy
+!! input
+******* Foo {{bullet}}
+!! result
+<ul><li><ul><li><ul><li><ul><li><ul><li><ul><li><ul><li> Foo
+</li></ul>
+</li></ul>
+</li></ul>
+</li></ul>
+</li></ul>
+</li></ul>
+</li><li> Bar
+</li></ul>
+
+!! end
+
!! test
Bug 529: Uncovered table already at line-start
!! input
# - wgDefaultLanguageVariant
# - Optional message
return array(
- array( 'fr', 'Main_page', 'fr', 'fr', false ),
- array( 'es', 'Main_page', 'es', 'zh-tw', false ),
- array( 'zh', 'Main_page', 'zh', 'zh-tw', false ),
+ array( 'fr', 'Help:I_need_somebody', 'fr', 'fr', false ),
+ array( 'es', 'Help:I_need_somebody', 'es', 'zh-tw', false ),
+ array( 'zh', 'Help:I_need_somebody', 'zh', 'zh-tw', false ),
- array( 'es', 'Main_page', 'es', 'zh-tw', 'zh-cn' ),
+ array( 'es', 'Help:I_need_somebody', 'es', 'zh-tw', 'zh-cn' ),
array( 'es', 'MediaWiki:About', 'es', 'zh-tw', 'zh-cn' ),
array( 'es', 'MediaWiki:About/', 'es', 'zh-tw', 'zh-cn' ),
array( 'de', 'MediaWiki:About/de', 'es', 'zh-tw', 'zh-cn' ),
array( 'en', 'User:JohnDoe/Common.js', 'es', 'zh-tw', 'zh-cn' ),
array( 'en', 'User:JohnDoe/Monobook.css', 'es', 'zh-tw', 'zh-cn' ),
- array( 'zh-cn', 'Main_page', 'zh', 'zh-tw', 'zh-cn' ),
+ array( 'zh-cn', 'Help:I_need_somebody', 'zh', 'zh-tw', 'zh-cn' ),
array( 'zh', 'MediaWiki:About', 'zh', 'zh-tw', 'zh-cn' ),
array( 'zh', 'MediaWiki:About/', 'zh', 'zh-tw', 'zh-cn' ),
array( 'de', 'MediaWiki:About/de', 'zh', 'zh-tw', 'zh-cn' ),
$user->addToDatabase();
// let the user have a few (3) edits
- $page = WikiPage::factory( Title::newFromText( 'UserTest_EditCount' ) );
+ $page = WikiPage::factory( Title::newFromText( 'Help:UserTest_EditCount' ) );
for( $i = 0; $i < 3; $i++ ) {
$page->doEdit( (string) $i, 'test', 0, false, $user );
}
--- /dev/null
+<?php
+/**
+ * Abstract class to construct tests for ORMTable deriving classes.
+ *
+ * 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
+ * @since 1.21
+ *
+ * @ingroup Test
+ *
+ * @group ORM
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+abstract class ORMTableTest extends MediaWikiTestCase {
+
+ /**
+ * @since 1.21
+ * @return string
+ */
+ protected abstract function getTableClass();
+
+ /**
+ * @since 1.21
+ * @return IORMTable
+ */
+ public function getTable() {
+ $class = $this->getTableClass();
+ return $class::singleton();
+ }
+
+ /**
+ * @since 1.21
+ * @return string
+ */
+ public function getRowClass() {
+ return $this->getTable()->getRowClass();
+ }
+
+ /**
+ * @since 1.21
+ */
+ public function testSingleton() {
+ $class = $this->getTableClass();
+
+ $this->assertInstanceOf( $class, $class::singleton() );
+ $this->assertTrue( $class::singleton() === $class::singleton() );
+ }
+
+}
$this->assertEquals( null, $tmpFile, "Local ref of not existing file is null ($backendName)." );
}
+ /**
+ * @dataProvider provider_testGetFileHttpUrl
+ */
+ public function testGetFileHttpUrl( $source, $content ) {
+ $this->backend = $this->singleBackend;
+ $this->tearDownFiles();
+ $this->doTestGetFileHttpUrl( $source, $content );
+ $this->tearDownFiles();
+
+ $this->backend = $this->multiBackend;
+ $this->tearDownFiles();
+ $this->doTestGetFileHttpUrl( $source, $content );
+ $this->tearDownFiles();
+ }
+
+ private function doTestGetFileHttpUrl( $source, $content ) {
+ $backendName = $this->backendClass();
+
+ $this->prepare( array( 'dir' => dirname( $source ) ) );
+ $status = $this->backend->doOperation(
+ array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
+ $this->assertGoodStatus( $status,
+ "Creation of file at $source succeeded ($backendName)." );
+
+ $url = $this->backend->getFileHttpUrl( array( 'src' => $source ) );
+
+ if ( $url !== null ) { // supported
+ $data = Http::request( "GET", $url );
+ $this->assertEquals( $content, $data,
+ "HTTP GET of URL has right contents ($backendName)." );
+ }
+ }
+
+ function provider_testGetFileHttpUrl() {
+ $cases = array();
+
+ $base = self::baseStorePath();
+ $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
+ $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
+ $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
+
+ return $cases;
+ }
+
/**
* @dataProvider provider_testPrepareAndClean
*/
// Called directly, use $_REQUEST params
wfThumbHandleRequest();
}
+
wfLogProfilingData();
//--------------------------------------------------------------------------
* @return void
*/
function wfThumbHandle404() {
- # lighttpd puts the original request in REQUEST_URI, while sjs sets
- # that to the 404 handler, and puts the original request in REDIRECT_URL.
- if ( isset( $_SERVER['REDIRECT_URL'] ) ) {
- # The URL is un-encoded, so put it back how it was
- $uriPath = str_replace( "%2F", "/", urlencode( $_SERVER['REDIRECT_URL'] ) );
- } else {
- $uriPath = $_SERVER['REQUEST_URI'];
- }
- # Just get the URI path (REDIRECT_URL/REQUEST_URI is either a full URL or a path)
- if ( substr( $uriPath, 0, 1 ) !== '/' ) {
- $bits = wfParseUrl( $uriPath );
- if ( $bits && isset( $bits['path'] ) ) {
- $uriPath = $bits['path'];
- } else {
- wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' );
- return;
- }
+ global $wgArticlePath;
+
+ # Set action base paths so that WebRequest::getPathInfo()
+ # recognizes the "X" as the 'title' in ../thumb_handler.php/X urls.
+ $wgArticlePath = false; # Don't let a "/*" article path clober our action path
+
+ $matches = WebRequest::getPathInfo();
+ if ( !isset( $matches['title'] ) ) {
+ wfThumbError( 404, 'Could not determine the name of the requested thumbnail.' );
+ return;
}
- $params = wfExtractThumbParams( $uriPath ); // basic wiki URL param extracting
+ $params = wfExtractThumbParams( $matches['title'] ); // basic wiki URL param extracting
if ( $params == null ) {
- wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' );
+ wfThumbError( 400, 'The specified thumbnail parameters are not recognized.' );
return;
}
* Extract the required params for thumb.php from the thumbnail request URI.
* At least 'width' and 'f' should be set if the result is an array.
*
- * @param $uriPath String Thumbnail request URI path
+ * @param $thumbRel String Thumbnail path relative to the thumb zone
* @return Array|null associative params array or null
*/
-function wfExtractThumbParams( $uriPath ) {
+function wfExtractThumbParams( $thumbRel ) {
$repo = RepoGroup::singleton()->getLocalRepo();
- // Zone URL might be relative ("/images") or protocol-relative ("//lang.site/image")
- $zoneUriPath = $repo->getZoneHandlerUrl( 'thumb' )
- ? $repo->getZoneHandlerUrl( 'thumb' ) // custom URL
- : $repo->getZoneUrl( 'thumb' ); // default to main URL
- $bits = wfParseUrl( wfExpandUrl( $zoneUriPath, PROTO_INTERNAL ) );
- if ( $bits && isset( $bits['path'] ) ) {
- $zoneUriPath = $bits['path'];
- } else {
- return null; // not a valid thumbnail URL
- }
-
$hashDirReg = $subdirReg = '';
for ( $i = 0; $i < $repo->getHashLevels(); $i++ ) {
$subdirReg .= '[0-9a-f]';
$hashDirReg .= "$subdirReg/";
}
- $zoneReg = preg_quote( $zoneUriPath ); // regex for thumb zone URI
// Check if this is a thumbnail of an original in the local file repo
- if ( preg_match( "!^$zoneReg/((archive/)?$hashDirReg([^/]*)/([^/]*))$!", $uriPath, $m ) ) {
+ if ( preg_match( "!^((archive/)?$hashDirReg([^/]*)/([^/]*))$!", $thumbRel, $m ) ) {
list( /*all*/, $rel, $archOrTemp, $filename, $thumbname ) = $m;
// Check if this is a thumbnail of an temp file in the local file repo
- } elseif ( preg_match( "!^$zoneReg/(temp/)($hashDirReg([^/]*)/([^/]*))$!", $uriPath, $m ) ) {
+ } elseif ( preg_match( "!^(temp/)($hashDirReg([^/]*)/([^/]*))$!", $thumbRel, $m ) ) {
list( /*all*/, $archOrTemp, $rel, $filename, $thumbname ) = $m;
} else {
return null; // not a valid looking thumbnail request
}
- $filename = urldecode( $filename );
- $thumbname = urldecode( $thumbname );
-
$params = array( 'f' => $filename, 'rel404' => $rel );
if ( $archOrTemp === 'archive/' ) {
$params['archived'] = 1;