* ResourceLoader supports hashes as module cache invalidation trigger (instead
of or in addition to timestamps).
* Added $wgExtensionEntryPointListFiles for use in mergeMessageFileList.php.
+* Added a hook, APIQuerySiteInfoStatisticsInfo, to allow extensions to modify
+ the output of the API query meta=siteinfo&siprop=statistics
+* Primary keys have been added to both the archive table and the externallinks
+ tables.
+* Added $wgEnableParserLimitReporting to control whether the NewPP limit report is
+ output in a HTML comment.
+* The 'UnwatchArticle' and 'WatchArticle' hooks now support a Status object
+ instead of just a boolean return value to abort the hook.
=== Bug fixes in 1.22 ===
* Disable Special:PasswordReset when $wgEnableEmail is false. Previously one
database is created.
* (bug 47191) Fixed "Column 'si_title' cannot be part of FULLTEXT index"
MySQL error when installing using the binary character set option.
+* (bug 45288) Support mysqli PHP extension
=== API changes in 1.22 ===
* (bug 25553) The JSON output formatter now leaves forward slashes unescaped
user blocks.
* (bug 48201) action=parse&text=foo now assumes wikitext if no title is given,
rather than using the content model of the page "API".
-* action=watch may now return errors.
+* action=watch no longer silently ignores hook abort.
* (bug 50785) action=purge with forcelinkupdate=1 no longer queues refreshLinks
jobs in the job queue for link table updates of pages that use the given page
as a template. Instead, forcerecursivelinkupdate=1 is introduced and should
of media handler overriding MediaHandler::parseParamString.
* (bug 46512) The collapsibleNav feature from the Vector extension has been moved
to the Vector skin in core.
+* SpecialRecentChanges::addRecentChangesJS() function has been renamed
+ to addModules() and made protected.
+* Methods WatchAction::doWatch and WatchAction::doUnwatch now return a Status
+ object instead of a boolean.
== Compatibility ==
$module: the current ApiQuerySiteInfo module
&$results: array of results, add things here
+'APIQuerySiteInfoStatisticsInfo': Use this hook to add extra information to the
+sites statistics information.
+&$results: array of results, add things here
+
'APIQueryUsersTokens': Use this hook to add custom token to list=users. Every
token has an action, which will be used in the ustoken parameter and in the
output (actiontoken="..."), and a callback function which should return the
'CdbWriter_DBA' => 'includes/Cdb.php',
'CdbWriter_PHP' => 'includes/Cdb_PHP.php',
'ChangesFeed' => 'includes/ChangesFeed.php',
- 'ChangesList' => 'includes/ChangesList.php',
'ChangeTags' => 'includes/ChangeTags.php',
'ChannelFeed' => 'includes/Feed.php',
'Collation' => 'includes/Collation.php',
'DumpPipeOutput' => 'includes/Export.php',
'EditPage' => 'includes/EditPage.php',
'EmailNotification' => 'includes/UserMailer.php',
- 'EnhancedChangesList' => 'includes/ChangesList.php',
'ErrorPageError' => 'includes/Exception.php',
'ExplodeIterator' => 'includes/StringUtils.php',
'FakeTitle' => 'includes/FakeTitle.php',
'MWHttpRequest' => 'includes/HttpFunctions.php',
'MWInit' => 'includes/Init.php',
'MWNamespace' => 'includes/Namespace.php',
- 'OldChangesList' => 'includes/ChangesList.php',
'OutputPage' => 'includes/OutputPage.php',
'Page' => 'includes/WikiPage.php',
'PageQueryPage' => 'includes/PageQueryPage.php',
'QueryPage' => 'includes/QueryPage.php',
'QuickTemplate' => 'includes/SkinTemplate.php',
'RawMessage' => 'includes/Message.php',
- 'RCCacheEntry' => 'includes/ChangesList.php',
'RdfMetaData' => 'includes/Metadata.php',
'ReadOnlyError' => 'includes/Exception.php',
- 'RecentChange' => 'includes/RecentChange.php',
'RedirectSpecialArticle' => 'includes/SpecialPage.php',
'RedirectSpecialPage' => 'includes/SpecialPage.php',
'RegexlikeReplacer' => 'includes/StringUtils.php',
'TitleDependency' => 'includes/cache/CacheDependency.php',
'TitleListDependency' => 'includes/cache/CacheDependency.php',
+ # includes/changes
+ 'ChangesList' => 'includes/changes/ChangesList.php',
+ 'EnhancedChangesList' => 'includes/changes/EnhancedChangesList.php',
+ 'OldChangesList' => 'includes/changes/OldChangesList.php',
+ 'RCCacheEntry' => 'includes/changes/RCCacheEntry.php',
+ 'RecentChange' => 'includes/changes/RecentChange.php',
+
# includes/clientpool
'RedisConnectionPool' => 'includes/clientpool/RedisConnectionPool.php',
'RedisConnRef' => 'includes/clientpool/RedisConnectionPool.php',
'DatabaseMssql' => 'includes/db/DatabaseMssql.php',
'DatabaseMysql' => 'includes/db/DatabaseMysql.php',
'DatabaseMysqlBase' => 'includes/db/DatabaseMysqlBase.php',
+ 'DatabaseMysqli' => 'includes/db/DatabaseMysqli.php',
'DatabaseOracle' => 'includes/db/DatabaseOracle.php',
'DatabasePostgres' => 'includes/db/DatabasePostgres.php',
'DatabaseSqlite' => 'includes/db/DatabaseSqlite.php',
+++ /dev/null
-<?php
-/**
- * Classes to show lists of changes.
- *
- * These can be:
- * - watchlist
- * - related changes
- * - recent changes
- *
- * 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
- */
-
-/**
- * @todo document
- */
-class RCCacheEntry extends RecentChange {
- var $secureName, $link;
- var $curlink, $difflink, $lastlink, $usertalklink, $versionlink;
- var $userlink, $timestamp, $watched;
-
- /**
- * @param $rc RecentChange
- * @return RCCacheEntry
- */
- static function newFromParent( $rc ) {
- $rc2 = new RCCacheEntry;
- $rc2->mAttribs = $rc->mAttribs;
- $rc2->mExtra = $rc->mExtra;
- return $rc2;
- }
-}
-
-/**
- * Base class for all changes lists
- */
-class ChangesList extends ContextSource {
-
- /**
- * @var Skin
- */
- public $skin;
-
- protected $watchlist = false;
-
- protected $message;
-
- /**
- * Changeslist constructor
- *
- * @param $obj Skin or IContextSource
- */
- public function __construct( $obj ) {
- if ( $obj instanceof IContextSource ) {
- $this->setContext( $obj );
- $this->skin = $obj->getSkin();
- } else {
- $this->setContext( $obj->getContext() );
- $this->skin = $obj;
- }
- $this->preCacheMessages();
- }
-
- /**
- * Fetch an appropriate changes list class for the main context
- * This first argument used to be an User object.
- *
- * @deprecated in 1.18; use newFromContext() instead
- * @param string|User $unused Unused
- * @return ChangesList|EnhancedChangesList|OldChangesList derivative
- */
- public static function newFromUser( $unused ) {
- wfDeprecated( __METHOD__, '1.18' );
- return self::newFromContext( RequestContext::getMain() );
- }
-
- /**
- * Fetch an appropriate changes list class for the specified context
- * Some users might want to use an enhanced list format, for instance
- *
- * @param $context IContextSource to use
- * @return ChangesList|EnhancedChangesList|OldChangesList derivative
- */
- public static function newFromContext( IContextSource $context ) {
- $user = $context->getUser();
- $sk = $context->getSkin();
- $list = null;
- if ( wfRunHooks( 'FetchChangesList', array( $user, &$sk, &$list ) ) ) {
- $new = $context->getRequest()->getBool( 'enhanced', $user->getOption( 'usenewrc' ) );
- return $new ? new EnhancedChangesList( $context ) : new OldChangesList( $context );
- } else {
- return $list;
- }
- }
-
- /**
- * Sets the list to use a "<li class='watchlist-(namespace)-(page)'>" tag
- * @param $value Boolean
- */
- public function setWatchlistDivs( $value = true ) {
- $this->watchlist = $value;
- }
-
- /**
- * As we use the same small set of messages in various methods and that
- * they are called often, we call them once and save them in $this->message
- */
- private function preCacheMessages() {
- if ( !isset( $this->message ) ) {
- foreach ( array(
- 'cur', 'diff', 'hist', 'enhancedrc-history', 'last', 'blocklink', 'history',
- 'semicolon-separator', 'pipe-separator' ) as $msg
- ) {
- $this->message[$msg] = $this->msg( $msg )->escaped();
- }
- }
- }
-
- /**
- * Returns the appropriate flags for new page, minor change and patrolling
- * @param array $flags Associative array of 'flag' => Bool
- * @param string $nothing to use for empty space
- * @return String
- */
- public function recentChangesFlags( $flags, $nothing = ' ' ) {
- global $wgRecentChangesFlags;
- $f = '';
- foreach ( array_keys( $wgRecentChangesFlags ) as $flag ) {
- $f .= isset( $flags[$flag] ) && $flags[$flag]
- ? self::flag( $flag )
- : $nothing;
- }
- return $f;
- }
-
- /**
- * Provide the "<abbr>" element appropriate to a given abbreviated flag,
- * namely the flag indicating a new page, a minor edit, a bot edit, or an
- * unpatrolled edit. By default in English it will contain "N", "m", "b",
- * "!" respectively, plus it will have an appropriate title and class.
- *
- * @param string $flag One key of $wgRecentChangesFlags
- * @return String: Raw HTML
- */
- public static function flag( $flag ) {
- static $flagInfos = null;
- if ( is_null( $flagInfos ) ) {
- global $wgRecentChangesFlags;
- $flagInfos = array();
- foreach ( $wgRecentChangesFlags as $key => $value ) {
- $flagInfos[$key]['letter'] = wfMessage( $value['letter'] )->escaped();
- $flagInfos[$key]['title'] = wfMessage( $value['title'] )->escaped();
- // Allow customized class name, fall back to flag name
- $flagInfos[$key]['class'] = Sanitizer::escapeClass(
- isset( $value['class'] ) ? $value['class'] : $key );
- }
- }
-
- // Inconsistent naming, bleh, kepted for b/c
- $map = array(
- 'minoredit' => 'minor',
- 'botedit' => 'bot',
- );
- if ( isset( $map[$flag] ) ) {
- $flag = $map[$flag];
- }
-
- return "<abbr class='" . $flagInfos[$flag]['class'] . "' title='" . $flagInfos[$flag]['title'] . "'>" .
- $flagInfos[$flag]['letter'] .
- '</abbr>';
- }
-
- /**
- * Returns text for the start of the tabular part of RC
- * @return String
- */
- public function beginRecentChangesList() {
- $this->rc_cache = array();
- $this->rcMoveIndex = 0;
- $this->rcCacheIndex = 0;
- $this->lastdate = '';
- $this->rclistOpen = false;
- $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
- return '';
- }
-
- /**
- * Show formatted char difference
- * @param $old Integer: bytes
- * @param $new Integer: bytes
- * @param $context IContextSource context to use
- * @return String
- */
- public static function showCharacterDifference( $old, $new, IContextSource $context = null ) {
- global $wgRCChangedSizeThreshold, $wgMiserMode;
-
- if ( !$context ) {
- $context = RequestContext::getMain();
- }
-
- $new = (int)$new;
- $old = (int)$old;
- $szdiff = $new - $old;
-
- $lang = $context->getLanguage();
- $code = $lang->getCode();
- static $fastCharDiff = array();
- if ( !isset( $fastCharDiff[$code] ) ) {
- $fastCharDiff[$code] = $wgMiserMode || $context->msg( 'rc-change-size' )->plain() === '$1';
- }
-
- $formattedSize = $lang->formatNum( $szdiff );
-
- if ( !$fastCharDiff[$code] ) {
- $formattedSize = $context->msg( 'rc-change-size', $formattedSize )->text();
- }
-
- if ( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) {
- $tag = 'strong';
- } else {
- $tag = 'span';
- }
-
- if ( $szdiff === 0 ) {
- $formattedSizeClass = 'mw-plusminus-null';
- }
- if ( $szdiff > 0 ) {
- $formattedSize = '+' . $formattedSize;
- $formattedSizeClass = 'mw-plusminus-pos';
- }
- if ( $szdiff < 0 ) {
- $formattedSizeClass = 'mw-plusminus-neg';
- }
-
- $formattedTotalSize = $context->msg( 'rc-change-size-new' )->numParams( $new )->text();
-
- return Html::element( $tag,
- array( 'dir' => 'ltr', 'class' => $formattedSizeClass, 'title' => $formattedTotalSize ),
- $context->msg( 'parentheses', $formattedSize )->plain() ) . $lang->getDirMark();
- }
-
- /**
- * Format the character difference of one or several changes.
- *
- * @param $old RecentChange
- * @param $new RecentChange last change to use, if not provided, $old will be used
- * @return string HTML fragment
- */
- public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) {
- $oldlen = $old->mAttribs['rc_old_len'];
-
- if ( $new ) {
- $newlen = $new->mAttribs['rc_new_len'];
- } else {
- $newlen = $old->mAttribs['rc_new_len'];
- }
-
- if ( $oldlen === null || $newlen === null ) {
- return '';
- }
-
- return self::showCharacterDifference( $oldlen, $newlen, $this->getContext() );
- }
-
- /**
- * Returns text for the end of RC
- * @return String
- */
- public function endRecentChangesList() {
- if ( $this->rclistOpen ) {
- return "</ul>\n";
- } else {
- return '';
- }
- }
-
- /**
- * @param string $s HTML to update
- * @param $rc_timestamp mixed
- */
- public function insertDateHeader( &$s, $rc_timestamp ) {
- # Make date header if necessary
- $date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() );
- if ( $date != $this->lastdate ) {
- if ( $this->lastdate != '' ) {
- $s .= "</ul>\n";
- }
- $s .= Xml::element( 'h4', null, $date ) . "\n<ul class=\"special\">";
- $this->lastdate = $date;
- $this->rclistOpen = true;
- }
- }
-
- /**
- * @param string $s HTML to update
- * @param $title Title
- * @param $logtype string
- */
- public function insertLog( &$s, $title, $logtype ) {
- $page = new LogPage( $logtype );
- $logname = $page->getName()->escaped();
- $s .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $title, $logname ) )->escaped();
- }
-
- /**
- * @param string $s HTML to update
- * @param $rc RecentChange
- * @param $unpatrolled
- */
- public function insertDiffHist( &$s, &$rc, $unpatrolled ) {
- # Diff link
- if ( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
- $diffLink = $this->message['diff'];
- } elseif ( !self::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
- $diffLink = $this->message['diff'];
- } else {
- $query = array(
- 'curid' => $rc->mAttribs['rc_cur_id'],
- 'diff' => $rc->mAttribs['rc_this_oldid'],
- 'oldid' => $rc->mAttribs['rc_last_oldid']
- );
-
- $diffLink = Linker::linkKnown(
- $rc->getTitle(),
- $this->message['diff'],
- array( 'tabindex' => $rc->counter ),
- $query
- );
- }
- $diffhist = $diffLink . $this->message['pipe-separator'];
- # History link
- $diffhist .= Linker::linkKnown(
- $rc->getTitle(),
- $this->message['hist'],
- array(),
- array(
- 'curid' => $rc->mAttribs['rc_cur_id'],
- 'action' => 'history'
- )
- );
- $s .= $this->msg( 'parentheses' )->rawParams( $diffhist )->escaped() . ' <span class="mw-changeslist-separator">. .</span> ';
- }
-
- /**
- * @param string $s HTML to update
- * @param $rc RecentChange
- * @param $unpatrolled
- * @param $watched
- */
- public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
- $params = array();
-
- $articlelink = Linker::linkKnown(
- $rc->getTitle(),
- null,
- array( 'class' => 'mw-changeslist-title' ),
- $params
- );
- if ( $this->isDeleted( $rc, Revision::DELETED_TEXT ) ) {
- $articlelink = '<span class="history-deleted">' . $articlelink . '</span>';
- }
- # To allow for boldening pages watched by this user
- $articlelink = "<span class=\"mw-title\">{$articlelink}</span>";
- # RTL/LTR marker
- $articlelink .= $this->getLanguage()->getDirMark();
-
- wfRunHooks( 'ChangesListInsertArticleLink',
- array( &$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched ) );
-
- $s .= " $articlelink";
- }
-
- /**
- * Get the timestamp from $rc formatted with current user's settings
- * and a separator
- *
- * @param $rc RecentChange
- * @return string HTML fragment
- */
- public function getTimestamp( $rc ) {
- return $this->message['semicolon-separator'] . '<span class="mw-changeslist-date">' .
- $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() ) . '</span> <span class="mw-changeslist-separator">. .</span> ';
- }
-
- /**
- * Insert time timestamp string from $rc into $s
- *
- * @param string $s HTML to update
- * @param $rc RecentChange
- */
- public function insertTimestamp( &$s, $rc ) {
- $s .= $this->getTimestamp( $rc );
- }
-
- /**
- * Insert links to user page, user talk page and eventually a blocking link
- *
- * @param &$s String HTML to update
- * @param &$rc RecentChange
- */
- public function insertUserRelatedLinks( &$s, &$rc ) {
- if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
- $s .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
- } else {
- $s .= $this->getLanguage()->getDirMark() . Linker::userLink( $rc->mAttribs['rc_user'],
- $rc->mAttribs['rc_user_text'] );
- $s .= Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
- }
- }
-
- /**
- * Insert a formatted action
- *
- * @param $rc RecentChange
- * @return string
- */
- public function insertLogEntry( $rc ) {
- $formatter = LogFormatter::newFromRow( $rc->mAttribs );
- $formatter->setContext( $this->getContext() );
- $formatter->setShowUserToolLinks( true );
- $mark = $this->getLanguage()->getDirMark();
- return $formatter->getActionText() . " $mark" . $formatter->getComment();
- }
-
- /**
- * Insert a formatted comment
- * @param $rc RecentChange
- * @return string
- */
- public function insertComment( $rc ) {
- if ( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
- if ( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) {
- return ' <span class="history-deleted">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
- } else {
- return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
- }
- }
- return '';
- }
-
- /**
- * Check whether to enable recent changes patrol features
- *
- * @deprecated since 1.22
- * @return Boolean
- */
- public static function usePatrol() {
- global $wgUser;
-
- wfDeprecated( __METHOD__, '1.22' );
-
- return $wgUser->useRCPatrol();
- }
-
- /**
- * Returns the string which indicates the number of watching users
- * @return string
- */
- protected function numberofWatchingusers( $count ) {
- static $cache = array();
- if ( $count > 0 ) {
- if ( !isset( $cache[$count] ) ) {
- $cache[$count] = $this->msg( 'number_of_watching_users_RCview' )->numParams( $count )->escaped();
- }
- return $cache[$count];
- } else {
- return '';
- }
- }
-
- /**
- * Determine if said field of a revision is hidden
- * @param $rc RCCacheEntry
- * @param $field Integer: one of DELETED_* bitfield constants
- * @return Boolean
- */
- public static function isDeleted( $rc, $field ) {
- return ( $rc->mAttribs['rc_deleted'] & $field ) == $field;
- }
-
- /**
- * Determine if the current user is allowed to view a particular
- * field of this revision, if it's marked as deleted.
- * @param $rc RCCacheEntry
- * @param $field Integer
- * @param $user User object to check, or null to use $wgUser
- * @return Boolean
- */
- public static function userCan( $rc, $field, User $user = null ) {
- if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
- return LogEventsList::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
- } else {
- return Revision::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
- }
- }
-
- /**
- * @param $link string
- * @param $watched bool
- * @return string
- */
- protected function maybeWatchedLink( $link, $watched = false ) {
- if ( $watched ) {
- return '<strong class="mw-watched">' . $link . '</strong>';
- } else {
- return '<span class="mw-rc-unwatched">' . $link . '</span>';
- }
- }
-
- /** Inserts a rollback link
- *
- * @param $s string
- * @param $rc RecentChange
- */
- public function insertRollback( &$s, &$rc ) {
- if ( $rc->mAttribs['rc_type'] == RC_EDIT && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
- $page = $rc->getTitle();
- /** Check for rollback and edit permissions, disallow special pages, and only
- * show a link on the top-most revision */
- if ( $this->getUser()->isAllowed( 'rollback' ) && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] )
- {
- $rev = new Revision( array(
- 'title' => $page,
- 'id' => $rc->mAttribs['rc_this_oldid'],
- 'user' => $rc->mAttribs['rc_user'],
- 'user_text' => $rc->mAttribs['rc_user_text'],
- 'deleted' => $rc->mAttribs['rc_deleted']
- ) );
- $s .= ' ' . Linker::generateRollback( $rev, $this->getContext() );
- }
- }
- }
-
- /**
- * @param $s string
- * @param $rc RecentChange
- * @param $classes
- */
- public function insertTags( &$s, &$rc, &$classes ) {
- if ( empty( $rc->mAttribs['ts_tags'] ) ) {
- return;
- }
-
- list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( $rc->mAttribs['ts_tags'], 'changeslist' );
- $classes = array_merge( $classes, $newClasses );
- $s .= ' ' . $tagSummary;
- }
-
- public function insertExtra( &$s, &$rc, &$classes ) {
- // Empty, used for subclasses to add anything special.
- }
-
- protected function showAsUnpatrolled( RecentChange $rc ) {
- $unpatrolled = false;
- if ( !$rc->mAttribs['rc_patrolled'] ) {
- if ( $this->getUser()->useRCPatrol() ) {
- $unpatrolled = true;
- } elseif ( $this->getUser()->useNPPatrol() && $rc->mAttribs['rc_type'] == RC_NEW ) {
- $unpatrolled = true;
- }
- }
- return $unpatrolled;
- }
-}
-
-/**
- * Generate a list of changes using the good old system (no javascript)
- */
-class OldChangesList extends ChangesList {
- /**
- * Format a line using the old system (aka without any javascript).
- *
- * @param $rc RecentChange, passed by reference
- * @param bool $watched (default false)
- * @param int $linenumber (default null)
- *
- * @return string|bool
- */
- public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
- global $wgRCShowChangedSize;
- wfProfileIn( __METHOD__ );
-
- # Should patrol-related stuff be shown?
- $unpatrolled = $this->showAsUnpatrolled( $rc );
-
- $dateheader = ''; // $s now contains only <li>...</li>, for hooks' convenience.
- $this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
-
- $s = '';
- $classes = array();
- // use mw-line-even/mw-line-odd class only if linenumber is given (feature from bug 14468)
- if ( $linenumber ) {
- if ( $linenumber & 1 ) {
- $classes[] = 'mw-line-odd';
- } else {
- $classes[] = 'mw-line-even';
- }
- }
-
- // Indicate watched status on the line to allow for more
- // comprehensive styling.
- $classes[] = $watched && $rc->mAttribs['rc_timestamp'] >= $watched
- ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
-
- // Moved pages (very very old, not supported anymore)
- if ( $rc->mAttribs['rc_type'] == RC_MOVE || $rc->mAttribs['rc_type'] == RC_MOVE_OVER_REDIRECT ) {
- // Log entries
- } elseif ( $rc->mAttribs['rc_log_type'] ) {
- $logtitle = SpecialPage::getTitleFor( 'Log', $rc->mAttribs['rc_log_type'] );
- $this->insertLog( $s, $logtitle, $rc->mAttribs['rc_log_type'] );
- // Log entries (old format) or log targets, and special pages
- } elseif ( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
- list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $rc->mAttribs['rc_title'] );
- if ( $name == 'Log' ) {
- $this->insertLog( $s, $rc->getTitle(), $subpage );
- }
- // Regular entries
- } else {
- $this->insertDiffHist( $s, $rc, $unpatrolled );
- # M, N, b and ! (minor, new, bot and unpatrolled)
- $s .= $this->recentChangesFlags(
- array(
- 'newpage' => $rc->mAttribs['rc_type'] == RC_NEW,
- 'minor' => $rc->mAttribs['rc_minor'],
- 'unpatrolled' => $unpatrolled,
- 'bot' => $rc->mAttribs['rc_bot']
- ),
- ''
- );
- $this->insertArticleLink( $s, $rc, $unpatrolled, $watched );
- }
- # Edit/log timestamp
- $this->insertTimestamp( $s, $rc );
- # Bytes added or removed
- if ( $wgRCShowChangedSize ) {
- $cd = $this->formatCharacterDifference( $rc );
- if ( $cd !== '' ) {
- $s .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
- }
- }
-
- if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
- $s .= $this->insertLogEntry( $rc );
- } else {
- # User tool links
- $this->insertUserRelatedLinks( $s, $rc );
- # LTR/RTL direction mark
- $s .= $this->getLanguage()->getDirMark();
- $s .= $this->insertComment( $rc );
- }
-
- # Tags
- $this->insertTags( $s, $rc, $classes );
- # Rollback
- $this->insertRollback( $s, $rc );
- # For subclasses
- $this->insertExtra( $s, $rc, $classes );
-
- # How many users watch this page
- if ( $rc->numberofWatchingusers > 0 ) {
- $s .= ' ' . $this->numberofWatchingusers( $rc->numberofWatchingusers );
- }
-
- if ( $this->watchlist ) {
- $classes[] = Sanitizer::escapeClass( 'watchlist-' . $rc->mAttribs['rc_namespace'] . '-' . $rc->mAttribs['rc_title'] );
- }
-
- if ( !wfRunHooks( 'OldChangesListRecentChangesLine', array( &$this, &$s, $rc, &$classes ) ) ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- wfProfileOut( __METHOD__ );
- return "$dateheader<li class=\"" . implode( ' ', $classes ) . "\">" . $s . "</li>\n";
- }
-}
-
-/**
- * Generate a list of changes using an Enhanced system (uses javascript).
- */
-class EnhancedChangesList extends ChangesList {
-
- protected $rc_cache;
-
- /**
- * Add the JavaScript file for enhanced changeslist
- * @return String
- */
- public function beginRecentChangesList() {
- $this->rc_cache = array();
- $this->rcMoveIndex = 0;
- $this->rcCacheIndex = 0;
- $this->lastdate = '';
- $this->rclistOpen = false;
- $this->getOutput()->addModuleStyles( array(
- 'mediawiki.special.changeslist',
- 'mediawiki.special.changeslist.enhanced',
- ) );
- $this->getOutput()->addModules( array(
- 'jquery.makeCollapsible',
- 'mediawiki.icon',
- ) );
- return '';
- }
- /**
- * Format a line for enhanced recentchange (aka with javascript and block of lines).
- *
- * @param $baseRC RecentChange
- * @param $watched bool
- *
- * @return string
- */
- public function recentChangesLine( &$baseRC, $watched = false ) {
- wfProfileIn( __METHOD__ );
-
- # Create a specialised object
- $rc = RCCacheEntry::newFromParent( $baseRC );
-
- $curIdEq = array( 'curid' => $rc->mAttribs['rc_cur_id'] );
-
- # If it's a new day, add the headline and flush the cache
- $date = $this->getLanguage()->userDate( $rc->mAttribs['rc_timestamp'], $this->getUser() );
- $ret = '';
- if ( $date != $this->lastdate ) {
- # Process current cache
- $ret = $this->recentChangesBlock();
- $this->rc_cache = array();
- $ret .= Xml::element( 'h4', null, $date ) . "\n";
- $this->lastdate = $date;
- }
-
- # Should patrol-related stuff be shown?
- $rc->unpatrolled = $this->showAsUnpatrolled( $rc );
-
- $showdifflinks = true;
- # Make article link
- $type = $rc->mAttribs['rc_type'];
- $logType = $rc->mAttribs['rc_log_type'];
- // Page moves, very old style, not supported anymore
- if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
- // New unpatrolled pages
- } elseif ( $rc->unpatrolled && $type == RC_NEW ) {
- $clink = Linker::linkKnown( $rc->getTitle() );
- // Log entries
- } elseif ( $type == RC_LOG ) {
- if ( $logType ) {
- $logtitle = SpecialPage::getTitleFor( 'Log', $logType );
- $logpage = new LogPage( $logType );
- $logname = $logpage->getName()->escaped();
- $clink = $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logtitle, $logname ) )->escaped();
- } else {
- $clink = Linker::link( $rc->getTitle() );
- }
- $watched = false;
- // Log entries (old format) and special pages
- } elseif ( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
- wfDebug( "Unexpected special page in recentchanges\n" );
- $clink = '';
- // Edits
- } else {
- $clink = Linker::linkKnown( $rc->getTitle() );
- }
-
- # Don't show unusable diff links
- if ( !ChangesList::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
- $showdifflinks = false;
- }
-
- $time = $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() );
- $rc->watched = $watched;
- $rc->link = $clink;
- $rc->timestamp = $time;
- $rc->numberofWatchingusers = $baseRC->numberofWatchingusers;
-
- # Make "cur" and "diff" links. Do not use link(), it is too slow if
- # called too many times (50% of CPU time on RecentChanges!).
- $thisOldid = $rc->mAttribs['rc_this_oldid'];
- $lastOldid = $rc->mAttribs['rc_last_oldid'];
-
- $querycur = $curIdEq + array( 'diff' => '0', 'oldid' => $thisOldid );
- $querydiff = $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid );
-
- if ( !$showdifflinks ) {
- $curLink = $this->message['cur'];
- $diffLink = $this->message['diff'];
- } elseif ( in_array( $type, array( RC_NEW, RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
- if ( $type != RC_NEW ) {
- $curLink = $this->message['cur'];
- } else {
- $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
- $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
- }
- $diffLink = $this->message['diff'];
- } else {
- $diffUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querydiff ) );
- $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
- $diffLink = "<a href=\"$diffUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['diff']}</a>";
- $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
- }
-
- # Make "last" link
- if ( !$showdifflinks || !$lastOldid ) {
- $lastLink = $this->message['last'];
- } elseif ( in_array( $type, array( RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
- $lastLink = $this->message['last'];
- } else {
- $lastLink = Linker::linkKnown( $rc->getTitle(), $this->message['last'],
- array(), $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid ) );
- }
-
- # Make user links
- if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
- $rc->userlink = ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
- } else {
- $rc->userlink = Linker::userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
- $rc->usertalklink = Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
- }
-
- $rc->lastlink = $lastLink;
- $rc->curlink = $curLink;
- $rc->difflink = $diffLink;
-
- # Put accumulated information into the cache, for later display
- # Page moves go on their own line
- $title = $rc->getTitle();
- $secureName = $title->getPrefixedDBkey();
- if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
- # Use an @ character to prevent collision with page names
- $this->rc_cache['@@' . ( $this->rcMoveIndex++ )] = array( $rc );
- } else {
- # Logs are grouped by type
- if ( $type == RC_LOG ) {
- $secureName = SpecialPage::getTitleFor( 'Log', $logType )->getPrefixedDBkey();
- }
- if ( !isset( $this->rc_cache[$secureName] ) ) {
- $this->rc_cache[$secureName] = array();
- }
-
- array_push( $this->rc_cache[$secureName], $rc );
- }
-
- wfProfileOut( __METHOD__ );
-
- return $ret;
- }
-
- /**
- * Enhanced RC group
- * @return string
- */
- protected function recentChangesBlockGroup( $block ) {
- global $wgRCShowChangedSize;
-
- wfProfileIn( __METHOD__ );
-
- # Add the namespace and title of the block as part of the class
- $classes = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' );
- if ( $block[0]->mAttribs['rc_log_type'] ) {
- # Log entry
- $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
- . $block[0]->mAttribs['rc_log_type'] . '-' . $block[0]->mAttribs['rc_title'] );
- } else {
- $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns'
- . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] );
- }
- $classes[] = $block[0]->watched && $block[0]->mAttribs['rc_timestamp'] >= $block[0]->watched
- ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
- $r = Html::openElement( 'table', array( 'class' => $classes ) ) .
- Html::openElement( 'tr' );
-
- # Collate list of users
- $userlinks = array();
- # Other properties
- $unpatrolled = false;
- $isnew = false;
- $allBots = true;
- $allMinors = true;
- $curId = $currentRevision = 0;
- # Some catalyst variables...
- $namehidden = true;
- $allLogs = true;
- foreach ( $block as $rcObj ) {
- $oldid = $rcObj->mAttribs['rc_last_oldid'];
- if ( $rcObj->mAttribs['rc_type'] == RC_NEW ) {
- $isnew = true;
- }
- // If all log actions to this page were hidden, then don't
- // give the name of the affected page for this block!
- if ( !$this->isDeleted( $rcObj, LogPage::DELETED_ACTION ) ) {
- $namehidden = false;
- }
- $u = $rcObj->userlink;
- if ( !isset( $userlinks[$u] ) ) {
- $userlinks[$u] = 0;
- }
- if ( $rcObj->unpatrolled ) {
- $unpatrolled = true;
- }
- if ( $rcObj->mAttribs['rc_type'] != RC_LOG ) {
- $allLogs = false;
- }
- # Get the latest entry with a page_id and oldid
- # since logs may not have these.
- if ( !$curId && $rcObj->mAttribs['rc_cur_id'] ) {
- $curId = $rcObj->mAttribs['rc_cur_id'];
- }
- if ( !$currentRevision && $rcObj->mAttribs['rc_this_oldid'] ) {
- $currentRevision = $rcObj->mAttribs['rc_this_oldid'];
- }
-
- if ( !$rcObj->mAttribs['rc_bot'] ) {
- $allBots = false;
- }
- if ( !$rcObj->mAttribs['rc_minor'] ) {
- $allMinors = false;
- }
-
- $userlinks[$u]++;
- }
-
- # Sort the list and convert to text
- krsort( $userlinks );
- asort( $userlinks );
- $users = array();
- foreach ( $userlinks as $userlink => $count ) {
- $text = $userlink;
- $text .= $this->getLanguage()->getDirMark();
- if ( $count > 1 ) {
- $text .= ' ' . $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->formatNum( $count ) . '×' )->escaped();
- }
- array_push( $users, $text );
- }
-
- $users = ' <span class="changedby">'
- . $this->msg( 'brackets' )->rawParams(
- implode( $this->message['semicolon-separator'], $users )
- )->escaped() . '</span>';
-
- $tl = '<span class="mw-collapsible-toggle mw-collapsible-arrow mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>';
- $r .= "<td>$tl</td>";
-
- # Main line
- $r .= '<td class="mw-enhanced-rc">' . $this->recentChangesFlags( array(
- 'newpage' => $isnew, # show, when one have this flag
- 'minor' => $allMinors, # show only, when all have this flag
- 'unpatrolled' => $unpatrolled, # show, when one have this flag
- 'bot' => $allBots, # show only, when all have this flag
- ) );
-
- # Timestamp
- $r .= ' ' . $block[0]->timestamp . ' </td><td>';
-
- # Article link
- if ( $namehidden ) {
- $r .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-event' )->escaped() . '</span>';
- } elseif ( $allLogs ) {
- $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
- } else {
- $this->insertArticleLink( $r, $block[0], $block[0]->unpatrolled, $block[0]->watched );
- }
-
- $r .= $this->getLanguage()->getDirMark();
-
- $queryParams['curid'] = $curId;
-
- # Changes message
- static $nchanges = array();
- static $sinceLastVisitMsg = array();
-
- $n = count( $block );
- if ( !isset( $nchanges[$n] ) ) {
- $nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped();
- }
-
- $sinceLast = 0;
- $unvisitedOldid = null;
- foreach ( $block as $rcObj ) {
- // Same logic as below inside main foreach
- if ( $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched ) {
- $sinceLast++;
- $unvisitedOldid = $rcObj->mAttribs['rc_last_oldid'];
- }
- }
- if ( !isset( $sinceLastVisitMsg[$sinceLast] ) ) {
- $sinceLastVisitMsg[$sinceLast] =
- $this->msg( 'enhancedrc-since-last-visit' )->numParams( $sinceLast )->escaped();
- }
-
- # Total change link
- $r .= ' ';
- $logtext = '';
- if ( !$allLogs ) {
- if ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
- $logtext .= $nchanges[$n];
- } elseif ( $isnew ) {
- $logtext .= $nchanges[$n];
- } else {
- $logtext .= Linker::link(
- $block[0]->getTitle(),
- $nchanges[$n],
- array(),
- $queryParams + array(
- 'diff' => $currentRevision,
- 'oldid' => $oldid,
- ),
- array( 'known', 'noclasses' )
- );
- if ( $sinceLast > 0 && $sinceLast < $n ) {
- $logtext .= $this->message['pipe-separator'] . Linker::link(
- $block[0]->getTitle(),
- $sinceLastVisitMsg[$sinceLast],
- array(),
- $queryParams + array(
- 'diff' => $currentRevision,
- 'oldid' => $unvisitedOldid,
- ),
- array( 'known', 'noclasses' )
- );
- }
- }
- }
-
- # History
- if ( $allLogs ) {
- // don't show history link for logs
- } elseif ( $namehidden || !$block[0]->getTitle()->exists() ) {
- $logtext .= $this->message['pipe-separator'] . $this->message['enhancedrc-history'];
- } else {
- $params = $queryParams;
- $params['action'] = 'history';
-
- $logtext .= $this->message['pipe-separator'] .
- Linker::linkKnown(
- $block[0]->getTitle(),
- $this->message['enhancedrc-history'],
- array(),
- $params
- );
- }
-
- if ( $logtext !== '' ) {
- $r .= $this->msg( 'parentheses' )->rawParams( $logtext )->escaped();
- }
-
- $r .= ' <span class="mw-changeslist-separator">. .</span> ';
-
- # Character difference (does not apply if only log items)
- if ( $wgRCShowChangedSize && !$allLogs ) {
- $last = 0;
- $first = count( $block ) - 1;
- # Some events (like logs) have an "empty" size, so we need to skip those...
- while ( $last < $first && $block[$last]->mAttribs['rc_new_len'] === null ) {
- $last++;
- }
- while ( $first > $last && $block[$first]->mAttribs['rc_old_len'] === null ) {
- $first--;
- }
- # Get net change
- $chardiff = $this->formatCharacterDifference( $block[$first], $block[$last] );
-
- if ( $chardiff == '' ) {
- $r .= ' ';
- } else {
- $r .= ' ' . $chardiff . ' <span class="mw-changeslist-separator">. .</span> ';
- }
- }
-
- $r .= $users;
- $r .= $this->numberofWatchingusers( $block[0]->numberofWatchingusers );
-
- # Sub-entries
- foreach ( $block as $rcObj ) {
- # Classes to apply -- TODO implement
- $classes = array();
- $type = $rcObj->mAttribs['rc_type'];
-
- $trClass = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
- ? ' class="mw-enhanced-watched"' : '';
-
- $r .= '<tr' . $trClass . '><td></td><td class="mw-enhanced-rc">';
- $r .= $this->recentChangesFlags( array(
- 'newpage' => $type == RC_NEW,
- 'minor' => $rcObj->mAttribs['rc_minor'],
- 'unpatrolled' => $rcObj->unpatrolled,
- 'bot' => $rcObj->mAttribs['rc_bot'],
- ) );
- $r .= ' </td><td class="mw-enhanced-rc-nested"><span class="mw-enhanced-rc-time">';
-
- $params = $queryParams;
-
- if ( $rcObj->mAttribs['rc_this_oldid'] != 0 ) {
- $params['oldid'] = $rcObj->mAttribs['rc_this_oldid'];
- }
-
- # Log timestamp
- if ( $type == RC_LOG ) {
- $link = $rcObj->timestamp;
- # Revision link
- } elseif ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
- $link = '<span class="history-deleted">' . $rcObj->timestamp . '</span> ';
- } else {
-
- $link = Linker::linkKnown(
- $rcObj->getTitle(),
- $rcObj->timestamp,
- array(),
- $params
- );
- if ( $this->isDeleted( $rcObj, Revision::DELETED_TEXT ) ) {
- $link = '<span class="history-deleted">' . $link . '</span> ';
- }
- }
- $r .= $link . '</span>';
-
- if ( !$type == RC_LOG || $type == RC_NEW ) {
- $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->curlink . $this->message['pipe-separator'] . $rcObj->lastlink )->escaped();
- }
- $r .= ' <span class="mw-changeslist-separator">. .</span> ';
-
- # Character diff
- if ( $wgRCShowChangedSize ) {
- $cd = $this->formatCharacterDifference( $rcObj );
- if ( $cd !== '' ) {
- $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
- }
- }
-
- if ( $rcObj->mAttribs['rc_type'] == RC_LOG ) {
- $r .= $this->insertLogEntry( $rcObj );
- } else {
- # User links
- $r .= $rcObj->userlink;
- $r .= $rcObj->usertalklink;
- $r .= $this->insertComment( $rcObj );
- }
-
- # Rollback
- $this->insertRollback( $r, $rcObj );
- # Tags
- $this->insertTags( $r, $rcObj, $classes );
-
- $r .= "</td></tr>\n";
- }
- $r .= "</table>\n";
-
- $this->rcCacheIndex++;
-
- wfProfileOut( __METHOD__ );
-
- return $r;
- }
-
- /**
- * Generate HTML for an arrow or placeholder graphic
- * @param string $dir one of '', 'd', 'l', 'r'
- * @param string $alt text
- * @param string $title text
- * @return String: HTML "<img>" tag
- */
- protected function arrow( $dir, $alt = '', $title = '' ) {
- global $wgStylePath;
- $encUrl = htmlspecialchars( $wgStylePath . '/common/images/Arr_' . $dir . '.png' );
- $encAlt = htmlspecialchars( $alt );
- $encTitle = htmlspecialchars( $title );
- return "<img src=\"$encUrl\" width=\"12\" height=\"12\" alt=\"$encAlt\" title=\"$encTitle\" />";
- }
-
- /**
- * Generate HTML for a right- or left-facing arrow,
- * depending on language direction.
- * @return String: HTML "<img>" tag
- */
- protected function sideArrow() {
- $dir = $this->getLanguage()->isRTL() ? 'l' : 'r';
- return $this->arrow( $dir, '+', $this->msg( 'rc-enhanced-expand' )->text() );
- }
-
- /**
- * Generate HTML for a down-facing arrow
- * depending on language direction.
- * @return String: HTML "<img>" tag
- */
- protected function downArrow() {
- return $this->arrow( 'd', '-', $this->msg( 'rc-enhanced-hide' )->text() );
- }
-
- /**
- * Generate HTML for a spacer image
- * @return String: HTML "<img>" tag
- */
- protected function spacerArrow() {
- return $this->arrow( '', codepointToUtf8( 0xa0 ) ); // non-breaking space
- }
-
- /**
- * Enhanced RC ungrouped line.
- *
- * @param $rcObj RecentChange
- * @return String: a HTML formatted line (generated using $r)
- */
- protected function recentChangesBlockLine( $rcObj ) {
- global $wgRCShowChangedSize;
-
- wfProfileIn( __METHOD__ );
- $query['curid'] = $rcObj->mAttribs['rc_cur_id'];
-
- $type = $rcObj->mAttribs['rc_type'];
- $logType = $rcObj->mAttribs['rc_log_type'];
- $classes = array( 'mw-enhanced-rc' );
- if ( $logType ) {
- # Log entry
- $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
- . $logType . '-' . $rcObj->mAttribs['rc_title'] );
- } else {
- $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' .
- $rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] );
- }
- $classes[] = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
- ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
- $r = Html::openElement( 'table', array( 'class' => $classes ) ) .
- Html::openElement( 'tr' );
-
- $r .= '<td class="mw-enhanced-rc"><span class="mw-enhancedchanges-arrow-space"></span>';
- # Flag and Timestamp
- if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
- $r .= $this->recentChangesFlags( array() ); // no flags, but need the placeholders
- } else {
- $r .= $this->recentChangesFlags( array(
- 'newpage' => $type == RC_NEW,
- 'minor' => $rcObj->mAttribs['rc_minor'],
- 'unpatrolled' => $rcObj->unpatrolled,
- 'bot' => $rcObj->mAttribs['rc_bot'],
- ) );
- }
- $r .= ' ' . $rcObj->timestamp . ' </td><td>';
- # Article or log link
- if ( $logType ) {
- $logPage = new LogPage( $logType );
- $logTitle = SpecialPage::getTitleFor( 'Log', $logType );
- $logName = $logPage->getName()->escaped();
- $r .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logTitle, $logName ) )->escaped();
- } else {
- $this->insertArticleLink( $r, $rcObj, $rcObj->unpatrolled, $rcObj->watched );
- }
- # Diff and hist links
- if ( $type != RC_LOG ) {
- $query['action'] = 'history';
- $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->difflink . $this->message['pipe-separator'] . Linker::linkKnown(
- $rcObj->getTitle(),
- $this->message['hist'],
- array(),
- $query
- ) )->escaped();
- }
- $r .= ' <span class="mw-changeslist-separator">. .</span> ';
- # Character diff
- if ( $wgRCShowChangedSize ) {
- $cd = $this->formatCharacterDifference( $rcObj );
- if ( $cd !== '' ) {
- $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
- }
- }
-
- if ( $type == RC_LOG ) {
- $r .= $this->insertLogEntry( $rcObj );
- } else {
- $r .= ' ' . $rcObj->userlink . $rcObj->usertalklink;
- $r .= $this->insertComment( $rcObj );
- $this->insertRollback( $r, $rcObj );
- }
-
- # Tags
- $this->insertTags( $r, $rcObj, $classes );
- # Show how many people are watching this if enabled
- $r .= $this->numberofWatchingusers( $rcObj->numberofWatchingusers );
-
- $r .= "</td></tr></table>\n";
-
- wfProfileOut( __METHOD__ );
-
- return $r;
- }
-
- /**
- * If enhanced RC is in use, this function takes the previously cached
- * RC lines, arranges them, and outputs the HTML
- *
- * @return string
- */
- protected function recentChangesBlock() {
- if ( count ( $this->rc_cache ) == 0 ) {
- return '';
- }
-
- wfProfileIn( __METHOD__ );
-
- $blockOut = '';
- foreach ( $this->rc_cache as $block ) {
- if ( count( $block ) < 2 ) {
- $blockOut .= $this->recentChangesBlockLine( array_shift( $block ) );
- } else {
- $blockOut .= $this->recentChangesBlockGroup( $block );
- }
- }
-
- wfProfileOut( __METHOD__ );
-
- return '<div>' . $blockOut . '</div>';
- }
-
- /**
- * Returns text for the end of RC
- * If enhanced RC is in use, returns pretty much all the text
- * @return string
- */
- public function endRecentChangesList() {
- return $this->recentChangesBlock() . parent::endRecentChangesList();
- }
-
-}
*/
$wgAllowSchemaUpdates = true;
-/**
- * Do DELETE/INSERT for link updates instead of incremental
- */
-$wgUseDumbLinkUpdate = false;
-
/**
* Anti-lock flags - bitfield
* - ALF_NO_LINK_LOCK:
'ip' => null,
'subnet' => null,
),
- 'mailpassword' => array(
+ 'mailpassword' => array( // triggering password resets emails
'anon' => null,
),
- 'emailuser' => array(
+ 'emailuser' => array( // emailing other users using MediaWiki
+ 'user' => null,
+ ),
+ 'linkpurge' => array( // purges of link tables
+ 'anon' => null,
'user' => null,
+ 'newbie' => null,
+ 'ip' => null,
+ 'subnet' => null,
),
- 'linkpurge' => array(
+ 'renderfile' => array( // files rendered via thumb.php or thumb_handler.php
'anon' => null,
'user' => null,
'newbie' => null,
*/
$wgParserOutputHooks = array();
+/**
+ * Whether to include the NewPP limit report as a HTML comment
+ */
+$wgEnableParserLimitReporting = true;
+
/**
* List of valid skin names.
* The key should be the name in all lower case, the value should be a properly
* @ingroup Exception
*/
class MWException extends Exception {
- var $logId;
/**
* Should the exception use $wgOut to output the error?
"</p>\n";
} else {
return "<div class=\"errorbox\">" .
- '[' . $this->getLogId() . '] ' .
+ '[' . MWExceptionHandler::getLogId( $this ) . '] ' .
gmdate( 'Y-m-d H:i:s' ) .
": Fatal exception of type " . get_class( $this ) . "</div>\n" .
"<!-- Set \$wgShowExceptionDetails = true; " .
}
/**
- * Get a random ID for this error.
- * This allows to link the exception to its corresponding log entry when
- * $wgShowExceptionDetails is set to false.
+ * Get a the ID for this error.
*
+ * @since 1.20
+ * @deprecated since 1.22 Use MWExceptionHandler::getLogId instead.
* @return string
*/
function getLogId() {
- if ( $this->logId === null ) {
- $this->logId = wfRandomString( 8 );
- }
- return $this->logId;
+ wfDeprecated( __METHOD__, '1.22' );
+ return MWExceptionHandler::getLogId( $this );
}
/**
* Return the requested URL and point to file and line number from which the
* exception occurred
*
+ * @since 1.8
+ * @deprecated since 1.22 Use MWExceptionHandler::getLogMessage instead.
* @return string
*/
function getLogMessage() {
- global $wgRequest;
-
- $id = $this->getLogId();
- $file = $this->getFile();
- $line = $this->getLine();
- $message = $this->getMessage();
-
- if ( isset( $wgRequest ) && !$wgRequest instanceof FauxRequest ) {
- $url = $wgRequest->getRequestURL();
- if ( !$url ) {
- $url = '[no URL]';
- }
- } else {
- $url = '[no req]';
- }
-
- return "[$id] $url Exception from line $line of $file: $message";
+ wfDeprecated( __METHOD__, '1.22' );
+ return MWExceptionHandler::getLogMessage( $this );
}
/**
function report() {
global $wgMimeType;
- $this->logException();
+ MWExceptionHandler::logException( $this );
if ( defined( 'MW_API' ) ) {
// Unhandled API exception, we can't be sure that format printer is alive
}
}
- /**
- * Log the error message to the exception log (if enabled)
- */
- function logException() {
- global $wgLogExceptionBacktrace;
-
- $log = $this->getLogMessage();
- if ( $log ) {
- if ( $wgLogExceptionBacktrace ) {
- wfDebugLog( 'exception', $log . "\n" . MWExceptionHandler::formatRedactedTrace( $this ) . "\n" );
- } else {
- wfDebugLog( 'exception', $log );
- }
- }
- }
-
/**
* Check whether we are in command line mode or not to report the exception
* in the correct format.
$finalExceptionText .= $call['function'];
}
$args = array();
- foreach ( $call['args'] as $arg ) {
- if ( is_object( $arg ) ) {
- $args[] = 'Object(' . get_class( $arg ) . ')';
- } elseif( is_array( $arg ) ) {
- $args[] = 'Array';
- } else {
- $args[] = var_export( $arg, true );
+ if ( isset( $call['args'] ) ) {
+ foreach ( $call['args'] as $arg ) {
+ if ( is_object( $arg ) ) {
+ $args[] = 'Object(' . get_class( $arg ) . ')';
+ } elseif( is_array( $arg ) ) {
+ $args[] = 'Array';
+ } else {
+ $args[] = var_export( $arg, true );
+ }
}
}
$finalExceptionText .= '(' . implode( ', ', $args ) . ")\n";
}
return $finalExceptionText . '#' . ( $i + 1 ) . ' {main}';
}
+
+
+ /**
+ * Get the ID for this error.
+ *
+ * The ID is saved so that one can match the one output to the user (when
+ * $wgShowExceptionDetails is set to false), to the entry in the debug log.
+ *
+ * @since 1.22
+ * @param Exception $e
+ * @return string
+ */
+ public static function getLogId( Exception $e ) {
+ if ( !isset( $e->_mwLogId ) ) {
+ $e->_mwLogId = wfRandomString( 8 );
+ }
+ return $e->_mwLogId;
+ }
+
+ /**
+ * Return the requested URL and point to file and line number from which the
+ * exception occurred.
+ *
+ * @since 1.22
+ * @param Exception $e
+ * @return string
+ */
+ public static function getLogMessage( Exception $e ) {
+ global $wgRequest;
+
+ $id = self::getLogId( $e );
+ $file = $e->getFile();
+ $line = $e->getLine();
+ $message = $e->getMessage();
+
+ if ( isset( $wgRequest ) && !$wgRequest instanceof FauxRequest ) {
+ $url = $wgRequest->getRequestURL();
+ if ( !$url ) {
+ $url = '[no URL]';
+ }
+ } else {
+ $url = '[no req]';
+ }
+
+ return "[$id] $url Exception from line $line of $file: $message";
+ }
+
+ /**
+ * Log an exception to the exception log (if enabled).
+ *
+ * This method must not assume the exception is an MWException,
+ * it is also used to handle PHP errors or errors from other libraries.
+ *
+ * @since 1.22
+ * @param Exception $e
+ */
+ public static function logException( Exception $e ) {
+ global $wgLogExceptionBacktrace;
+
+ $log = self::getLogMessage( $e );
+ if ( $log ) {
+ if ( $wgLogExceptionBacktrace ) {
+ wfDebugLog( 'exception', $log . "\n" . self::formatRedactedTrace( $e ) . "\n" );
+ } else {
+ wfDebugLog( 'exception', $log );
+ }
+ }
+ }
+
}
'table',
'div',
'raw',
+ 'vform',
);
/**
}
$field = self::loadInputFromParameters( $fieldname, $info );
+ // FIXME During field's construct, the parent form isn't available!
+ // could add a 'parent' name-value to $info, could add a third parameter.
$field->mParent = $this;
+ // vform gets too much space if empty labels generate HTML.
+ if ( $this->isVForm() ) {
+ $field->setShowEmptyLabel( false );
+ }
+
$setSection =& $loadedDescriptor;
if ( $section ) {
$sectionParts = explode( '/', $section );
return $this->displayFormat;
}
+ /**
+ * Test if displayFormat is 'vform'
+ * @since 1.22
+ * @return Bool
+ */
+ public function isVForm() {
+ return $this->displayFormat === 'vform';
+ }
+
/**
* Add the HTMLForm-specific JavaScript, if it hasn't been
* done already.
# For good measure (it is the default)
$this->getOutput()->preventClickjacking();
$this->getOutput()->addModules( 'mediawiki.htmlform' );
+ if ( $this->isVForm() ) {
+ $this->getOutput()->addModuleStyles( 'mediawiki.ui' );
+ // TODO should vertical form set setWrapperLegend( false )
+ // to hide ugly fieldsets?
+ }
$html = ''
. $this->getErrors( $submitResult )
$attribs = array(
'action' => $this->getAction(),
'method' => $this->getMethod(),
- 'class' => 'visualClear',
+ 'class' => array( 'visualClear' ),
'enctype' => $encType,
);
if ( !empty( $this->mId ) ) {
$attribs['id'] = $this->mId;
}
+ if ( $this->isVForm() ) {
+ array_push( $attribs['class'], 'mw-ui-vform', 'mw-ui-container' );
+ }
return Html::rawElement( 'form', $attribs, $html );
}
$attribs += Linker::tooltipAndAccesskeyAttribs( $this->mSubmitTooltip );
}
- $attribs['class'] = 'mw-htmlform-submit';
+ $attribs['class'] = array( 'mw-htmlform-submit' );
+
+ if ( $this->isVForm() ) {
+ // mw-ui-block is necessary because the buttons aren't necessarily in an
+ // immediate child div of the vform.
+ array_push( $attribs['class'], 'mw-ui-button', 'mw-ui-big', 'mw-ui-primary', 'mw-ui-block' );
+ }
$html .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n";
+
+ // Buttons are top-level form elements in table and div layouts,
+ // but vform wants all elements inside divs to get spaced-out block
+ // styling.
+ if ( $this->isVForm() ) {
+ $html = Html::rawElement( 'div', null, "\n$html\n" );
+ }
}
if ( $this->mShowReset ) {
/**
* Prompt the whole form to be wrapped in a "<fieldset>", with
* this text as its "<legend>" element.
- * @param string $legend HTML to go inside the "<legend>" element.
+ * @param string|false $legend HTML to go inside the "<legend>" element, or
+ * false for no <legend>
* Will be escaped
* @return HTMLForm $this for chaining calls (since 1.20)
*/
/**
* @todo Document
- * @param $fields array[]|HTMLFormField[] array of fields (either arrays or objects)
+ * @param array[]|HTMLFormField[] $fields array of fields (either arrays or objects)
* @param string $sectionName ID attribute of the "<table>" tag for this section, ignored if empty
* @param string $fieldsetIDPrefix ID prefix for the "<fieldset>" tag of each subsection, ignored if empty
* @param boolean &$hasUserVisibleFields Whether the section had user-visible fields
$subsectionHtml = '';
$hasLabel = false;
- $getFieldHtmlMethod = ( $displayFormat == 'table' ) ? 'getTableRow' : 'get' . ucfirst( $displayFormat );
+ switch( $displayFormat ) {
+ case 'table':
+ $getFieldHtmlMethod = 'getTableRow';
+ break;
+ case 'vform':
+ // Close enough to a div.
+ $getFieldHtmlMethod = 'getDiv';
+ break;
+ default:
+ $getFieldHtmlMethod = 'get' . ucfirst( $displayFormat );
+ }
foreach ( $fields as $key => $value ) {
if ( $value instanceof HTMLFormField ) {
if ( $displayFormat === 'table' ) {
$html = Html::rawElement( 'table', $attribs,
Html::rawElement( 'tbody', array(), "\n$html\n" ) ) . "\n";
- } elseif ( $displayFormat === 'div' ) {
+ } elseif ( $displayFormat === 'div' || $displayFormat === 'vform' ) {
$html = Html::rawElement( 'div', $attribs, "\n$html\n" );
}
}
return true;
}
+ /**
+ * Tell the field whether to generate a separate label element if its label
+ * is blank.
+ *
+ * @since 1.22
+ * @param bool $show Set to false to not generate a label.
+ * @return void
+ */
+ public function setShowEmptyLabel( $show ) {
+ $this->mShowEmptyLabels = $show;
+ }
+
/**
* Get the value that this input has been set to from a posted form,
* or the input's default value if it has not been set.
array( 'class' => $outerDivClass ) + $cellAttributes,
$inputHtml . "\n$errors"
);
+ $divCssClasses = array( "mw-htmlform-field-$fieldType", $this->mClass, $errorClass );
+ if ( $this->mParent->isVForm() ) {
+ $divCssClasses[] = 'mw-ui-vform-div';
+ }
$html = Html::rawElement( 'div',
- array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ),
+ array( 'class' => $divCssClasses ),
$label . $field );
$html .= $helptext;
return $html;
$attr['class'] = $this->mClass;
}
- return Xml::check( $this->mName, $value, $attr ) . ' ' .
- Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel );
+ if ( $this->mParent->isVForm() ) {
+ // Nest checkbox inside label.
+ return Html::rawElement(
+ 'label',
+ array(
+ 'class' => 'mw-ui-checkbox-label'
+ ),
+ Xml::check(
+ $this->mName,
+ $value,
+ $attr
+ ) .
+ // Html:rawElement doesn't escape contents.
+ htmlspecialchars( $this->mLabel )
+ );
+ } else {
+ return Xml::check( $this->mName, $value, $attr ) . ' ' .
+ Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel );
+ }
}
/**
return ' ';
}
+ /**
+ * checkboxes don't need a label.
+ */
+ protected function needsLabel() {
+ return false;
+ }
+
/**
* @param $request WebRequest
* @return String
* @since 1.22
*/
class HashRing {
+ /** @var Array (location => weight) */
+ protected $sourceMap = array();
/** @var Array (location => (start, end)) */
protected $ring = array();
if ( !count( $map ) ) {
throw new MWException( "Ring is empty or all weights are zero." );
}
+ $this->sourceMap = $map;
// Sort the locations based on the hash of their names
$hashes = array();
foreach ( $map as $location => $weight ) {
}
return $locations;
}
+
+ /**
+ * Get the map of locations to weight (ignores 0-weight items)
+ *
+ * @return array
+ */
+ public function getLocationWeights() {
+ return $this->sourceMap;
+ }
+
+ /**
+ * Get a new hash ring with a location removed from the ring
+ *
+ * @param string $location
+ * @return HashRing|bool Returns false if no non-zero weighted spots are left
+ */
+ public function newWithoutLocation( $location ) {
+ $map = $this->sourceMap;
+ unset( $map[$location] );
+ if ( count( $map ) ) {
+ return new self( $map );
+ }
+ return false;
+ }
}
$ret = '';
$attribs = (array)$attribs;
foreach ( $attribs as $key => $value ) {
+ // Support intuitive array( 'checked' => true/false ) form
if ( $value === false || is_null( $value ) ) {
continue;
}
':(?::|(?::' . RE_IPV6_WORD . '){1,7})' .
'|' . // ends with "::" (except "::")
RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){0,6}::' .
- '|' . // contains one "::" in the middle, ending in "::WORD"
- RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){0,5}' . '::' . RE_IPV6_WORD .
- '|' . // contains one "::" in the middle, not ending in "::WORD" (regex for PCRE 4.0+)
- RE_IPV6_WORD . '(?::(?P<abn>:(?P<iabn>))?' . RE_IPV6_WORD . '(?!:(?P=abn))){1,5}' .
- ':' . RE_IPV6_WORD . '(?P=iabn)' .
- // NOTE: (?!(?P=abn)) fails iff "::" used twice; (?P=iabn) passes iff a "::" was found.
+ '|' . // contains one "::" in the middle (the ^ makes the test fail if none found)
+ RE_IPV6_WORD . '(?::((?(-1)|:))?' . RE_IPV6_WORD . '){1,6}(?(-2)|^)' .
'|' . // contains no "::"
RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){7}' .
')'
- // NOTE: With PCRE 7.2+, we can combine the two '"::" in the middle' cases into:
- // RE_IPV6_WORD . '(?::((?(-1)|:))?' . RE_IPV6_WORD . '){1,6}(?(-2)|^)'
- // This also improves regex concatenation by using relative references.
);
// An IPv6 block is an IP address and a prefix (d1 to d128)
define( 'RE_IPV6_BLOCK', RE_IPV6_ADD . '\/' . RE_IPV6_PREFIX );
$mOptions, //!< SELECT options to be used (array)
$mRecursive; //!< Whether to queue jobs for recursive updates
+ /**
+ * @var null|array Added links if calculated.
+ */
+ private $linkInsertions = null;
+
+ /**
+ * @var null|array Deleted links if calculated.
+ */
+ private $linkDeletions = null;
+
/**
* Constructor
*
* Update link tables with outgoing links from an updated article
*/
public function doUpdate() {
- global $wgUseDumbLinkUpdate;
-
wfRunHooks( 'LinksUpdate', array( &$this ) );
- if ( $wgUseDumbLinkUpdate ) {
- $this->doDumbUpdate();
- } else {
- $this->doIncrementalUpdate();
- }
+ $this->doIncrementalUpdate();
wfRunHooks( 'LinksUpdateComplete', array( &$this ) );
}
# Page links
$existing = $this->getExistingLinks();
- $this->incrTableUpdate( 'pagelinks', 'pl', $this->getLinkDeletions( $existing ),
- $this->getLinkInsertions( $existing ) );
+ $this->linkDeletions = $this->getLinkDeletions( $existing );
+ $this->linkInsertions = $this->getLinkInsertions( $existing );
+ $this->incrTableUpdate( 'pagelinks', 'pl', $this->linkDeletions, $this->linkInsertions );
# Image links
$existing = $this->getExistingImages();
wfProfileOut( __METHOD__ );
}
- /**
- * Link update which clears the previous entries and inserts new ones
- * May be slower or faster depending on level of lock contention and write speed of DB
- * Also useful where link table corruption needs to be repaired, e.g. in refreshLinks.php
- */
- protected function doDumbUpdate() {
- wfProfileIn( __METHOD__ );
-
- # Refresh category pages and image description pages
- $existing = $this->getExistingCategories();
- $categoryInserts = array_diff_assoc( $this->mCategories, $existing );
- $categoryDeletes = array_diff_assoc( $existing, $this->mCategories );
- $categoryUpdates = $categoryInserts + $categoryDeletes;
- $existing = $this->getExistingImages();
- $imageUpdates = array_diff_key( $existing, $this->mImages ) + array_diff_key( $this->mImages, $existing );
-
- $this->dumbTableUpdate( 'pagelinks', $this->getLinkInsertions(), 'pl_from' );
- $this->dumbTableUpdate( 'imagelinks', $this->getImageInsertions(), 'il_from' );
- $this->dumbTableUpdate( 'categorylinks', $this->getCategoryInsertions(), 'cl_from' );
- $this->dumbTableUpdate( 'templatelinks', $this->getTemplateInsertions(), 'tl_from' );
- $this->dumbTableUpdate( 'externallinks', $this->getExternalInsertions(), 'el_from' );
- $this->dumbTableUpdate( 'langlinks', $this->getInterlangInsertions(), 'll_from' );
- $this->dumbTableUpdate( 'iwlinks', $this->getInterwikiInsertions(), 'iwl_from' );
- $this->dumbTableUpdate( 'page_props', $this->getPropertyInsertions(), 'pp_page' );
-
- # Update the cache of all the category pages and image description
- # pages which were changed, and fix the category table count
- $this->invalidateCategories( $categoryUpdates );
- $this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
- $this->invalidateImageDescriptions( $imageUpdates );
-
- # Refresh links of all pages including this page
- # This will be in a separate transaction
- if ( $this->mRecursive ) {
- $this->queueRecursiveJobs();
- }
-
- wfProfileOut( __METHOD__ );
- }
-
/**
* Queue recursive jobs for this page
*
$this->invalidatePages( NS_FILE, array_keys( $images ) );
}
- /**
- * @param $table
- * @param $insertions
- * @param $fromField
- */
- private function dumbTableUpdate( $table, $insertions, $fromField ) {
- $this->mDb->delete( $table, array( $fromField => $this->mId ), __METHOD__ );
- if ( count( $insertions ) ) {
- # The link array was constructed without FOR UPDATE, so there may
- # be collisions. This may cause minor link table inconsistencies,
- # which is better than crippling the site with lock contention.
- $this->mDb->insert( $table, $insertions, __METHOD__, array( 'IGNORE' ) );
- }
- }
-
/**
* Update a table by doing a delete query then an insert query
* @param $table
}
}
}
+
+ /**
+ * Fetch page links added by this LinksUpdate. Only available after the update is complete.
+ * @since 1.22
+ * @return null|array of Titles
+ */
+ public function getAddedLinks() {
+ if ( $this->linkInsertions === null ) {
+ return null;
+ }
+ $result = array();
+ foreach ( $this->linkInsertions as $insertion ) {
+ $result[] = Title::makeTitle( $insertion[ 'pl_namespace' ], $insertion[ 'pl_title' ] );
+ }
+ return $result;
+ }
+
+ /**
+ * Fetch page links removed by this LinksUpdate. Only available after the update is complete.
+ * @since 1.22
+ * @return null|array of Titles
+ */
+ public function getRemovedLinks() {
+ if ( $this->linkDeletions === null ) {
+ return null;
+ }
+ $result = array();
+ foreach ( $this->linkDeletions as $ns => $titles ) {
+ foreach ( $titles as $title => $unused ) {
+ $result[] = Title::makeTitle( $ns, $title );
+ }
+ }
+ return $result;
+ }
}
/**
return $this;
}
+ /**
+ * Add parameters that are durations of time and will be passed through
+ * Language::formatDuration before substitution
+ * @since 1.22
+ * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+ * @return Message: $this
+ */
+ public function durationParams( /*...*/ ) {
+ $params = func_get_args();
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ foreach ( $params as $param ) {
+ $this->parameters[] = self::durationParam( $param );
+ }
+ return $this;
+ }
+
+ /**
+ * Add parameters that are expiration times and will be passed through
+ * Language::formatExpiry before substitution
+ * @since 1.22
+ * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+ * @return Message: $this
+ */
+ public function expiryParams( /*...*/ ) {
+ $params = func_get_args();
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ foreach ( $params as $param ) {
+ $this->parameters[] = self::expiryParam( $param );
+ }
+ return $this;
+ }
+
+ /**
+ * Add parameters that are time periods and will be passed through
+ * Language::formatTimePeriod before substitution
+ * @since 1.22
+ * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+ * @return Message: $this
+ */
+ public function timeperiodParams( /*...*/ ) {
+ $params = func_get_args();
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ foreach ( $params as $param ) {
+ $this->parameters[] = self::timeperiodParam( $param );
+ }
+ return $this;
+ }
+
+ /**
+ * Add parameters that are file sizes and will be passed through
+ * Language::formatSize before substitution
+ * @since 1.22
+ * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+ * @return Message: $this
+ */
+ public function sizeParams( /*...*/ ) {
+ $params = func_get_args();
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ foreach ( $params as $param ) {
+ $this->parameters[] = self::sizeParam( $param );
+ }
+ return $this;
+ }
+
+ /**
+ * Add parameters that are bitrates and will be passed through
+ * Language::formatBitrate before substitution
+ * @since 1.22
+ * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+ * @return Message: $this
+ */
+ public function bitrateParams( /*...*/ ) {
+ $params = func_get_args();
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ foreach ( $params as $param ) {
+ $this->parameters[] = self::bitrateParam( $param );
+ }
+ return $this;
+ }
+
/**
* Set the language and the title from a context object
* @since 1.19
return array( 'num' => $value );
}
+ /**
+ * @since 1.22
+ * @param $value
+ * @return array
+ */
+ public static function durationParam( $value ) {
+ return array( 'duration' => $value );
+ }
+
+ /**
+ * @since 1.22
+ * @param $value
+ * @return array
+ */
+ public static function expiryParam( $value ) {
+ return array( 'expiry' => $value );
+ }
+
+ /**
+ * @since 1.22
+ * @param $value
+ * @return array
+ */
+ public static function timeperiodParam( $value ) {
+ return array( 'period' => $value );
+ }
+
+ /**
+ * @since 1.22
+ * @param $value
+ * @return array
+ */
+ public static function sizeParam( $value ) {
+ return array( 'size' => $value );
+ }
+
+ /**
+ * @since 1.22
+ * @param $value
+ * @return array
+ */
+ public static function bitrateParam( $value ) {
+ return array( 'bitrate' => $value );
+ }
+
/**
* Substitutes any parameters into the message text.
* @since 1.17
* @return Tuple(type, value)
*/
protected function extractParam( $param ) {
- if ( is_array( $param ) && isset( $param['raw'] ) ) {
- return array( 'after', $param['raw'] );
- } elseif ( is_array( $param ) && isset( $param['num'] ) ) {
- // Replace number params always in before step for now.
- // No support for combined raw and num params
- return array( 'before', $this->language->formatNum( $param['num'] ) );
- } elseif ( !is_array( $param ) ) {
- return array( 'before', $param );
+ if ( is_array( $param ) ){
+ if ( isset( $param['raw'] ) ) {
+ return array( 'after', $param['raw'] );
+ } elseif ( isset( $param['num'] ) ) {
+ // Replace number params always in before step for now.
+ // No support for combined raw and num params
+ return array( 'before', $this->language->formatNum( $param['num'] ) );
+ } elseif ( isset( $param['duration'] ) ) {
+ return array( 'before', $this->language->formatDuration( $param['duration'] ) );
+ } elseif ( isset( $param['expiry'] ) ) {
+ return array( 'before', $this->language->formatExpiry( $param['expiry'] ) );
+ } elseif ( isset( $param['period'] ) ) {
+ return array( 'before', $this->language->formatTimePeriod( $param['period'] ) );
+ } elseif ( isset( $param['size'] ) ) {
+ return array( 'before', $this->language->formatSize( $param['size'] ) );
+ } elseif ( isset( $param['bitrate'] ) ) {
+ return array( 'before', $this->language->formatBitrate( $param['bitrate'] ) );
+ } else {
+ trigger_error(
+ "Invalid message parameter: " . htmlspecialchars( serialize( $param ) ),
+ E_USER_WARNING
+ );
+ return array( 'before', '[INVALID]' );
+ }
} else {
- trigger_error(
- "Invalid message parameter: " . htmlspecialchars( serialize( $param ) ),
- E_USER_WARNING
- );
- return array( 'before', '[INVALID]' );
+ return array( 'before', $param );
}
}
+++ /dev/null
-<?php
-/**
- * Utility class for creating and accessing recent change entries.
- *
- * 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
- */
-
-/**
- * Utility class for creating new RC entries
- *
- * mAttribs:
- * rc_id id of the row in the recentchanges table
- * rc_timestamp time the entry was made
- * rc_cur_time timestamp on the cur row
- * rc_namespace namespace #
- * rc_title non-prefixed db key
- * rc_type is new entry, used to determine whether updating is necessary
- * rc_minor is minor
- * rc_cur_id page_id of associated page entry
- * rc_user user id who made the entry
- * rc_user_text user name who made the entry
- * rc_comment edit summary
- * rc_this_oldid rev_id associated with this entry (or zero)
- * rc_last_oldid rev_id associated with the entry before this one (or zero)
- * rc_bot is bot, hidden
- * rc_ip IP address of the user in dotted quad notation
- * rc_new obsolete, use rc_type==RC_NEW
- * rc_patrolled boolean whether or not someone has marked this edit as patrolled
- * rc_old_len integer byte length of the text before the edit
- * rc_new_len the same after the edit
- * rc_deleted partial deletion
- * rc_logid the log_id value for this log entry (or zero)
- * rc_log_type the log type (or null)
- * rc_log_action the log action (or null)
- * rc_params log params
- *
- * mExtra:
- * prefixedDBkey prefixed db key, used by external app via msg queue
- * lastTimestamp timestamp of previous entry, used in WHERE clause during update
- * lang the interwiki prefix, automatically set in save()
- * oldSize text size before the change
- * newSize text size after the change
- * pageStatus status of the page: created, deleted, moved, restored, changed
- *
- * temporary: not stored in the database
- * notificationtimestamp
- * numberofWatchingusers
- *
- * @todo document functions and variables
- */
-class RecentChange {
- var $mAttribs = array(), $mExtra = array();
-
- /**
- * @var Title
- */
- var $mTitle = false;
-
- /**
- * @var User
- */
- private $mPerformer = false;
-
- /**
- * @var Title
- */
- var $mMovedToTitle = false;
- var $numberofWatchingusers = 0; # Dummy to prevent error message in SpecialRecentchangeslinked
- var $notificationtimestamp;
-
- # Factory methods
-
- /**
- * @param $row
- * @return RecentChange
- */
- public static function newFromRow( $row ) {
- $rc = new RecentChange;
- $rc->loadFromRow( $row );
- return $rc;
- }
-
- /**
- * @deprecated in 1.22
- * @param $row
- * @return RecentChange
- */
- public static function newFromCurRow( $row ) {
- wfDeprecated( __METHOD__, '1.22' );
- $rc = new RecentChange;
- $rc->loadFromCurRow( $row );
- $rc->notificationtimestamp = false;
- $rc->numberofWatchingusers = false;
- return $rc;
- }
-
- /**
- * Obtain the recent change with a given rc_id value
- *
- * @param int $rcid rc_id value to retrieve
- * @return RecentChange
- */
- public static function newFromId( $rcid ) {
- return self::newFromConds( array( 'rc_id' => $rcid ), __METHOD__ );
- }
-
- /**
- * Find the first recent change matching some specific conditions
- *
- * @param array $conds of conditions
- * @param $fname Mixed: override the method name in profiling/logs
- * @param $options Array Query options
- * @return RecentChange
- */
- public static function newFromConds( $conds, $fname = __METHOD__, $options = array() ) {
- $dbr = wfGetDB( DB_SLAVE );
- $row = $dbr->selectRow( 'recentchanges', self::selectFields(), $conds, $fname, $options );
- if ( $row !== false ) {
- return self::newFromRow( $row );
- } else {
- return null;
- }
- }
-
- /**
- * 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
-
- /**
- * @param $attribs array
- */
- public function setAttribs( $attribs ) {
- $this->mAttribs = $attribs;
- }
-
- /**
- * @param $extra array
- */
- public function setExtra( $extra ) {
- $this->mExtra = $extra;
- }
-
- /**
- *
- * @return Title
- */
- public function &getTitle() {
- if ( $this->mTitle === false ) {
- $this->mTitle = Title::makeTitle( $this->mAttribs['rc_namespace'], $this->mAttribs['rc_title'] );
- }
- return $this->mTitle;
- }
-
- /**
- * Get the User object of the person who performed this change.
- *
- * @return User
- */
- public function getPerformer() {
- if ( $this->mPerformer === false ) {
- if ( $this->mAttribs['rc_user'] ) {
- $this->mPerformer = User::newFromID( $this->mAttribs['rc_user'] );
- } else {
- $this->mPerformer = User::newFromName( $this->mAttribs['rc_user_text'], false );
- }
- }
- return $this->mPerformer;
- }
-
- /**
- * Writes the data in this object to the database
- * @param $noudp bool
- */
- public function save( $noudp = false ) {
- global $wgLocalInterwiki, $wgPutIPinRC, $wgUseEnotif, $wgShowUpdatedMarker, $wgContLang;
-
- $dbw = wfGetDB( DB_MASTER );
- if ( !is_array( $this->mExtra ) ) {
- $this->mExtra = array();
- }
- $this->mExtra['lang'] = $wgLocalInterwiki;
-
- if ( !$wgPutIPinRC ) {
- $this->mAttribs['rc_ip'] = '';
- }
-
- # If our database is strict about IP addresses, use NULL instead of an empty string
- if ( $dbw->strictIPs() and $this->mAttribs['rc_ip'] == '' ) {
- unset( $this->mAttribs['rc_ip'] );
- }
-
- # Trim spaces on user supplied text
- $this->mAttribs['rc_comment'] = trim( $this->mAttribs['rc_comment'] );
-
- # Make sure summary is truncated (whole multibyte characters)
- $this->mAttribs['rc_comment'] = $wgContLang->truncate( $this->mAttribs['rc_comment'], 255 );
-
- # Fixup database timestamps
- $this->mAttribs['rc_timestamp'] = $dbw->timestamp( $this->mAttribs['rc_timestamp'] );
- $this->mAttribs['rc_cur_time'] = $dbw->timestamp( $this->mAttribs['rc_cur_time'] );
- $this->mAttribs['rc_id'] = $dbw->nextSequenceValue( 'recentchanges_rc_id_seq' );
-
- ## If we are using foreign keys, an entry of 0 for the page_id will fail, so use NULL
- if ( $dbw->cascadingDeletes() and $this->mAttribs['rc_cur_id'] == 0 ) {
- unset( $this->mAttribs['rc_cur_id'] );
- }
-
- # Insert new row
- $dbw->insert( 'recentchanges', $this->mAttribs, __METHOD__ );
-
- # Set the ID
- $this->mAttribs['rc_id'] = $dbw->insertId();
-
- # Notify extensions
- wfRunHooks( 'RecentChange_save', array( &$this ) );
-
- # Notify external application via UDP
- if ( !$noudp ) {
- $this->notifyRCFeeds();
- }
-
- # E-mail notifications
- if ( $wgUseEnotif || $wgShowUpdatedMarker ) {
- $editor = $this->getPerformer();
- $title = $this->getTitle();
-
- if ( wfRunHooks( 'AbortEmailNotification', array( $editor, $title ) ) ) {
- # @todo FIXME: This would be better as an extension hook
- $enotif = new EmailNotification();
- $enotif->notifyOnPageChange( $editor, $title,
- $this->mAttribs['rc_timestamp'],
- $this->mAttribs['rc_comment'],
- $this->mAttribs['rc_minor'],
- $this->mAttribs['rc_last_oldid'],
- $this->mExtra['pageStatus'] );
- }
- }
- }
-
- /**
- * @deprecated since 1.22, use notifyRCFeeds instead.
- */
- public function notifyRC2UDP() {
- wfDeprecated( __METHOD__, '1.22' );
- $this->notifyRCFeeds();
- }
-
- /**
- * Send some text to UDP.
- * @deprecated since 1.22
- */
- public static function sendToUDP( $line, $address = '', $prefix = '', $port = '' ) {
- global $wgRC2UDPAddress, $wgRC2UDPInterwikiPrefix, $wgRC2UDPPort, $wgRC2UDPPrefix;
-
- wfDeprecated( __METHOD__, '1.22' );
-
- # Assume default for standard RC case
- $address = $address ? $address : $wgRC2UDPAddress;
- $prefix = $prefix ? $prefix : $wgRC2UDPPrefix;
- $port = $port ? $port : $wgRC2UDPPort;
-
- $engine = new UDPRCFeedEngine();
- $feed = array(
- 'uri' => "udp://$address:$port/$prefix",
- 'formatter' => 'IRCColourfulRCFeedFormatter',
- 'add_interwiki_prefix' => $wgRC2UDPInterwikiPrefix,
- );
-
- return $engine->send( $feed, $line );
- }
-
- /**
- * Notify all the feeds about the change.
- */
- public function notifyRCFeeds() {
- global $wgRCFeeds;
-
- foreach ( $wgRCFeeds as $feed ) {
- $engine = self::getEngine( $feed['uri'] );
-
- if ( isset( $this->mExtra['actionCommentIRC'] ) ) {
- $actionComment = $this->mExtra['actionCommentIRC'];
- } else {
- $actionComment = null;
- }
-
- $omitBots = isset( $feed['omit_bots'] ) ? $feed['omit_bots'] : false;
-
- if (
- ( $omitBots && $this->mAttribs['rc_bot'] ) ||
- $this->mAttribs['rc_type'] == RC_EXTERNAL
- ) {
- continue;
- }
-
- $formatter = new $feed['formatter']();
- $line = $formatter->getLine( $feed, $this, $actionComment );
-
- $engine->send( $feed, $line );
- }
- }
-
- /**
- * Gets the stream engine object for a given URI from $wgRCEngines
- *
- * @param $uri string URI to get the engine object for
- * @return object The engine object
- */
- private static function getEngine( $uri ) {
- global $wgRCEngines;
-
- $scheme = parse_url( $uri, PHP_URL_SCHEME );
- if ( !$scheme ) {
- throw new MWException( __FUNCTION__ . ": Invalid stream logger URI: '$uri'" );
- }
-
- if ( !isset( $wgRCEngines[$scheme] ) ) {
- throw new MWException( __FUNCTION__ . ": Unknown stream logger URI scheme: $scheme" );
- }
-
- return new $wgRCEngines[$scheme];
- }
-
- /**
- * @deprecated since 1.22, moved to IRCColourfulRCFeedFormatter
- */
- public static function cleanupForIRC( $text ) {
- wfDeprecated( __METHOD__, '1.22' );
- return IRCColourfulRCFeedFormatter::cleanupForIRC( $text );
- }
-
- /**
- * Mark a given change as patrolled
- *
- * @param $change Mixed: RecentChange or corresponding rc_id
- * @param $auto Boolean: for automatic patrol
- * @return Array See doMarkPatrolled(), or null if $change is not an existing rc_id
- */
- public static function markPatrolled( $change, $auto = false ) {
- global $wgUser;
-
- $change = $change instanceof RecentChange
- ? $change
- : RecentChange::newFromId( $change );
-
- if ( !$change instanceof RecentChange ) {
- return null;
- }
- return $change->doMarkPatrolled( $wgUser, $auto );
- }
-
- /**
- * Mark this RecentChange as patrolled
- *
- * NOTE: Can also return 'rcpatroldisabled', 'hookaborted' and 'markedaspatrollederror-noautopatrol' as errors
- * @param $user User object doing the action
- * @param $auto Boolean: for automatic patrol
- * @return array of permissions errors, see Title::getUserPermissionsErrors()
- */
- public function doMarkPatrolled( User $user, $auto = false ) {
- global $wgUseRCPatrol, $wgUseNPPatrol;
- $errors = array();
- // If recentchanges patrol is disabled, only new pages
- // can be patrolled
- if ( !$wgUseRCPatrol && ( !$wgUseNPPatrol || $this->getAttribute( 'rc_type' ) != RC_NEW ) ) {
- $errors[] = array( 'rcpatroldisabled' );
- }
- // Automatic patrol needs "autopatrol", ordinary patrol needs "patrol"
- $right = $auto ? 'autopatrol' : 'patrol';
- $errors = array_merge( $errors, $this->getTitle()->getUserPermissionsErrors( $right, $user ) );
- if ( !wfRunHooks( 'MarkPatrolled', array( $this->getAttribute( 'rc_id' ), &$user, false ) ) ) {
- $errors[] = array( 'hookaborted' );
- }
- // Users without the 'autopatrol' right can't patrol their
- // own revisions
- if ( $user->getName() == $this->getAttribute( 'rc_user_text' ) && !$user->isAllowed( 'autopatrol' ) ) {
- $errors[] = array( 'markedaspatrollederror-noautopatrol' );
- }
- if ( $errors ) {
- return $errors;
- }
- // If the change was patrolled already, do nothing
- if ( $this->getAttribute( 'rc_patrolled' ) ) {
- return array();
- }
- // Actually set the 'patrolled' flag in RC
- $this->reallyMarkPatrolled();
- // Log this patrol event
- PatrolLog::record( $this, $auto, $user );
- wfRunHooks( 'MarkPatrolledComplete', array( $this->getAttribute( 'rc_id' ), &$user, false ) );
- return array();
- }
-
- /**
- * Mark this RecentChange patrolled, without error checking
- * @return Integer: number of affected rows
- */
- public function reallyMarkPatrolled() {
- $dbw = wfGetDB( DB_MASTER );
- $dbw->update(
- 'recentchanges',
- array(
- 'rc_patrolled' => 1
- ),
- array(
- 'rc_id' => $this->getAttribute( 'rc_id' )
- ),
- __METHOD__
- );
- // Invalidate the page cache after the page has been patrolled
- // to make sure that the Patrol link isn't visible any longer!
- $this->getTitle()->invalidateCache();
- return $dbw->affectedRows();
- }
-
- /**
- * Makes an entry in the database corresponding to an edit
- *
- * @param $timestamp
- * @param $title Title
- * @param $minor
- * @param $user User
- * @param $comment
- * @param $oldId
- * @param $lastTimestamp
- * @param $bot
- * @param $ip string
- * @param $oldSize int
- * @param $newSize int
- * @param $newId int
- * @param $patrol int
- * @return RecentChange
- */
- public static function notifyEdit( $timestamp, &$title, $minor, &$user, $comment, $oldId,
- $lastTimestamp, $bot, $ip = '', $oldSize = 0, $newSize = 0, $newId = 0, $patrol = 0 ) {
- $rc = new RecentChange;
- $rc->mTitle = $title;
- $rc->mPerformer = $user;
- $rc->mAttribs = array(
- 'rc_timestamp' => $timestamp,
- 'rc_cur_time' => $timestamp,
- 'rc_namespace' => $title->getNamespace(),
- 'rc_title' => $title->getDBkey(),
- 'rc_type' => RC_EDIT,
- 'rc_minor' => $minor ? 1 : 0,
- 'rc_cur_id' => $title->getArticleID(),
- 'rc_user' => $user->getId(),
- 'rc_user_text' => $user->getName(),
- 'rc_comment' => $comment,
- 'rc_this_oldid' => $newId,
- 'rc_last_oldid' => $oldId,
- 'rc_bot' => $bot ? 1 : 0,
- 'rc_ip' => self::checkIPAddress( $ip ),
- 'rc_patrolled' => intval( $patrol ),
- 'rc_new' => 0, # obsolete
- 'rc_old_len' => $oldSize,
- 'rc_new_len' => $newSize,
- 'rc_deleted' => 0,
- 'rc_logid' => 0,
- 'rc_log_type' => null,
- 'rc_log_action' => '',
- 'rc_params' => ''
- );
-
- $rc->mExtra = array(
- 'prefixedDBkey' => $title->getPrefixedDBkey(),
- 'lastTimestamp' => $lastTimestamp,
- 'oldSize' => $oldSize,
- 'newSize' => $newSize,
- 'pageStatus' => 'changed'
- );
- $rc->save();
- return $rc;
- }
-
- /**
- * Makes an entry in the database corresponding to page creation
- * Note: the title object must be loaded with the new id using resetArticleID()
- * @todo Document parameters and return
- *
- * @param $timestamp
- * @param $title Title
- * @param $minor
- * @param $user User
- * @param $comment
- * @param $bot
- * @param $ip string
- * @param $size int
- * @param $newId int
- * @param $patrol int
- * @return RecentChange
- */
- public static function notifyNew( $timestamp, &$title, $minor, &$user, $comment, $bot,
- $ip = '', $size = 0, $newId = 0, $patrol = 0 ) {
- $rc = new RecentChange;
- $rc->mTitle = $title;
- $rc->mPerformer = $user;
- $rc->mAttribs = array(
- 'rc_timestamp' => $timestamp,
- 'rc_cur_time' => $timestamp,
- 'rc_namespace' => $title->getNamespace(),
- 'rc_title' => $title->getDBkey(),
- 'rc_type' => RC_NEW,
- 'rc_minor' => $minor ? 1 : 0,
- 'rc_cur_id' => $title->getArticleID(),
- 'rc_user' => $user->getId(),
- 'rc_user_text' => $user->getName(),
- 'rc_comment' => $comment,
- 'rc_this_oldid' => $newId,
- 'rc_last_oldid' => 0,
- 'rc_bot' => $bot ? 1 : 0,
- 'rc_ip' => self::checkIPAddress( $ip ),
- 'rc_patrolled' => intval( $patrol ),
- 'rc_new' => 1, # obsolete
- 'rc_old_len' => 0,
- 'rc_new_len' => $size,
- 'rc_deleted' => 0,
- 'rc_logid' => 0,
- 'rc_log_type' => null,
- 'rc_log_action' => '',
- 'rc_params' => ''
- );
-
- $rc->mExtra = array(
- 'prefixedDBkey' => $title->getPrefixedDBkey(),
- 'lastTimestamp' => 0,
- 'oldSize' => 0,
- 'newSize' => $size,
- 'pageStatus' => 'created'
- );
- $rc->save();
- return $rc;
- }
-
- /**
- * @param $timestamp
- * @param $title
- * @param $user
- * @param $actionComment
- * @param $ip string
- * @param $type
- * @param $action
- * @param $target
- * @param $logComment
- * @param $params
- * @param $newId int
- * @param $actionCommentIRC string
- * @return bool
- */
- public static function notifyLog( $timestamp, &$title, &$user, $actionComment, $ip, $type,
- $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = '' )
- {
- global $wgLogRestrictions;
- # Don't add private logs to RC!
- if ( isset( $wgLogRestrictions[$type] ) && $wgLogRestrictions[$type] != '*' ) {
- return false;
- }
- $rc = self::newLogEntry( $timestamp, $title, $user, $actionComment, $ip, $type, $action,
- $target, $logComment, $params, $newId, $actionCommentIRC );
- $rc->save();
- return true;
- }
-
- /**
- * @param $timestamp
- * @param $title Title
- * @param $user User
- * @param $actionComment
- * @param $ip string
- * @param $type
- * @param $action
- * @param $target Title
- * @param $logComment
- * @param $params
- * @param $newId int
- * @param $actionCommentIRC string
- * @return RecentChange
- */
- public static function newLogEntry( $timestamp, &$title, &$user, $actionComment, $ip,
- $type, $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = '' ) {
- global $wgRequest;
-
- ## Get pageStatus for email notification
- switch ( $type . '-' . $action ) {
- case 'delete-delete':
- $pageStatus = 'deleted';
- break;
- case 'move-move':
- case 'move-move_redir':
- $pageStatus = 'moved';
- break;
- case 'delete-restore':
- $pageStatus = 'restored';
- break;
- case 'upload-upload':
- $pageStatus = 'created';
- break;
- case 'upload-overwrite':
- default:
- $pageStatus = 'changed';
- break;
- }
-
- $rc = new RecentChange;
- $rc->mTitle = $target;
- $rc->mPerformer = $user;
- $rc->mAttribs = array(
- 'rc_timestamp' => $timestamp,
- 'rc_cur_time' => $timestamp,
- 'rc_namespace' => $target->getNamespace(),
- 'rc_title' => $target->getDBkey(),
- 'rc_type' => RC_LOG,
- 'rc_minor' => 0,
- 'rc_cur_id' => $target->getArticleID(),
- 'rc_user' => $user->getId(),
- 'rc_user_text' => $user->getName(),
- 'rc_comment' => $logComment,
- 'rc_this_oldid' => 0,
- 'rc_last_oldid' => 0,
- 'rc_bot' => $user->isAllowed( 'bot' ) ? $wgRequest->getBool( 'bot', true ) : 0,
- 'rc_ip' => self::checkIPAddress( $ip ),
- 'rc_patrolled' => 1,
- 'rc_new' => 0, # obsolete
- 'rc_old_len' => null,
- 'rc_new_len' => null,
- 'rc_deleted' => 0,
- 'rc_logid' => $newId,
- 'rc_log_type' => $type,
- 'rc_log_action' => $action,
- 'rc_params' => $params
- );
-
- $rc->mExtra = array(
- 'prefixedDBkey' => $title->getPrefixedDBkey(),
- 'lastTimestamp' => 0,
- 'actionComment' => $actionComment, // the comment appended to the action, passed from LogPage
- 'pageStatus' => $pageStatus,
- 'actionCommentIRC' => $actionCommentIRC
- );
- return $rc;
- }
-
- /**
- * Initialises the members of this object from a mysql row object
- *
- * @param $row
- */
- public function loadFromRow( $row ) {
- $this->mAttribs = get_object_vars( $row );
- $this->mAttribs['rc_timestamp'] = wfTimestamp( TS_MW, $this->mAttribs['rc_timestamp'] );
- $this->mAttribs['rc_deleted'] = $row->rc_deleted; // MUST be set
- }
-
- /**
- * Makes a pseudo-RC entry from a cur row
- *
- * @deprected in 1.22
- * @param $row
- */
- public function loadFromCurRow( $row ) {
- wfDeprecated( __METHOD__, '1.22' );
- $this->mAttribs = array(
- 'rc_timestamp' => wfTimestamp( TS_MW, $row->rev_timestamp ),
- 'rc_cur_time' => $row->rev_timestamp,
- 'rc_user' => $row->rev_user,
- 'rc_user_text' => $row->rev_user_text,
- 'rc_namespace' => $row->page_namespace,
- 'rc_title' => $row->page_title,
- 'rc_comment' => $row->rev_comment,
- 'rc_minor' => $row->rev_minor_edit ? 1 : 0,
- 'rc_type' => $row->page_is_new ? RC_NEW : RC_EDIT,
- 'rc_cur_id' => $row->page_id,
- 'rc_this_oldid' => $row->rev_id,
- 'rc_last_oldid' => isset( $row->rc_last_oldid ) ? $row->rc_last_oldid : 0,
- 'rc_bot' => 0,
- 'rc_ip' => '',
- 'rc_id' => $row->rc_id,
- 'rc_patrolled' => $row->rc_patrolled,
- 'rc_new' => $row->page_is_new, # obsolete
- 'rc_old_len' => $row->rc_old_len,
- 'rc_new_len' => $row->rc_new_len,
- 'rc_params' => isset( $row->rc_params ) ? $row->rc_params : '',
- 'rc_log_type' => isset( $row->rc_log_type ) ? $row->rc_log_type : null,
- 'rc_log_action' => isset( $row->rc_log_action ) ? $row->rc_log_action : null,
- 'rc_logid' => isset( $row->rc_logid ) ? $row->rc_logid : 0,
- 'rc_deleted' => $row->rc_deleted // MUST be set
- );
- }
-
- /**
- * Get an attribute value
- *
- * @param string $name Attribute name
- * @return mixed
- */
- public function getAttribute( $name ) {
- return isset( $this->mAttribs[$name] ) ? $this->mAttribs[$name] : null;
- }
-
- /**
- * @return array
- */
- public function getAttributes() {
- return $this->mAttribs;
- }
-
- /**
- * Gets the end part of the diff URL associated with this object
- * Blank if no diff link should be displayed
- * @param $forceCur
- * @return string
- */
- public function diffLinkTrail( $forceCur ) {
- if ( $this->mAttribs['rc_type'] == RC_EDIT ) {
- $trail = "curid=" . (int)( $this->mAttribs['rc_cur_id'] ) .
- "&oldid=" . (int)( $this->mAttribs['rc_last_oldid'] );
- if ( $forceCur ) {
- $trail .= '&diff=0';
- } else {
- $trail .= '&diff=' . (int)( $this->mAttribs['rc_this_oldid'] );
- }
- } else {
- $trail = '';
- }
- return $trail;
- }
-
- /**
- * Returns the change size (HTML).
- * The lengths can be given optionally.
- * @param $old int
- * @param $new int
- * @return string
- */
- public function getCharacterDifference( $old = 0, $new = 0 ) {
- if ( $old === 0 ) {
- $old = $this->mAttribs['rc_old_len'];
- }
- if ( $new === 0 ) {
- $new = $this->mAttribs['rc_new_len'];
- }
- if ( $old === null || $new === null ) {
- return '';
- }
- return ChangesList::showCharacterDifference( $old, $new );
- }
-
- /**
- * Purge expired changes from the recentchanges table
- * @since 1.22
- */
- public static function purgeExpiredChanges() {
- if ( wfReadOnly() ) {
- return;
- }
-
- $method = __METHOD__;
- $dbw = wfGetDB( DB_MASTER );
- $dbw->onTransactionIdle( function() use ( $dbw, $method ) {
- global $wgRCMaxAge;
-
- $cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
- $dbw->delete(
- 'recentchanges',
- array( 'rc_timestamp < ' . $dbw->addQuotes( $cutoff ) ),
- $method
- );
- } );
- }
-
- private static function checkIPAddress( $ip ) {
- global $wgRequest;
- if ( $ip ) {
- if ( !IP::isIPAddress( $ip ) ) {
- throw new MWException( "Attempt to write \"" . $ip . "\" as an IP address into recent changes" );
- }
- } else {
- $ip = $wgRequest->getIP();
- if ( !$ip ) {
- $ip = '';
- }
- }
- return $ip;
- }
-
- /**
- * Check whether the given timestamp is new enough to have a RC row with a given tolerance
- * as the recentchanges table might not be cleared out regularly (so older entries might exist)
- * or rows which will be deleted soon shouldn't be included.
- *
- * @param $timestamp mixed MWTimestamp compatible timestamp
- * @param $tolerance integer Tolerance in seconds
- * @return bool
- */
- public static function isInRCLifespan( $timestamp, $tolerance = 0 ) {
- global $wgRCMaxAge;
- return wfTimestamp( TS_UNIX, $timestamp ) > time() - $tolerance - $wgRCMaxAge;
- }
-}
$form = new HTMLForm( $this->fields, $this->getContext(), $this->getMessagePrefix() );
$form->setSubmitCallback( array( $this, 'onSubmit' ) );
- $form->setWrapperLegendMsg( $this->getMessagePrefix() . '-legend' );
+ // If the form is a compact vertical form, then don't output this ugly
+ // fieldset surrounding it.
+ // XXX Special pages can setDisplayFormat to 'vform' in alterForm(), but that
+ // is called after this.
+ if ( !$form->isVForm() ) {
+ $form->setWrapperLegendMsg( $this->getMessagePrefix() . '-legend' );
+ }
$headerMsg = $this->msg( $this->getMessagePrefix() . '-text' );
if ( !$headerMsg->isDisabled() ) {
*/
class SpecialCreateAccount extends SpecialRedirectToSpecial {
function __construct() {
- parent::__construct( 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) );
+ parent::__construct( 'CreateAccount', 'Userlogin', 'signup', array( 'returnto', 'returntoquery', 'uselang' ) );
}
// No reason to hide this link on Special:Specialpages
$data['activeusers'] = intval( SiteStats::activeUsers() );
$data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
$data['jobs'] = intval( SiteStats::jobs() );
+
+ wfRunHooks( 'APIQuerySiteInfoStatisticsInfo', array( &$data ) );
+
return $this->getResult()->addValue( 'query', $property, $data );
}
--- /dev/null
+<?php
+/**
+ * Base class for all changes lists.
+ *
+ * The class is used for formatting recent changes, related changes and watchlist.
+ *
+ * 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
+ */
+
+class ChangesList extends ContextSource {
+
+ /**
+ * @var Skin
+ */
+ public $skin;
+
+ protected $watchlist = false;
+
+ protected $message;
+
+ /**
+ * Changeslist constructor
+ *
+ * @param $obj Skin or IContextSource
+ */
+ public function __construct( $obj ) {
+ if ( $obj instanceof IContextSource ) {
+ $this->setContext( $obj );
+ $this->skin = $obj->getSkin();
+ } else {
+ $this->setContext( $obj->getContext() );
+ $this->skin = $obj;
+ }
+ $this->preCacheMessages();
+ }
+
+ /**
+ * Fetch an appropriate changes list class for the main context
+ * This first argument used to be an User object.
+ *
+ * @deprecated in 1.18; use newFromContext() instead
+ * @param string|User $unused Unused
+ * @return ChangesList|EnhancedChangesList|OldChangesList derivative
+ */
+ public static function newFromUser( $unused ) {
+ wfDeprecated( __METHOD__, '1.18' );
+ return self::newFromContext( RequestContext::getMain() );
+ }
+
+ /**
+ * Fetch an appropriate changes list class for the specified context
+ * Some users might want to use an enhanced list format, for instance
+ *
+ * @param $context IContextSource to use
+ * @return ChangesList|EnhancedChangesList|OldChangesList derivative
+ */
+ public static function newFromContext( IContextSource $context ) {
+ $user = $context->getUser();
+ $sk = $context->getSkin();
+ $list = null;
+ if ( wfRunHooks( 'FetchChangesList', array( $user, &$sk, &$list ) ) ) {
+ $new = $context->getRequest()->getBool( 'enhanced', $user->getOption( 'usenewrc' ) );
+ return $new ? new EnhancedChangesList( $context ) : new OldChangesList( $context );
+ } else {
+ return $list;
+ }
+ }
+
+ /**
+ * Sets the list to use a "<li class='watchlist-(namespace)-(page)'>" tag
+ * @param $value Boolean
+ */
+ public function setWatchlistDivs( $value = true ) {
+ $this->watchlist = $value;
+ }
+
+ /**
+ * As we use the same small set of messages in various methods and that
+ * they are called often, we call them once and save them in $this->message
+ */
+ private function preCacheMessages() {
+ if ( !isset( $this->message ) ) {
+ foreach ( array(
+ 'cur', 'diff', 'hist', 'enhancedrc-history', 'last', 'blocklink', 'history',
+ 'semicolon-separator', 'pipe-separator' ) as $msg
+ ) {
+ $this->message[$msg] = $this->msg( $msg )->escaped();
+ }
+ }
+ }
+
+ /**
+ * Returns the appropriate flags for new page, minor change and patrolling
+ * @param array $flags Associative array of 'flag' => Bool
+ * @param string $nothing to use for empty space
+ * @return String
+ */
+ public function recentChangesFlags( $flags, $nothing = ' ' ) {
+ global $wgRecentChangesFlags;
+ $f = '';
+ foreach ( array_keys( $wgRecentChangesFlags ) as $flag ) {
+ $f .= isset( $flags[$flag] ) && $flags[$flag]
+ ? self::flag( $flag )
+ : $nothing;
+ }
+ return $f;
+ }
+
+ /**
+ * Provide the "<abbr>" element appropriate to a given abbreviated flag,
+ * namely the flag indicating a new page, a minor edit, a bot edit, or an
+ * unpatrolled edit. By default in English it will contain "N", "m", "b",
+ * "!" respectively, plus it will have an appropriate title and class.
+ *
+ * @param string $flag One key of $wgRecentChangesFlags
+ * @return String: Raw HTML
+ */
+ public static function flag( $flag ) {
+ static $flagInfos = null;
+ if ( is_null( $flagInfos ) ) {
+ global $wgRecentChangesFlags;
+ $flagInfos = array();
+ foreach ( $wgRecentChangesFlags as $key => $value ) {
+ $flagInfos[$key]['letter'] = wfMessage( $value['letter'] )->escaped();
+ $flagInfos[$key]['title'] = wfMessage( $value['title'] )->escaped();
+ // Allow customized class name, fall back to flag name
+ $flagInfos[$key]['class'] = Sanitizer::escapeClass(
+ isset( $value['class'] ) ? $value['class'] : $key );
+ }
+ }
+
+ // Inconsistent naming, bleh, kepted for b/c
+ $map = array(
+ 'minoredit' => 'minor',
+ 'botedit' => 'bot',
+ );
+ if ( isset( $map[$flag] ) ) {
+ $flag = $map[$flag];
+ }
+
+ return "<abbr class='" . $flagInfos[$flag]['class'] . "' title='" . $flagInfos[$flag]['title'] . "'>" .
+ $flagInfos[$flag]['letter'] .
+ '</abbr>';
+ }
+
+ /**
+ * Returns text for the start of the tabular part of RC
+ * @return String
+ */
+ public function beginRecentChangesList() {
+ $this->rc_cache = array();
+ $this->rcMoveIndex = 0;
+ $this->rcCacheIndex = 0;
+ $this->lastdate = '';
+ $this->rclistOpen = false;
+ $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
+ return '';
+ }
+
+ /**
+ * Show formatted char difference
+ * @param $old Integer: bytes
+ * @param $new Integer: bytes
+ * @param $context IContextSource context to use
+ * @return String
+ */
+ public static function showCharacterDifference( $old, $new, IContextSource $context = null ) {
+ global $wgRCChangedSizeThreshold, $wgMiserMode;
+
+ if ( !$context ) {
+ $context = RequestContext::getMain();
+ }
+
+ $new = (int)$new;
+ $old = (int)$old;
+ $szdiff = $new - $old;
+
+ $lang = $context->getLanguage();
+ $code = $lang->getCode();
+ static $fastCharDiff = array();
+ if ( !isset( $fastCharDiff[$code] ) ) {
+ $fastCharDiff[$code] = $wgMiserMode || $context->msg( 'rc-change-size' )->plain() === '$1';
+ }
+
+ $formattedSize = $lang->formatNum( $szdiff );
+
+ if ( !$fastCharDiff[$code] ) {
+ $formattedSize = $context->msg( 'rc-change-size', $formattedSize )->text();
+ }
+
+ if ( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) {
+ $tag = 'strong';
+ } else {
+ $tag = 'span';
+ }
+
+ if ( $szdiff === 0 ) {
+ $formattedSizeClass = 'mw-plusminus-null';
+ }
+ if ( $szdiff > 0 ) {
+ $formattedSize = '+' . $formattedSize;
+ $formattedSizeClass = 'mw-plusminus-pos';
+ }
+ if ( $szdiff < 0 ) {
+ $formattedSizeClass = 'mw-plusminus-neg';
+ }
+
+ $formattedTotalSize = $context->msg( 'rc-change-size-new' )->numParams( $new )->text();
+
+ return Html::element( $tag,
+ array( 'dir' => 'ltr', 'class' => $formattedSizeClass, 'title' => $formattedTotalSize ),
+ $context->msg( 'parentheses', $formattedSize )->plain() ) . $lang->getDirMark();
+ }
+
+ /**
+ * Format the character difference of one or several changes.
+ *
+ * @param $old RecentChange
+ * @param $new RecentChange last change to use, if not provided, $old will be used
+ * @return string HTML fragment
+ */
+ public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) {
+ $oldlen = $old->mAttribs['rc_old_len'];
+
+ if ( $new ) {
+ $newlen = $new->mAttribs['rc_new_len'];
+ } else {
+ $newlen = $old->mAttribs['rc_new_len'];
+ }
+
+ if ( $oldlen === null || $newlen === null ) {
+ return '';
+ }
+
+ return self::showCharacterDifference( $oldlen, $newlen, $this->getContext() );
+ }
+
+ /**
+ * Returns text for the end of RC
+ * @return String
+ */
+ public function endRecentChangesList() {
+ if ( $this->rclistOpen ) {
+ return "</ul>\n";
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @param string $s HTML to update
+ * @param $rc_timestamp mixed
+ */
+ public function insertDateHeader( &$s, $rc_timestamp ) {
+ # Make date header if necessary
+ $date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() );
+ if ( $date != $this->lastdate ) {
+ if ( $this->lastdate != '' ) {
+ $s .= "</ul>\n";
+ }
+ $s .= Xml::element( 'h4', null, $date ) . "\n<ul class=\"special\">";
+ $this->lastdate = $date;
+ $this->rclistOpen = true;
+ }
+ }
+
+ /**
+ * @param string $s HTML to update
+ * @param $title Title
+ * @param $logtype string
+ */
+ public function insertLog( &$s, $title, $logtype ) {
+ $page = new LogPage( $logtype );
+ $logname = $page->getName()->escaped();
+ $s .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $title, $logname ) )->escaped();
+ }
+
+ /**
+ * @param string $s HTML to update
+ * @param $rc RecentChange
+ * @param $unpatrolled
+ */
+ public function insertDiffHist( &$s, &$rc, $unpatrolled ) {
+ # Diff link
+ if ( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
+ $diffLink = $this->message['diff'];
+ } elseif ( !self::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
+ $diffLink = $this->message['diff'];
+ } else {
+ $query = array(
+ 'curid' => $rc->mAttribs['rc_cur_id'],
+ 'diff' => $rc->mAttribs['rc_this_oldid'],
+ 'oldid' => $rc->mAttribs['rc_last_oldid']
+ );
+
+ $diffLink = Linker::linkKnown(
+ $rc->getTitle(),
+ $this->message['diff'],
+ array( 'tabindex' => $rc->counter ),
+ $query
+ );
+ }
+ $diffhist = $diffLink . $this->message['pipe-separator'];
+ # History link
+ $diffhist .= Linker::linkKnown(
+ $rc->getTitle(),
+ $this->message['hist'],
+ array(),
+ array(
+ 'curid' => $rc->mAttribs['rc_cur_id'],
+ 'action' => 'history'
+ )
+ );
+ $s .= $this->msg( 'parentheses' )->rawParams( $diffhist )->escaped() . ' <span class="mw-changeslist-separator">. .</span> ';
+ }
+
+ /**
+ * @param string $s HTML to update
+ * @param $rc RecentChange
+ * @param $unpatrolled
+ * @param $watched
+ */
+ public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
+ $params = array();
+
+ $articlelink = Linker::linkKnown(
+ $rc->getTitle(),
+ null,
+ array( 'class' => 'mw-changeslist-title' ),
+ $params
+ );
+ if ( $this->isDeleted( $rc, Revision::DELETED_TEXT ) ) {
+ $articlelink = '<span class="history-deleted">' . $articlelink . '</span>';
+ }
+ # To allow for boldening pages watched by this user
+ $articlelink = "<span class=\"mw-title\">{$articlelink}</span>";
+ # RTL/LTR marker
+ $articlelink .= $this->getLanguage()->getDirMark();
+
+ wfRunHooks( 'ChangesListInsertArticleLink',
+ array( &$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched ) );
+
+ $s .= " $articlelink";
+ }
+
+ /**
+ * Get the timestamp from $rc formatted with current user's settings
+ * and a separator
+ *
+ * @param $rc RecentChange
+ * @return string HTML fragment
+ */
+ public function getTimestamp( $rc ) {
+ return $this->message['semicolon-separator'] . '<span class="mw-changeslist-date">' .
+ $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() ) . '</span> <span class="mw-changeslist-separator">. .</span> ';
+ }
+
+ /**
+ * Insert time timestamp string from $rc into $s
+ *
+ * @param string $s HTML to update
+ * @param $rc RecentChange
+ */
+ public function insertTimestamp( &$s, $rc ) {
+ $s .= $this->getTimestamp( $rc );
+ }
+
+ /**
+ * Insert links to user page, user talk page and eventually a blocking link
+ *
+ * @param &$s String HTML to update
+ * @param &$rc RecentChange
+ */
+ public function insertUserRelatedLinks( &$s, &$rc ) {
+ if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
+ $s .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
+ } else {
+ $s .= $this->getLanguage()->getDirMark() . Linker::userLink( $rc->mAttribs['rc_user'],
+ $rc->mAttribs['rc_user_text'] );
+ $s .= Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ }
+ }
+
+ /**
+ * Insert a formatted action
+ *
+ * @param $rc RecentChange
+ * @return string
+ */
+ public function insertLogEntry( $rc ) {
+ $formatter = LogFormatter::newFromRow( $rc->mAttribs );
+ $formatter->setContext( $this->getContext() );
+ $formatter->setShowUserToolLinks( true );
+ $mark = $this->getLanguage()->getDirMark();
+ return $formatter->getActionText() . " $mark" . $formatter->getComment();
+ }
+
+ /**
+ * Insert a formatted comment
+ * @param $rc RecentChange
+ * @return string
+ */
+ public function insertComment( $rc ) {
+ if ( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
+ if ( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) {
+ return ' <span class="history-deleted">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
+ } else {
+ return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
+ }
+ }
+ return '';
+ }
+
+ /**
+ * Check whether to enable recent changes patrol features
+ *
+ * @deprecated since 1.22
+ * @return Boolean
+ */
+ public static function usePatrol() {
+ global $wgUser;
+
+ wfDeprecated( __METHOD__, '1.22' );
+
+ return $wgUser->useRCPatrol();
+ }
+
+ /**
+ * Returns the string which indicates the number of watching users
+ * @return string
+ */
+ protected function numberofWatchingusers( $count ) {
+ static $cache = array();
+ if ( $count > 0 ) {
+ if ( !isset( $cache[$count] ) ) {
+ $cache[$count] = $this->msg( 'number_of_watching_users_RCview' )->numParams( $count )->escaped();
+ }
+ return $cache[$count];
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Determine if said field of a revision is hidden
+ * @param $rc RCCacheEntry
+ * @param $field Integer: one of DELETED_* bitfield constants
+ * @return Boolean
+ */
+ public static function isDeleted( $rc, $field ) {
+ return ( $rc->mAttribs['rc_deleted'] & $field ) == $field;
+ }
+
+ /**
+ * Determine if the current user is allowed to view a particular
+ * field of this revision, if it's marked as deleted.
+ * @param $rc RCCacheEntry
+ * @param $field Integer
+ * @param $user User object to check, or null to use $wgUser
+ * @return Boolean
+ */
+ public static function userCan( $rc, $field, User $user = null ) {
+ if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
+ return LogEventsList::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
+ } else {
+ return Revision::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
+ }
+ }
+
+ /**
+ * @param $link string
+ * @param $watched bool
+ * @return string
+ */
+ protected function maybeWatchedLink( $link, $watched = false ) {
+ if ( $watched ) {
+ return '<strong class="mw-watched">' . $link . '</strong>';
+ } else {
+ return '<span class="mw-rc-unwatched">' . $link . '</span>';
+ }
+ }
+
+ /** Inserts a rollback link
+ *
+ * @param $s string
+ * @param $rc RecentChange
+ */
+ public function insertRollback( &$s, &$rc ) {
+ if ( $rc->mAttribs['rc_type'] == RC_EDIT && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
+ $page = $rc->getTitle();
+ /** Check for rollback and edit permissions, disallow special pages, and only
+ * show a link on the top-most revision */
+ if ( $this->getUser()->isAllowed( 'rollback' ) && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] )
+ {
+ $rev = new Revision( array(
+ 'title' => $page,
+ 'id' => $rc->mAttribs['rc_this_oldid'],
+ 'user' => $rc->mAttribs['rc_user'],
+ 'user_text' => $rc->mAttribs['rc_user_text'],
+ 'deleted' => $rc->mAttribs['rc_deleted']
+ ) );
+ $s .= ' ' . Linker::generateRollback( $rev, $this->getContext() );
+ }
+ }
+ }
+
+ /**
+ * @param $s string
+ * @param $rc RecentChange
+ * @param $classes
+ */
+ public function insertTags( &$s, &$rc, &$classes ) {
+ if ( empty( $rc->mAttribs['ts_tags'] ) ) {
+ return;
+ }
+
+ list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( $rc->mAttribs['ts_tags'], 'changeslist' );
+ $classes = array_merge( $classes, $newClasses );
+ $s .= ' ' . $tagSummary;
+ }
+
+ public function insertExtra( &$s, &$rc, &$classes ) {
+ // Empty, used for subclasses to add anything special.
+ }
+
+ protected function showAsUnpatrolled( RecentChange $rc ) {
+ $unpatrolled = false;
+ if ( !$rc->mAttribs['rc_patrolled'] ) {
+ if ( $this->getUser()->useRCPatrol() ) {
+ $unpatrolled = true;
+ } elseif ( $this->getUser()->useNPPatrol() && $rc->mAttribs['rc_type'] == RC_NEW ) {
+ $unpatrolled = true;
+ }
+ }
+ return $unpatrolled;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Generates a list of changes using an Enhanced system (uses javascript).
+ *
+ * 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
+ */
+
+class EnhancedChangesList extends ChangesList {
+
+ protected $rc_cache;
+
+ /**
+ * Add the JavaScript file for enhanced changeslist
+ * @return String
+ */
+ public function beginRecentChangesList() {
+ $this->rc_cache = array();
+ $this->rcMoveIndex = 0;
+ $this->rcCacheIndex = 0;
+ $this->lastdate = '';
+ $this->rclistOpen = false;
+ $this->getOutput()->addModuleStyles( array(
+ 'mediawiki.special.changeslist',
+ 'mediawiki.special.changeslist.enhanced',
+ ) );
+ $this->getOutput()->addModules( array(
+ 'jquery.makeCollapsible',
+ 'mediawiki.icon',
+ ) );
+ return '';
+ }
+ /**
+ * Format a line for enhanced recentchange (aka with javascript and block of lines).
+ *
+ * @param $baseRC RecentChange
+ * @param $watched bool
+ *
+ * @return string
+ */
+ public function recentChangesLine( &$baseRC, $watched = false ) {
+ wfProfileIn( __METHOD__ );
+
+ # Create a specialised object
+ $rc = RCCacheEntry::newFromParent( $baseRC );
+
+ $curIdEq = array( 'curid' => $rc->mAttribs['rc_cur_id'] );
+
+ # If it's a new day, add the headline and flush the cache
+ $date = $this->getLanguage()->userDate( $rc->mAttribs['rc_timestamp'], $this->getUser() );
+ $ret = '';
+ if ( $date != $this->lastdate ) {
+ # Process current cache
+ $ret = $this->recentChangesBlock();
+ $this->rc_cache = array();
+ $ret .= Xml::element( 'h4', null, $date ) . "\n";
+ $this->lastdate = $date;
+ }
+
+ # Should patrol-related stuff be shown?
+ $rc->unpatrolled = $this->showAsUnpatrolled( $rc );
+
+ $showdifflinks = true;
+ # Make article link
+ $type = $rc->mAttribs['rc_type'];
+ $logType = $rc->mAttribs['rc_log_type'];
+ // Page moves, very old style, not supported anymore
+ if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
+ // New unpatrolled pages
+ } elseif ( $rc->unpatrolled && $type == RC_NEW ) {
+ $clink = Linker::linkKnown( $rc->getTitle() );
+ // Log entries
+ } elseif ( $type == RC_LOG ) {
+ if ( $logType ) {
+ $logtitle = SpecialPage::getTitleFor( 'Log', $logType );
+ $logpage = new LogPage( $logType );
+ $logname = $logpage->getName()->escaped();
+ $clink = $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logtitle, $logname ) )->escaped();
+ } else {
+ $clink = Linker::link( $rc->getTitle() );
+ }
+ $watched = false;
+ // Log entries (old format) and special pages
+ } elseif ( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
+ wfDebug( "Unexpected special page in recentchanges\n" );
+ $clink = '';
+ // Edits
+ } else {
+ $clink = Linker::linkKnown( $rc->getTitle() );
+ }
+
+ # Don't show unusable diff links
+ if ( !ChangesList::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
+ $showdifflinks = false;
+ }
+
+ $time = $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() );
+ $rc->watched = $watched;
+ $rc->link = $clink;
+ $rc->timestamp = $time;
+ $rc->numberofWatchingusers = $baseRC->numberofWatchingusers;
+
+ # Make "cur" and "diff" links. Do not use link(), it is too slow if
+ # called too many times (50% of CPU time on RecentChanges!).
+ $thisOldid = $rc->mAttribs['rc_this_oldid'];
+ $lastOldid = $rc->mAttribs['rc_last_oldid'];
+
+ $querycur = $curIdEq + array( 'diff' => '0', 'oldid' => $thisOldid );
+ $querydiff = $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid );
+
+ if ( !$showdifflinks ) {
+ $curLink = $this->message['cur'];
+ $diffLink = $this->message['diff'];
+ } elseif ( in_array( $type, array( RC_NEW, RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
+ if ( $type != RC_NEW ) {
+ $curLink = $this->message['cur'];
+ } else {
+ $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
+ $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
+ }
+ $diffLink = $this->message['diff'];
+ } else {
+ $diffUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querydiff ) );
+ $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
+ $diffLink = "<a href=\"$diffUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['diff']}</a>";
+ $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
+ }
+
+ # Make "last" link
+ if ( !$showdifflinks || !$lastOldid ) {
+ $lastLink = $this->message['last'];
+ } elseif ( in_array( $type, array( RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
+ $lastLink = $this->message['last'];
+ } else {
+ $lastLink = Linker::linkKnown( $rc->getTitle(), $this->message['last'],
+ array(), $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid ) );
+ }
+
+ # Make user links
+ if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
+ $rc->userlink = ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
+ } else {
+ $rc->userlink = Linker::userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ $rc->usertalklink = Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ }
+
+ $rc->lastlink = $lastLink;
+ $rc->curlink = $curLink;
+ $rc->difflink = $diffLink;
+
+ # Put accumulated information into the cache, for later display
+ # Page moves go on their own line
+ $title = $rc->getTitle();
+ $secureName = $title->getPrefixedDBkey();
+ if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
+ # Use an @ character to prevent collision with page names
+ $this->rc_cache['@@' . ( $this->rcMoveIndex++ )] = array( $rc );
+ } else {
+ # Logs are grouped by type
+ if ( $type == RC_LOG ) {
+ $secureName = SpecialPage::getTitleFor( 'Log', $logType )->getPrefixedDBkey();
+ }
+ if ( !isset( $this->rc_cache[$secureName] ) ) {
+ $this->rc_cache[$secureName] = array();
+ }
+
+ array_push( $this->rc_cache[$secureName], $rc );
+ }
+
+ wfProfileOut( __METHOD__ );
+
+ return $ret;
+ }
+
+ /**
+ * Enhanced RC group
+ * @return string
+ */
+ protected function recentChangesBlockGroup( $block ) {
+ global $wgRCShowChangedSize;
+
+ wfProfileIn( __METHOD__ );
+
+ # Add the namespace and title of the block as part of the class
+ $classes = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' );
+ if ( $block[0]->mAttribs['rc_log_type'] ) {
+ # Log entry
+ $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
+ . $block[0]->mAttribs['rc_log_type'] . '-' . $block[0]->mAttribs['rc_title'] );
+ } else {
+ $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns'
+ . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] );
+ }
+ $classes[] = $block[0]->watched && $block[0]->mAttribs['rc_timestamp'] >= $block[0]->watched
+ ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
+ $r = Html::openElement( 'table', array( 'class' => $classes ) ) .
+ Html::openElement( 'tr' );
+
+ # Collate list of users
+ $userlinks = array();
+ # Other properties
+ $unpatrolled = false;
+ $isnew = false;
+ $allBots = true;
+ $allMinors = true;
+ $curId = $currentRevision = 0;
+ # Some catalyst variables...
+ $namehidden = true;
+ $allLogs = true;
+ foreach ( $block as $rcObj ) {
+ $oldid = $rcObj->mAttribs['rc_last_oldid'];
+ if ( $rcObj->mAttribs['rc_type'] == RC_NEW ) {
+ $isnew = true;
+ }
+ // If all log actions to this page were hidden, then don't
+ // give the name of the affected page for this block!
+ if ( !$this->isDeleted( $rcObj, LogPage::DELETED_ACTION ) ) {
+ $namehidden = false;
+ }
+ $u = $rcObj->userlink;
+ if ( !isset( $userlinks[$u] ) ) {
+ $userlinks[$u] = 0;
+ }
+ if ( $rcObj->unpatrolled ) {
+ $unpatrolled = true;
+ }
+ if ( $rcObj->mAttribs['rc_type'] != RC_LOG ) {
+ $allLogs = false;
+ }
+ # Get the latest entry with a page_id and oldid
+ # since logs may not have these.
+ if ( !$curId && $rcObj->mAttribs['rc_cur_id'] ) {
+ $curId = $rcObj->mAttribs['rc_cur_id'];
+ }
+ if ( !$currentRevision && $rcObj->mAttribs['rc_this_oldid'] ) {
+ $currentRevision = $rcObj->mAttribs['rc_this_oldid'];
+ }
+
+ if ( !$rcObj->mAttribs['rc_bot'] ) {
+ $allBots = false;
+ }
+ if ( !$rcObj->mAttribs['rc_minor'] ) {
+ $allMinors = false;
+ }
+
+ $userlinks[$u]++;
+ }
+
+ # Sort the list and convert to text
+ krsort( $userlinks );
+ asort( $userlinks );
+ $users = array();
+ foreach ( $userlinks as $userlink => $count ) {
+ $text = $userlink;
+ $text .= $this->getLanguage()->getDirMark();
+ if ( $count > 1 ) {
+ $text .= ' ' . $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->formatNum( $count ) . '×' )->escaped();
+ }
+ array_push( $users, $text );
+ }
+
+ $users = ' <span class="changedby">'
+ . $this->msg( 'brackets' )->rawParams(
+ implode( $this->message['semicolon-separator'], $users )
+ )->escaped() . '</span>';
+
+ $tl = '<span class="mw-collapsible-toggle mw-collapsible-arrow mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>';
+ $r .= "<td>$tl</td>";
+
+ # Main line
+ $r .= '<td class="mw-enhanced-rc">' . $this->recentChangesFlags( array(
+ 'newpage' => $isnew, # show, when one have this flag
+ 'minor' => $allMinors, # show only, when all have this flag
+ 'unpatrolled' => $unpatrolled, # show, when one have this flag
+ 'bot' => $allBots, # show only, when all have this flag
+ ) );
+
+ # Timestamp
+ $r .= ' ' . $block[0]->timestamp . ' </td><td>';
+
+ # Article link
+ if ( $namehidden ) {
+ $r .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-event' )->escaped() . '</span>';
+ } elseif ( $allLogs ) {
+ $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
+ } else {
+ $this->insertArticleLink( $r, $block[0], $block[0]->unpatrolled, $block[0]->watched );
+ }
+
+ $r .= $this->getLanguage()->getDirMark();
+
+ $queryParams['curid'] = $curId;
+
+ # Changes message
+ static $nchanges = array();
+ static $sinceLastVisitMsg = array();
+
+ $n = count( $block );
+ if ( !isset( $nchanges[$n] ) ) {
+ $nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped();
+ }
+
+ $sinceLast = 0;
+ $unvisitedOldid = null;
+ foreach ( $block as $rcObj ) {
+ // Same logic as below inside main foreach
+ if ( $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched ) {
+ $sinceLast++;
+ $unvisitedOldid = $rcObj->mAttribs['rc_last_oldid'];
+ }
+ }
+ if ( !isset( $sinceLastVisitMsg[$sinceLast] ) ) {
+ $sinceLastVisitMsg[$sinceLast] =
+ $this->msg( 'enhancedrc-since-last-visit' )->numParams( $sinceLast )->escaped();
+ }
+
+ # Total change link
+ $r .= ' ';
+ $logtext = '';
+ if ( !$allLogs ) {
+ if ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
+ $logtext .= $nchanges[$n];
+ } elseif ( $isnew ) {
+ $logtext .= $nchanges[$n];
+ } else {
+ $logtext .= Linker::link(
+ $block[0]->getTitle(),
+ $nchanges[$n],
+ array(),
+ $queryParams + array(
+ 'diff' => $currentRevision,
+ 'oldid' => $oldid,
+ ),
+ array( 'known', 'noclasses' )
+ );
+ if ( $sinceLast > 0 && $sinceLast < $n ) {
+ $logtext .= $this->message['pipe-separator'] . Linker::link(
+ $block[0]->getTitle(),
+ $sinceLastVisitMsg[$sinceLast],
+ array(),
+ $queryParams + array(
+ 'diff' => $currentRevision,
+ 'oldid' => $unvisitedOldid,
+ ),
+ array( 'known', 'noclasses' )
+ );
+ }
+ }
+ }
+
+ # History
+ if ( $allLogs ) {
+ // don't show history link for logs
+ } elseif ( $namehidden || !$block[0]->getTitle()->exists() ) {
+ $logtext .= $this->message['pipe-separator'] . $this->message['enhancedrc-history'];
+ } else {
+ $params = $queryParams;
+ $params['action'] = 'history';
+
+ $logtext .= $this->message['pipe-separator'] .
+ Linker::linkKnown(
+ $block[0]->getTitle(),
+ $this->message['enhancedrc-history'],
+ array(),
+ $params
+ );
+ }
+
+ if ( $logtext !== '' ) {
+ $r .= $this->msg( 'parentheses' )->rawParams( $logtext )->escaped();
+ }
+
+ $r .= ' <span class="mw-changeslist-separator">. .</span> ';
+
+ # Character difference (does not apply if only log items)
+ if ( $wgRCShowChangedSize && !$allLogs ) {
+ $last = 0;
+ $first = count( $block ) - 1;
+ # Some events (like logs) have an "empty" size, so we need to skip those...
+ while ( $last < $first && $block[$last]->mAttribs['rc_new_len'] === null ) {
+ $last++;
+ }
+ while ( $first > $last && $block[$first]->mAttribs['rc_old_len'] === null ) {
+ $first--;
+ }
+ # Get net change
+ $chardiff = $this->formatCharacterDifference( $block[$first], $block[$last] );
+
+ if ( $chardiff == '' ) {
+ $r .= ' ';
+ } else {
+ $r .= ' ' . $chardiff . ' <span class="mw-changeslist-separator">. .</span> ';
+ }
+ }
+
+ $r .= $users;
+ $r .= $this->numberofWatchingusers( $block[0]->numberofWatchingusers );
+
+ # Sub-entries
+ foreach ( $block as $rcObj ) {
+ # Classes to apply -- TODO implement
+ $classes = array();
+ $type = $rcObj->mAttribs['rc_type'];
+
+ $trClass = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
+ ? ' class="mw-enhanced-watched"' : '';
+
+ $r .= '<tr' . $trClass . '><td></td><td class="mw-enhanced-rc">';
+ $r .= $this->recentChangesFlags( array(
+ 'newpage' => $type == RC_NEW,
+ 'minor' => $rcObj->mAttribs['rc_minor'],
+ 'unpatrolled' => $rcObj->unpatrolled,
+ 'bot' => $rcObj->mAttribs['rc_bot'],
+ ) );
+ $r .= ' </td><td class="mw-enhanced-rc-nested"><span class="mw-enhanced-rc-time">';
+
+ $params = $queryParams;
+
+ if ( $rcObj->mAttribs['rc_this_oldid'] != 0 ) {
+ $params['oldid'] = $rcObj->mAttribs['rc_this_oldid'];
+ }
+
+ # Log timestamp
+ if ( $type == RC_LOG ) {
+ $link = $rcObj->timestamp;
+ # Revision link
+ } elseif ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
+ $link = '<span class="history-deleted">' . $rcObj->timestamp . '</span> ';
+ } else {
+
+ $link = Linker::linkKnown(
+ $rcObj->getTitle(),
+ $rcObj->timestamp,
+ array(),
+ $params
+ );
+ if ( $this->isDeleted( $rcObj, Revision::DELETED_TEXT ) ) {
+ $link = '<span class="history-deleted">' . $link . '</span> ';
+ }
+ }
+ $r .= $link . '</span>';
+
+ if ( !$type == RC_LOG || $type == RC_NEW ) {
+ $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->curlink . $this->message['pipe-separator'] . $rcObj->lastlink )->escaped();
+ }
+ $r .= ' <span class="mw-changeslist-separator">. .</span> ';
+
+ # Character diff
+ if ( $wgRCShowChangedSize ) {
+ $cd = $this->formatCharacterDifference( $rcObj );
+ if ( $cd !== '' ) {
+ $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
+ }
+ }
+
+ if ( $rcObj->mAttribs['rc_type'] == RC_LOG ) {
+ $r .= $this->insertLogEntry( $rcObj );
+ } else {
+ # User links
+ $r .= $rcObj->userlink;
+ $r .= $rcObj->usertalklink;
+ $r .= $this->insertComment( $rcObj );
+ }
+
+ # Rollback
+ $this->insertRollback( $r, $rcObj );
+ # Tags
+ $this->insertTags( $r, $rcObj, $classes );
+
+ $r .= "</td></tr>\n";
+ }
+ $r .= "</table>\n";
+
+ $this->rcCacheIndex++;
+
+ wfProfileOut( __METHOD__ );
+
+ return $r;
+ }
+
+ /**
+ * Generate HTML for an arrow or placeholder graphic
+ * @param string $dir one of '', 'd', 'l', 'r'
+ * @param string $alt text
+ * @param string $title text
+ * @return String: HTML "<img>" tag
+ */
+ protected function arrow( $dir, $alt = '', $title = '' ) {
+ global $wgStylePath;
+ $encUrl = htmlspecialchars( $wgStylePath . '/common/images/Arr_' . $dir . '.png' );
+ $encAlt = htmlspecialchars( $alt );
+ $encTitle = htmlspecialchars( $title );
+ return "<img src=\"$encUrl\" width=\"12\" height=\"12\" alt=\"$encAlt\" title=\"$encTitle\" />";
+ }
+
+ /**
+ * Generate HTML for a right- or left-facing arrow,
+ * depending on language direction.
+ * @return String: HTML "<img>" tag
+ */
+ protected function sideArrow() {
+ $dir = $this->getLanguage()->isRTL() ? 'l' : 'r';
+ return $this->arrow( $dir, '+', $this->msg( 'rc-enhanced-expand' )->text() );
+ }
+
+ /**
+ * Generate HTML for a down-facing arrow
+ * depending on language direction.
+ * @return String: HTML "<img>" tag
+ */
+ protected function downArrow() {
+ return $this->arrow( 'd', '-', $this->msg( 'rc-enhanced-hide' )->text() );
+ }
+
+ /**
+ * Generate HTML for a spacer image
+ * @return String: HTML "<img>" tag
+ */
+ protected function spacerArrow() {
+ return $this->arrow( '', codepointToUtf8( 0xa0 ) ); // non-breaking space
+ }
+
+ /**
+ * Enhanced RC ungrouped line.
+ *
+ * @param $rcObj RecentChange
+ * @return String: a HTML formatted line (generated using $r)
+ */
+ protected function recentChangesBlockLine( $rcObj ) {
+ global $wgRCShowChangedSize;
+
+ wfProfileIn( __METHOD__ );
+ $query['curid'] = $rcObj->mAttribs['rc_cur_id'];
+
+ $type = $rcObj->mAttribs['rc_type'];
+ $logType = $rcObj->mAttribs['rc_log_type'];
+ $classes = array( 'mw-enhanced-rc' );
+ if ( $logType ) {
+ # Log entry
+ $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
+ . $logType . '-' . $rcObj->mAttribs['rc_title'] );
+ } else {
+ $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' .
+ $rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] );
+ }
+ $classes[] = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
+ ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
+ $r = Html::openElement( 'table', array( 'class' => $classes ) ) .
+ Html::openElement( 'tr' );
+
+ $r .= '<td class="mw-enhanced-rc"><span class="mw-enhancedchanges-arrow-space"></span>';
+ # Flag and Timestamp
+ if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
+ $r .= $this->recentChangesFlags( array() ); // no flags, but need the placeholders
+ } else {
+ $r .= $this->recentChangesFlags( array(
+ 'newpage' => $type == RC_NEW,
+ 'minor' => $rcObj->mAttribs['rc_minor'],
+ 'unpatrolled' => $rcObj->unpatrolled,
+ 'bot' => $rcObj->mAttribs['rc_bot'],
+ ) );
+ }
+ $r .= ' ' . $rcObj->timestamp . ' </td><td>';
+ # Article or log link
+ if ( $logType ) {
+ $logPage = new LogPage( $logType );
+ $logTitle = SpecialPage::getTitleFor( 'Log', $logType );
+ $logName = $logPage->getName()->escaped();
+ $r .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logTitle, $logName ) )->escaped();
+ } else {
+ $this->insertArticleLink( $r, $rcObj, $rcObj->unpatrolled, $rcObj->watched );
+ }
+ # Diff and hist links
+ if ( $type != RC_LOG ) {
+ $query['action'] = 'history';
+ $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->difflink . $this->message['pipe-separator'] . Linker::linkKnown(
+ $rcObj->getTitle(),
+ $this->message['hist'],
+ array(),
+ $query
+ ) )->escaped();
+ }
+ $r .= ' <span class="mw-changeslist-separator">. .</span> ';
+ # Character diff
+ if ( $wgRCShowChangedSize ) {
+ $cd = $this->formatCharacterDifference( $rcObj );
+ if ( $cd !== '' ) {
+ $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
+ }
+ }
+
+ if ( $type == RC_LOG ) {
+ $r .= $this->insertLogEntry( $rcObj );
+ } else {
+ $r .= ' ' . $rcObj->userlink . $rcObj->usertalklink;
+ $r .= $this->insertComment( $rcObj );
+ $this->insertRollback( $r, $rcObj );
+ }
+
+ # Tags
+ $this->insertTags( $r, $rcObj, $classes );
+ # Show how many people are watching this if enabled
+ $r .= $this->numberofWatchingusers( $rcObj->numberofWatchingusers );
+
+ $r .= "</td></tr></table>\n";
+
+ wfProfileOut( __METHOD__ );
+
+ return $r;
+ }
+
+ /**
+ * If enhanced RC is in use, this function takes the previously cached
+ * RC lines, arranges them, and outputs the HTML
+ *
+ * @return string
+ */
+ protected function recentChangesBlock() {
+ if ( count ( $this->rc_cache ) == 0 ) {
+ return '';
+ }
+
+ wfProfileIn( __METHOD__ );
+
+ $blockOut = '';
+ foreach ( $this->rc_cache as $block ) {
+ if ( count( $block ) < 2 ) {
+ $blockOut .= $this->recentChangesBlockLine( array_shift( $block ) );
+ } else {
+ $blockOut .= $this->recentChangesBlockGroup( $block );
+ }
+ }
+
+ wfProfileOut( __METHOD__ );
+
+ return '<div>' . $blockOut . '</div>';
+ }
+
+ /**
+ * Returns text for the end of RC
+ * If enhanced RC is in use, returns pretty much all the text
+ * @return string
+ */
+ public function endRecentChangesList() {
+ return $this->recentChangesBlock() . parent::endRecentChangesList();
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Generate a list of changes using the good old system (no javascript).
+ *
+ * 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
+ */
+class OldChangesList extends ChangesList {
+
+ /**
+ * Format a line using the old system (aka without any javascript).
+ *
+ * @param $rc RecentChange, passed by reference
+ * @param bool $watched (default false)
+ * @param int $linenumber (default null)
+ *
+ * @return string|bool
+ */
+ public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
+ global $wgRCShowChangedSize;
+ wfProfileIn( __METHOD__ );
+
+ # Should patrol-related stuff be shown?
+ $unpatrolled = $this->showAsUnpatrolled( $rc );
+
+ $dateheader = ''; // $s now contains only <li>...</li>, for hooks' convenience.
+ $this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
+
+ $s = '';
+ $classes = array();
+ // use mw-line-even/mw-line-odd class only if linenumber is given (feature from bug 14468)
+ if ( $linenumber ) {
+ if ( $linenumber & 1 ) {
+ $classes[] = 'mw-line-odd';
+ } else {
+ $classes[] = 'mw-line-even';
+ }
+ }
+
+ // Indicate watched status on the line to allow for more
+ // comprehensive styling.
+ $classes[] = $watched && $rc->mAttribs['rc_timestamp'] >= $watched
+ ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
+
+ // Moved pages (very very old, not supported anymore)
+ if ( $rc->mAttribs['rc_type'] == RC_MOVE || $rc->mAttribs['rc_type'] == RC_MOVE_OVER_REDIRECT ) {
+ // Log entries
+ } elseif ( $rc->mAttribs['rc_log_type'] ) {
+ $logtitle = SpecialPage::getTitleFor( 'Log', $rc->mAttribs['rc_log_type'] );
+ $this->insertLog( $s, $logtitle, $rc->mAttribs['rc_log_type'] );
+ // Log entries (old format) or log targets, and special pages
+ } elseif ( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
+ list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $rc->mAttribs['rc_title'] );
+ if ( $name == 'Log' ) {
+ $this->insertLog( $s, $rc->getTitle(), $subpage );
+ }
+ // Regular entries
+ } else {
+ $this->insertDiffHist( $s, $rc, $unpatrolled );
+ # M, N, b and ! (minor, new, bot and unpatrolled)
+ $s .= $this->recentChangesFlags(
+ array(
+ 'newpage' => $rc->mAttribs['rc_type'] == RC_NEW,
+ 'minor' => $rc->mAttribs['rc_minor'],
+ 'unpatrolled' => $unpatrolled,
+ 'bot' => $rc->mAttribs['rc_bot']
+ ),
+ ''
+ );
+ $this->insertArticleLink( $s, $rc, $unpatrolled, $watched );
+ }
+ # Edit/log timestamp
+ $this->insertTimestamp( $s, $rc );
+ # Bytes added or removed
+ if ( $wgRCShowChangedSize ) {
+ $cd = $this->formatCharacterDifference( $rc );
+ if ( $cd !== '' ) {
+ $s .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
+ }
+ }
+
+ if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
+ $s .= $this->insertLogEntry( $rc );
+ } else {
+ # User tool links
+ $this->insertUserRelatedLinks( $s, $rc );
+ # LTR/RTL direction mark
+ $s .= $this->getLanguage()->getDirMark();
+ $s .= $this->insertComment( $rc );
+ }
+
+ # Tags
+ $this->insertTags( $s, $rc, $classes );
+ # Rollback
+ $this->insertRollback( $s, $rc );
+ # For subclasses
+ $this->insertExtra( $s, $rc, $classes );
+
+ # How many users watch this page
+ if ( $rc->numberofWatchingusers > 0 ) {
+ $s .= ' ' . $this->numberofWatchingusers( $rc->numberofWatchingusers );
+ }
+
+ if ( $this->watchlist ) {
+ $classes[] = Sanitizer::escapeClass( 'watchlist-' . $rc->mAttribs['rc_namespace'] . '-' . $rc->mAttribs['rc_title'] );
+ }
+
+ if ( !wfRunHooks( 'OldChangesListRecentChangesLine', array( &$this, &$s, $rc, &$classes ) ) ) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ wfProfileOut( __METHOD__ );
+ return "$dateheader<li class=\"" . implode( ' ', $classes ) . "\">" . $s . "</li>\n";
+ }
+}
--- /dev/null
+<?php
+/**
+ * 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
+ */
+class RCCacheEntry extends RecentChange {
+ var $secureName, $link;
+ var $curlink, $difflink, $lastlink, $usertalklink, $versionlink;
+ var $userlink, $timestamp, $watched;
+
+ /**
+ * @param $rc RecentChange
+ * @return RCCacheEntry
+ */
+ static function newFromParent( $rc ) {
+ $rc2 = new RCCacheEntry;
+ $rc2->mAttribs = $rc->mAttribs;
+ $rc2->mExtra = $rc->mExtra;
+ return $rc2;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Utility class for creating and accessing recent change entries.
+ *
+ * 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
+ */
+
+/**
+ * Utility class for creating new RC entries
+ *
+ * mAttribs:
+ * rc_id id of the row in the recentchanges table
+ * rc_timestamp time the entry was made
+ * rc_cur_time timestamp on the cur row
+ * rc_namespace namespace #
+ * rc_title non-prefixed db key
+ * rc_type is new entry, used to determine whether updating is necessary
+ * rc_minor is minor
+ * rc_cur_id page_id of associated page entry
+ * rc_user user id who made the entry
+ * rc_user_text user name who made the entry
+ * rc_comment edit summary
+ * rc_this_oldid rev_id associated with this entry (or zero)
+ * rc_last_oldid rev_id associated with the entry before this one (or zero)
+ * rc_bot is bot, hidden
+ * rc_ip IP address of the user in dotted quad notation
+ * rc_new obsolete, use rc_type==RC_NEW
+ * rc_patrolled boolean whether or not someone has marked this edit as patrolled
+ * rc_old_len integer byte length of the text before the edit
+ * rc_new_len the same after the edit
+ * rc_deleted partial deletion
+ * rc_logid the log_id value for this log entry (or zero)
+ * rc_log_type the log type (or null)
+ * rc_log_action the log action (or null)
+ * rc_params log params
+ *
+ * mExtra:
+ * prefixedDBkey prefixed db key, used by external app via msg queue
+ * lastTimestamp timestamp of previous entry, used in WHERE clause during update
+ * lang the interwiki prefix, automatically set in save()
+ * oldSize text size before the change
+ * newSize text size after the change
+ * pageStatus status of the page: created, deleted, moved, restored, changed
+ *
+ * temporary: not stored in the database
+ * notificationtimestamp
+ * numberofWatchingusers
+ *
+ * @todo document functions and variables
+ */
+class RecentChange {
+ var $mAttribs = array(), $mExtra = array();
+
+ /**
+ * @var Title
+ */
+ var $mTitle = false;
+
+ /**
+ * @var User
+ */
+ private $mPerformer = false;
+
+ /**
+ * @var Title
+ */
+ var $mMovedToTitle = false;
+ var $numberofWatchingusers = 0; # Dummy to prevent error message in SpecialRecentchangeslinked
+ var $notificationtimestamp;
+
+ # Factory methods
+
+ /**
+ * @param $row
+ * @return RecentChange
+ */
+ public static function newFromRow( $row ) {
+ $rc = new RecentChange;
+ $rc->loadFromRow( $row );
+ return $rc;
+ }
+
+ /**
+ * @deprecated in 1.22
+ * @param $row
+ * @return RecentChange
+ */
+ public static function newFromCurRow( $row ) {
+ wfDeprecated( __METHOD__, '1.22' );
+ $rc = new RecentChange;
+ $rc->loadFromCurRow( $row );
+ $rc->notificationtimestamp = false;
+ $rc->numberofWatchingusers = false;
+ return $rc;
+ }
+
+ /**
+ * Obtain the recent change with a given rc_id value
+ *
+ * @param int $rcid rc_id value to retrieve
+ * @return RecentChange
+ */
+ public static function newFromId( $rcid ) {
+ return self::newFromConds( array( 'rc_id' => $rcid ), __METHOD__ );
+ }
+
+ /**
+ * Find the first recent change matching some specific conditions
+ *
+ * @param array $conds of conditions
+ * @param $fname Mixed: override the method name in profiling/logs
+ * @param $options Array Query options
+ * @return RecentChange
+ */
+ public static function newFromConds( $conds, $fname = __METHOD__, $options = array() ) {
+ $dbr = wfGetDB( DB_SLAVE );
+ $row = $dbr->selectRow( 'recentchanges', self::selectFields(), $conds, $fname, $options );
+ if ( $row !== false ) {
+ return self::newFromRow( $row );
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * 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
+
+ /**
+ * @param $attribs array
+ */
+ public function setAttribs( $attribs ) {
+ $this->mAttribs = $attribs;
+ }
+
+ /**
+ * @param $extra array
+ */
+ public function setExtra( $extra ) {
+ $this->mExtra = $extra;
+ }
+
+ /**
+ *
+ * @return Title
+ */
+ public function &getTitle() {
+ if ( $this->mTitle === false ) {
+ $this->mTitle = Title::makeTitle( $this->mAttribs['rc_namespace'], $this->mAttribs['rc_title'] );
+ }
+ return $this->mTitle;
+ }
+
+ /**
+ * Get the User object of the person who performed this change.
+ *
+ * @return User
+ */
+ public function getPerformer() {
+ if ( $this->mPerformer === false ) {
+ if ( $this->mAttribs['rc_user'] ) {
+ $this->mPerformer = User::newFromID( $this->mAttribs['rc_user'] );
+ } else {
+ $this->mPerformer = User::newFromName( $this->mAttribs['rc_user_text'], false );
+ }
+ }
+ return $this->mPerformer;
+ }
+
+ /**
+ * Writes the data in this object to the database
+ * @param $noudp bool
+ */
+ public function save( $noudp = false ) {
+ global $wgLocalInterwiki, $wgPutIPinRC, $wgUseEnotif, $wgShowUpdatedMarker, $wgContLang;
+
+ $dbw = wfGetDB( DB_MASTER );
+ if ( !is_array( $this->mExtra ) ) {
+ $this->mExtra = array();
+ }
+ $this->mExtra['lang'] = $wgLocalInterwiki;
+
+ if ( !$wgPutIPinRC ) {
+ $this->mAttribs['rc_ip'] = '';
+ }
+
+ # If our database is strict about IP addresses, use NULL instead of an empty string
+ if ( $dbw->strictIPs() and $this->mAttribs['rc_ip'] == '' ) {
+ unset( $this->mAttribs['rc_ip'] );
+ }
+
+ # Trim spaces on user supplied text
+ $this->mAttribs['rc_comment'] = trim( $this->mAttribs['rc_comment'] );
+
+ # Make sure summary is truncated (whole multibyte characters)
+ $this->mAttribs['rc_comment'] = $wgContLang->truncate( $this->mAttribs['rc_comment'], 255 );
+
+ # Fixup database timestamps
+ $this->mAttribs['rc_timestamp'] = $dbw->timestamp( $this->mAttribs['rc_timestamp'] );
+ $this->mAttribs['rc_cur_time'] = $dbw->timestamp( $this->mAttribs['rc_cur_time'] );
+ $this->mAttribs['rc_id'] = $dbw->nextSequenceValue( 'recentchanges_rc_id_seq' );
+
+ ## If we are using foreign keys, an entry of 0 for the page_id will fail, so use NULL
+ if ( $dbw->cascadingDeletes() and $this->mAttribs['rc_cur_id'] == 0 ) {
+ unset( $this->mAttribs['rc_cur_id'] );
+ }
+
+ # Insert new row
+ $dbw->insert( 'recentchanges', $this->mAttribs, __METHOD__ );
+
+ # Set the ID
+ $this->mAttribs['rc_id'] = $dbw->insertId();
+
+ # Notify extensions
+ wfRunHooks( 'RecentChange_save', array( &$this ) );
+
+ # Notify external application via UDP
+ if ( !$noudp ) {
+ $this->notifyRCFeeds();
+ }
+
+ # E-mail notifications
+ if ( $wgUseEnotif || $wgShowUpdatedMarker ) {
+ $editor = $this->getPerformer();
+ $title = $this->getTitle();
+
+ if ( wfRunHooks( 'AbortEmailNotification', array( $editor, $title ) ) ) {
+ # @todo FIXME: This would be better as an extension hook
+ $enotif = new EmailNotification();
+ $enotif->notifyOnPageChange( $editor, $title,
+ $this->mAttribs['rc_timestamp'],
+ $this->mAttribs['rc_comment'],
+ $this->mAttribs['rc_minor'],
+ $this->mAttribs['rc_last_oldid'],
+ $this->mExtra['pageStatus'] );
+ }
+ }
+ }
+
+ /**
+ * @deprecated since 1.22, use notifyRCFeeds instead.
+ */
+ public function notifyRC2UDP() {
+ wfDeprecated( __METHOD__, '1.22' );
+ $this->notifyRCFeeds();
+ }
+
+ /**
+ * Send some text to UDP.
+ * @deprecated since 1.22
+ */
+ public static function sendToUDP( $line, $address = '', $prefix = '', $port = '' ) {
+ global $wgRC2UDPAddress, $wgRC2UDPInterwikiPrefix, $wgRC2UDPPort, $wgRC2UDPPrefix;
+
+ wfDeprecated( __METHOD__, '1.22' );
+
+ # Assume default for standard RC case
+ $address = $address ? $address : $wgRC2UDPAddress;
+ $prefix = $prefix ? $prefix : $wgRC2UDPPrefix;
+ $port = $port ? $port : $wgRC2UDPPort;
+
+ $engine = new UDPRCFeedEngine();
+ $feed = array(
+ 'uri' => "udp://$address:$port/$prefix",
+ 'formatter' => 'IRCColourfulRCFeedFormatter',
+ 'add_interwiki_prefix' => $wgRC2UDPInterwikiPrefix,
+ );
+
+ return $engine->send( $feed, $line );
+ }
+
+ /**
+ * Notify all the feeds about the change.
+ */
+ public function notifyRCFeeds() {
+ global $wgRCFeeds;
+
+ foreach ( $wgRCFeeds as $feed ) {
+ $engine = self::getEngine( $feed['uri'] );
+
+ if ( isset( $this->mExtra['actionCommentIRC'] ) ) {
+ $actionComment = $this->mExtra['actionCommentIRC'];
+ } else {
+ $actionComment = null;
+ }
+
+ $omitBots = isset( $feed['omit_bots'] ) ? $feed['omit_bots'] : false;
+
+ if (
+ ( $omitBots && $this->mAttribs['rc_bot'] ) ||
+ $this->mAttribs['rc_type'] == RC_EXTERNAL
+ ) {
+ continue;
+ }
+
+ $formatter = new $feed['formatter']();
+ $line = $formatter->getLine( $feed, $this, $actionComment );
+
+ $engine->send( $feed, $line );
+ }
+ }
+
+ /**
+ * Gets the stream engine object for a given URI from $wgRCEngines
+ *
+ * @param $uri string URI to get the engine object for
+ * @return object The engine object
+ */
+ private static function getEngine( $uri ) {
+ global $wgRCEngines;
+
+ $scheme = parse_url( $uri, PHP_URL_SCHEME );
+ if ( !$scheme ) {
+ throw new MWException( __FUNCTION__ . ": Invalid stream logger URI: '$uri'" );
+ }
+
+ if ( !isset( $wgRCEngines[$scheme] ) ) {
+ throw new MWException( __FUNCTION__ . ": Unknown stream logger URI scheme: $scheme" );
+ }
+
+ return new $wgRCEngines[$scheme];
+ }
+
+ /**
+ * @deprecated since 1.22, moved to IRCColourfulRCFeedFormatter
+ */
+ public static function cleanupForIRC( $text ) {
+ wfDeprecated( __METHOD__, '1.22' );
+ return IRCColourfulRCFeedFormatter::cleanupForIRC( $text );
+ }
+
+ /**
+ * Mark a given change as patrolled
+ *
+ * @param $change Mixed: RecentChange or corresponding rc_id
+ * @param $auto Boolean: for automatic patrol
+ * @return Array See doMarkPatrolled(), or null if $change is not an existing rc_id
+ */
+ public static function markPatrolled( $change, $auto = false ) {
+ global $wgUser;
+
+ $change = $change instanceof RecentChange
+ ? $change
+ : RecentChange::newFromId( $change );
+
+ if ( !$change instanceof RecentChange ) {
+ return null;
+ }
+ return $change->doMarkPatrolled( $wgUser, $auto );
+ }
+
+ /**
+ * Mark this RecentChange as patrolled
+ *
+ * NOTE: Can also return 'rcpatroldisabled', 'hookaborted' and 'markedaspatrollederror-noautopatrol' as errors
+ * @param $user User object doing the action
+ * @param $auto Boolean: for automatic patrol
+ * @return array of permissions errors, see Title::getUserPermissionsErrors()
+ */
+ public function doMarkPatrolled( User $user, $auto = false ) {
+ global $wgUseRCPatrol, $wgUseNPPatrol;
+ $errors = array();
+ // If recentchanges patrol is disabled, only new pages
+ // can be patrolled
+ if ( !$wgUseRCPatrol && ( !$wgUseNPPatrol || $this->getAttribute( 'rc_type' ) != RC_NEW ) ) {
+ $errors[] = array( 'rcpatroldisabled' );
+ }
+ // Automatic patrol needs "autopatrol", ordinary patrol needs "patrol"
+ $right = $auto ? 'autopatrol' : 'patrol';
+ $errors = array_merge( $errors, $this->getTitle()->getUserPermissionsErrors( $right, $user ) );
+ if ( !wfRunHooks( 'MarkPatrolled', array( $this->getAttribute( 'rc_id' ), &$user, false ) ) ) {
+ $errors[] = array( 'hookaborted' );
+ }
+ // Users without the 'autopatrol' right can't patrol their
+ // own revisions
+ if ( $user->getName() == $this->getAttribute( 'rc_user_text' ) && !$user->isAllowed( 'autopatrol' ) ) {
+ $errors[] = array( 'markedaspatrollederror-noautopatrol' );
+ }
+ if ( $errors ) {
+ return $errors;
+ }
+ // If the change was patrolled already, do nothing
+ if ( $this->getAttribute( 'rc_patrolled' ) ) {
+ return array();
+ }
+ // Actually set the 'patrolled' flag in RC
+ $this->reallyMarkPatrolled();
+ // Log this patrol event
+ PatrolLog::record( $this, $auto, $user );
+ wfRunHooks( 'MarkPatrolledComplete', array( $this->getAttribute( 'rc_id' ), &$user, false ) );
+ return array();
+ }
+
+ /**
+ * Mark this RecentChange patrolled, without error checking
+ * @return Integer: number of affected rows
+ */
+ public function reallyMarkPatrolled() {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->update(
+ 'recentchanges',
+ array(
+ 'rc_patrolled' => 1
+ ),
+ array(
+ 'rc_id' => $this->getAttribute( 'rc_id' )
+ ),
+ __METHOD__
+ );
+ // Invalidate the page cache after the page has been patrolled
+ // to make sure that the Patrol link isn't visible any longer!
+ $this->getTitle()->invalidateCache();
+ return $dbw->affectedRows();
+ }
+
+ /**
+ * Makes an entry in the database corresponding to an edit
+ *
+ * @param $timestamp
+ * @param $title Title
+ * @param $minor
+ * @param $user User
+ * @param $comment
+ * @param $oldId
+ * @param $lastTimestamp
+ * @param $bot
+ * @param $ip string
+ * @param $oldSize int
+ * @param $newSize int
+ * @param $newId int
+ * @param $patrol int
+ * @return RecentChange
+ */
+ public static function notifyEdit( $timestamp, &$title, $minor, &$user, $comment, $oldId,
+ $lastTimestamp, $bot, $ip = '', $oldSize = 0, $newSize = 0, $newId = 0, $patrol = 0 ) {
+ $rc = new RecentChange;
+ $rc->mTitle = $title;
+ $rc->mPerformer = $user;
+ $rc->mAttribs = array(
+ 'rc_timestamp' => $timestamp,
+ 'rc_cur_time' => $timestamp,
+ 'rc_namespace' => $title->getNamespace(),
+ 'rc_title' => $title->getDBkey(),
+ 'rc_type' => RC_EDIT,
+ 'rc_minor' => $minor ? 1 : 0,
+ 'rc_cur_id' => $title->getArticleID(),
+ 'rc_user' => $user->getId(),
+ 'rc_user_text' => $user->getName(),
+ 'rc_comment' => $comment,
+ 'rc_this_oldid' => $newId,
+ 'rc_last_oldid' => $oldId,
+ 'rc_bot' => $bot ? 1 : 0,
+ 'rc_ip' => self::checkIPAddress( $ip ),
+ 'rc_patrolled' => intval( $patrol ),
+ 'rc_new' => 0, # obsolete
+ 'rc_old_len' => $oldSize,
+ 'rc_new_len' => $newSize,
+ 'rc_deleted' => 0,
+ 'rc_logid' => 0,
+ 'rc_log_type' => null,
+ 'rc_log_action' => '',
+ 'rc_params' => ''
+ );
+
+ $rc->mExtra = array(
+ 'prefixedDBkey' => $title->getPrefixedDBkey(),
+ 'lastTimestamp' => $lastTimestamp,
+ 'oldSize' => $oldSize,
+ 'newSize' => $newSize,
+ 'pageStatus' => 'changed'
+ );
+ $rc->save();
+ return $rc;
+ }
+
+ /**
+ * Makes an entry in the database corresponding to page creation
+ * Note: the title object must be loaded with the new id using resetArticleID()
+ * @todo Document parameters and return
+ *
+ * @param $timestamp
+ * @param $title Title
+ * @param $minor
+ * @param $user User
+ * @param $comment
+ * @param $bot
+ * @param $ip string
+ * @param $size int
+ * @param $newId int
+ * @param $patrol int
+ * @return RecentChange
+ */
+ public static function notifyNew( $timestamp, &$title, $minor, &$user, $comment, $bot,
+ $ip = '', $size = 0, $newId = 0, $patrol = 0 ) {
+ $rc = new RecentChange;
+ $rc->mTitle = $title;
+ $rc->mPerformer = $user;
+ $rc->mAttribs = array(
+ 'rc_timestamp' => $timestamp,
+ 'rc_cur_time' => $timestamp,
+ 'rc_namespace' => $title->getNamespace(),
+ 'rc_title' => $title->getDBkey(),
+ 'rc_type' => RC_NEW,
+ 'rc_minor' => $minor ? 1 : 0,
+ 'rc_cur_id' => $title->getArticleID(),
+ 'rc_user' => $user->getId(),
+ 'rc_user_text' => $user->getName(),
+ 'rc_comment' => $comment,
+ 'rc_this_oldid' => $newId,
+ 'rc_last_oldid' => 0,
+ 'rc_bot' => $bot ? 1 : 0,
+ 'rc_ip' => self::checkIPAddress( $ip ),
+ 'rc_patrolled' => intval( $patrol ),
+ 'rc_new' => 1, # obsolete
+ 'rc_old_len' => 0,
+ 'rc_new_len' => $size,
+ 'rc_deleted' => 0,
+ 'rc_logid' => 0,
+ 'rc_log_type' => null,
+ 'rc_log_action' => '',
+ 'rc_params' => ''
+ );
+
+ $rc->mExtra = array(
+ 'prefixedDBkey' => $title->getPrefixedDBkey(),
+ 'lastTimestamp' => 0,
+ 'oldSize' => 0,
+ 'newSize' => $size,
+ 'pageStatus' => 'created'
+ );
+ $rc->save();
+ return $rc;
+ }
+
+ /**
+ * @param $timestamp
+ * @param $title
+ * @param $user
+ * @param $actionComment
+ * @param $ip string
+ * @param $type
+ * @param $action
+ * @param $target
+ * @param $logComment
+ * @param $params
+ * @param $newId int
+ * @param $actionCommentIRC string
+ * @return bool
+ */
+ public static function notifyLog( $timestamp, &$title, &$user, $actionComment, $ip, $type,
+ $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = '' )
+ {
+ global $wgLogRestrictions;
+ # Don't add private logs to RC!
+ if ( isset( $wgLogRestrictions[$type] ) && $wgLogRestrictions[$type] != '*' ) {
+ return false;
+ }
+ $rc = self::newLogEntry( $timestamp, $title, $user, $actionComment, $ip, $type, $action,
+ $target, $logComment, $params, $newId, $actionCommentIRC );
+ $rc->save();
+ return true;
+ }
+
+ /**
+ * @param $timestamp
+ * @param $title Title
+ * @param $user User
+ * @param $actionComment
+ * @param $ip string
+ * @param $type
+ * @param $action
+ * @param $target Title
+ * @param $logComment
+ * @param $params
+ * @param $newId int
+ * @param $actionCommentIRC string
+ * @return RecentChange
+ */
+ public static function newLogEntry( $timestamp, &$title, &$user, $actionComment, $ip,
+ $type, $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = '' ) {
+ global $wgRequest;
+
+ ## Get pageStatus for email notification
+ switch ( $type . '-' . $action ) {
+ case 'delete-delete':
+ $pageStatus = 'deleted';
+ break;
+ case 'move-move':
+ case 'move-move_redir':
+ $pageStatus = 'moved';
+ break;
+ case 'delete-restore':
+ $pageStatus = 'restored';
+ break;
+ case 'upload-upload':
+ $pageStatus = 'created';
+ break;
+ case 'upload-overwrite':
+ default:
+ $pageStatus = 'changed';
+ break;
+ }
+
+ $rc = new RecentChange;
+ $rc->mTitle = $target;
+ $rc->mPerformer = $user;
+ $rc->mAttribs = array(
+ 'rc_timestamp' => $timestamp,
+ 'rc_cur_time' => $timestamp,
+ 'rc_namespace' => $target->getNamespace(),
+ 'rc_title' => $target->getDBkey(),
+ 'rc_type' => RC_LOG,
+ 'rc_minor' => 0,
+ 'rc_cur_id' => $target->getArticleID(),
+ 'rc_user' => $user->getId(),
+ 'rc_user_text' => $user->getName(),
+ 'rc_comment' => $logComment,
+ 'rc_this_oldid' => 0,
+ 'rc_last_oldid' => 0,
+ 'rc_bot' => $user->isAllowed( 'bot' ) ? $wgRequest->getBool( 'bot', true ) : 0,
+ 'rc_ip' => self::checkIPAddress( $ip ),
+ 'rc_patrolled' => 1,
+ 'rc_new' => 0, # obsolete
+ 'rc_old_len' => null,
+ 'rc_new_len' => null,
+ 'rc_deleted' => 0,
+ 'rc_logid' => $newId,
+ 'rc_log_type' => $type,
+ 'rc_log_action' => $action,
+ 'rc_params' => $params
+ );
+
+ $rc->mExtra = array(
+ 'prefixedDBkey' => $title->getPrefixedDBkey(),
+ 'lastTimestamp' => 0,
+ 'actionComment' => $actionComment, // the comment appended to the action, passed from LogPage
+ 'pageStatus' => $pageStatus,
+ 'actionCommentIRC' => $actionCommentIRC
+ );
+ return $rc;
+ }
+
+ /**
+ * Initialises the members of this object from a mysql row object
+ *
+ * @param $row
+ */
+ public function loadFromRow( $row ) {
+ $this->mAttribs = get_object_vars( $row );
+ $this->mAttribs['rc_timestamp'] = wfTimestamp( TS_MW, $this->mAttribs['rc_timestamp'] );
+ $this->mAttribs['rc_deleted'] = $row->rc_deleted; // MUST be set
+ }
+
+ /**
+ * Makes a pseudo-RC entry from a cur row
+ *
+ * @deprected in 1.22
+ * @param $row
+ */
+ public function loadFromCurRow( $row ) {
+ wfDeprecated( __METHOD__, '1.22' );
+ $this->mAttribs = array(
+ 'rc_timestamp' => wfTimestamp( TS_MW, $row->rev_timestamp ),
+ 'rc_cur_time' => $row->rev_timestamp,
+ 'rc_user' => $row->rev_user,
+ 'rc_user_text' => $row->rev_user_text,
+ 'rc_namespace' => $row->page_namespace,
+ 'rc_title' => $row->page_title,
+ 'rc_comment' => $row->rev_comment,
+ 'rc_minor' => $row->rev_minor_edit ? 1 : 0,
+ 'rc_type' => $row->page_is_new ? RC_NEW : RC_EDIT,
+ 'rc_cur_id' => $row->page_id,
+ 'rc_this_oldid' => $row->rev_id,
+ 'rc_last_oldid' => isset( $row->rc_last_oldid ) ? $row->rc_last_oldid : 0,
+ 'rc_bot' => 0,
+ 'rc_ip' => '',
+ 'rc_id' => $row->rc_id,
+ 'rc_patrolled' => $row->rc_patrolled,
+ 'rc_new' => $row->page_is_new, # obsolete
+ 'rc_old_len' => $row->rc_old_len,
+ 'rc_new_len' => $row->rc_new_len,
+ 'rc_params' => isset( $row->rc_params ) ? $row->rc_params : '',
+ 'rc_log_type' => isset( $row->rc_log_type ) ? $row->rc_log_type : null,
+ 'rc_log_action' => isset( $row->rc_log_action ) ? $row->rc_log_action : null,
+ 'rc_logid' => isset( $row->rc_logid ) ? $row->rc_logid : 0,
+ 'rc_deleted' => $row->rc_deleted // MUST be set
+ );
+ }
+
+ /**
+ * Get an attribute value
+ *
+ * @param string $name Attribute name
+ * @return mixed
+ */
+ public function getAttribute( $name ) {
+ return isset( $this->mAttribs[$name] ) ? $this->mAttribs[$name] : null;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAttributes() {
+ return $this->mAttribs;
+ }
+
+ /**
+ * Gets the end part of the diff URL associated with this object
+ * Blank if no diff link should be displayed
+ * @param $forceCur
+ * @return string
+ */
+ public function diffLinkTrail( $forceCur ) {
+ if ( $this->mAttribs['rc_type'] == RC_EDIT ) {
+ $trail = "curid=" . (int)( $this->mAttribs['rc_cur_id'] ) .
+ "&oldid=" . (int)( $this->mAttribs['rc_last_oldid'] );
+ if ( $forceCur ) {
+ $trail .= '&diff=0';
+ } else {
+ $trail .= '&diff=' . (int)( $this->mAttribs['rc_this_oldid'] );
+ }
+ } else {
+ $trail = '';
+ }
+ return $trail;
+ }
+
+ /**
+ * Returns the change size (HTML).
+ * The lengths can be given optionally.
+ * @param $old int
+ * @param $new int
+ * @return string
+ */
+ public function getCharacterDifference( $old = 0, $new = 0 ) {
+ if ( $old === 0 ) {
+ $old = $this->mAttribs['rc_old_len'];
+ }
+ if ( $new === 0 ) {
+ $new = $this->mAttribs['rc_new_len'];
+ }
+ if ( $old === null || $new === null ) {
+ return '';
+ }
+ return ChangesList::showCharacterDifference( $old, $new );
+ }
+
+ /**
+ * Purge expired changes from the recentchanges table
+ * @since 1.22
+ */
+ public static function purgeExpiredChanges() {
+ if ( wfReadOnly() ) {
+ return;
+ }
+
+ $method = __METHOD__;
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->onTransactionIdle( function() use ( $dbw, $method ) {
+ global $wgRCMaxAge;
+
+ $cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
+ $dbw->delete(
+ 'recentchanges',
+ array( 'rc_timestamp < ' . $dbw->addQuotes( $cutoff ) ),
+ $method
+ );
+ } );
+ }
+
+ private static function checkIPAddress( $ip ) {
+ global $wgRequest;
+ if ( $ip ) {
+ if ( !IP::isIPAddress( $ip ) ) {
+ throw new MWException( "Attempt to write \"" . $ip . "\" as an IP address into recent changes" );
+ }
+ } else {
+ $ip = $wgRequest->getIP();
+ if ( !$ip ) {
+ $ip = '';
+ }
+ }
+ return $ip;
+ }
+
+ /**
+ * Check whether the given timestamp is new enough to have a RC row with a given tolerance
+ * as the recentchanges table might not be cleared out regularly (so older entries might exist)
+ * or rows which will be deleted soon shouldn't be included.
+ *
+ * @param $timestamp mixed MWTimestamp compatible timestamp
+ * @param $tolerance integer Tolerance in seconds
+ * @return bool
+ */
+ public static function isInRCLifespan( $timestamp, $tolerance = 0 ) {
+ global $wgRCMaxAge;
+ return wfTimestamp( TS_UNIX, $timestamp ) > time() - $tolerance - $wgRCMaxAge;
+ }
+}
* @return ParserOptions
*/
public function makeParserOptions( $context ) {
- global $wgContLang;
+ global $wgContLang, $wgEnableParserLimitReporting;
if ( $context instanceof IContextSource ) {
$options = ParserOptions::newFromContext( $context );
throw new MWException( "Bad context for parser options: $context" );
}
- $options->enableLimitReport(); // show inclusion/loop reports
+ $options->enableLimitReport( $wgEnableParserLimitReporting ); // show inclusion/loop reports
$options->setTidy( true ); // fix bad HTML
return $options;
/**
* Constructor.
+ *
+ * FIXME: It is possible to construct a Database object with no associated
+ * connection object, by specifying no parameters to __construct(). This
+ * feature is deprecated and should be removed.
+ *
+ * FIXME: The long list of formal parameters here is not really appropriate
+ * for MySQL, and not at all appropriate for any other DBMS. It should be
+ * replaced by named parameters as in DatabaseBase::factory().
+ *
+ * DatabaseBase subclasses should not be constructed directly in external
+ * code. DatabaseBase::factory() should be used instead.
+ *
* @param string $server database server host
* @param string $user database user name
* @param string $password database user password
/**
* Given a DB type, construct the name of the appropriate child class of
* DatabaseBase. This is designed to replace all of the manual stuff like:
- * $class = 'Database' . ucfirst( strtolower( $type ) );
+ * $class = 'Database' . ucfirst( strtolower( $dbType ) );
* as well as validate against the canonical list of DB types we have
*
* This factory function is mostly useful for when you need to connect to a
*
* @param string $dbType A possible DB type
* @param array $p An array of options to pass to the constructor.
- * Valid options are: host, user, password, dbname, flags, tablePrefix
+ * Valid options are: host, user, password, dbname, flags, tablePrefix, driver
* @return DatabaseBase subclass or null
*/
final public static function factory( $dbType, $p = array() ) {
$canonicalDBTypes = array(
- 'mysql', 'postgres', 'sqlite', 'oracle', 'mssql'
+ 'mysql' => array( 'mysqli', 'mysql' ),
+ 'postgres' => array(),
+ 'sqlite' => array(),
+ 'oracle' => array(),
+ 'mssql' => array(),
);
+
+ $driver = false;
$dbType = strtolower( $dbType );
- $class = 'Database' . ucfirst( $dbType );
+ if ( isset( $canonicalDBTypes[$dbType] ) && $canonicalDBTypes[$dbType] ) {
+ $possibleDrivers = $canonicalDBTypes[$dbType];
+ if ( !empty( $p['driver'] ) ) {
+ if ( in_array( $p['driver'], $possibleDrivers ) ) {
+ $driver = $p['driver'];
+ } else {
+ throw new MWException( __METHOD__ .
+ " cannot construct Database with type '$dbType' and driver '{$p['driver']}'" );
+ }
+ } else {
+ foreach ( $possibleDrivers as $posDriver ) {
+ if ( extension_loaded( $posDriver ) ) {
+ $driver = $posDriver;
+ break;
+ }
+ }
+ }
+ } else {
+ $driver = $dbType;
+ }
+ if ( $driver === false ) {
+ throw new MWException( __METHOD__ .
+ " no viable database extension found for type '$dbType'" );
+ }
- if ( in_array( $dbType, $canonicalDBTypes ) || ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) ) {
+ $class = 'Database' . ucfirst( $driver );
+ if ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) {
return new $class(
isset( $p['host'] ) ? $p['host'] : false,
isset( $p['user'] ) ? $p['user'] : false,
}
/**
- * If it's a string, adds quotes and backslashes
- * Otherwise returns as-is
+ * Adds quotes and backslashes.
*
* @param $s string
*
*/
class MySQLField implements Field {
private $name, $tablename, $default, $max_length, $nullable,
- $is_pk, $is_unique, $is_multiple, $is_key, $type;
+ $is_pk, $is_unique, $is_multiple, $is_key, $type, $binary;
function __construct( $info ) {
$this->name = $info->name;
$this->is_multiple = $info->multiple_key;
$this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
$this->type = $info->type;
+ $this->binary = isset( $info->binary ) ? $info->binary : false;
}
/**
function isMultipleKey() {
return $this->is_multiple;
}
+
+ function isBinary() {
+ return $this->binary;
+ }
}
class MySQLMasterPos implements DBMasterPos {
--- /dev/null
+<?php
+/**
+ * This is the MySQLi database abstraction layer.
+ *
+ * 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 Database
+ */
+
+/**
+ * Database abstraction object for PHP extension mysqli.
+ *
+ * @ingroup Database
+ * @since 1.22
+ * @see Database
+ */
+class DatabaseMysqli extends DatabaseMysqlBase {
+
+ /**
+ * @param $sql string
+ * @return resource
+ */
+ protected function doQuery( $sql ) {
+ if ( $this->bufferResults() ) {
+ $ret = $this->mConn->query( $sql );
+ } else {
+ $ret = $this->mConn->query( $sql, MYSQLI_USE_RESULT );
+ }
+ return $ret;
+ }
+
+ protected function mysqlConnect( $realServer ) {
+ # Fail now
+ # Otherwise we get a suppressed fatal error, which is very hard to track down
+ if ( !function_exists( 'mysqli_init' ) ) {
+ throw new DBConnectionError( $this, "MySQLi functions missing,"
+ . " have you compiled PHP with the --with-mysqli option?\n" );
+ }
+
+ $connFlags = 0;
+ if ( $this->mFlags & DBO_SSL ) {
+ $connFlags |= MYSQLI_CLIENT_SSL;
+ }
+ if ( $this->mFlags & DBO_COMPRESS ) {
+ $connFlags |= MYSQLI_CLIENT_COMPRESS;
+ }
+ if ( $this->mFlags & DBO_PERSISTENT ) {
+ $realServer = 'p:' . $realServer;
+ }
+
+ $mysqli = mysqli_init();
+ $numAttempts = 2;
+
+ for ( $i = 0; $i < $numAttempts; $i++ ) {
+ if ( $i > 1 ) {
+ usleep( 1000 );
+ }
+ if ( $mysqli->real_connect( $realServer, $this->mUser,
+ $this->mPassword, $this->mDBname, null, null, $connFlags ) )
+ {
+ return $mysqli;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @return bool
+ */
+ protected function closeConnection() {
+ return $this->mConn->close();
+ }
+
+ /**
+ * @return int
+ */
+ function insertId() {
+ return $this->mConn->insert_id;
+ }
+
+ /**
+ * @return int
+ */
+ function lastErrno() {
+ if ( $this->mConn ) {
+ return $this->mConn->errno;
+ } else {
+ return mysqli_connect_errno();
+ }
+ }
+
+ /**
+ * @return int
+ */
+ function affectedRows() {
+ return $this->mConn->affected_rows;
+ }
+
+ /**
+ * @param $db
+ * @return bool
+ */
+ function selectDB( $db ) {
+ $this->mDBname = $db;
+ return $this->mConn->select_db( $db );
+ }
+
+ /**
+ * @return string
+ */
+ function getServerVersion() {
+ return $this->mConn->server_info;
+ }
+
+ protected function mysqlFreeResult( $res ) {
+ $res->free_result();
+ return true;
+ }
+
+ protected function mysqlFetchObject( $res ) {
+ $object = $res->fetch_object();
+ if ( $object === null ) {
+ return false;
+ }
+ return $object;
+ }
+
+ protected function mysqlFetchArray( $res ) {
+ $array = $res->fetch_array();
+ if ( $array === null ) {
+ return false;
+ }
+ return $array;
+ }
+
+ protected function mysqlNumRows( $res ) {
+ return $res->num_rows;
+ }
+
+ protected function mysqlNumFields( $res ) {
+ return $res->field_count;
+ }
+
+ protected function mysqlFetchField( $res, $n ) {
+ $field = $res->fetch_field_direct( $n );
+ $field->not_null = $field->flags & MYSQLI_NOT_NULL_FLAG;
+ $field->primary_key = $field->flags & MYSQLI_PRI_KEY_FLAG;
+ $field->unique_key = $field->flags & MYSQLI_UNIQUE_KEY_FLAG;
+ $field->multiple_key = $field->flags & MYSQLI_MULTIPLE_KEY_FLAG;
+ $field->binary = $field->flags & MYSQLI_BINARY_FLAG;
+ return $field;
+ }
+
+ protected function mysqlFieldName( $res, $n ) {
+ $field = $res->fetch_field_direct( $n );
+ return $field->name;
+ }
+
+ protected function mysqlDataSeek( $res, $row ) {
+ return $res->data_seek( $row );
+ }
+
+ protected function mysqlError( $conn = null ) {
+ if ($conn === null) {
+ return mysqli_connect_error();
+ } else {
+ return $conn->error;
+ }
+ }
+
+ protected function mysqlRealEscapeString( $s ) {
+ return $this->mConn->real_escape_string( $s );
+ }
+
+ protected function mysqlPing() {
+ return $this->mConn->ping();
+ }
+
+}
$this->mName = $dbName;
parent::__construct( $server, $user, $password, $dbName, $flags );
// parent doesn't open when $user is false, but we can work with $dbName
- if ( $dbName ) {
+ if ( $dbName && !$this->isOpen() ) {
global $wgSharedDB;
if ( $this->open( $server, $user, $password, $dbName ) && $wgSharedDB ) {
$this->attachDatabase( $wgSharedDB );
function open( $server, $user, $pass, $dbName ) {
global $wgSQLiteDataDir;
+ $this->close();
$fileName = self::generateFileName( $wgSQLiteDataDir, $dbName );
if ( !is_readable( $fileName ) ) {
$this->mConn = false;
if ( $this->mTrxLevel == 1 ) {
$this->commit( __METHOD__ );
}
- $this->mConn->beginTransaction();
+ try {
+ $this->mConn->beginTransaction();
+ } catch ( PDOException $e ) {
+ throw new DBUnexpectedError( $this, 'Error in BEGIN query: ' . $e->getMessage() );
+ }
$this->mTrxLevel = 1;
}
if ( $this->mTrxLevel == 0 ) {
return;
}
- $this->mConn->commit();
+ try {
+ $this->mConn->commit();
+ } catch ( PDOException $e ) {
+ throw new DBUnexpectedError( $this, 'Error in COMMIT query: ' . $e->getMessage() );
+ }
$this->mTrxLevel = 0;
}
* Once the return value goes out scope, the locks will be released and
* the status updated. Unlock fatals will not change the status "OK" value.
*
- * @param array $paths Storage paths
- * @param integer $type LockManager::LOCK_* constant
+ * @see ScopedLock::factory()
+ *
+ * @param array $paths List of storage paths or map of lock types to path lists
+ * @param integer|string $type LockManager::LOCK_* constant or "mixed"
* @param Status $status Status to update on lock/unlock
* @return ScopedLock|null Returns null on failure
*/
$mbe = $this->backends[$this->masterIndex]; // convenience
- // Get the paths to lock from the master backend
- $realOps = $this->substOpBatchPaths( $ops, $mbe );
- $paths = $mbe->getPathsToLockForOpsInternal( $mbe->getOperationsInternal( $realOps ) );
- // Get the paths under the proxy backend's name
- $paths['sh'] = $this->unsubstPaths( $paths['sh'] );
- $paths['ex'] = $this->unsubstPaths( $paths['ex'] );
// Try to lock those files for the scope of this function...
if ( empty( $opts['nonLocking'] ) ) {
// Try to lock those files for the scope of this function...
- $scopeLockS = $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status );
- $scopeLockE = $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status );
+ $scopeLock = $this->getScopedLocksForOps( $ops, $status );
if ( !$status->isOK() ) {
return $status; // abort
}
}
}
// Actually attempt the operation batch on the master backend...
+ $realOps = $this->substOpBatchPaths( $ops, $mbe );
$masterStatus = $mbe->doOperations( $realOps, $opts );
$status->merge( $masterStatus );
// Propagate the operations to the clone backends if there were no unexpected errors
}
public function getScopedLocksForOps( array $ops, Status $status ) {
- $fileOps = $this->backends[$this->masterIndex]->getOperationsInternal( $ops );
+ $realOps = $this->substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
+ $fileOps = $this->backends[$this->masterIndex]->getOperationsInternal( $realOps );
// Get the paths to lock from the master backend
$paths = $this->backends[$this->masterIndex]->getPathsToLockForOpsInternal( $fileOps );
// Get the paths under the proxy backend's name
- $paths['sh'] = $this->unsubstPaths( $paths['sh'] );
- $paths['ex'] = $this->unsubstPaths( $paths['ex'] );
- return array(
- $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status ),
- $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status )
+ $pbPaths = array(
+ LockManager::LOCK_UW => $this->unsubstPaths( $paths[LockManager::LOCK_UW] ),
+ LockManager::LOCK_EX => $this->unsubstPaths( $paths[LockManager::LOCK_EX] )
);
+ // Actually acquire the locks
+ return array( $this->getScopedFileLocks( $pbPaths, 'mixed', $status ) );
}
}
/**
* Get a list of storage paths to lock for a list of operations
- * Returns an array with 'sh' (shared) and 'ex' (exclusive) keys,
- * each corresponding to a list of storage paths to be locked.
- * All returned paths are normalized.
+ * Returns an array with LockManager::LOCK_UW (shared locks) and
+ * LockManager::LOCK_EX (exclusive locks) keys, each corresponding
+ * to a list of storage paths to be locked. All returned paths are
+ * normalized.
*
* @param array $performOps List of FileOp objects
- * @return Array ('sh' => list of paths, 'ex' => list of paths)
+ * @return Array (LockManager::LOCK_UW => path list, LockManager::LOCK_EX => path list)
*/
final public function getPathsToLockForOpsInternal( array $performOps ) {
// Build up a list of files to lock...
// Get a shared lock on the parent directory of each path changed
$paths['sh'] = array_merge( $paths['sh'], array_map( 'dirname', $paths['ex'] ) );
- return $paths;
+ return array(
+ LockManager::LOCK_UW => $paths['sh'],
+ LockManager::LOCK_EX => $paths['ex']
+ );
}
public function getScopedLocksForOps( array $ops, Status $status ) {
$paths = $this->getPathsToLockForOpsInternal( $this->getOperationsInternal( $ops ) );
- return array(
- $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status ),
- $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status )
- );
+ return array( $this->getScopedFileLocks( $paths, 'mixed', $status ) );
}
final protected function doOperationsInternal( array $ops, array $opts ) {
// Build up a list of files to lock...
$paths = $this->getPathsToLockForOpsInternal( $performOps );
// Try to lock those files for the scope of this function...
- $scopeLockS = $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status );
- $scopeLockE = $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status );
+ $scopeLock = $this->getScopedFileLocks( $paths, 'mixed', $status );
if ( !$status->isOK() ) {
return $status; // abort
}
'img_media_type' => $this->media_type,
'img_major_mime' => $major,
'img_minor_mime' => $minor,
- 'img_metadata' => $this->metadata,
+ 'img_metadata' => $dbw->encodeBlob($this->metadata),
'img_sha1' => $this->sha1,
),
array( 'img_name' => $this->getName() ),
'img_description' => $comment,
'img_user' => $user->getId(),
'img_user_text' => $user->getName(),
- 'img_metadata' => $this->metadata,
+ 'img_metadata' => $dbw->encodeBlob($this->metadata),
'img_sha1' => $this->sha1
),
__METHOD__,
'img_description' => $comment,
'img_user' => $user->getId(),
'img_user_text' => $user->getName(),
- 'img_metadata' => $this->metadata,
+ 'img_metadata' => $dbw->encodeBlob($this->metadata),
'img_sha1' => $this->sha1
),
array( 'img_name' => $this->getName() ),
wfDebugLog( 'imagemove', "Finished moving {$this->name}" );
- $this->purgeEverything();
- foreach ( $archiveNames as $archiveName ) {
- $this->purgeOldThumbnails( $archiveName );
- }
+ // Purge the source and target files...
+ $oldTitleFile = wfLocalFile( $this->title );
+ $newTitleFile = wfLocalFile( $target );
+ // Hack: the lock()/unlock() pair is nested in a transaction so the locking is not
+ // tied to BEGIN/COMMIT. To avoid slow purges in the transaction, move them outside.
+ $this->getRepo()->getMasterDB()->onTransactionIdle(
+ function() use ( $oldTitleFile, $newTitleFile, $archiveNames ) {
+ $oldTitleFile->purgeEverything();
+ foreach ( $archiveNames as $archiveName ) {
+ $oldTitleFile->purgeOldThumbnails( $archiveName );
+ }
+ $newTitleFile->purgeEverything();
+ }
+ );
+
if ( $status->isOK() ) {
// Now switch the object
$this->title = $target;
// Force regeneration of the name and hashpath
unset( $this->name );
unset( $this->hashPath );
- // Purge the new image
- $this->purgeEverything();
}
return $status;
To je pravděpodobně příliš málo.
Instalace může selhat!",
'config-ctype' => "'''Kritická chyba''': PHP musí být přeloženo s podporou pro [http://www.php.net/manual/en/ctype.installation.php rozšíření Ctype].",
+ 'config-json' => "'''Kritická chyba:''' PHP bylo přeloženo bez podpory JSON.
+Před instalací MediaWiki musíte buď nainstalovat rozšíření PHP JSON nebo rozšíření [http://pecl.php.net/package/jsonc PECL jsonc].
+* Rozšíření PHP je součástí Red Hat Enterprise Linux (CentOS) 5 a 6, avšak musí se povolit v <code>/etc/php.ini</code> nebo <code>/etc/php.d/json.ini</code>.
+* V některých linuxových distribucích vydaných po květnu 2013 může toto rozšíření PHP chybět a místo toho mohou používat rozšíření PECL jako <code>php5-json</code> nebo <code>php-pecl-jsonc</code>.",
'config-xcache' => 'Je nainstalována [http://xcache.lighttpd.net/ XCache]',
'config-apc' => 'Je nainstalováno [http://www.php.net/apc APC]',
'config-wincache' => 'Je nainstalována [http://www.iis.net/download/WinCacheForPhp WinCache]',
'config-download-localsettings' => 'Prenesi <code>LocalSettings.php</code>',
'config-help' => 'pomoč',
'mainpagetext' => "'''Programje MediaWiki je bilo uspešno nameščeno.'''",
- 'mainpagedocfooter' => 'Za uporabo in pomoč pri nastavitvi, prosimo, preglejte [//meta.wikimedia.org/wiki/MediaWiki_localisation dokumentacijo za prilagajanje vmesnika]
-in [//meta.wikimedia.org/wiki/MediaWiki_User%27s_Guide Uporabniški priročnik].', # Fuzzy
+ 'mainpagedocfooter' => 'Oglejte si [//meta.wikimedia.org/wiki/Help:Contents Uporabniški priročnik] za informacije o uporabi programja wiki.
+
+== Kako začeti ==
+* [//www.mediawiki.org/wiki/Manual:Configuration_settings Seznam konfiguracijskih nastavitev]
+* [//www.mediawiki.org/wiki/Manual:FAQ Poogsto zastavljena vprašanja MediaWiki]
+* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Poštni seznam izdaj MediaWiki]
+* [//www.mediawiki.org/wiki/Localisation#Translation_resources Prevedite MediaWiki v svoj jezik]',
);
/** Lower Silesian (Schläsch)
* @return Bool
*/
public function isCompiled() {
- return self::checkExtension( 'mysql' );
+ return self::checkExtension( 'mysql' ) || self::checkExtension( 'mysqli' );
}
/**
public function openConnection() {
$status = Status::newGood();
try {
- $db = new DatabaseMysql(
- $this->getVar( 'wgDBserver' ),
- $this->getVar( '_InstallUser' ),
- $this->getVar( '_InstallPassword' ),
- false,
- 0,
- $this->getVar( 'wgDBprefix' )
- );
+ $db = DatabaseBase::factory( 'mysql', array(
+ 'host' => $this->getVar( 'wgDBserver' ),
+ 'user' => $this->getVar( '_InstallUser' ),
+ 'password' => $this->getVar( '_InstallPassword' ),
+ 'dbname' => false,
+ 'flags' => 0,
+ 'tablePrefix' => $this->getVar( 'wgDBprefix' ) ) );
$status->value = $db;
} catch ( DBConnectionError $e ) {
$status->fatal( 'config-connection-error', $e->getMessage() );
if ( !$create ) {
// Test the web account
try {
- new DatabaseMysql(
- $this->getVar( 'wgDBserver' ),
- $this->getVar( 'wgDBuser' ),
- $this->getVar( 'wgDBpassword' ),
- false,
- 0,
- $this->getVar( 'wgDBprefix' )
- );
+ $db = DatabaseBase::factory( 'mysql', array(
+ 'host' => $this->getVar( 'wgDBserver' ),
+ 'user' => $this->getVar( 'wgDBuser' ),
+ 'password' => $this->getVar( 'wgDBpassword' ),
+ 'dbname' => false,
+ 'flags' => 0,
+ 'tablePrefix' => $this->getVar( 'wgDBprefix' ) ) );
} catch ( DBConnectionError $e ) {
return Status::newFatal( 'config-connection-error', $e->getMessage() );
}
if ( $this->getVar( '_CreateDBAccount' ) ) {
// Before we blindly try to create a user that already has access,
try { // first attempt to connect to the database
- new DatabaseMysql(
- $server,
- $dbUser,
- $password,
- false,
- 0,
- $this->getVar( 'wgDBprefix' )
- );
+ $db = DatabaseBase::factory( 'mysql', array(
+ 'host' => $server,
+ 'user' => $dbUser,
+ 'password' => $password,
+ 'dbname' => false,
+ 'flags' => 0,
+ 'tablePrefix' => $this->getVar( 'wgDBprefix' ) ) );
$grantableNames[] = $this->buildFullUserName( $dbUser, $server );
$tryToCreate = false;
} catch ( DBConnectionError $e ) {
// 1.15
array( 'doUniquePlTlIl' ),
array( 'addTable', 'change_tag', 'patch-change_tag.sql' ),
- /* array( 'addTable', 'tag_summary', 'patch-change_tag.sql' ), */
- /* array( 'addTable', 'valid_tag', 'patch-change_tag.sql' ), */
+ array( 'addTable', 'tag_summary', 'patch-tag_summary.sql' ),
+ array( 'addTable', 'valid_tag', 'patch-valid_tag.sql' ),
// 1.16
array( 'addTable', 'user_properties', 'patch-user_properties.sql' ),
// 1.22
array( 'doIwlinksIndexNonUnique' ),
array( 'addIndex', 'iwlinks', 'iwl_prefix_from_title', 'patch-iwlinks-from-title-index.sql' ),
+ array( 'addField', 'archive', 'ar_id', 'patch-archive-ar_id.sql' ),
+ array( 'addField', 'externallinks', 'el_id', 'patch-externallinks-el_id.sql' ),
);
}
return true;
}
- $tableName = $this->db->tableName( $table );
- $res = $this->db->query( "SELECT $field FROM $tableName LIMIT 0", __METHOD__ );
- $flags = explode( ' ', mysql_field_flags( $res->result, 0 ) );
-
- if ( in_array( 'binary', $flags ) ) {
+ $fieldInfo = $this->db->fieldInfo( $table, $field );
+ if ( $fieldInfo->isBinary() ) {
$this->output( "...$table table has correct $field encoding.\n" );
} else {
$this->applyPatch( $patchFile, false, "Fixing $field encoding on $table table" );
array( 'addField', 'revision', 'rev_content_format', 'patch-revision-rev_content_format.sql' ),
array( 'addField', 'revision', 'rev_content_model', 'patch-revision-rev_content_model.sql' ),
array( 'addField', 'archive', 'ar_content_format', 'patch-archive-ar_content_format.sql' ),
- array( 'addField', 'archive', 'ar_content_model', 'patch-archive-ar_content_model.sql' ),
+ array( 'addField', 'archive', 'ar_content_model', 'patch-archive-ar_content_model.sql' ),
+ array( 'addField', 'archive', 'ar_id', 'patch-archive-ar_id.sql' ),
+ array( 'addField', 'externallinks', 'el_id', 'patch-externallinks-el_id.sql' ),
array( 'addField', 'page', 'page_content_model', 'patch-page-page_content_model.sql' ),
array( 'dropField', 'site_stats', 'ss_admins', 'patch-ss_admins.sql' ),
array( 'dropField', 'recentchanges', 'rc_moved_to_title', 'patch-rc_moved.sql' ),
array( 'addPgField', 'archive', 'ar_content_model', 'TEXT' ),
array( 'addPgField', 'archive', 'ar_content_format', 'TEXT' ),
array( 'addPgField', 'categorylinks', 'cl_sortkey_prefix', "TEXT NOT NULL DEFAULT ''"),
- array( 'addPgField', 'categorylinks', 'cl_collation', "TEXT NOT NULL DEFAULT 0"),
- array( 'addPgField', 'categorylinks', 'cl_type', "TEXT NOT NULL DEFAULT 'page'"),
+ array( 'addPgField', 'categorylinks', 'cl_collation', "TEXT NOT NULL DEFAULT 0" ),
+ array( 'addPgField', 'categorylinks', 'cl_type', "TEXT NOT NULL DEFAULT 'page'" ),
array( 'addPgField', 'image', 'img_sha1', "TEXT NOT NULL DEFAULT ''" ),
array( 'addPgField', 'ipblocks', 'ipb_allow_usertalk', 'SMALLINT NOT NULL DEFAULT 0' ),
array( 'addPgField', 'ipblocks', 'ipb_anon_only', 'SMALLINT NOT NULL DEFAULT 0' ),
array( 'addPgField', 'job', 'job_token', "TEXT NOT NULL DEFAULT ''" ),
array( 'addPgField', 'job', 'job_token_timestamp', "TIMESTAMPTZ" ),
array( 'addPgField', 'job', 'job_sha1', "TEXT NOT NULL DEFAULT ''" ),
+ array( 'addPgField', 'archive', 'ar_id', "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('archive_ar_id_seq')" ),
+ array( 'addPgField', 'externallinks', 'el_id', "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('externallinks_el_id_seq')" ),
+
# type changes
array( 'changeField', 'archive', 'ar_deleted', 'smallint', '' ),
array( 'changeField', 'templatelinks', 'tl_namespace', 'smallint', 'tl_namespace::smallint' ),
array( 'changeField', 'user_newtalk', 'user_ip', 'text', 'host(user_ip)' ),
array( 'changeField', 'uploadstash', 'us_image_bits', 'smallint', '' ),
+ array( 'changeField', 'profiling', 'pf_time', 'float', '' ),
+ array( 'changeField', 'profiling', 'pf_memory', 'float', '' ),
# null changes
array( 'changeNullableField', 'oldimage', 'oi_bits', 'NULL' ),
// 1.15
array( 'addTable', 'change_tag', 'patch-change_tag.sql' ),
- array( 'addTable', 'tag_summary', 'patch-change_tag.sql' ),
- array( 'addTable', 'valid_tag', 'patch-change_tag.sql' ),
+ array( 'addTable', 'tag_summary', 'patch-tag_summary.sql' ),
+ array( 'addTable', 'valid_tag', 'patch-valid_tag.sql' ),
// 1.16
array( 'addTable', 'user_properties', 'patch-user_properties.sql' ),
array( 'addField', 'archive', 'ar_content_format', 'patch-archive-ar_content_format.sql' ),
array( 'addField', 'archive', 'ar_content_model', 'patch-archive-ar_content_model.sql' ),
array( 'addField', 'page', 'page_content_model', 'patch-page-page_content_model.sql' ),
-
array( 'dropField', 'site_stats', 'ss_admins', 'patch-drop-ss_admins.sql' ),
array( 'dropField', 'recentchanges', 'rc_moved_to_title', 'patch-rc_moved.sql' ),
array( 'addTable', 'sites', 'patch-sites.sql' ),
array( 'addIndex', 'page_props', 'pp_propname_page', 'patch-page_props-propname-page-index.sql' ),
array( 'addIndex', 'image', 'img_media_mime', 'patch-img_media_mime-index.sql' ),
array( 'addIndex', 'iwlinks', 'iwl_prefix_from_title', 'patch-iwlinks-from-title-index.sql' ),
+ array( 'addField', 'archive', 'ar_id', 'patch-archive-ar_id.sql' ),
+ array( 'addField', 'externallinks', 'el_id', 'patch-externallinks-el_id.sql' ),
);
}
protected $dupCache;
const QOS_ATOMIC = 1; // integer; "all-or-nothing" job insertions
- const QoS_Atomic = 1; // integer; "all-or-nothing" job insertions (b/c)
const ROOTJOB_TTL = 2419200; // integer; seconds to remember root jobs (28 days)
*
* If used for performance, then $wgMainCacheType should be set to memcached/redis.
* Note that "fifo" cannot be used for the ordering, since the data is distributed.
- * One can still use "timestamp" instead, as in "roughly timestamp ordered".
+ * One can still use "timestamp" instead, as in "roughly timestamp ordered". Also,
+ * queue classes used by this should ignore down servers (with TTL) to avoid slowness.
*
* @ingroup JobQueue
* @since 1.22
*/
class JobQueueFederated extends JobQueue {
- /** @var Array (wiki ID => section name) */
- protected $sectionsByWiki = array();
- /** @var Array (section name => (partition name => weight)) */
- protected $partitionsBySection = array();
- /** @var Array (section name => config array) */
- protected $configByPartition = array();
- /** @var Array (partition names => integer) */
- protected $partitionsNoPush = array();
-
- /** @var HashRing */
- protected $partitionRing;
- /** @var Array (partition name => JobQueue) */
+ /** @var Array (partition name => weight) reverse sorted by weight */
+ protected $partitionMap = array();
+ /** @var Array (partition name => JobQueue) reverse sorted by weight */
protected $partitionQueues = array();
+ /** @var HashRing */
+ protected $partitionPushRing;
/** @var BagOStuff */
protected $cache;
*/
protected function __construct( array $params ) {
parent::__construct( $params );
- $this->sectionsByWiki = isset( $params['sectionsByWiki'] )
- ? $params['sectionsByWiki']
- : array(); // all in "default" section
- $this->partitionsBySection = $params['partitionsBySection'];
- $this->configByPartition = $params['configByPartition'];
+ $section = isset( $params['sectionsByWiki'][$this->wiki] )
+ ? $params['sectionsByWiki'][$this->wiki]
+ : 'default';
+ if ( !isset( $params['partitionsBySection'][$section] ) ) {
+ throw new MWException( "No configuration for section '$section'." );
+ }
+ // Get the full partition map
+ $this->partitionMap = $params['partitionsBySection'][$section];
+ arsort( $this->partitionMap, SORT_NUMERIC );
+ // Get the partitions jobs can actually be pushed to
+ $partitionPushMap = $this->partitionMap;
if ( isset( $params['partitionsNoPush'] ) ) {
- $this->partitionsNoPush = array_flip( $params['partitionsNoPush'] );
+ foreach ( $params['partitionsNoPush'] as $partition ) {
+ unset( $partitionPushMap[$partition] );
+ }
}
+ // Get the config to pass to merge into each partition queue config
$baseConfig = $params;
foreach ( array( 'class', 'sectionsByWiki',
'partitionsBySection', 'configByPartition', 'partitionsNoPush' ) as $o )
{
unset( $baseConfig[$o] );
}
- foreach ( $this->getPartitionMap() as $partition => $w ) {
- if ( !isset( $this->configByPartition[$partition] ) ) {
+ // Get the partition queue objects
+ foreach ( $this->partitionMap as $partition => $w ) {
+ if ( !isset( $params['configByPartition'][$partition] ) ) {
throw new MWException( "No configuration for partition '$partition'." );
}
$this->partitionQueues[$partition] = JobQueue::factory(
- $baseConfig + $this->configByPartition[$partition]
- );
+ $baseConfig + $params['configByPartition'][$partition] );
}
- // Get the ring of partitions to push job de-duplication information into
- $partitionsTry = array_diff_key(
- $this->getPartitionMap(),
- $this->partitionsNoPush
- ); // (partition => weight)
- $this->partitionRing = new HashRing( $partitionsTry );
+ // Get the ring of partitions to push jobs into
+ $this->partitionPushRing = new HashRing( $partitionPushMap );
// Aggregate cache some per-queue values if there are multiple partition queues
- $this->cache = $this->isFederated() ? wfGetMainCache() : new EmptyBagOStuff();
+ $this->cache = count( $this->partitionMap ) > 1 ? wfGetMainCache() : new EmptyBagOStuff();
}
protected function supportedOrders() {
return false;
}
} catch ( JobQueueError $e ) {
- wfDebugLog( 'exception', $e->getLogMessage() );
+ MWExceptionHandler::logException( $e );
}
}
try {
$count += $queue->$method();
} catch ( JobQueueError $e ) {
- wfDebugLog( 'exception', $e->getLogMessage() );
+ MWExceptionHandler::logException( $e );
}
}
if ( !count( $jobs ) ) {
return true; // nothing to do
}
-
- $partitionsTry = array_diff_key(
- $this->getPartitionMap(),
- $this->partitionsNoPush
- ); // (partition => weight)
-
+ // Local ring variable that may be changed to point to a new ring on failure
+ $partitionRing = $this->partitionPushRing;
// Try to insert the jobs and update $partitionsTry on any failures
- $jobsLeft = $this->tryJobInsertions( $jobs, $partitionsTry, $flags );
+ $jobsLeft = $this->tryJobInsertions( $jobs, $partitionRing, $flags );
if ( count( $jobsLeft ) ) { // some jobs failed to insert?
// Try to insert the remaning jobs once more, ignoring the bad partitions
- return !count( $this->tryJobInsertions( $jobsLeft, $partitionsTry, $flags ) );
- } else {
- return true;
+ return !count( $this->tryJobInsertions( $jobsLeft, $partitionRing, $flags ) );
}
+ return true;
}
/**
* @param array $jobs
- * @param array $partitionsTry
+ * @param HashRing $partitionRing
* @param integer $flags
* @return array List of Job object that could not be inserted
*/
- protected function tryJobInsertions( array $jobs, array &$partitionsTry, $flags ) {
- if ( !count( $partitionsTry ) ) {
- return $jobs; // can't insert anything
- }
-
+ protected function tryJobInsertions( array $jobs, HashRing &$partitionRing, $flags ) {
$jobsLeft = array();
- $partitionRing = new HashRing( $partitionsTry );
// Because jobs are spread across partitions, per-job de-duplication needs
// to use a consistent hash to avoid allowing duplicate jobs per partition.
// When inserting a batch of de-duplicated jobs, QOS_ATOMIC is disregarded.
foreach ( $uJobsByPartition as $partition => $jobBatch ) {
$queue = $this->partitionQueues[$partition];
try {
- $ok = $queue->doBatchPush( $jobBatch, $flags );
+ $ok = $queue->doBatchPush( $jobBatch, $flags | self::QOS_ATOMIC );
} catch ( JobQueueError $e ) {
$ok = false;
- wfDebugLog( 'exception', $e->getLogMessage() );
+ MWExceptionHandler::logException( $e );
}
if ( $ok ) {
$key = $this->getCacheKey( 'empty' );
$this->cache->set( $key, 'false', JobQueueDB::CACHE_TTL_LONG );
} else {
- unset( $partitionsTry[$partition] ); // blacklist partition
+ $partitionRing = $partitionRing->newWithoutLocation( $partition ); // blacklist
+ if ( !$partitionRing ) {
+ throw new JobQueueError( "Could not insert job(s), all partitions are down." );
+ }
$jobsLeft = array_merge( $jobsLeft, $jobBatch ); // not inserted
}
}
+
// Insert the jobs that are not de-duplicated into the queues...
foreach ( $nuJobBatches as $jobBatch ) {
- $partition = ArrayUtils::pickRandom( $partitionsTry );
- if ( $partition === false ) { // all partitions at 0 weight?
- $jobsLeft = array_merge( $jobsLeft, $jobBatch ); // not inserted
+ $partition = ArrayUtils::pickRandom( $partitionRing->getLocationWeights() );
+ $queue = $this->partitionQueues[$partition];
+ try {
+ $ok = $queue->doBatchPush( $jobBatch, $flags | self::QOS_ATOMIC );
+ } catch ( JobQueueError $e ) {
+ $ok = false;
+ MWExceptionHandler::logException( $e );
+ }
+ if ( $ok ) {
+ $key = $this->getCacheKey( 'empty' );
+ $this->cache->set( $key, 'false', JobQueueDB::CACHE_TTL_LONG );
} else {
- $queue = $this->partitionQueues[$partition];
- try {
- $ok = $queue->doBatchPush( $jobBatch, $flags );
- } catch ( JobQueueError $e ) {
- $ok = false;
- wfDebugLog( 'exception', $e->getLogMessage() );
- }
- if ( $ok ) {
- $key = $this->getCacheKey( 'empty' );
- $this->cache->set( $key, 'false', JobQueueDB::CACHE_TTL_LONG );
- } else {
- unset( $partitionsTry[$partition] ); // blacklist partition
- $jobsLeft = array_merge( $jobsLeft, $jobBatch ); // not inserted
+ $partitionRing = $partitionRing->newWithoutLocation( $partition ); // blacklist
+ if ( !$partitionRing ) {
+ throw new JobQueueError( "Could not insert job(s), all partitions are down." );
}
+ $jobsLeft = array_merge( $jobsLeft, $jobBatch ); // not inserted
}
}
return false;
}
- $partitionsTry = $this->getPartitionMap(); // (partition => weight)
+ $partitionsTry = $this->partitionMap; // (partition => weight)
while ( count( $partitionsTry ) ) {
$partition = ArrayUtils::pickRandom( $partitionsTry );
$job = $queue->pop();
} catch ( JobQueueError $e ) {
$job = false;
- wfDebugLog( 'exception', $e->getLogMessage() );
+ MWExceptionHandler::logException( $e );
}
if ( $job ) {
$job->metadata['QueuePartition'] = $partition;
protected function doIsRootJobOldDuplicate( Job $job ) {
$params = $job->getRootJobParams();
- $partitions = $this->partitionRing->getLocations( $params['rootJobSignature'], 2 );
+ $partitions = $this->partitionPushRing->getLocations( $params['rootJobSignature'], 2 );
try {
return $this->partitionQueues[$partitions[0]]->doIsRootJobOldDuplicate( $job );
- } catch ( MWException $e ) {
+ } catch ( JobQueueError $e ) {
if ( isset( $partitions[1] ) ) { // check fallback partition
return $this->partitionQueues[$partitions[1]]->doIsRootJobOldDuplicate( $job );
}
protected function doDeduplicateRootJob( Job $job ) {
$params = $job->getRootJobParams();
- $partitions = $this->partitionRing->getLocations( $params['rootJobSignature'], 2 );
+ $partitions = $this->partitionPushRing->getLocations( $params['rootJobSignature'], 2 );
try {
return $this->partitionQueues[$partitions[0]]->doDeduplicateRootJob( $job );
- } catch ( MWException $e ) {
+ } catch ( JobQueueError $e ) {
if ( isset( $partitions[1] ) ) { // check fallback partition
return $this->partitionQueues[$partitions[1]]->doDeduplicateRootJob( $job );
}
try {
$queue->doDelete();
} catch ( JobQueueError $e ) {
- wfDebugLog( 'exception', $e->getLogMessage() );
+ MWExceptionHandler::logException( $e );
}
}
}
try {
$queue->waitForBackups();
} catch ( JobQueueError $e ) {
- wfDebugLog( 'exception', $e->getLogMessage() );
+ MWExceptionHandler::logException( $e );
}
}
}
}
public function getCoalesceLocationInternal() {
- return "JobQueueFederated:wiki:" . $this->wiki;
+ return "JobQueueFederated:wiki:{$this->wiki}" .
+ sha1( serialize( array_keys( $this->partitionMap ) ) );
}
protected function doGetSiblingQueuesWithJobs( array $types ) {
$result = array();
foreach ( $this->partitionQueues as $queue ) {
- $nonEmpty = $queue->doGetSiblingQueuesWithJobs( $types );
- if ( is_array( $nonEmpty ) ) {
- $result = array_merge( $result, $nonEmpty );
- } else {
- return null; // not supported on all partitions; bail
+ try {
+ $nonEmpty = $queue->doGetSiblingQueuesWithJobs( $types );
+ if ( is_array( $nonEmpty ) ) {
+ $result = array_unique( array_merge( $result, $nonEmpty ) );
+ } else {
+ return null; // not supported on all partitions; bail
+ }
+ if ( count( $result ) == count( $types ) ) {
+ break; // short-circuit
+ }
+ } catch ( JobQueueError $e ) {
+ MWExceptionHandler::logException( $e );
}
}
- return array_values( array_unique( $result ) );
+ return array_values( $result );
}
protected function doGetSiblingQueueSizes( array $types ) {
$result = array();
foreach ( $this->partitionQueues as $queue ) {
- $sizes = $queue->doGetSiblingQueueSizes( $types );
- if ( is_array( $sizes ) ) {
- foreach ( $sizes as $type => $size ) {
- $result[$type] = isset( $result[$type] ) ? $result[$type] + $size : $size;
+ try {
+ $sizes = $queue->doGetSiblingQueueSizes( $types );
+ if ( is_array( $sizes ) ) {
+ foreach ( $sizes as $type => $size ) {
+ $result[$type] = isset( $result[$type] ) ? $result[$type] + $size : $size;
+ }
+ } else {
+ return null; // not supported on all partitions; bail
}
- } else {
- return null; // not supported on all partitions; bail
+ } catch ( JobQueueError $e ) {
+ MWExceptionHandler::logException( $e );
}
}
return $result;
}
}
- /**
- * @return Array Map of (partition name => weight)
- */
- protected function getPartitionMap() {
- $section = isset( $this->sectionsByWiki[$this->wiki] )
- ? $this->sectionsByWiki[$this->wiki]
- : 'default';
- if ( !isset( $this->partitionsBySection[$section] ) ) {
- throw new MWException( "No configuration for section '$section'." );
- }
- return $this->partitionsBySection[$section];
- }
-
- /**
- * @return bool The queue is actually split up across multiple queue partitions
- */
- protected function isFederated() {
- return ( count( $this->getPartitionMap() ) > 1 );
- }
-
/**
* @return string
*/
* to internalParse() which does all the real work.
*/
- global $wgUseTidy, $wgAlwaysUseTidy;
+ global $wgUseTidy, $wgAlwaysUseTidy, $wgShowHostnames;
$fname = __METHOD__ . '-' . wfGetCaller();
wfProfileIn( __METHOD__ );
wfProfileIn( $fname );
wfRunHooks( 'ParserLimitReportPrepare', array( $this, $this->mOutput ) );
$limitReport = "NewPP limit report\n";
+ if ( $wgShowHostnames ) {
+ $limitReport .= 'Parsed by ' . wfHostname() . "\n";
+ }
foreach ( $this->mOutput->getLimitReportData() as $key => $value ) {
if ( wfRunHooks( 'ParserLimitReportFormat',
array( $key, $value, &$limitReport, false, false )
// Save filtered text to Memcached
$cache->set( $key, $result );
- } catch ( Exception $exception ) {
- $exception->logException();
- wfDebugLog( 'resourceloader', __METHOD__ . ": minification failed: $exception" );
+ } catch ( Exception $e ) {
+ MWExceptionHandler::logException( $e );
+ wfDebugLog( 'resourceloader', __METHOD__ . ": minification failed: $e" );
$this->hasErrors = true;
// Return exception as a comment
- $result = self::formatException( $exception );
+ $result = self::formatException( $e );
}
wfProfileOut( __METHOD__ );
try {
$this->preloadModuleInfo( array_keys( $modules ), $context );
} catch ( Exception $e ) {
- $e->logException();
+ MWExceptionHandler::logException( $e );
wfDebugLog( 'resourceloader', __METHOD__ . ": preloading module info failed: $e" );
$this->hasErrors = true;
// Add exception to the output as a comment
// Calculate maximum modified time
$mtime = max( $mtime, $module->getModifiedTime( $context ) );
} catch ( Exception $e ) {
- $e->logException();
+ MWExceptionHandler::logException( $e );
wfDebugLog( 'resourceloader', __METHOD__ . ": calculating maximum modified time failed: $e" );
$this->hasErrors = true;
// Add exception to the output as a comment
try {
$blobs = MessageBlobStore::get( $this, $modules, $context->getLanguage() );
} catch ( Exception $e ) {
- $e->logException();
+ MWExceptionHandler::logException( $e );
wfDebugLog( 'resourceloader', __METHOD__ . ": pre-fetching blobs from MessageBlobStore failed: $e" );
$this->hasErrors = true;
// Add exception to the output as a comment
break;
}
} catch ( Exception $e ) {
- $e->logException();
+ MWExceptionHandler::logException( $e );
wfDebugLog( 'resourceloader', __METHOD__ . ": generating module package failed: $e" );
$this->hasErrors = true;
// Add exception to the output as a comment
/**
* Generate a cache key for a LESS file.
*
- * The cache key varies on the file name, the names and values of global
- * LESS variables, and the value of $wgShowExceptionDetails. Varying on
- * $wgShowExceptionDetails ensures the CSS comment indicating compilation
- * failure shows the right level of detail.
+ * The cache key varies on the file name and the names and values of global
+ * LESS variables.
*
* @since 1.22
* @param string $fileName File name of root LESS file.
* @return string: Cache key
*/
protected static function getLESSCacheKey( $fileName ) {
- global $wgShowExceptionDetails;
-
$vars = json_encode( ResourceLoader::getLESSVars() );
$hash = md5( $fileName . $vars );
- return wfMemcKey( 'resourceloader', 'less', (string)$wgShowExceptionDetails, $hash );
+ return wfMemcKey( 'resourceloader', 'less', $hash );
}
/**
* @return string: CSS source
*/
protected function compileLESSFile( $fileName ) {
- global $wgShowExceptionDetails;
-
$key = self::getLESSCacheKey( $fileName );
$cache = wfGetCache( CACHE_ANYTHING );
}
$compiler = ResourceLoader::getLessCompiler();
- $expire = 0;
- try {
- $result = $compiler->cachedCompile( $source );
- if ( !is_array( $result ) ) {
- throw new Exception( 'LESS compiler result has type ' . gettype( $result ) . '; array expected.' );
- }
- } catch ( Exception $e ) {
- // The exception might have been caused by an imported file rather
- // than the root node. But we don't know which files were imported,
- // because compilation failed; we thus cannot rely on file mtime to
- // know when to reattempt compilation. Expire in 5 mins. instead.
- $expire = 300;
- wfDebugLog( 'resourceloader', __METHOD__ . ": $e" );
- $result = array();
- $result['root'] = $fileName;
-
- if ( $wgShowExceptionDetails ) {
- $result['compiled'] = ResourceLoader::makeComment( 'LESS error: ' . $e->getMessage() );
- } else {
- $result['compiled'] = ResourceLoader::makeComment( 'LESS stylesheet compilation failed. ' .
- 'Set "$wgShowExceptionDetails = true;" to show detailed debugging information.' );
- }
+ $result = null;
- $result['files'] = array( $fileName => self::safeFilemtime( $fileName ) );
- $result['updated'] = time();
+ $result = $compiler->cachedCompile( $source );
+
+ if ( !is_array( $result ) ) {
+ throw new MWException( 'LESS compiler result has type ' . gettype( $result ) . '; array expected.' );
}
+
$this->localFileRefs += array_keys( $result['files'] );
- $cache->set( $key, $result, $expire );
+ $cache->set( $key, $result );
return $result['compiled'];
}
}
'default' => 50,
),
);
- $form = new HTMLForm( $fields, $this->getContext() );
- $form->setTitle( $this->getTitle() ); // Remove subpage
+ $context = new DerivativeContext( $this->getContext() );
+ $context->setTitle( $this->getTitle() ); // Remove subpage
+ $form = new HTMLForm( $fields, $context );
$form->setMethod( 'get' );
$form->setWrapperLegendMsg( 'ipblocklist-legend' );
$form->setSubmitTextMsg( 'ipblocklist-submit' );
# Show user names for /newbies as there may be different users.
# Note that we already excluded rows with hidden user names.
if ( $this->contribs == 'newbie' ) {
- $userlink = ' . . ' . Linker::userLink( $rev->getUser(), $rev->getUserText() );
+ $userlink = ' . . ' . $lang->getDirMark() . Linker::userLink( $rev->getUser(), $rev->getUserText() );
$userlink .= ' ' . $this->msg( 'parentheses' )->rawParams(
Linker::userTalkLink( $rev->getUser(), $rev->getUserText() ) )->escaped() . ' ';
} else {
$this->toc = false;
}
- $form = new EditWatchlistNormalHTMLForm( $fields, $this->getContext() );
- $form->setTitle( $this->getTitle() );
+ $context = new DerivativeContext( $this->getContext() );
+ $context->setTitle( $this->getTitle() ); // Remove subpage
+ $form = new EditWatchlistNormalHTMLForm( $fields, $context );
$form->setSubmitTextMsg( 'watchlistedit-normal-submit' );
# Used message keys: 'accesskey-watchlistedit-normal-submit', 'tooltip-watchlistedit-normal-submit'
$form->setSubmitTooltip( 'watchlistedit-normal-submit' );
'default' => $titles,
),
);
- $form = new HTMLForm( $fields, $this->getContext() );
- $form->setTitle( $this->getTitle( 'raw' ) );
+ $context = new DerivativeContext( $this->getContext() );
+ $context->setTitle( $this->getTitle( 'raw' ) ); // Reset subpage
+ $form = new HTMLForm( $fields, $context );
$form->setSubmitTextMsg( 'watchlistedit-raw-submit' );
# Used message keys: 'accesskey-watchlistedit-raw-submit', 'tooltip-watchlistedit-raw-submit'
$form->setSubmitTooltip( 'watchlistedit-raw-submit' );
$this->mTargetObj = $ret;
- $form = new HTMLForm( $this->getFormFields(), $this->getContext() );
+ $context = new DerivativeContext( $this->getContext() );
+ $context->setTitle( $this->getTitle() ); // Remove subpage
+ $form = new HTMLForm( $this->getFormFields(), $context );
// By now we are supposed to be sure that $this->mTarget is a user name
$form->addPreText( $this->msg( 'emailpagetext', $this->mTarget )->parse() );
$form->setSubmitTextMsg( 'emailsend' );
- $form->setTitle( $this->getTitle() );
$form->setSubmitCallback( array( __CLASS__, 'uiSubmit' ) );
$form->setWrapperLegendMsg( 'email-legend' );
$form->loadData();
unset( $fields['like'] );
}
- $form = new HTMLForm( $fields, $this->getContext() );
- $form->setTitle( $this->getTitle() );
+ $context = new DerivativeContext( $this->getContext() );
+ $context->setTitle( $this->getTitle() ); // Remove subpage
+ $form = new HTMLForm( $fields, $context );
$form->setSubmitTextMsg( 'ilsubmit' );
$form->setMethod( 'get' );
$form->setWrapperLegendMsg( 'newimages-legend' );
public function alterForm( HTMLForm $form ) {
global $wgPasswordResetRoutes;
+ $form->setDisplayFormat( 'vform' );
+ // Turn the old-school line around the form off.
+ // XXX This wouldn't be necessary here if we could set the format of
+ // the HTMLForm to 'vform' at its creation, but there's no way to do so
+ // from a FormSpecialPage class.
+ $form->setWrapperLegend( false );
+
$i = 0;
if ( isset( $wgPasswordResetRoutes['username'] ) && $wgPasswordResetRoutes['username'] ) {
$i++;
$this->getOutput()->addWikiMsg( 'prefs-reset-intro' );
- $htmlForm = new HTMLForm( array(), $this->getContext(), 'prefs-restore' );
+ $context = new DerivativeContext( $this->getContext() );
+ $context->setTitle( $this->getTitle( 'reset' ) ); // Reset subpage
+ $htmlForm = new HTMLForm( array(), $context, 'prefs-restore' );
$htmlForm->setSubmitTextMsg( 'restoreprefs' );
- $htmlForm->setTitle( $this->getTitle( 'reset' ) );
$htmlForm->setSubmitCallback( array( $this, 'submitReset' ) );
$htmlForm->suppressReset();
*/
public function getDefaultOptions() {
$opts = new FormOptions();
+ $user = $this->getUser();
- $opts->add( 'days', $this->getUser()->getIntOption( 'rcdays' ) );
- $opts->add( 'limit', $this->getUser()->getIntOption( 'rclimit' ) );
+ $opts->add( 'days', $user->getIntOption( 'rcdays' ) );
+ $opts->add( 'limit', $user->getIntOption( 'rclimit' ) );
$opts->add( 'from', '' );
- $opts->add( 'hideminor', $this->getUser()->getBoolOption( 'hideminor' ) );
+ $opts->add( 'hideminor', $user->getBoolOption( 'hideminor' ) );
$opts->add( 'hidebots', true );
$opts->add( 'hideanons', false );
$opts->add( 'hideliu', false );
- $opts->add( 'hidepatrolled', $this->getUser()->getBoolOption( 'hidepatrolled' ) );
+ $opts->add( 'hidepatrolled', $user->getBoolOption( 'hidepatrolled' ) );
$opts->add( 'hidemyself', false );
$opts->add( 'namespace', '', FormOptions::INTNULL );
$opts = $this->getOptions();
$this->setHeaders();
$this->outputHeader();
- $this->addRecentChangesJS();
+ $this->addModules();
// Fetch results, prepare a batch link existence check query
$conds = $this->buildMainQueryConds( $opts );
}
/**
- * Add JavaScript to the page
+ * Add page-specific modules.
*/
- function addRecentChangesJS() {
+ protected function addModules() {
$this->getOutput()->addModules( array(
'mediawiki.special.recentchanges',
) );
* @ingroup SpecialPage
*/
class SpecialTags extends SpecialPage {
+ /**
+ * @var array List of defined tags
+ */
+ public $definedTags;
function __construct() {
parent::__construct( 'Tags' );
$html .= $this->doTagRow( $tag, $hitcount );
}
- $out->addHTML( Xml::tags( 'table', array( 'class' => 'wikitable sortable mw-tags-table' ), $html ) );
+ $out->addHTML( Xml::tags(
+ 'table',
+ array( 'class' => 'wikitable sortable mw-tags-table' ),
+ $html
+ ) );
}
function doTagRow( $tag, $hitcount ) {
$disp = ChangeTags::tagDescription( $tag );
if ( $user->isAllowed( 'editinterface' ) ) {
$disp .= ' ';
- $editLink = Linker::link( Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag" ), $this->msg( 'tags-edit' )->escaped() );
+ $editLink = Linker::link(
+ Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag" ),
+ $this->msg( 'tags-edit' )->escaped()
+ );
$disp .= $this->msg( 'parentheses' )->rawParams( $editLink )->escaped();
}
$newRow .= Xml::tags( 'td', null, $disp );
$desc = !$msg->exists() ? '' : $msg->parse();
if ( $user->isAllowed( 'editinterface' ) ) {
$desc .= ' ';
- $editDescLink = Linker::link( Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag-description" ), $this->msg( 'tags-edit' )->escaped() );
+ $editDescLink = Linker::link(
+ Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag-description" ),
+ $this->msg( 'tags-edit' )->escaped()
+ );
$desc .= $this->msg( 'parentheses' )->rawParams( $editDescLink )->escaped();
}
$newRow .= Xml::tags( 'td', null, $desc );
- $active = $this->msg( isset( $this->definedTags[$tag] ) ? 'tags-active-yes' : 'tags-active-no' )->escaped();
+ $active = isset( $this->definedTags[$tag] ) ? 'tags-active-yes' : 'tags-active-no';
+ $active = $this->msg( $active )->escaped();
$newRow .= Xml::tags( 'td', null, $active );
$hitcountLabel = $this->msg( 'tags-hitcount' )->numParams( $hitcount )->escaped();
- $hitcountLink = Linker::link( SpecialPage::getTitleFor( 'Recentchanges' ), $hitcountLabel, array(), array( 'tagfilter' => $tag ) );
+ $hitcountLink = Linker::link(
+ SpecialPage::getTitleFor( 'Recentchanges' ),
+ $hitcountLabel,
+ array(),
+ array( 'tagfilter' => $tag )
+ );
+
// add raw $hitcount for sorting, because tags-hitcount contains numbers and letters
$newRow .= Xml::tags( 'td', array( 'data-sort-value' => $hitcount ), $hitcountLink );
*/
protected function getUploadForm( $message = '', $sessionKey = '', $hideIgnoreWarning = false ) {
# Initialize form
+ $context = new DerivativeContext( $this->getContext() );
+ $context->setTitle( $this->getTitle() ); // Remove subpage
$form = new UploadForm( array(
'watch' => $this->getWatchCheck(),
'forreupload' => $this->mForReUpload,
'texttop' => $this->uploadFormTextTop,
'textaftersummary' => $this->uploadFormTextAfterSummary,
'destfile' => $this->mDesiredDestName,
- ), $this->getContext() );
- $form->setTitle( $this->getTitle() );
+ ), $context );
# Check the token, but only if necessary
if (
// create the form, which will also be used to execute a callback to process incoming form data
// this design is extremely dubious, but supposedly HTMLForm is our standard now?
+ $context = new DerivativeContext( $this->getContext() );
+ $context->setTitle( $this->getTitle() ); // Remove subpage
$form = new HTMLForm( array(
'Clear' => array(
'type' => 'hidden',
'default' => true,
'name' => 'clear',
)
- ), $this->getContext(), 'clearStashedUploads' );
+ ), $context, 'clearStashedUploads' );
$form->setSubmitCallback( array( __CLASS__, 'tryClearStashedUploads' ) );
- $form->setTitle( $this->getTitle() );
$form->setSubmitTextMsg( 'uploadstash-clear' );
$form->prepareForm();
$query = array(
'returnto' => $this->mReturnTo,
'returntoquery' => $this->mReturnToQuery,
- );
+ 'title' => null,
+ ) + $this->mRequest->getQueryValues();
$url = $title->getFullURL( $query, false, PROTO_HTTPS );
if ( $wgSecureLogin && wfCanIPUseHTTPS( $this->getRequest()->getIP() ) ) {
$url = wfAppendQuery( $url, 'fromhttp=1' );
// @todo use FormOptions!
$defaults = array(
- /* float */ 'days' => floatval( $user->getOption( 'watchlistdays' ) ), /* 3.0 or 0.5, watch further below */
+ /* float */ 'days' => floatval( $user->getOption( 'watchlistdays' ) ),
/* bool */ 'hideMinor' => (int)$user->getBoolOption( 'watchlisthideminor' ),
/* bool */ 'hideBots' => (int)$user->getBoolOption( 'watchlisthidebots' ),
/* bool */ 'hideAnons' => (int)$user->getBoolOption( 'watchlisthideanons' ),
# Extract variables from the request, falling back to user preferences or
# other default values if these don't exist
$values = array();
- $values['days'] = $request->getVal( 'days', $defaults['days'] );
+ $values['days'] = floatval( $request->getVal( 'days', $defaults['days'] ) );
$values['hideMinor'] = (int)$request->getBool( 'hideMinor', $defaults['hideMinor'] );
$values['hideBots'] = (int)$request->getBool( 'hideBots', $defaults['hideBots'] );
$values['hideAnons'] = (int)$request->getBool( 'hideAnons', $defaults['hideAnons'] );
$values['invert'] = $invert;
$values['associated'] = $associated;
- if ( is_null( $values['days'] ) || !is_numeric( $values['days'] ) ) {
- $big = 1000; /* The magical big */
- if ( $nitems > $big ) {
- # Set default cutoff shorter
- $values['days'] = $defaults['days'] = ( 12.0 / 24.0 ); # 12 hours...
- } else {
- $values['days'] = $defaults['days']; # default cutoff for shortlisters
- }
- } else {
- $values['days'] = floatval( $values['days'] );
- }
-
// Dump everything here
$nondefaults = array();
foreach ( $defaults as $name => $defValue ) {
$conds[] = 'rc_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( time() - intval( $values['days'] * 86400 ) ) );
}
- # If the watchlist is relatively short, it's simplest to zip
- # down its entirety and then sort the results.
-
- # If it's relatively long, it may be worth our while to zip
- # through the time-sorted page list checking for watched items.
-
- # Up estimate of watched items by 15% to compensate for talk pages...
-
# Toggles
if ( $values['hideOwn'] ) {
$conds[] = 'rc_user != ' . $user->getId();
* @ingroup Templates
*/
-if ( !defined( 'MEDIAWIKI' ) ) {
- die( -1 );
-}
-
class UsercreateTemplate extends BaseTemplate {
/**
$expirationDays = ceil( $wgCookieExpiration / ( 3600 * 24 ) );
?>
<div class="mw-ui-container">
- <?php
- if ( $this->haveData( 'languages' ) ) {
- ?>
+ <?php if ( $this->haveData( 'languages' ) ) { ?>
<div id="languagelinks">
<p><?php $this->html( 'languages' ); ?></p>
</div>
- <?php
- }
- ?>
-<div id="userloginForm">
-<h2 class="createaccount-join">
- <?php
- $this->msg( $this->data['loggedin'] ?
- 'createacct-another-join' : 'createacct-join' );
- ?>
-</h2>
-<form name="userlogin2" id="userlogin2" class="mw-ui-vform" method="post" action="<?php $this->text( 'action' ); ?>">
- <section class="mw-form-header">
- <?php $this->html( 'header' ); /* extensions such as ConfirmEdit add form HTML here */ ?>
- </section>
- <?php
- if ( $this->data['message'] ) {
-?>
- <div class="<?php $this->text( 'messagetype' ); ?>box">
- <?php if ( $this->data['messagetype'] == 'error' ) { ?>
- <strong><?php $this->msg( 'createacct-error' ); ?></strong><br />
- <?php } ?>
- <?php $this->html( 'message' ); ?>
- </div>
<?php } ?>
- <div>
- <label for='wpName2'>
- <?php $this->msg( 'userlogin-yourname' ); ?>
+ <div id="userloginForm">
+ <h2 class="createaccount-join">
+ <?php $this->msg( $this->data['loggedin'] ? 'createacct-another-join' : 'createacct-join' ); ?>
+ </h2>
+ <form name="userlogin2" id="userlogin2" class="mw-ui-vform" method="post" action="<?php $this->text( 'action' ); ?>">
+ <section class="mw-form-header">
+ <?php $this->html( 'header' ); /* extensions such as ConfirmEdit add form HTML here */ ?>
+ </section>
+ <?php if ( $this->data['message'] ) { ?>
+ <div class="<?php $this->text( 'messagetype' ); ?>box">
+ <?php if ( $this->data['messagetype'] == 'error' ) { ?>
+ <strong><?php $this->msg( 'createacct-error' ); ?></strong>
+ <br />
+ <?php } ?>
+ <?php $this->html( 'message' ); ?>
+ </div>
+ <?php } ?>
- <span class="mw-ui-flush-right"><?php echo $this->getMsg( 'createacct-helpusername' )->parse(); ?></span>
- </label>
- <?php
- echo Html::input( 'wpName', $this->data['name'], 'text', array(
- 'class' => 'mw-input loginText',
- 'id' => 'wpName2',
- 'tabindex' => '1',
- 'size' => '20',
- 'required',
- 'placeholder' => $this->getMsg( $this->data['loggedin'] ?
- 'createacct-another-username-ph' : 'userlogin-yourname-ph' )->text(),
- ) );
- ?>
- </div>
- <div>
- <?php if ( $this->data['createemail'] ) { ?>
- <label class="mw-ui-checkbox-label">
- <input name="wpCreateaccountMail" type="checkbox" value="1" id="wpCreateaccountMail" tabindex="2"
- <?php if ( $this->data['createemailset'] ) {
- echo 'checked="checked"';
- } ?>
- >
- <?php $this->msg( 'createaccountmail' ); ?>
- </label>
- <?php } ?>
- </div>
- <div class="mw-row-password">
- <label for='wpPassword2'><?php $this->msg( 'userlogin-yourpassword' ); ?></label>
- <?php echo Html::input( 'wpPassword', null, 'password', array(
- 'class' => 'mw-input loginPassword',
- 'id' => 'wpPassword2',
- 'tabindex' => '3',
- 'size' => '20',
- 'required',
- 'placeholder' => $this->getMsg( 'createacct-yourpassword-ph' )->text()
- ) + User::passwordChangeInputAttribs() ); ?>
- </div>
- <?php if ( $this->data['usedomain'] ) {
- $doms = "";
- foreach ( $this->data['domainnames'] as $dom ) {
- $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
- }
- ?>
- <div id="mw-user-domain-section">
- <label for="wpDomain"><?php $this->msg( 'yourdomainname' ); ?></label>
- <div class="mw-input">
- <select name="wpDomain" value="<?php $this->text( 'domain' ); ?>"
- tabindex="4">
- <?php echo $doms ?>
- </select>
- </div>
- </div>
- <?php } ?>
- <div class="mw-row-password">
- <label for='wpRetype'><?php $this->msg( 'createacct-yourpasswordagain' ); ?></label>
- <?php
- echo Html::input( 'wpRetype', null, 'password', array(
- 'class' => 'mw-input loginPassword',
- 'id' => 'wpRetype',
- 'tabindex' => '5',
- 'size' => '20',
- 'required',
- 'placeholder' => $this->getMsg( 'createacct-yourpasswordagain-ph' )->text()
- ) + User::passwordChangeInputAttribs() );
- ?>
- </div>
- <div>
- <?php if ( $this->data['useemail'] ) { ?>
- <label for='wpEmail'>
+ <div>
+ <label for='wpName2'>
+ <?php $this->msg( 'userlogin-yourname' ); ?>
+
+ <span class="mw-ui-flush-right"><?php echo $this->getMsg( 'createacct-helpusername' )->parse(); ?></span>
+ </label>
<?php
- $this->msg( $this->data['emailrequired'] ?
- 'createacct-emailrequired' :
- 'createacct-emailoptional'
- );
- ?>
- </label>
- <?php
- echo Html::input( 'wpEmail', $this->data['email'], 'email', array(
+ echo Html::input( 'wpName', $this->data['name'], 'text', array(
'class' => 'mw-input loginText',
- 'id' => 'wpEmail',
- 'tabindex' => '6',
+ 'id' => 'wpName2',
+ 'tabindex' => '1',
'size' => '20',
+ 'required',
'placeholder' => $this->getMsg( $this->data['loggedin'] ?
- 'createacct-another-email-ph' : 'createacct-email-ph' )->text()
- ) + ( $this->data['emailrequired'] ? array() : array( 'required' => '' ) ) );
- ?>
- <?php } ?>
- </div>
- <?php if ( $this->data['userealname'] ) { ?>
- <div>
- <label for='wpRealName'><?php $this->msg( 'createacct-realname' ); ?></label>
- <input type='text' class='mw-input loginText' name="wpRealName" id="wpRealName"
- tabindex="7"
- value="<?php $this->text( 'realname' ); ?>" size='20' />
- <div class="prefsectiontip">
- <?php $this->msgWiki( $this->data['loggedin'] ? 'createacct-another-realname-tip' : 'prefs-help-realname' ); ?>
- </div>
- </div>
- <?php }
- if ( $this->data['usereason'] ) { ?>
- <div>
- <label for='wpReason'><?php $this->msg( 'createacct-reason' ); ?></label>
- <?php echo Html::input( 'wpReason', $this->data['reason'], 'text', array(
- 'class' => 'mw-input loginText',
- 'id' => 'wpReason',
- 'tabindex' => '8',
- 'size' => '20',
- 'placeholder' => $this->getMsg( 'createacct-reason-ph' )->text()
- ) ); ?>
+ 'createacct-another-username-ph' : 'userlogin-yourname-ph' )->text(),
+ ) );
+ ?>
</div>
- <?php }
- $tabIndex = 9;
- if ( isset( $this->data['extraInput'] ) && is_array( $this->data['extraInput'] ) ) {
- foreach ( $this->data['extraInput'] as $inputItem ) { ?>
+
<div>
- <?php
- // If it's a checkbox, output the whole thing (assume it has a msg).
- if ( $inputItem['type'] == 'checkbox' ) {
- ?>
+ <?php if ( $this->data['createemail'] ) { ?>
<label class="mw-ui-checkbox-label">
- <input
- name="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
- id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
- type="checkbox" value="1"
- tabindex="<?php echo $tabIndex++; ?>"
- <?php if ( !empty( $inputItem['value'] ) ) {
+ <input name="wpCreateaccountMail" type="checkbox" value="1" id="wpCreateaccountMail" tabindex="2"
+ <?php if ( $this->data['createemailset'] ) {
echo 'checked="checked"';
} ?>
>
- <?php $this->msg( $inputItem['msg'] ); ?>
+ <?php $this->msg( 'createaccountmail' ); ?>
</label>
+ <?php } ?>
+ </div>
+
+ <div class="mw-row-password">
+ <label for='wpPassword2'><?php $this->msg( 'userlogin-yourpassword' ); ?></label>
<?php
- } else {
- // Not a checkbox.
- // TODO (bug 31909) support other input types, e.g. select boxes.
- if ( !empty( $inputItem['msg'] ) ) {
- // Output the message label
- ?>
- <label for="<?php echo htmlspecialchars( $inputItem['name'] ); ?>">
- <?php $this->msgWiki( $inputItem['msg'] ); ?>
- </label>
- <?php
- }
- ?>
- <input
- type="<?php echo htmlspecialchars( $inputItem['type'] ); ?>"
- class="mw-input"
- name="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
- tabindex="<?php echo $tabIndex++; ?>"
- value="<?php echo htmlspecialchars( $inputItem['value'] ); ?>"
- id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
- />
- <?php
- }
- if ( $inputItem['helptext'] !== false ) {
+ echo Html::input( 'wpPassword', null, 'password', array(
+ 'class' => 'mw-input loginPassword',
+ 'id' => 'wpPassword2',
+ 'tabindex' => '3',
+ 'size' => '20',
+ 'required',
+ 'placeholder' => $this->getMsg( 'createacct-yourpassword-ph' )->text()
+ ) + User::passwordChangeInputAttribs() );
?>
- <div class="prefsectiontip">
- <?php $this->msgWiki( $inputItem['helptext'] ); ?>
+ </div>
+
+ <?php
+ if ( $this->data['usedomain'] ) {
+ $doms = "";
+ foreach ( $this->data['domainnames'] as $dom ) {
+ $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
+ }
+ ?>
+ <div id="mw-user-domain-section">
+ <label for="wpDomain"><?php $this->msg( 'yourdomainname' ); ?></label>
+ <div class="mw-input">
+ <select name="wpDomain" value="<?php $this->text( 'domain' ); ?>" tabindex="4">
+ <?php echo $doms ?>
+ </select>
</div>
+ </div>
+ <?php } ?>
+
+ <div class="mw-row-password">
+ <label for='wpRetype'><?php $this->msg( 'createacct-yourpasswordagain' ); ?></label>
<?php
- }
+ echo Html::input( 'wpRetype', null, 'password', array(
+ 'class' => 'mw-input loginPassword',
+ 'id' => 'wpRetype',
+ 'tabindex' => '5',
+ 'size' => '20',
+ 'required',
+ 'placeholder' => $this->getMsg( 'createacct-yourpasswordagain-ph' )->text()
+ ) + User::passwordChangeInputAttribs() );
?>
+ </div>
+
+ <div>
+ <?php if ( $this->data['useemail'] ) { ?>
+ <label for='wpEmail'>
+ <?php
+ $this->msg( $this->data['emailrequired'] ?
+ 'createacct-emailrequired' :
+ 'createacct-emailoptional'
+ );
+ ?>
+ </label>
+ <?php
+ echo Html::input( 'wpEmail', $this->data['email'], 'email', array(
+ 'class' => 'mw-input loginText',
+ 'id' => 'wpEmail',
+ 'tabindex' => '6',
+ 'size' => '20',
+ 'placeholder' => $this->getMsg( $this->data['loggedin'] ?
+ 'createacct-another-email-ph' : 'createacct-email-ph' )->text()
+ ) + ( $this->data['emailrequired'] ? array() : array( 'required' => '' ) ) );
+ ?>
+ <?php } ?>
+ </div>
+
+ <?php if ( $this->data['userealname'] ) { ?>
+ <div>
+ <label for='wpRealName'><?php $this->msg( 'createacct-realname' ); ?></label>
+ <input type='text' class='mw-input loginText' name="wpRealName" id="wpRealName"
+ tabindex="7"
+ value="<?php $this->text( 'realname' ); ?>" size='20' />
+ <div class="prefsectiontip">
+ <?php $this->msgWiki( $this->data['loggedin'] ? 'createacct-another-realname-tip' : 'prefs-help-realname' ); ?>
+ </div>
</div>
+ <?php } ?>
+
+ <?php if ( $this->data['usereason'] ) { ?>
+ <div>
+ <label for='wpReason'><?php $this->msg( 'createacct-reason' ); ?></label>
+ <?php echo Html::input( 'wpReason', $this->data['reason'], 'text', array(
+ 'class' => 'mw-input loginText',
+ 'id' => 'wpReason',
+ 'tabindex' => '8',
+ 'size' => '20',
+ 'placeholder' => $this->getMsg( 'createacct-reason-ph' )->text()
+ ) ); ?>
+ </div>
+ <?php } ?>
+
<?php
+ $tabIndex = 9;
+ if ( isset( $this->data['extraInput'] ) && is_array( $this->data['extraInput'] ) ) {
+ foreach ( $this->data['extraInput'] as $inputItem ) { ?>
+ <div>
+ <?php
+ // If it's a checkbox, output the whole thing (assume it has a msg).
+ if ( $inputItem['type'] == 'checkbox' ) {
+ ?>
+ <label class="mw-ui-checkbox-label">
+ <input
+ name="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
+ id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
+ type="checkbox" value="1"
+ tabindex="<?php echo $tabIndex++; ?>"
+ <?php if ( !empty( $inputItem['value'] ) ) {
+ echo 'checked="checked"';
+ } ?>
+ >
+ <?php $this->msg( $inputItem['msg'] ); ?>
+ </label>
+ <?php
+ } else {
+ // Not a checkbox.
+ // TODO (bug 31909) support other input types, e.g. select boxes.
+ ?>
+ <?php if ( !empty( $inputItem['msg'] ) ) { ?>
+ <label for="<?php echo htmlspecialchars( $inputItem['name'] ); ?>">
+ <?php $this->msgWiki( $inputItem['msg'] ); ?>
+ </label>
+ <?php } ?>
+ <input
+ type="<?php echo htmlspecialchars( $inputItem['type'] ); ?>"
+ class="mw-input"
+ name="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
+ tabindex="<?php echo $tabIndex++; ?>"
+ value="<?php echo htmlspecialchars( $inputItem['value'] ); ?>"
+ id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
+ />
+ <?php } ?>
+ <?php if ( $inputItem['helptext'] !== false ) { ?>
+ <div class="prefsectiontip">
+ <?php $this->msgWiki( $inputItem['helptext'] ); ?>
+ </div>
+ <?php } ?>
+ </div>
+ <?php
+ }
}
- }
- // JS attempts to move the image CAPTCHA below this part of the form,
- // so skip one index.
- $tabIndex++;
- ?>
- <div class="mw-submit">
+
+ // JS attempts to move the image CAPTCHA below this part of the form,
+ // so skip one index.
+ $tabIndex++;
+ ?>
+ <div class="mw-submit">
+ <?php
+ echo Html::input(
+ 'wpCreateaccount',
+ $this->getMsg( $this->data['loggedin'] ? 'createacct-another-submit' : 'createacct-submit' ),
+ 'submit',
+ array(
+ 'class' => "mw-ui-button mw-ui-big mw-ui-block mw-ui-primary",
+ 'id' => 'wpCreateaccount',
+ 'tabindex' => $tabIndex++
+ )
+ );
+ ?>
+ </div>
+ <?php if ( $this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
+ <?php if ( $this->haveData( 'token' ) ) { ?><input type="hidden" name="wpCreateaccountToken" value="<?php $this->text( 'token' ); ?>" /><?php } ?>
+ </form>
+ </div>
+ <div class="mw-createacct-benefits-container">
+ <h2><?php $this->msg( 'createacct-benefit-heading' ); ?></h2>
+ <div class="mw-createacct-benefits-list">
<?php
- echo Html::input( 'wpCreateaccount',
- $this->getMsg( $this->data['loggedin'] ?
- 'createacct-another-submit' : 'createacct-submit' ),
- 'submit',
- array(
- 'class' => "mw-ui-button mw-ui-big mw-ui-block mw-ui-primary",
- 'id' => 'wpCreateaccount',
- 'tabindex' => $tabIndex++
- ) );
+ for ( $benefitIdx = 1; $benefitIdx <= $this->data['benefitCount']; $benefitIdx++ ) {
+ // Pass each benefit's head text (by default a number) as a parameter to the body's message for PLURAL handling.
+ $headUnescaped = $this->getMsg( "createacct-benefit-head$benefitIdx" )->text();
?>
+ <div class="mw-number-text <?php $this->msg( "createacct-benefit-icon$benefitIdx" ); ?>">
+ <h3><?php $this->msg( "createacct-benefit-head$benefitIdx" ); ?></h3>
+ <p><?php echo $this->getMsg( "createacct-benefit-body$benefitIdx" )->params( $headUnescaped )->escaped(); ?></p>
+ </div>
+ <?php } ?>
</div>
-<?php if ( $this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
-<?php if ( $this->haveData( 'token' ) ) { ?><input type="hidden" name="wpCreateaccountToken" value="<?php $this->text( 'token' ); ?>" /><?php } ?>
-</form>
-</div>
-<div class="mw-createacct-benefits-container">
- <h2><?php $this->msg( 'createacct-benefit-heading' ); ?></h2>
- <div class="mw-createacct-benefits-list">
- <?php
- for ( $benefitIdx = 1; $benefitIdx <= $this->data['benefitCount']; $benefitIdx++ ) {
- // Pass each benefit's head text (by default a number) as a parameter to the body's message for PLURAL handling.
- $headUnescaped = $this->getMsg( "createacct-benefit-head$benefitIdx" )->text();
- ?>
- <div class="mw-number-text <?php $this->msg( "createacct-benefit-icon$benefitIdx" ); ?>">
- <h3><?php $this->msg( "createacct-benefit-head$benefitIdx" ); ?></h3>
- <p><?php echo $this->getMsg( "createacct-benefit-body$benefitIdx" )->params( $headUnescaped )->escaped(); ?></p>
- </div>
- <?php
- }
- ?>
</div>
</div>
-</div>
<?php
}
$expirationDays = ceil( $wgCookieExpiration / ( 3600 * 24 ) );
?>
<div class="mw-ui-container">
- <?php
- if ( $this->haveData( 'languages' ) ) {
- ?>
+ <?php if ( $this->haveData( 'languages' ) ) { ?>
<div id="languagelinks">
<p><?php $this->html( 'languages' ); ?></p>
</div>
- <?php
- }
- ?>
-<div id="userloginForm">
-<form name="userlogin" class="mw-ui-vform" method="post" action="<?php $this->text( 'action' ); ?>">
- <?php if ( $this->data['loggedin'] ) { ?>
- <div class="warningbox">
- <?php echo $this->getMsg( 'userlogin-loggedin' )->params( $this->data['loggedinuser'] )->parse(); ?>
- </div>
<?php } ?>
- <section class="mw-form-header">
- <?php $this->html( 'header' ); /* extensions such as ConfirmEdit add form HTML here */ ?>
- </section>
- <?php
+ <div id="userloginForm">
+ <form name="userlogin" class="mw-ui-vform" method="post" action="<?php $this->text( 'action' ); ?>">
+ <?php if ( $this->data['loggedin'] ) { ?>
+ <div class="warningbox">
+ <?php echo $this->getMsg( 'userlogin-loggedin' )->params( $this->data['loggedinuser'] )->parse(); ?>
+ </div>
+ <?php } ?>
+ <section class="mw-form-header">
+ <?php $this->html( 'header' ); /* extensions such as ConfirmEdit add form HTML here */ ?>
+ </section>
- if ( $this->data['message'] ) {
- ?>
- <div class="<?php $this->text( 'messagetype' ); ?>box">
- <?php
- if ( $this->data['messagetype'] == 'error' ) {
- ?>
- <strong><?php $this->msg( 'loginerror' ) ?></strong><br />
- <?php
- }
- $this->html( 'message' );
- ?>
- </div>
- <?php
- }
- ?>
- <div>
- <label for='wpName1'>
- <?php
- $this->msg( 'userlogin-yourname' );
- if ( $this->data['secureLoginUrl'] ) {
- echo Html::element( 'a', array(
+ <?php if ( $this->data['message'] ) { ?>
+ <div class="<?php $this->text( 'messagetype' ); ?>box">
+ <?php if ( $this->data['messagetype'] == 'error' ) { ?>
+ <strong><?php $this->msg( 'loginerror' ); ?></strong>
+ <br />
+ <?php } ?>
+ <?php $this->html( 'message' ); ?>
+ </div>
+ <?php } ?>
+
+ <div>
+ <label for='wpName1'>
+ <?php
+ $this->msg( 'userlogin-yourname' );
+
+ if ( $this->data['secureLoginUrl'] ) {
+ echo Html::element( 'a', array(
'href' => $this->data['secureLoginUrl'],
'class' => 'mw-ui-flush-right mw-secure',
), $this->getMsg( 'userlogin-signwithsecure' )->text() );
- } ?>
- </label>
+ }
+ ?>
+ </label>
+ <?php
+ $extraAttrs = array();
+ // Set focus to this field if it's blank.
+ if ( !$this->data['name'] ) {
+ $extraAttrs['autofocus'] = '';
+ }
+ echo Html::input( 'wpName', $this->data['name'], 'text', array(
+ 'class' => 'loginText',
+ 'id' => 'wpName1',
+ 'tabindex' => '1',
+ 'size' => '20',
+ // 'required' is blacklisted for now in Html.php due to browser issues.
+ // Keeping here in case that changes.
+ 'required',
+ 'placeholder' => $this->getMsg( 'userlogin-yourname-ph' )->text()
+ ) + $extraAttrs );
+ ?>
+ </div>
+
+ <div>
+ <label for='wpPassword1'>
+ <?php
+ $this->msg( 'userlogin-yourpassword' );
+
+ if ( $this->data['useemail'] && $this->data['canreset'] && $this->data['resetlink'] === true ) {
+ echo ' ' . Linker::link(
+ SpecialPage::getTitleFor( 'PasswordReset' ),
+ $this->getMsg( 'userlogin-resetpassword-link' )->parse(),
+ array( 'class' => 'mw-ui-flush-right' )
+ );
+ }
+ ?>
+ </label>
+ <?php
+ $extraAttrs = array();
+ // Set focus to this field if username is filled in.
+ if ( $this->data['name'] ) {
+ $extraAttrs['autofocus'] = '';
+ }
+ echo Html::input( 'wpPassword', null, 'password', array(
+ 'class' => 'loginPassword',
+ 'id' => 'wpPassword1',
+ 'tabindex' => '2',
+ 'size' => '20',
+ 'placeholder' => $this->getMsg( 'userlogin-yourpassword-ph' )->text()
+ ) + $extraAttrs );
+ ?>
+ </div>
+
<?php
- $extraAttrs = array();
- // Set focus to this field if its blank.
- if ( !$this->data['name'] ) {
- $extraAttrs['autofocus'] = '';
- }
- echo Html::input( 'wpName', $this->data['name'], 'text', array(
- 'class' => 'loginText',
- 'id' => 'wpName1',
- 'tabindex' => '1',
- 'size' => '20',
- // 'required' is blacklisted for now in Html.php due to browser issues.
- // Keeping here in case that changes
- 'required',
- 'placeholder' => $this->getMsg( 'userlogin-yourname-ph' )->text()
- ) + $extraAttrs );
+ if ( isset( $this->data['usedomain'] ) && $this->data['usedomain'] ) {
+ $doms = "";
+ foreach ( $this->data['domainnames'] as $dom ) {
+ $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
+ }
?>
- </div>
- <div>
- <label for='wpPassword1'>
- <?php
- $this->msg( 'userlogin-yourpassword' );
+ <div id="mw-user-domain-section">
+ <label for='wpDomain'><?php $this->msg( 'yourdomainname' ); ?></label>
+ <select name="wpDomain" value="<?php $this->text( 'domain' ); ?>" tabindex="3">
+ <?php echo $doms; ?>
+ </select>
+ </div>
+ <?php } ?>
- if ( $this->data['useemail'] && $this->data['canreset'] && $this->data['resetlink'] === true ) {
- echo ' ' . Linker::link(
- SpecialPage::getTitleFor( 'PasswordReset' ),
- $this->getMsg( 'userlogin-resetpassword-link' )->parse(),
- array( 'class' => 'mw-ui-flush-right' )
- );
- }
- ?>
- </label>
<?php
- $extraAttrs = array();
- // Set focus to this field if username is filled in.
- if ( $this->data['name'] ) {
- $extraAttrs['autofocus'] = '';
+ if ( $this->haveData( 'extrafields' ) ) {
+ echo $this->data['extrafields'];
}
- echo Html::input( 'wpPassword', null, 'password', array(
- 'class' => 'loginPassword',
- 'id' => 'wpPassword1',
- 'tabindex' => '2',
- 'size' => '20',
- 'placeholder' => $this->getMsg( 'userlogin-yourpassword-ph' )->text()
- ) + $extraAttrs );
?>
- </div>
- <?php
- if ( isset( $this->data['usedomain'] ) && $this->data['usedomain'] ) {
- $doms = "";
- foreach ( $this->data['domainnames'] as $dom ) {
- $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
- }
- ?>
- <div id="mw-user-domain-section">
- <label for='wpDomain'><?php $this->msg( 'yourdomainname' ); ?></label>
- <select name="wpDomain" value="<?php $this->text( 'domain' ); ?>"
- tabindex="3">
- <?php echo $doms ?>
- </select>
- </div>
- <?php }
-
- if ( $this->haveData( 'extrafields' ) ) {
- echo $this->data['extrafields'];
- } ?>
- <div>
+ <div>
+ <?php if ( $this->data['canremember'] ) { ?>
+ <label class="mw-ui-checkbox-label">
+ <input name="wpRemember" type="checkbox" value="1" id="wpRemember" tabindex="4"
+ <?php if ( $this->data['remember'] ) {
+ echo 'checked="checked"';
+ } ?>
+ >
+ <?php echo $this->getMsg( 'userlogin-remembermypassword' )->numParams( $expirationDays )->escaped(); ?>
+ </label>
+ <?php } ?>
+ </div>
- <?php if ( $this->data['canremember'] ) { ?>
- <label class="mw-ui-checkbox-label">
- <input name="wpRemember" type="checkbox" value="1" id="wpRemember" tabindex="4"
- <?php if ( $this->data['remember'] ) {
- echo 'checked="checked"';
- } ?>
- >
- <?php echo $this->getMsg( 'userlogin-remembermypassword' )->numParams( $expirationDays )->escaped(); ?>
- </label>
- <?php } ?>
- </div>
+ <div>
+ <?php
+ echo Html::input( 'wpLoginAttempt', $this->getMsg( 'login' )->text(), 'submit', array(
+ 'id' => 'wpLoginAttempt',
+ 'tabindex' => '6',
+ 'class' => 'mw-ui-button mw-ui-big mw-ui-block mw-ui-primary'
+ ) );
+ ?>
+ </div>
- <div>
- <?php
- echo Html::input( 'wpLoginAttempt', $this->getMsg( 'login' )->text(), 'submit', array(
- 'id' => 'wpLoginAttempt',
- 'tabindex' => '6',
- 'class' => 'mw-ui-button mw-ui-big mw-ui-block mw-ui-primary'
- ) );
- ?>
- </div>
- <div id="mw-userlogin-help">
- <?php echo $this->getMsg( 'userlogin-helplink' )->parse(); ?>
- </div>
- <?php if ( $this->haveData( 'createOrLoginHref' ) ) { ?>
- <?php if ( $this->data['loggedin'] ) { ?>
- <div id="mw-createaccount-another">
- <h3 id="mw-userloginlink"><a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7" class="mw-ui-button"><?php $this->msg( 'userlogin-createanother' ); ?></a></h3>
- </div>
- <?php } else { ?>
- <div id="mw-createaccount-cta">
- <h3 id="mw-userloginlink"><?php $this->msg( 'userlogin-noaccount' ); ?><a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7" class="mw-ui-button mw-ui-constructive"><?php $this->msg( 'userlogin-joinproject' ); ?></a></h3>
- </div>
+ <div id="mw-userlogin-help">
+ <?php echo $this->getMsg( 'userlogin-helplink' )->parse(); ?>
+ </div>
+ <?php if ( $this->haveData( 'createOrLoginHref' ) ) { ?>
+ <?php if ( $this->data['loggedin'] ) { ?>
+ <div id="mw-createaccount-another">
+ <h3 id="mw-userloginlink"><a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7" class="mw-ui-button"><?php $this->msg( 'userlogin-createanother' ); ?></a></h3>
+ </div>
+ <?php } else { ?>
+ <div id="mw-createaccount-cta">
+ <h3 id="mw-userloginlink"><?php $this->msg( 'userlogin-noaccount' ); ?><a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7" class="mw-ui-button mw-ui-constructive"><?php $this->msg( 'userlogin-joinproject' ); ?></a></h3>
+ </div>
+ <?php } ?>
<?php } ?>
- <?php } ?>
-<?php if ( $this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
-<?php if ( $this->haveData( 'token' ) ) { ?><input type="hidden" name="wpLoginToken" value="<?php $this->text( 'token' ); ?>" /><?php } ?>
-<?php if ( $this->data['cansecurelogin'] ) {?><input type="hidden" name="wpForceHttps" value="<?php $this->text( 'stickhttps' ); ?>" /><?php } ?>
-</form>
-</div>
+ <?php if ( $this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
+ <?php if ( $this->haveData( 'token' ) ) { ?><input type="hidden" name="wpLoginToken" value="<?php $this->text( 'token' ); ?>" /><?php } ?>
+ <?php if ( $this->data['cansecurelogin'] ) {?><input type="hidden" name="wpForceHttps" value="<?php $this->text( 'stickhttps' ); ?>" /><?php } ?>
+ </form>
+ </div>
</div>
<?php
+
}
}
* @author عصام بايزيدي
* @author عمرو
* @author محمد الجداوي
+ * @author مشعل الحربي
* @author نصوح
* @author وهراني
*/
'userlogin-resetpassword-link' => 'صفّر كلمة سرّك',
'helplogin-url' => 'Help:تسجيل الدخول',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|المساعدة في تسجيل الدخول]]',
+'userlogin-loggedin' => 'أنت {{GENDER:$1|مسجل|مسجلة}} الدخول مسبقًا باسم $1. {{GENDER:$1|استخدم|استخدمي}} النموذج بالأسفل لتسجيل الدخول بحساب آخر.',
+'userlogin-createanother' => 'إنشاء حساب آخر',
'createacct-join' => 'قم بإدخال المعلومات الخاصة بك أدناه.',
'createacct-another-join' => 'أدخل معلومات الحساب الجديد أدناه.',
'createacct-emailrequired' => 'عنوان البريد الإلكتروني',
'email-address-validity-invalid' => 'أدخل عنوان بريد إلكتروني صالح',
# User rights
-'userrights' => 'إدارة صÙ\84اØÙ\8aات اÙ\84Ù\85ستخدÙ\85',
+'userrights' => 'صلاحيات المستخدم',
'userrights-lookup-user' => 'أدِر مجموعات المستخدم',
'userrights-user-editname' => 'أدخل اسم مستخدم:',
'editusergroup' => 'عدل مجموعات المستخدم',
'sp-contributions-uploads' => 'مرفوعات',
'sp-contributions-logs' => 'سجلات',
'sp-contributions-talk' => 'نقاش',
-'sp-contributions-userrights' => 'إدارة صÙ\84اØÙ\8aات اÙ\84Ù\85ستخدÙ\85',
+'sp-contributions-userrights' => 'صلاحيات المستخدم',
'sp-contributions-blocked-notice' => 'هذا المستخدم ممنوع حاليا.
إن آخر مدخلة في سجل المنع موجودة أدناه كمرجع:',
'sp-contributions-blocked-notice-anon' => 'عنوان الأيبي هذا ممنوع حاليا.
'tags-tag' => 'اسم الوسم',
'tags-display-header' => 'الظهور في قوائم التغييرات',
'tags-description-header' => 'وصف كامل للمعنى',
+'tags-active-header' => 'نشط؟',
'tags-hitcount-header' => 'تغييرات موسومة',
+'tags-active-yes' => 'نعم',
+'tags-active-no' => 'لا',
'tags-edit' => 'عدل',
'tags-hitcount' => '{{PLURAL:$1|لا تغييرات|تغيير واحد|تغييران|$1 تغييرات|$1 تغييرا|$1 تغيير}}',
'userlogin-resetpassword-link' => 'Reaniciar la contraseña',
'helplogin-url' => 'Help:Aniciar sesión',
'userlogin-helplink' => "[[{{MediaWiki:helplogin-url}}|Ayuda p'aniciar sesión]]",
+'userlogin-loggedin' => "Yá anició sesión como {{GENDER:$1|$1}}.
+Utilice'l formulariu de más abaxo p'aniciar sesión como otru usuariu.",
+'userlogin-createanother' => 'Crear otra cuenta',
'createacct-join' => 'Escriba abaxo la so información.',
'createacct-another-join' => 'Escriba abaxo la información de la cuenta nueva.',
'createacct-emailrequired' => 'Direición de corréu electrónicu',
'deletecomment' => 'Motivu:',
'deleteotherreason' => 'Motivu distintu/adicional:',
'deletereasonotherlist' => 'Otru motivu',
-'deletereason-dropdown' => "*Motivos comunes d'esborráu
+'deletereason-dropdown' => "*Motivos comúnes d'esborráu
+** Puxarra
+** Vandalismu
+** Violación de drechos d'autor
** A pidimientu del autor
-** Violación de Copyright
-** Vandalismu",
+** Redireición frañada",
'delete-edit-reasonlist' => "Editar los motivos d'esborráu",
'delete-toobig' => "Esta páxina tien un historial d'ediciones grande, más de $1 {{PLURAL:$1|revisión|revisiones}}.
Restrinxóse l'esborráu d'estes páxines pa evitar perturbaciones accidentales de {{SITENAME}}.",
'contributions' => 'Collaboraciones {{GENDER:$1|del usuariu|de la usuaria}}',
'contributions-title' => "Contribuciones d'usuariu pa $1",
'mycontris' => 'Collaboraciones',
-'contribsub2' => 'Pa $1 ($2)',
+'contribsub2' => 'Pa {{GENDER:$3|$1}} ($2)',
'nocontribs' => "Nun s'atoparon cambeos que coincidan con esi criteriu.",
'uctop' => '(actual)',
'month' => "Dende'l mes (y anteriores):",
# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
'aboutsite' => 'За {{SITENAME}}',
'aboutpage' => 'Project:За {{SITENAME}}',
-'copyright' => 'Съдържанието е достъпно при условията на $1.',
+'copyright' => 'Ð\9eÑ\81вен ако не е поÑ\81оÑ\87ено дÑ\80Ñ\83го, Ñ\81ъдържанието е достъпно при условията на $1.',
'copyrightpage' => '{{ns:project}}:Авторски права',
'currentevents' => 'Текущи събития',
'currentevents-url' => 'Project:Текущи събития',
# General errors
'error' => 'Грешка',
'databaseerror' => 'Грешка при работа с базата от данни',
+'databaseerror-text' => 'Възникна грешка при заявката за базата данни.
+Това може да означава бъг в софтуера.',
'databaseerror-query' => 'Заявка: $1',
'databaseerror-function' => 'Функция: $1',
'databaseerror-error' => 'Грешка: $1',
'badarticleerror' => 'Действието не може да се изпълни върху страницата.',
'cannotdelete' => 'Указаната страница или файл "$1" не можа да бъде изтрит(а). Възможно е вече да е бил(а) изтрит(а) от някой друг.',
'cannotdelete-title' => 'Страницата „$1“ не може да бъде изтрита',
+'no-null-revision' => 'Не може да бъде създадена празна версия на страницата „$1“',
'badtitle' => 'Невалидно заглавие',
'badtitletext' => 'Желаното заглавие на страница е невалидно, празно или неправилна препратка към друго уики. Възможно е да съдържа знаци, които не са позволени в заглавия.',
'perfcached' => 'Следните данни са извлечени от склада и затова може да не отговарят на текущото състояние. В складираното копие {{PLURAL:$1|е допустим най-много един резултат|са допустими най-много $1 резултата}}.',
'customjsprotected' => 'Нямате права за редактиране на тази Джаваскрипт страница, защото тя съдържа чужди потребителски настройки.',
'mycustomcssprotected' => 'Нямате права за редактиране на тази CSS страница.',
'mycustomjsprotected' => 'Нямате права за редактиране на тази JavaScript страница.',
+'mypreferencesprotected' => 'Нямате права да редактирате настройките си.',
'ns-specialprotected' => 'Специалните страници не могат да бъдат редактирани.',
'titleprotected' => "Тази страница е била защитена срещу създаване от [[User:$1|$1]].
Посочената причина е ''$2''.",
'userlogin-resetpassword-link' => 'Възстановяване на паролата',
'helplogin-url' => 'Help:Влизане',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Помощ за влизане]] в системата',
+'userlogin-createanother' => 'Създаване на друга сметка',
'createacct-join' => 'Въведете своите данни по-долу.',
'createacct-emailrequired' => 'Адрес за електронна поща',
'createacct-emailoptional' => 'Адрес за електронна поща (незадължително)',
'createacct-reason' => 'Причина',
'createacct-imgcaptcha-ph' => 'Въведете текста, който виждате по-горе',
'createacct-submit' => 'Създаване на сметката',
+'createacct-another-submit' => 'Създаване на друга сметка',
'createacct-benefit-heading' => '{{SITENAME}} се създава от хора като вас.',
'createacct-benefit-body1' => '{{PLURAL:$1|редакция|редакции}}',
'createacct-benefit-body2' => '{{PLURAL:$1|страница|страници}}',
'userexists' => 'Въведеното потребителско име вече се използва.
Изберете друго име.',
'loginerror' => 'Грешка при влизане',
+'createacct-error' => 'Грешка при създаване на сметка',
'createaccounterror' => 'Не може да бъде създадена сметка: $1',
'nocookiesnew' => 'Потребителската сметка беше създадена, но все още не сте влезли. {{SITENAME}} използва бисквитки при влизането на потребителите. Разрешете бисквитките в браузъра си, тъй като те са забранени, а след това влезте с потребителското си име и парола.',
'nocookieslogin' => '{{SITENAME}} използва бисквитки (cookies) за запис на влизанията. Разрешете бисквитките в браузъра си, тъй като те са забранени, и опитайте отново.',
'resetpass-wrong-oldpass' => 'Невалидна временна или текуща парола.
Възможно е вече успешно да сте сменили паролата си или да сте поискали нова временна парола.',
'resetpass-temp-password' => 'Временна парола:',
+'resetpass-abort-generic' => 'Промяната на паролата беше прекъсната от използвано разширение.',
# Special:PasswordReset
'passwordreset' => 'Възстановяване на парола',
+'passwordreset-text-many' => '{{PLURAL:$1|За възстановяване на паролата е необходимо да се попълни едно от полетата.}}',
'passwordreset-legend' => 'Възстановяване на парола',
'passwordreset-disabled' => 'Възстановяването на паролата е изключено в това уики.',
'passwordreset-username' => 'Потребителско име:',
<em>Легенда:</em> (<strong>тек</strong>) = разлика с текущата версия, (<strong>пред</strong>) = разлика с предишната версия, <strong>м</strong> = малка промяна',
'history-fieldset-title' => 'Търсене в историята',
'history-show-deleted' => 'Само изтритите',
-'histfirst' => 'Ð\9fÑ\8aÑ\80ви',
-'histlast' => 'Ð\9fоÑ\81ледни',
+'histfirst' => 'най-Ñ\81Ñ\82аÑ\80и',
+'histlast' => 'най-нови',
'historysize' => '({{PLURAL:$1|1 байт|$1 байта}})',
'historyempty' => '(празна)',
'badsiglength' => 'Вашият подпис е твърде дълъг.
Подписите не могат да надвишават $1 {{PLURAL:$1|знак|знака}}.',
'yourgender' => 'Пол:',
-'gender-unknown' => 'Ð\9dе е поÑ\81оÑ\87ено',
-'gender-male' => 'Ð\9cÑ\8aж',
-'gender-female' => 'Ð\96ена',
+'gender-unknown' => 'Ð\9fÑ\80едпоÑ\87иÑ\82ам да не поÑ\81оÑ\87а',
+'gender-male' => 'Той Ñ\80едакÑ\82иÑ\80а Ñ\83ики Ñ\81Ñ\82Ñ\80аниÑ\86иÑ\82е',
+'gender-female' => 'ТÑ\8f Ñ\80едакÑ\82иÑ\80а Ñ\83ики Ñ\81Ñ\82Ñ\80аниÑ\86иÑ\82е',
'prefs-help-gender' => 'По желание: използва се за коректно обръщение по род в системните съобщения на софтуера. Тази информация е публично достъпна.',
'email' => 'Е-поща',
'prefs-help-realname' => '* <strong>Истинско име</strong> <em>(незадължително)</em>: Ако го посочите, на него ще бъдат приписани вашите приноси.',
'prefs-signature' => 'Подпис',
'prefs-dateformat' => 'Формат на датата',
'prefs-timeoffset' => 'Часово отместване',
-'prefs-advancedediting' => 'РазÑ\88иÑ\80ени настройки',
+'prefs-advancedediting' => 'Ð\9eбÑ\89и настройки',
'prefs-preview' => 'Преглед',
'prefs-advancedrc' => 'Разширени настройки',
'prefs-advancedrendering' => 'Разширени настройки',
'prefs-displaysearchoptions' => 'Настройки на изгледа',
'prefs-displaywatchlist' => 'Видими настройки',
'prefs-diffs' => 'Разлики',
+'prefs-help-prefershttps' => 'Това предпочитание ще бъде активирано при следващото влизане.',
# User preference: email validation using jQuery
'email-address-validity-valid' => 'Адресът за е-поща изглежда валиден',
'deleteotherreason' => 'Друга/допълнителна причина:',
'deletereasonotherlist' => 'Друга причина',
'deletereason-dropdown' => '*Стандартни причини за изтриване
+** Спам
** По молба на автора
** Нарушение на авторски права
-** Вандализъм',
+** Вандализъм
+** По желание на автора
+** Грешно пренасочване',
'delete-edit-reasonlist' => 'Редактиране на причините за изтриване',
'delete-toobig' => 'Тази страница има голяма редакционна история с над $1 {{PLURAL:$1|версия|версии}}. Изтриването на такива страници е ограничено, за да се предотвратят евентуални поражения на {{SITENAME}}.',
'delete-warning-toobig' => 'Тази страница има голяма редакционна история с над $1 {{PLURAL:$1|версия|версии}}. Възможно е изтриването да наруши някои операции в базата данни на {{SITENAME}}; необходимо е особено внимание при продължаване на действието.',
'contributions' => '{{GENDER:$1|Потребителски}} приноси',
'contributions-title' => 'Потребителски приноси за $1',
'mycontris' => 'Приноси',
-'contribsub2' => 'За $1 ($2)',
+'contribsub2' => 'За {{GENDER:$3|$1}} ($2)',
'nocontribs' => 'Не са намерени промени, отговарящи на критерия.',
'uctop' => '(текуща)',
'month' => 'Месец:',
'tags-display-header' => 'Изглед в списъците с промени',
'tags-description-header' => 'Пълно описание на значението',
'tags-hitcount-header' => 'Отбелязани промени',
+'tags-active-yes' => 'Да',
+'tags-active-no' => 'Не',
'tags-edit' => 'редактиране',
'tags-hitcount' => '$1 {{PLURAL:$1|промяна|промени}}',
# Limit report
'limitreport-cputime-value' => '$1 {{PLURAL:$1|секунда|секунди}}',
'limitreport-walltime-value' => '$1 {{PLURAL:$1|секунда|секунди}}',
-'limitreport-postexpandincludesize-value' => '$1/$2 байта',
-'limitreport-templateargumentsize-value' => '$1/$2 байта',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|байт|байта}}',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|байт|байта}}',
);
'searchprofile-images-tooltip' => 'ফাইলের জন্য অনুসন্ধান',
'searchprofile-everything-tooltip' => 'সকল বিষয়বস্তু অনুসন্ধান করো (আলাপের পাতা সহ)',
'searchprofile-advanced-tooltip' => 'স্বনির্ধারিত নামস্থানে অনুসন্ধান করো',
-'search-result-size' => '$1 ({{PLURAL:$2|1 শব্দ|$2 শব্দসমূহ}})',
+'search-result-size' => '$1 ({{PLURAL:$2|১টি শব্দ|$2টি শব্দ}})',
'search-result-category-size' => '{{PLURAL: $1 | 1 সদস্য | $1 সদস্যবৃন্দ}} ({{PLURAL: $2 | 1 উপবিষয়শ্রেণীটি | $2 টি}}, {{PLURAL: $3 | 1 ফাইল | $3 ফাইল}})',
'search-result-score' => 'মিলেছে: $1%',
'search-redirect' => '(পুনর্নিদেশনা $1)',
'userlogin-resetpassword-link' => 'Reinicia la contrasenya',
'helplogin-url' => 'Help:Registrar-se',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Ajuda]]',
+'userlogin-loggedin' => 'Heu iniciat una sessió com {{GENDER:$1|$1}}.
+Feu servir el formulari de sota per iniciar la sessió com un altre usuari.',
+'userlogin-createanother' => 'Crea un altre compte',
'createacct-join' => 'Introduïu les vostres dades.',
'createacct-another-join' => 'Introdueix la informació del nou compte a continuació:',
'createacct-emailrequired' => 'Adreça de correu electrònic',
# Special:ResetTokens
'resettokens' => 'Reinicia els testimonis',
+'resettokens-text' => "Des d'aquí podeu reiniciar els testimonis que permeten l'accés a certes dades privades associades amb el vostre compte.
+
+Ho hauríeu de fer si accidentalment els heu compartit amb algú o si el vostre compte ha estat compromès.",
'resettokens-no-tokens' => 'No hi ha testimonis per reiniciar.',
'resettokens-legend' => 'Reinicia els testimonis',
'resettokens-tokens' => 'Testimonis:',
'userrights-notallowed' => "No teniu autorització per concedir o retirar permisos d'usuari.",
'userrights-changeable-col' => 'Grups que podeu canviar',
'userrights-unchangeable-col' => 'Grups que no podeu canviar',
+'userrights-conflict' => "Conflicte de canvis dels permisos d'usuari. Reviseu i confirmeu els canvis.",
+'userrights-removed-self' => 'Heu suprimit els propis permisos correctament. Per tant, ja no podreu tornar a accedir a aquesta pàgina.',
# Groups
'group' => 'Grup:',
'right-unblockself' => 'Desblocar-se a si mateixos',
'right-protect' => 'Canviar el nivell de protecció i modificar pàgines protegides',
'right-editprotected' => 'Modificar pàgines protegides (sense protecció de cascada)',
+'right-editsemiprotected' => 'Edita les pàgines protegides com «{{int:protect-level-autoconfirmed}}»',
'right-editinterface' => "Editar la interfície d'usuari",
'right-editusercssjs' => "Editar els fitxers de configuració CSS i JS d'altres usuaris",
'right-editusercss' => "Editar els fitxers de configuració CSS d'altres usuaris",
'right-edituserjs' => "Editar els fitxers de configuració JS d'altres usuaris",
+'right-editmyusercss' => 'Editeu els fitxers CSS propis',
+'right-editmyuserjs' => 'Editeu els propis fitxers de JavaScript',
'right-viewmywatchlist' => 'Mostra la llista de seguiment pròpia',
+'right-editmywatchlist' => 'Edita la llista de seguiment pròpia. Tingueu en compte que algunes accions encara afegiran pàgina fins i tot sense aquest permís.',
+'right-viewmyprivateinfo' => 'Mostra les dades privades (p. ex., adreça electrònica o nom real)',
+'right-editmyprivateinfo' => 'Modifica les dades privades (p. ex., adreça electrònica o nom real)',
'right-editmyoptions' => 'Edita les pròpies preferències',
'right-rollback' => "Revertir ràpidament l'últim editor d'una pàgina particular",
'right-markbotedits' => 'Marcar les reversions com a edicions de bot',
'action-block' => 'blocar aquest usuari per a què no pugui editar',
'action-protect' => "canviar els nivells de protecció d'aquesta pàgina",
'action-rollback' => "desfer ràpidament les modificacions de l'últim usuari que va editar una determinada pàgina",
-'action-import' => "importar aquesta pàgina des d'un altre wiki",
-'action-importupload' => "importar aquesta pàgina mitjançant la càrrega des d'un fitxer",
+'action-import' => "importa pàgines des d'un altre wiki",
+'action-importupload' => "importa pàgines mitjançant la càrrega d'un fitxer",
'action-patrol' => 'marcar les edicions dels altres com a supervisades',
'action-autopatrol' => 'marcar les vostres edicions com a supervisades',
'action-unwatchedpages' => 'visualitzar la llista de pàgines no vigilades',
'pageswithprop-text' => 'Aquesta pàgina llista les pàgines que utilitzen una propietat de pàgina en particular.',
'pageswithprop-prop' => 'Nom de la propietat:',
'pageswithprop-submit' => 'Vés',
+'pageswithprop-prophidden-long' => 'valor de propietat text llarg ocult ($1)',
+'pageswithprop-prophidden-binary' => 'valor de propietat binària oculta ($1)',
'doubleredirects' => 'Redireccions dobles',
'doubleredirectstext' => 'Aquesta pàgina llista les pàgines que redirigeixen a altres pàgines de redirecció.
'mostrevisions' => 'Pàgines més modificades',
'prefixindex' => 'Totes les pàgines per prefix',
'prefixindex-namespace' => 'Totes les pàgines amb prefix (espai de noms $1)',
+'prefixindex-strip' => 'Suprimeix el prefix a la llista',
'shortpages' => 'Pàgines curtes',
'longpages' => 'Pàgines llargues',
'deadendpages' => 'Pàgines atzucac',
'listusers' => "Llista d'usuaris",
'listusers-editsonly' => 'Mostra només usuaris amb edicions',
'listusers-creationsort' => 'Ordena per data de creació',
+'listusers-desc' => 'Ordena en ordre descendent',
'usereditcount' => '$1 {{PLURAL:$1|modificació|modificacions}}',
'usercreated' => '{{GENDER:$3|Creat}}: $1 a les $2',
'newpages' => 'Pàgines noves',
'deleteotherreason' => 'Motiu diferent o addicional:',
'deletereasonotherlist' => 'Altres motius',
'deletereason-dropdown' => "*Motius freqüents d'esborrat
-** Demanada per l'autor
+** Brossa
+** Vandalisme
** Violació del copyright
-** Vandalisme",
+** Demanada per l'autor
+** Redirecció trencada",
'delete-edit-reasonlist' => "Edita els motius d'eliminació",
'delete-toobig' => "Aquesta pàgina té un historial d'edicions molt gran, amb més de $1 {{PLURAL:$1|canvi|canvis}}. L'eliminació d'aquestes pàgines està restringida per a prevenir que hi pugui haver un desajustament seriós de la base de dades de tot el projecte {{SITENAME}} per accident.",
'delete-warning-toobig' => "Aquesta pàgina té un historial d'edicions molt gran, amb més de $1 {{PLURAL:$1|canvi|canvis}}. Eliminar-la podria suposar un seriós desajustament de la base de dades de tot el projecte {{SITENAME}}; aneu en compte abans dur a terme l'acció.",
La darrera modificació ha estat feta per l'usuari [[User:$3|$3]] ([[User talk:$3|Discussió]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
'editcomment' => "El resum d'edició ha estat: «$1».",
'revertpage' => "Revertides les edicions de [[Special:Contributions/$2|$2]] ([[User talk:$2|discussió]]) a l'última versió de [[User:$1|$1]]",
-'revertpage-nouser' => "Les edicions realitzades per un usuari ocult s'han eliminat fins a l'última revisió de [[User:$1|$1]]",
+'revertpage-nouser' => "Edicions revertides per un usuari ocult a l'última revisió de {{GENDER:$1|[[User:$1|$1]]}}",
'rollback-success' => "Edicions revertides de $1; s'ha canviat a la darrera versió de $2.",
# Edit tokens
'contributions' => "Contribucions de {{GENDER:$1|l'usuari|la usuària}}",
'contributions-title' => "Contribucions de l'usuari $1",
'mycontris' => 'Contribucions',
-'contribsub2' => 'Per $1 ($2)',
+'contribsub2' => 'Per a {{GENDER:$3|$1}} ($2)',
'nocontribs' => "No s'ha trobat canvis que encaixessin amb aquests criteris.",
'uctop' => '(actual)',
'month' => 'Mes (i anteriors):',
# Special:Redirect
'redirect' => 'Redirigeix per fitxer, usuari o ID de la revisió',
'redirect-legend' => 'Redirigeix a un fitxer o a una pàgina',
+'redirect-summary' => "Aquesta pàgina especial redirigeix a un fitxer (donat el nom del fitxer), una pàgina (donada un ID de la revisió), o a una pàgina d'usuari (donat un ID numèric d'usuari).",
'redirect-submit' => 'Vés-hi',
'redirect-lookup' => 'Consulta:',
'redirect-value' => 'Valor:',
'tags-tag' => "Nom de l'etiqueta",
'tags-display-header' => 'Aparença de la llista de canvis',
'tags-description-header' => 'Descripció completa del significat',
+'tags-active-header' => 'Actiu?',
'tags-hitcount-header' => 'Canvis etiquetats',
+'tags-active-yes' => 'Sí',
'tags-edit' => 'modifica',
'tags-hitcount' => '$1 {{PLURAL:$1|canvi|canvis}}',
'limitreport-ppvisitednodes' => 'Nombre de nodes visitats pel preprocessador',
'limitreport-ppgeneratednodes' => 'Nombre de nodes generats pel preprocessador',
'limitreport-postexpandincludesize' => "Mida d'inclusió post-expansió",
-'limitreport-postexpandincludesize-value' => '$1/$2 bytes',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
'limitreport-templateargumentsize' => "Mida de l'argument de plantilla",
-'limitreport-templateargumentsize-value' => '$1/$2 bytes',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
'limitreport-expansiondepth' => "Profunditat màxima d'expansió",
'limitreport-expensivefunctioncount' => "Número de funcions d'anàlisi dispendioses",
# Random page
'randompage' => 'Цахууш нисйелла агӀо',
+# Random page in category
+'randomincategory' => 'Категори чу цахууш нийса елла агӀо',
+'randomincategory-selectcategory' => 'Категори чу цахууш нийса елла агӀона чу гӀо: $1 $2.',
+
# Random redirect
'randomredirect' => 'Цахууш нисделла дIасахьажор',
'logempty' => 'Тептарш чохь хӀокху агӀона дӀаяздарш дац.',
# Special:AllPages
-'allpages' => 'Массо агlонаш',
+'allpages' => 'Массо агӀонаш',
'alphaindexline' => 'оцу $1 кху $2',
'nextpage' => 'Тlаьхьа йогlу агlо ($1)',
'prevpage' => 'Хьалхалера агlо ($1)',
'allpagesfrom' => 'Гучé яха агlонаш, йуьлалуш йолу оцу:',
'allpagesto' => 'Арайахар сацадé оцу:',
-'allarticles' => 'Массо агlонаш',
+'allarticles' => 'Массо агӀонаш',
'allinnamespace' => 'Массо агlонаш оцу цlери анахь «$1»',
'allpagesnext' => 'Тlаьхьайогlурш',
'allpagessubmit' => 'Кхочушдé',
'tooltip-search' => 'Лаха иза дош',
'tooltip-search-go' => 'Билгала и санна цlе йолучу агlон чу дехьа вала',
'tooltip-search-fulltext' => 'Лаха агlонаш ше чулацамехь хlара йоза долуш',
-'tooltip-p-logo' => 'Коьрта агIо',
-'tooltip-n-mainpage' => 'Ð\94еÑ\85Ñ\8cавалаÑ\80 коÑ\8cÑ\80Ñ\82а агlонÑ\87Ñ\83',
-'tooltip-n-mainpage-description' => 'Ð\94еÑ\85Ñ\8cавалаÑ\80 коÑ\8cÑ\80Ñ\82а агlонÑ\87Ñ\83',
+'tooltip-p-logo' => 'Коьрта агӀона дехьа гӀо',
+'tooltip-n-mainpage' => 'Ð\9aоÑ\8cÑ\80Ñ\82а агÓ\80она деÑ\85Ñ\8cа гÓ\80о',
+'tooltip-n-mainpage-description' => 'Ð\9aоÑ\8cÑ\80Ñ\82а агÓ\80она деÑ\85Ñ\8cа гÓ\80о',
'tooltip-n-portal' => 'Оцу кхолламах, мичахь хlу йу лаьташ а хlудалур ду шуьга',
'tooltip-n-currentevents' => 'Дlаоьхуш болу хаамашна могlам',
'tooltip-n-recentchanges' => 'Тlаьххьаралера хийцаман могlам',
'dberr-outofdate' => 'Хьуна хаалахь, цуьна йолу меттиг хила мега тишйелла черахь.',
# HTML forms
+'htmlform-invalid-input' => 'Ахьа яздинчу цхьан дакхано гӀалат далина',
'htmlform-submit' => 'ДӀадахьийта',
'htmlform-reset' => 'Цаоьшу хийцамаш',
'htmlform-selectorother-other' => 'Кхин',
'deletecomment' => 'Důvod:',
'deleteotherreason' => 'Jiný/další důvod:',
'deletereasonotherlist' => 'Jiný důvod',
-'deletereason-dropdown' => '*Obvyklé důvody smazání
-** Na žádost autora
+'deletereason-dropdown' => '* Obvyklé důvody smazání
+** Spam
+** Vandalismus
** Porušení autorských práv
-** Vandalismus',
+** Na žádost autora
+** Rozbité přesměrování',
'delete-edit-reasonlist' => 'Editovat důvody smazání',
'delete-toobig' => 'Tato stránka má velkou historii editací, přes $1 {{plural:$1|verzi|verze|verzí}}. Mazání takových stránek je omezeno, aby se předešlo nechtěnému narušení {{grammar:2sg|{{SITENAME}}}}.',
'delete-warning-toobig' => 'Tato stránka má velkou historii editací, přes $1 {{plural:$1|verzi|verze|verzí}}. Mazání takových stránek může narušit databázové operace {{grammar:2sg|{{SITENAME}}}}; postupujte opatrně.',
'deleteotherreason' => 'Rheswm arall:',
'deletereasonotherlist' => 'Rheswm arall',
'deletereason-dropdown' => "*Rhesymau arferol dros ddileu
-** Ar gais yr awdur
+** Sbam
+** Fandaliaeth
** Torri'r hawlfraint
-** Fandaliaeth",
+** Ar gais yr awdur
+** Ailgyfeiriad wedi torri",
'delete-edit-reasonlist' => 'Golygu rhestr y rhesymau dros ddileu',
'delete-toobig' => "Cafwyd dros $1 {{PLURAL:$1|o olygiadau}} i'r dudalen hon.
Cyfyngwyd ar y gallu i ddileu tudalennau sydd wedi eu golygu cymaint â hyn, er mwyn osgoi amharu ar weithrediad databas {{SITENAME}} yn ddamweiniol.",
'youhavenewmessagesmulti' => 'Du har nye beskeder på $1',
'editsection' => 'redigér',
'editold' => 'redigér',
-'viewsourceold' => 'vis kildekode',
+'viewsourceold' => 'vis wikikode',
'editlink' => 'redigér',
'viewsourcelink' => 'vis kildetekst',
'editsectionhint' => 'Rediger afsnit: $1',
'deleteotherreason' => 'Anderer/ergänzender Grund:',
'deletereasonotherlist' => 'Anderer Grund',
'deletereason-dropdown' => '* Allgemeine Löschgründe
-** Wunsch des Autors
+** Spam
+** Vandalismus
** Urheberrechtsverletzung
-** Vandalismus',
+** Wunsch des Autors
+** Defekte Weiterleitung',
'delete-edit-reasonlist' => 'Löschgründe bearbeiten',
'delete-toobig' => 'Diese Seite hat mit mehr als $1 {{PLURAL:$1|Version|Versionen}} eine sehr lange Versionsgeschichte. Das Löschen solcher Seiten wurde eingeschränkt, um eine versehentliche Überlastung der Server zu verhindern.',
'delete-warning-toobig' => 'Diese Seite hat mit mehr als $1 {{PLURAL:$1|Version|Versionen}} eine sehr lange Versionsgeschichte. Das Löschen kann zu Störungen im Datenbankbetrieb führen.',
'blocklist-nousertalk' => 'darf eigene Diskussionsseite nicht bearbeiten',
'ipblocklist-empty' => 'Die Liste enthält keine Einträge.',
'ipblocklist-no-results' => 'Die gesuchte IP-Adresse/der Benutzername ist nicht gesperrt.',
-'blocklink' => 'sperren',
-'unblocklink' => 'freigeben',
+'blocklink' => 'Sperren',
+'unblocklink' => 'Freigeben',
'change-blocklink' => 'Sperre ändern',
'contribslink' => 'Beiträge',
'emaillink' => 'E-Mail senden',
'revdelete-uname-unhid' => 'Benutzername freigegeben',
'revdelete-restricted' => 'Einschränkungen gelten auch für Administratoren',
'revdelete-unrestricted' => 'Einschränkungen für Administratoren aufgehoben',
-'logentry-move-move' => '$1 {{GENDER:$2|verschob}} Seite $3 nach $4',
-'logentry-move-move-noredirect' => '$1 {{GENDER:$2|verschob}} Seite $3 nach $4, ohne dabei eine Weiterleitung anzulegen',
-'logentry-move-move_redir' => '$1 {{GENDER:$2|verschob}} Seite $3 nach $4 und überschrieb dabei eine Weiterleitung',
-'logentry-move-move_redir-noredirect' => '$1 {{GENDER:$2|verschob}} Seite $3 nach $4 und überschrieb dabei eine Weiterleitung, ohne selbst eine Weiterleitung anzulegen',
-'logentry-patrol-patrol' => '$1 {{GENDER:$2|markierte}} Version $4 von Seite $3 als kontrolliert',
-'logentry-patrol-patrol-auto' => '$1 {{GENDER:$2|markierte}} automatisch Version $4 von Seite $3 als kontrolliert',
+'logentry-move-move' => '$1 {{GENDER:$2|verschob}} die Seite $3 nach $4',
+'logentry-move-move-noredirect' => '$1 {{GENDER:$2|verschob}} die Seite $3 nach $4, ohne dabei eine Weiterleitung anzulegen',
+'logentry-move-move_redir' => '$1 {{GENDER:$2|verschob}} die Seite $3 nach $4 und überschrieb dabei eine Weiterleitung',
+'logentry-move-move_redir-noredirect' => '$1 {{GENDER:$2|verschob}} die Seite $3 nach $4 und überschrieb dabei eine Weiterleitung, ohne selbst eine Weiterleitung anzulegen',
+'logentry-patrol-patrol' => '$1 {{GENDER:$2|markierte}} die Version $4 von Seite $3 als kontrolliert',
+'logentry-patrol-patrol-auto' => '$1 {{GENDER:$2|markierte}} automatisch die Version $4 von Seite $3 als kontrolliert',
'logentry-newusers-newusers' => 'Benutzerkonto $1 wurde {{GENDER:$2|erstellt}}',
'logentry-newusers-create' => 'Benutzerkonto $1 wurde {{GENDER:$2|erstellt}}',
'logentry-newusers-create2' => 'Benutzerkonto $3 wurde von $1 {{GENDER:$2|erstellt}}',
'articlepage' => 'View content page',
'talk' => 'Discussion',
'views' => 'Views',
-'toolbox' => 'Toolbox',
+'toolbox' => 'Tools',
'userpage' => 'View user page',
'projectpage' => 'View project page',
'imagepage' => 'View file page',
'deletecomment' => 'Reason:',
'deleteotherreason' => 'Other/additional reason:',
'deletereasonotherlist' => 'Other reason',
-'deletereason-dropdown' => '*Common delete reasons
-** Author request
+'deletereason-dropdown' => '* Common delete reasons
+** Spam
+** Vandalism
** Copyright violation
-** Vandalism',
+** Author request
+** Broken redirect',
'delete-edit-reasonlist' => 'Edit deletion reasons',
'delete-toobig' => 'This page has a large edit history, over $1 {{PLURAL:$1|revision|revisions}}.
Deletion of such pages has been restricted to prevent accidental disruption of {{SITENAME}}.',
'blockipsuccesssub' => 'Block succeeded',
'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] has been blocked.<br />
See the [[Special:BlockList|block list]] to review blocks.',
-'ipb-blockingself' => 'You are about to block yourself! Are you sure you want to do that?',
-'ipb-confirmhideuser' => 'You are about to block a user with "hide user" enabled. This will suppress the user\'s name in all lists and log entries. Are you sure you want to do that?',
+'ipb-blockingself' => 'You are about to block yourself! Are you sure you want to do that?',
+'ipb-confirmhideuser' => 'You are about to block a user with "hide user" enabled. This will suppress the user\'s name in all lists and log entries. Are you sure you want to do that?',
'ipb-edit-dropdown' => 'Edit block reasons',
'ipb-unblock-addr' => 'Unblock $1',
'ipb-unblock' => 'Unblock a username or IP address',
'confirmrecreate' => "User [[User:$1|$1]] ([[User talk:$1|talk]]) deleted this page after you started editing with reason:
: ''$2''
Please confirm that you really want to recreate this page.",
-'confirmrecreate-noreason' => 'User [[User:$1|$1]] ([[User talk:$1|talk]]) deleted this page after you started editing. Please confirm that you really want to recreate this page.',
+'confirmrecreate-noreason' => 'User [[User:$1|$1]] ([[User talk:$1|talk]]) deleted this page after you started editing. Please confirm that you really want to recreate this page.',
'recreate' => 'Recreate',
'unit-pixel' => 'px', # only translate this message to other languages if you have to change it
'deletecomment' => 'Kialo:',
'deleteotherreason' => 'Alia/plua kialo:',
'deletereasonotherlist' => 'Alia kialo',
-'deletereason-dropdown' => '*Oftaj kialoj por forigo
-** Peto de aŭtoro
+'deletereason-dropdown' => '* Oftaj kialoj por forigo
+** Trudmesaĝoj
+** Vandalismo
** Neglekto de aŭtorrajto
-** Vandalismo',
+** Postulo de la aŭtoro
+** Nefunkcianta alidirektilo',
'delete-edit-reasonlist' => 'Redakti kialojn de forigo',
'delete-toobig' => 'Ĉi tiu paĝo havas grandan redakto-historion, pli ol $1 {{PLURAL:$1|version|versiojn}}. Forigo de ĉi tiaj paĝoj estis limigitaj por preventi akcidentan disrompigon de {{SITENAME}}.',
'delete-warning-toobig' => 'Ĉi tiu paĝo havas grandan redakto-historion, pli ol $1 {{PLURAL:$1|version|versiojn}}. Forigo de ĝi povas disrompigi operacion de {{SITENAME}}; forigu singarde.',
'deleteotherreason' => 'Otro motivo:',
'deletereasonotherlist' => 'Otro motivo',
'deletereason-dropdown' => '*Razones comunes de borrado
-** A petición del mismo autor
+** Spam
+** Vandalismo
** Violación de copyright
-** Vandalismo',
+** A petición del mismo autor
+** Redirección incorrecta',
'delete-edit-reasonlist' => 'Editar razones de borrado',
'delete-toobig' => 'Esta página tiene un historial muy grande, con más de $1 {{PLURAL:$1|revisión|revisiones}}. Borrar este tipo de páginas ha sido restringido para prevenir posibles problemas en {{SITENAME}}.',
'delete-warning-toobig' => 'Esta página tiene un historial de más de $1 {{PLURAL:$1|revisión|revisiones}}. Eliminarla puede perturbar las operaciones de la base de datos de {{SITENAME}}. Ten cuidado al borrar.',
'tog-hidepatrolled' => 'ویرایشهای گشتخورده از فهرست تغییرات اخیر پنهان شوند',
'tog-newpageshidepatrolled' => 'صفحههای نهگبانیشده از فهرست صفحههای تازه پنهان شوند',
'tog-extendwatchlist' => 'گسترش فهرست پیگیریها برای نمایش همهٔ تغییرات، نه فقط آخرینها',
-'tog-usenewrc' => 'گروهبندی تغییرات بر پایه صفحه در تغییرات اخیر و فهرست پیگیریها (نیازمند جاوااسکریپت)',
+'tog-usenewrc' => 'گروهبندی تغییرات بر پایهٔ صفحههای تغییرات اخیر و فهرست پیگیریها (نیازمند جاوااسکریپت)',
'tog-numberheadings' => 'شمارهگذاری خودکار عنوانها',
'tog-showtoolbar' => 'نوار ابزار جعبهٔ ویرایش نمایش یابد',
'tog-editondblclick' => 'ویرایش صفحهها با دوکلیک (نیازمند جاوااسکریپت)',
'newwindow' => '(در پنجرهٔ جدید باز میشود)',
'cancel' => 'لغو',
'moredotdotdot' => 'بیشتر...',
-'morenotlisted' => 'اÛ\8cÙ\86 Ù\84Û\8cست کامل نیست.',
+'morenotlisted' => 'اÛ\8cÙ\86 Ù\81Ù\87رست کامل نیست.',
'mypage' => 'صفحه',
'mytalk' => 'بحث',
'anontalk' => 'بحث برای این آیپی',
# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
'aboutsite' => 'دربارهٔ {{SITENAME}}',
'aboutpage' => 'Project:درباره',
-'copyright' => 'محتوا تحت اجازهنامهٔ $1 در دسترس است مگر اینکه خلافش ذکر شده باشد.',
+'copyright' => 'محتوایات تحت اجازهنامهٔ $1 هستند مگر اینکه خلافش ذکر شده باشد.',
'copyrightpage' => '{{ns:project}}:حق تکثیر',
'currentevents' => 'رویدادهای کنونی',
'currentevents-url' => 'Project:رویدادهای کنونی',
# General errors
'error' => 'خطا',
'databaseerror' => 'خطای پایگاه داده',
-'databaseerror-text' => 'مشکلی در پایگاه داده ها رخ داده است.
-این ممکن است نشان دهنده یک مشکل در نرم افزار باشد.',
-'databaseerror-textcl' => 'خطای پایگاه داده پرس و جو رخ داده است.',
-'databaseerror-query' => 'پرس و جو:$1',
+'databaseerror-text' => 'مشکلی در پایگاهدادهها رخ دادهاست.
+این ممکن است نشاندهندهٔ ایرادی در نرمافزار باشد.',
+'databaseerror-textcl' => 'یک خطای پرسوجوی پایگاه دادههای رخ دادهاست.',
+'databaseerror-query' => 'پرسوجو: $1',
'databaseerror-function' => 'تابع: $1',
'databaseerror-error' => 'خطا: $1',
'laggedslavemode' => "'''هشدار:''' صفحه ممکن است بهروزرسانیهای اخیر را شامل نشود.",
'userlogin-yourname-ph' => 'نام کاربریتان را وارد کنید',
'createacct-another-username-ph' => 'نام کاربریتان را وارد کنید',
'yourpassword' => 'رمز عبور:',
-'userlogin-yourpassword' => 'رمز عبور',
+'userlogin-yourpassword' => 'گذرواژه',
'userlogin-yourpassword-ph' => 'گذرواژه را وارد کنید',
'createacct-yourpassword-ph' => 'یک گذرواژه وارد کنید',
'yourpasswordagain' => 'تکرار گذرواژه:',
'userlogin-resetpassword-link' => 'گذرواژهتان را فراموش کردید؟',
'helplogin-url' => 'Help:ورود به سامانه',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|راهنمای ورود به سامانه]]',
-'userlogin-loggedin' => 'شما در حال حاضر به عنوان {{GENDER:$1|$1}} وارد سیستم شده اید.
-از فرم پایین برای ورود به عنوان یک کاربر دیگر استفاده کنید.',
+'userlogin-loggedin' => 'شما در حال حاضر بهعنوان {{GENDER:$1|$1}} وارد سیستم شدهاید.
+از فرم پایین برای ورود بهعنوان یک کاربر دیگر استفاده کنید.',
'userlogin-createanother' => 'ایجاد یک حساب کاربری دیگر',
'createacct-join' => 'اطلاعاتتان را در زیر وارد کنید',
'createacct-another-join' => 'در زیر اطلاعات کاربری جدیدتان را وارد کنید.',
# Email sending
'php-mail-error-unknown' => 'خطای ناشناخته در تابع mail() پیاچپی',
'user-mail-no-addy' => 'تلاش برای ارسال نامه بدون یک نشانی رایانامه.',
-'user-mail-no-body' => 'تلاش برای فرستادن پستالکترونیک بیدلیل کوتاه یا خالی',
+'user-mail-no-body' => 'تلاش برای فرستادن رایانامه بیدلیل کوتاه یا خالی',
# Change password dialog
'resetpass' => 'تغییر گذرواژه',
'revdel-restore-visible' => 'نسخههای پیدا',
'pagehist' => 'تاریخچهٔ صفحه',
'deletedhist' => 'تاریخچهٔ حذفشده',
-'revdelete-hide-current' => 'خطا در پنهان کردن مورد مورخ $2 ساعت $1: این نسخه، نسخهٔ اخیر میباشد و قابل پنهان کردن نیست.',
+'revdelete-hide-current' => 'خطا در پنهانکردن مورد مورخ $2 ساعت $1: این نسخه، نسخهٔ اخیر است و قابل پنهانکردن نیست.',
'revdelete-show-no-access' => 'خطا در پنهان کردن مورد مورخ $2 ساعت $1: این نسخه علامت «محدودیت» دارد و شما به آن دسترسی ندارید.',
'revdelete-modify-no-access' => 'خطا در پنهان کردن مورد مورخ $2 ساعت $1: این نسخه علامت «محدودیت» دارد و شما به آن دسترسی ندارید.',
'revdelete-modify-missing' => 'خطا در پنهان کردن مورد شمارهٔ $1: این نسخه در پایگاه داده وجود ندارد!',
'prefs-rendering' => 'نمایش صفحه',
'saveprefs' => 'ذخیره',
'resetprefs' => 'صفرکردن ترجیحات',
-'restoreprefs' => 'برگرداندن تمام تنظیمات پیشفرض (در تمامی قسمت ها)',
+'restoreprefs' => 'برگرداندن تمام تنظیمات پیشفرض (در تمامی قسمتها)',
'prefs-editing' => 'ویرایش',
'rows' => 'تعداد سطرها:',
'columns' => 'تعداد ستونها:',
'gender-male' => 'مرد',
'gender-female' => 'زن',
'prefs-help-gender' => 'انجام این تنظیم اختیاری است.
-نرم افزار از این تنظیم استفاده می کند که جنسیت گرامری شما را بدرستی بیان کند.
-این اطلاعات عمومی خواهد بود.',
+نرمافزار از این مقدار برای اشارهٔ صحیح به جنسیت شما و ذکر شما برای دیگران با استفاده از قوائد درست دستور زبان استفاده میکند.
+این اطلاعات عمومی خواهند بود.',
'email' => 'رایانامه',
'prefs-help-realname' => 'نام واقعی اختیاری است.
اگر آن را وارد کنید هنگام ارجاع به آثارتان و انتساب آنها به شما از نام واقعیتان استفاده خواهد شد.',
'prefs-displaywatchlist' => 'گزینههای نمایش',
'prefs-tokenwatchlist' => 'نشانه',
'prefs-diffs' => 'تفاوتها',
-'prefs-help-prefershttps' => 'تاثیر این ترجیح بعد از ورود بعدی شما اعمال خواهد شد.',
+'prefs-help-prefershttps' => 'تأثیر این ترجیح بعد از ورود بعدی شما اعمال خواهد شد.',
# User preference: email validation using jQuery
'email-address-validity-valid' => 'نشانی رایانامه معتبر به نظر میرسد',
'userrights-notallowed' => 'شما مجوز برای افزودن یا حذف حقوق کاربر را ندارید.',
'userrights-changeable-col' => 'گروههایی که میتوانید تغییر دهید',
'userrights-unchangeable-col' => 'گروههایی که نمیتوانید تغییر دهید',
-'userrights-conflict' => 'تعارض دسترسیهای کاربری! لطفا بررسی کنید و تغییرات را تایید کنید.',
+'userrights-conflict' => 'تعارض دسترسیهای کاربری! لطفاً بررسی کنید و تغییرات را تأیید کنید.',
'userrights-removed-self' => 'شما با موفقیت دسترسیهای خود را واستاندید. به این ترتیب شما دیگر به این صفحه دسترسی ندارید.',
# Groups
'right-editmyprivateinfo' => 'دادههای خصوصی خود را ویرایش کنید (مانند رایانشانی و نام واقعی)',
'right-editmyoptions' => 'ترجیحات خود را ویرایش',
'right-rollback' => 'واگردانی سریع ویرایشهای آخرین کاربری که یک صفحه را ویرایش کردهاست',
-'right-markbotedits' => 'علامت زدن ویرایشهای واگردانی شده به عنوان ویرایش ربات',
+'right-markbotedits' => 'علامتزدن ویرایشهای واگردانیشده بهعنوان ویرایش ربات',
'right-noratelimit' => 'تاثیر نپذیرفتن از محدودیت سرعت',
'right-import' => 'وارد کردن صفحه از ویکیهای دیگر',
'right-importupload' => 'وارد کردن صفحه از طریق بارگذاری پرونده',
'action-protect' => 'تغییر سطح محافظت این صفحه',
'action-rollback' => 'واگردانی سریع ویرایشهای آخرین کاربری که یک صفحه را ویرایش کردهاست',
'action-import' => 'وارد کردن صفحه از ویکی های دیگر',
-'action-importupload' => 'وارد کردن صفحه از طریق بارگذاری پرونده',
+'action-importupload' => 'واردکردن صفحه از طریق بارگذاری پرونده',
'action-patrol' => 'گشت زدن ویرایش دیگران',
'action-autopatrol' => 'گشت زدن ویرایش خودتان',
'action-unwatchedpages' => 'مشاهدهٔ صفحههای پیگیری نشده',
'upload_source_file' => '(پروندهای در رایانهٔ شما)',
# Special:ListFiles
-'listfiles-summary' => 'این صفحهٔ ویژه تمام پروندههای بارگذاری شده را نمایش میدهد.',
+'listfiles-summary' => 'این صفحهٔ ویژه تمام پروندههای بارگذاریشده را نمایش میدهد.',
'listfiles_search_for' => 'جستجو به دنبال نام پرونده چندرسانهای:',
'imgfile' => 'پرونده',
'listfiles' => 'فهرست پروندهها',
'listfiles_size' => 'اندازه',
'listfiles_description' => 'توضیح',
'listfiles_count' => 'نسخهها',
-'listfiles-show-all' => 'شامل نسخه های قدیمی عکس ها',
-'listfiles-latestversion' => 'نسخه فعلی',
+'listfiles-show-all' => 'شامل نسخههای قدیمی عکسها',
+'listfiles-latestversion' => 'نسخهٔ فعلی',
'listfiles-latestversion-yes' => 'بله',
'listfiles-latestversion-no' => 'خیر',
# Random page in category
'randomincategory' => 'صفحهٔ تصادفی در رده',
-'randomincategory-invalidcategory' => '"$1" نام یک رده معتبر نمی باشد.',
-'randomincategory-nopages' => 'هیج صفحه ای در رده [[:Category:$1|$1]] وجود ندارد.',
-'randomincategory-selectcategory' => 'دریافت صفحه تصادفی از دسته بندی: $1 $2 .',
+'randomincategory-invalidcategory' => '«$1» نامی معتبر برای یک ردهٔ نیست.',
+'randomincategory-nopages' => 'هیج صفحهای در رده [[:Category:$1|$1]] وجود ندارد.',
+'randomincategory-selectcategory' => 'دریافت صفحهای تصادفی از دستهبندی: $1 $2.',
'randomincategory-selectcategory-submit' => 'برو',
# Random redirect
'mostrevisions' => 'صفحههای دارای بیشترین نسخه',
'prefixindex' => 'تمام صفحهها با پیشوند',
'prefixindex-namespace' => 'همهٔ صفحههای دارای پیشوند (فضاینام $1)',
-'prefixindex-strip' => ' حذف پیشوند در فهرست',
+'prefixindex-strip' => 'حذف پیشوند در فهرست',
'shortpages' => 'صفحههای کوتاه',
'longpages' => 'صفحههای بلند',
'deadendpages' => 'صفحههای بنبست',
'listgrouprights' => 'اختیارات گروههای کاربری',
'listgrouprights-summary' => 'فهرست زیر شامل گروههای کاربری تعریف شده در این ویکی و اختیارات داده شده به آنها است.
اطلاعات بیشتر در مورد هر یک از اختیارات را در [[{{MediaWiki:Listgrouprights-helppage}}]] بیابید.',
-'listgrouprights-key' => '* <span class="listgrouprights-granted">اختیارات داده شده</span>
-* <span class="listgrouprights-revoked">اختیارات گرفته شده</span>',
+'listgrouprights-key' => '* <span class="listgrouprights-granted">اختیارات دادهشده</span>
+* <span class="listgrouprights-revoked">اختیارات گرفتهشده</span>',
'listgrouprights-group' => 'گروه',
'listgrouprights-rights' => 'دسترسیها',
'listgrouprights-helppage' => 'Help:دسترسیهای گروهی',
'deleteotherreason' => 'دلیل دیگر/اضافی:',
'deletereasonotherlist' => 'دلیل دیگر',
'deletereason-dropdown' => '*دلایل متداول حذف
-** درخواست کاربر
+** هرزنگار
+** خرابکاری
** نقض حق تکثیر
-** خرابکاری',
+** درخواست کاربر
+** تغییرمسیر شکسته',
'delete-edit-reasonlist' => 'ویرایش دلایل حذف',
'delete-toobig' => 'این صفحه تاریخچهٔ ویرایشی بزرگی دارد، که شامل بیش از $1 {{PLURAL:$1|نسخه|نسخه}} است.
به منظور جلوگیری از اختلال ناخواسته در {{SITENAME}} حذف این گونه صفحهها محدود شدهاست.',
'movenotallowedfile' => 'شما اجازهٔ انتقال پروندهها را ندارید.',
'cant-move-user-page' => 'شما اجازه ندارید صفحههای کاربری سرشاخه را انتقال دهید.',
'cant-move-to-user-page' => 'شما اجازه ندارید که یک صفحه را به یک صفحهٔ کاربر انتقال دهید (به استثنای زیر صفحههای کاربری).',
-'newtitle' => 'به عنوان جدید',
+'newtitle' => 'بهعنوان جدید',
'move-watch' => 'پیگیری صفحههای مبدأ و مقصد',
'movepagebtn' => 'صفحه منتقل شود',
'pagemovedsub' => 'انتقال با موفقیت انجام شد',
'pageinfo-length' => 'حجم صفحه (بایت)',
'pageinfo-article-id' => 'شناسهٔ صفحه',
'pageinfo-language' => 'زبان محتوای صفحه',
-'pageinfo-robot-policy' => 'فهرست کردن توسط روبات ها',
+'pageinfo-robot-policy' => 'فهرستکردن توسط رباتها',
'pageinfo-robot-index' => 'مجاز',
'pageinfo-robot-noindex' => 'غیر مجاز',
'pageinfo-views' => 'شمار بازدیدها',
'dberr-problems' => 'شرمنده! این تارنما از مشکلات فنی رنج میبرد.',
'dberr-again' => 'چند دقیقه صبر کند و دوباره صفحه را بارگیری کنید.',
'dberr-info' => '(امکان برقراری ارتباط با کارساز پایگاه داده وجود ندارد: $1)',
-'dberr-info-hidden' => '(امکان تماس با پایگاه داده سرور امکان پذیر نیست)',
+'dberr-info-hidden' => '(امکان تماس با پایگاهدادهها کارساز امکانپذیر نیست)',
'dberr-usegoogle' => 'شما در این مدت میتوانید با استفاده از گوگل جستجو کنید.',
'dberr-outofdate' => 'توجه کنید که نمایههای آنها از محتوای ما ممکن است به روز نباشد.',
'dberr-cachederror' => 'آنچه در ادامه میآید یک کپی از صفحهٔ درخواست شده است که در کاشه قرار دارد، و ممکن است به روز نباشد.',
# Limit report
'limitreport-title' => 'دادههای رخنمانگاری تجزیهکننده:',
-'limitreport-cputime' => 'زمان مصرف سی پی یو',
-'limitreport-cputime-value' => '$1 {{PLURAL:$1|ثانیه|ثانیه}}',
+'limitreport-cputime' => 'زمان مصرف سیپییو',
+'limitreport-cputime-value' => '$1 {{PLURAL:$1|ثانیه}}',
'limitreport-walltime' => 'استفاده زمان واقعی',
'limitreport-walltime-value' => '$1 {{PLURAL:$1|ثانیه|ثانیه}}',
-'limitreport-ppvisitednodes' => 'شمارش گره پیش پردازنده مشاهده شده',
-'limitreport-ppgeneratednodes' => 'شمارش گره پیش پردازنده تولید شده',
+'limitreport-ppvisitednodes' => 'شمارش گرهٔ پیشپردازنده مشاهدهشده',
+'limitreport-ppgeneratednodes' => 'شمارش گره پیشپردازنده تولیدشده',
'limitreport-postexpandincludesize' => 'شامل اندازه پس گسترش',
'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|بایت|بایت}}',
'limitreport-templateargumentsize' => 'اندازه عملگر الگو',
'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|بایت|بایت}}',
'limitreport-expansiondepth' => 'بیشترین عمق گسترش',
-'limitreport-expensivefunctioncount' => 'تعداد تابع تجزیه گر پرمصرف',
+'limitreport-expensivefunctioncount' => 'تعداد تابع تجزیهگر پرمصرف',
);
'undeletehistory' => 'Jos palautat sivun, kaikki versiot lisätään sivun historiaan. Jos uusi sivu samalla nimellä on luotu poistamisen jälkeen, palautetut versiot lisätään sen historiaan.',
'undeleterevdel' => 'Palautusta ei tehdä, jos sen seurauksena sivun uusin versio olisi osittain piilotettu.
Tässä tilanteessa älä valitse palautettavaksi näkyviin viimeisintä poistettua versiota tai poista version piilotus.',
-'undeletehistorynoadmin' => 'Tämä sivu on poistettu. Syy sivun poistamiseen näkyy yhteenvedossa, jossa on myös tiedot, ketkä ovat muokanneet tätä sivua ennen poistamista. Sivujen varsinainen sisältö on vain ylläpitäjien luettavissa.',
+'undeletehistorynoadmin' => 'Tämä sivu on poistettu.
+Syy sivun poistamiseen näkyy alla olevassa yhteenvedossa, jossa on myös tiedot, ketkä olivat muokanneet tätä sivua ennen poistamista.
+Näiden poistettujen versioiden varsinainen tekstisisältö on vain ylläpitäjien luettavissa.',
'undelete-revision' => 'Poistettu sivu $1 hetkellä $4 kello $5. Tekijä: $3.',
-'undeleterevision-missing' => 'Virheellinen tai puuttuva versio. Se on saatettu palauttaa tai poistaa arkistosta.',
+'undeleterevision-missing' => 'Virheellinen tai puuttuva versio.
+Sinulla on kenties käytössä väärä linkki, tai sitten versio on saatettu palauttaa tai poistaa arkistosta.',
'undelete-nodiff' => 'Aikaisempaa versiota ei löytynyt.',
'undeletebtn' => 'Palauta',
'undeletelink' => 'näytä tai palauta',
'deletecomment' => 'Motif :',
'deleteotherreason' => 'Motif autre ou supplémentaire :',
'deletereasonotherlist' => 'Autre motif',
-'deletereason-dropdown' => "* Motifs de suppression les plus courants
-** Demande de l'auteur
-** Violation des droits d'auteur
-** Vandalisme",
+'deletereason-dropdown' => '* Motifs de suppression les plus courants
+** Pourriel
+** Vandalisme
+** Violation des droits d’auteur
+** Demande de l’auteur
+** Redirection cassée',
'delete-edit-reasonlist' => 'Modifier les motifs de suppression de page',
'delete-toobig' => 'Cette page possède un historique important de modifications, dépassant $1 version{{PLURAL:$1||s}}.
La suppression de telles pages a été restreinte pour prévenir des perturbations accidentelles de {{SITENAME}}.',
'pageinfo-magic-words' => '{{PLURAL:$1|Mot magique|Mots magiques}} ($1)',
'pageinfo-hidden-categories' => '{{PLURAL:$1|Catégorie cachée|Catégories cachées}} ($1)',
'pageinfo-templates' => '{{PLURAL:$1|Modèle inclu|Modèles inclus}} ($1)',
-'pageinfo-transclusions' => '{{PLURAL:$1|Page traduite|Pages traduites}} sur ($1)',
+'pageinfo-transclusions' => '{{PLURAL:$1|Page dans laquelle|Pages dans lesquelles}} elle est incluse ($1)',
'pageinfo-toolboxlink' => 'Information sur la page',
'pageinfo-redirectsto' => 'Rediriger vers',
'pageinfo-redirectsto-info' => 'info',
'deletecomment' => 'Motivo:',
'deleteotherreason' => 'Outro motivo:',
'deletereasonotherlist' => 'Outro motivo',
-'deletereason-dropdown' => '*Motivos frecuentes para borrar
-** Solicitado pola persoa que o creou
+'deletereason-dropdown' => '* Motivos frecuentes para borrar
+** Spam
+** Vandalismo
** Violación dos dereitos de autoría
-** Vandalismo',
+** Solicitado pola persoa que creou a páxina
+** Redirección rota',
'delete-edit-reasonlist' => 'Editar os motivos de borrado',
'delete-toobig' => 'Esta páxina conta cun historial longo, de máis {{PLURAL:$1|dunha revisión|de $1 revisións}}.
Limitouse a eliminación destas páxinas para previr problemas de funcionamento accidentais en {{SITENAME}}.',
'deleteotherreason' => 'סיבה נוספת/אחרת:',
'deletereasonotherlist' => 'סיבה אחרת',
'deletereason-dropdown' => '* סיבות מחיקה נפוצות
-** לבקשת הכותב
+** ספאם
+** השחתה
** הפרת זכויות יוצרים
-** השחתה',
+** לבקשת הכותב
+** הפניה שבורה',
'delete-edit-reasonlist' => 'עריכת סיבות המחיקה',
'delete-toobig' => 'דף זה כולל מעל {{PLURAL:$1|גרסה אחת|$1 גרסאות}} בהיסטוריית העריכות שלו. מחיקת דפים כאלה הוגבלה כדי למנוע פגיעה בביצועי האתר.',
'delete-warning-toobig' => 'דף זה כולל מעל {{PLURAL:$1|גרסה אחת|$1 גרסאות}} בהיסטוריית העריכות שלו. מחיקה שלו עלולה להפריע לפעולות בבסיס הנתונים; אנא שקלו שנית את המחיקה.',
'specialpage' => 'विशेष पृष्ठ',
'personaltools' => 'वैयक्तिक औज़ार',
'postcomment' => 'नया अनुभाग',
-'articlepage' => 'लà¥\87à¤\96 देखें',
+'articlepage' => 'सामà¤\97à¥\8dरà¥\80 पà¥\83षà¥\8dठदेखें',
'talk' => 'चर्चा',
'views' => 'दर्शाव',
'toolbox' => 'साधन पेटी',
'aboutsite' => '{{SITENAME}} के बारे में',
'aboutpage' => 'Project:परिचय',
'copyright' => 'उपलब्ध सामग्री $1 के अधीन है जब तक अलग से उल्लेख ना किया गया हो।',
-'copyrightpage' => '{{ns:project}}:सरà¥\8dवाधिà¤\95ार',
+'copyrightpage' => '{{ns:project}}:à¤\95à¥\89पà¥\80राà¤\87à¤\9f',
'currentevents' => 'हाल की घटनाएँ',
'currentevents-url' => 'Project:हाल की घटनाएँ',
'disclaimers' => 'अस्वीकरण',
'hidetoc' => 'छिपाएँ',
'collapsible-collapse' => 'छोटा करें',
'collapsible-expand' => 'विस्तार करें',
-'thisisdeleted' => '$1 दà¥\87à¤\96à¥\87à¤\82 या बदलà¥\87à¤\82?',
+'thisisdeleted' => '$1 दà¥\87à¤\96à¥\87à¤\82 या वापिस लाà¤\8fà¤\81?',
'viewdeleted' => '$1 दिखायें?',
'restorelink' => '{{PLURAL:$1|एक हटाया हुआ|$1 हटाये हुए}} बदलाव',
'feedlinks' => 'फ़ीड:',
'prefs-personal' => 'सदस्य व्यक्तिरेखा',
'prefs-rc' => 'हाल में हुए बदलाव',
'prefs-watchlist' => 'ध्यानसूची',
-'prefs-watchlist-days' => 'ध्यानसूचीमें दिखाने के दिन:',
+'prefs-watchlist-days' => 'ध्यानसूची में दिखाने के दिन:',
'prefs-watchlist-days-max' => 'अधिकतम $1 {{PLURAL:$1|दिन}}',
'prefs-watchlist-edits' => 'बढ़ाई हुई ध्यानसूची में दिखाने हेतु अधिकतम बदलाव:',
-'prefs-watchlist-edits-max' => 'à¤\85धिà¤\95तम सà¤\82à¤\96à¥\8dया: १०००',
+'prefs-watchlist-edits-max' => 'à¤\85धिà¤\95तम सà¤\82à¤\96à¥\8dया: à¤\8fà¤\95 हà¤\9c़ार',
'prefs-watchlist-token' => 'ध्यानसूची टोकन',
'prefs-misc' => 'अन्य',
'prefs-resetpass' => 'कूटशब्द बदलें',
'newuserlogpagetext' => 'यह सदस्य खातों के निर्माण का लॉग है।',
# User rights log
-'rightslog' => 'सदसà¥\8dय à¤\85धिà¤\95ार सà¥\82à¤\9aà¥\80',
+'rightslog' => 'सदसà¥\8dय à¤\85धिà¤\95ार लà¥\89à¤\97',
'rightslogtext' => 'यह सदस्य अधिकारों में हुए बदलावों की सूची है।',
# Associated actions - in the sentence "You do not have permission to X"
'action-block' => 'इस सदस्य को संपादन करने से ब्लॉक करने',
'action-protect' => 'इस पृष्ठ के सुरक्षा स्तर बदलने',
'action-rollback' => 'किसी पृष्ठ का अंतिम सम्पादन करने वाले सदस्य के सम्पादन वापिस लेने',
-'action-import' => 'à¤\95िसà¥\80 à¤\94र विà¤\95ि सà¥\87 यह पà¥\83षà¥\8dठà¤\86यात à¤\95रनà¥\87',
+'action-import' => 'किसी और विकि से पृष्ठ आयात करने',
'action-importupload' => 'फ़ाइल अपलोड द्वारा यह पृष्ठ आयात करे',
'action-patrol' => 'अन्य सदस्यों के सम्पादन परीक्षित करने',
'action-autopatrol' => 'अपने सम्पादन स्वचालित रूप से परीक्षित करने',
'withoutinterwiki' => 'बिना अंतरविकि कड़ियों वाले पृष्ठ',
'withoutinterwiki-summary' => 'निम्न पृष्ठ अन्य भाषाओं के अवतरणों से नहीं जुड़ते हैं।',
-'withoutinterwiki-legend' => 'à¤\89पपद',
+'withoutinterwiki-legend' => 'à¤\89पसरà¥\8dà¤\97',
'withoutinterwiki-submit' => 'दिखायें',
'fewestrevisions' => 'सबसे कम अवतरणों वाले पृष्ठ',
'listusers' => 'सदस्यसूची',
'listusers-editsonly' => 'केवल संपादन कर चुके सदस्य दिखाएँ',
'listusers-creationsort' => 'निर्माण तिथि के आधार पर क्रमांकन करें',
-'usereditcount' => '$1 {{PLURAL:$1|सà¤\82पादन|सà¤\82पादन}}',
+'usereditcount' => '$1 {{PLURAL:$1|समà¥\8dपादन}}',
'usercreated' => '$1 को $2 बजे बनाया गया, सदस्यनाम $3 है',
'newpages' => 'नए पृष्ठ',
'newpages-username' => 'सदस्यनाम:',
# Special:Log
'specialloguserlabel' => 'कर्ता:',
-'speciallogtitlelabel' => 'प्रयोजन (शीर्षक):',
+'speciallogtitlelabel' => 'प्रयोजन (शीर्षक अथवा सदस्यनाम):',
'log' => 'लॉग',
'all-logs-page' => 'सभी सार्वजनिक लॉग',
'alllogstext' => '{{SITENAME}} की सभी उपलब्ध लॉगों की प्रविष्टियों का मिला-जुला प्रदर्शन।
'deleteotherreason' => 'अन्य/अतिरिक्त कारण:',
'deletereasonotherlist' => 'अन्य कारण',
'deletereason-dropdown' => '*हटाने के सामान्य कारण
-** लेखक की बिनती
+** स्पैम
+** बर्बरता
** कॉपीराइट उल्लंघन
-** बर्बरता',
+** लेखक का अनुरोध
+** टूटा अनुप्रेषण',
'delete-edit-reasonlist' => 'हटाने के कारण संपादित करें',
'delete-toobig' => 'इस पृष्ठ का संपादन इतिहास $1 से अधिक {{PLURAL:$1|अवतरण}} होने की वजह से बहुत बड़ा है।
{{SITENAME}} के अनपेक्षित रूप से बंद होने से रोकने के लिये ऐसे पृष्ठों को हटाने की अनुमति नहीं है।',
इस पृष्ठ का अन्तिम संपादन [[User:$3|$3]] ([[User talk:$3|वार्ता]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) ने किया है।',
'editcomment' => "संपादन सारांश था: \"''\$1''\"।",
'revertpage' => '[[Special:Contributions/$2|$2]] ([[User talk:$2|Talk]]) के संपादनों को हटाकर [[User:$1|$1]] के अन्तिम अवतरण को पूर्ववत किया',
-'revertpage-nouser' => '(सदसà¥\8dय नाम हà¤\9fाया à¤\97या हà¥\88) दà¥\8dवारा à¤\95िà¤\8f à¤\97à¤\8f सà¤\82पादन à¤\95à¥\8b वापिस पà¥\81रानà¥\80 सà¥\8dथिति मà¥\87à¤\82 ला à¤\95र à¤\87सà¤\95à¥\87 पहलà¥\87 à¤\95à¥\87 [[User:$1|$1]] दà¥\8dवारा बनà¥\87 à¤\85वतरण à¤\95à¥\8b फिर सà¥\87 ताà¤\9c़ा à¤\85वतरण बनाया।',
+'revertpage-nouser' => '(सदसà¥\8dय नाम हà¤\9fाया à¤\97या हà¥\88) à¤\95à¥\87 सà¤\82पादनà¥\8bà¤\82 à¤\95à¥\8b हà¤\9fाà¤\95र {{GENDER:$1|[[User:$1|$1]]}} à¤\95à¥\87 à¤\85नà¥\8dतिम à¤\85वतरण à¤\95à¥\8b पà¥\82रà¥\8dववत à¤\95िया।',
'rollback-success' => '$1 के संपादन हटाए;
$2 द्वारा संपादित अन्तिम अवतरण को पुनर्स्थापित किया।',
**अफलदायी सम्पादन युद्ध
**अधिक यातायात वाला पृष्ठ',
'protect-edit-reasonlist' => 'सुरक्षा के कारण बदलें',
-'protect-expiry-options' => '१ à¤\98à¤\82à¤\9fा:1 hour,१ दिन:1 day,१ सपà¥\8dताह:1 week,२ सपà¥\8dताह:2 weeks,१ महà¥\80ना:1 month,३ महà¥\80नà¥\87:3 months,६ महà¥\80नà¥\87:6 months,१ साल:1 year,हमेशा के लिए:infinite',
+'protect-expiry-options' => 'à¤\8fà¤\95 à¤\98à¤\82à¤\9fा:1 hour,à¤\8fà¤\95 दिन:1 day,à¤\8fà¤\95 सपà¥\8dताह:1 week,दà¥\8b सपà¥\8dताह:2 weeks,à¤\8fà¤\95 महà¥\80ना:1 month,तà¥\80न महà¥\80नà¥\87:3 months,à¤\9bà¤\83 महà¥\80नà¥\87:6 months,à¤\8fà¤\95 साल:1 year,हमेशा के लिए:infinite',
'restriction-type' => 'अधिकार:',
'restriction-level' => 'सुरक्षा-स्तर:',
'minimum-size' => 'न्यूनतम आकार',
'restriction-level-all' => 'कोई भी स्तर',
# Undelete
-'undelete' => 'हà¤\9fाया पृष्ठ देखें',
+'undelete' => 'हà¤\9fाà¤\8f पृष्ठ देखें',
'undeletepage' => 'हटाए गए पृष्ठ देखें और पुनर्स्थापित करें',
'undeletepagetitle' => "'''नीचे [[:$1|$1]] के हटाए गए अवतरण दर्शाए गये हैं।'''",
'viewdeletedpage' => 'हटाए गए पृष्ठ देखें',
'undelete-fieldset-title' => 'अवतरण पुरानी स्थिति पर लाएँ',
'undeleteextrahelp' => "पृष्ठ का संपूर्ण इतिहास वापस लाने के लिए सभी बक्सों से सही का निशान हटा दें और '''''{{int:undeletebtn}}''''' पर क्लिक करें।
चुनिंदा इतिहास को वापस लाने के लिए उन अवतरणों के बगल के बक्सों पर सही का निशान लगाएँ और '''''{{int:undeletebtn}}''''' पर क्लिक करें।",
-'undeleterevisions' => '$1 {{PLURAL:$1|अवतरण}} लेखागार में हैं',
+'undeleterevisions' => '$1 अवतरण लेखागार में {{PLURAL:$1|है|हैं}}',
'undeletehistory' => 'यदि आप पृष्ठ को पुनर्स्थापित करते हैं तो सभी अवतरण इतिहास में पुनर्स्थापित हो जायेंगे।
हटाने के बाद यदि एक नया पृष्ठ उसी नाम से बनाया गया है तो पुनर्स्थापित अवतरण पिछले इतिहास में दर्शित होंगे।',
'undeleterevdel' => 'यदि पुनर्स्थापन के फलस्वरूप शीर्ष पृष्ठ या फ़ाइल अवतरण आंशिक रूप से मिट सकता है, तो इसे नहीं किया जायेगा।
ऐसी स्थिति में, आपको नवीनतम मिटाए गए अवतरण को बिना सही के निशान लगाये हुए या बिना छुपाये रखना होगा।',
-'undeletehistorynoadmin' => 'यह पà¥\83षà¥\8dठनिà¤\95ाल दिया गया है।
-निà¤\95ालà¥\87 à¤\9cानà¥\87 à¤\95ा à¤\95ारन नà¥\80à¤\9aà¥\87 साराà¤\82श मà¥\87à¤\82 दिया à¤\97या हà¥\88, à¤\94र साथ हà¥\80 à¤\89न सदसà¥\8dयà¥\8bà¤\82 à¤\95à¥\87 बारà¥\87 मà¥\87à¤\82 विसà¥\8dतार à¤à¥\80 दिया à¤\97या हà¥\88, à¤\9cिनà¥\8dहà¥\8bà¤\82नà¥\87 निà¤\95ालà¥\87 à¤\9cानà¥\87 सà¥\87 पहलà¥\87 à¤\87स पà¥\83षà¥\8dठà¤\95à¥\8b सà¤\82पादित à¤\95िया हà¥\88।
-à¤\87न हà¤\9fायà¥\87 à¤\97à¤\8f à¤\85वतरणà¥\8bà¤\82 à¤\95à¥\87 विदà¥\8dयमान विषय वसà¥\8dतà¥\81 à¤\95à¥\87वल पà¥\8dरशासकों को ही उपलब्ध है।',
+'undeletehistorynoadmin' => 'यह पà¥\83षà¥\8dठहà¤\9fा दिया गया है।
+हà¤\9fाà¤\8f à¤\9cानà¥\87 à¤\95ा à¤\95ारन नà¥\80à¤\9aà¥\87 साराà¤\82श मà¥\87à¤\82 दिया à¤\97या हà¥\88, à¤\94र साथ हà¥\80 à¤\89न सदसà¥\8dयà¥\8bà¤\82 à¤\95à¥\87 बारà¥\87 मà¥\87à¤\82 विसà¥\8dतार à¤à¥\80 दिया à¤\97या हà¥\88, à¤\9cिनà¥\8dहà¥\8bà¤\82नà¥\87 हà¤\9fाà¤\8f à¤\9cानà¥\87 सà¥\87 पहलà¥\87 à¤\87स पà¥\83षà¥\8dठà¤\95à¥\8b सà¤\82पादित à¤\95िया था।
+à¤\87न हà¤\9fायà¥\87 à¤\97à¤\8f à¤\85वतरणà¥\8bà¤\82 à¤\95ा पाठà¤\95à¥\87वल पà¥\8dरबà¤\82धकों को ही उपलब्ध है।',
'undelete-revision' => '$1 ($4 को $5 बजे $3 द्वारा बनाया गया) का मिटाया हुआ संस्करण:',
'undeleterevision-missing' => 'अमान्य अथवा अनुपस्थित अवतरण।
-या तà¥\8b à¤\86प à¤\97़लत समà¥\8dपरà¥\8dà¤\95 पà¥\8dरयà¥\8bà¤\97 à¤\95र रहà¥\87 हà¥\88à¤\82, या यह à¤\85वतरण पà¥\81नरà¥\8dसà¥\8dथापित à¤\95िया à¤\9cा à¤\9aà¥\81à¤\95ा हà¥\88, à¤\85थवा à¤\87सà¥\87 लà¥\87à¤\96ाà¤\97ार सà¥\87 निà¤\95ाल दिया गया है।',
-'undelete-nodiff' => 'पà¥\81रान à¤\85वतरण नहà¥\80à¤\82 हà¥\88à¤\82।',
+या तà¥\8b à¤\86प à¤\97़लत à¤\95ड़à¥\80 पà¥\8dरयà¥\8bà¤\97 à¤\95र रहà¥\87 हà¥\88à¤\82, या यह à¤\85वतरण पà¥\81नरà¥\8dसà¥\8dथापित à¤\95िया à¤\9cा à¤\9aà¥\81à¤\95ा हà¥\88, à¤\85थवा à¤\87सà¥\87 लà¥\87à¤\96ाà¤\97ार सà¥\87 हà¤\9fा दिया गया है।',
+'undelete-nodiff' => 'à¤\95à¥\8bà¤\88 पà¥\81राना à¤\85वतरण नहà¥\80à¤\82 मिला।',
'undeletebtn' => 'वापस ले आयें',
-'undeletelink' => 'दà¥\87à¤\96à¥\87à¤\82/पà¥\81रानà¥\80 सà¥\8dथिति पर लाà¤\8fà¤\81',
+'undeletelink' => 'दà¥\87à¤\96à¥\87à¤\82/पà¥\81नरà¥\8dसà¥\8dथापित à¤\95रà¥\87à¤\82',
'undeleteviewlink' => 'देखें',
'undeletereset' => 'पूर्ववत करें',
'undeleteinvert' => 'चुनाव उलटें',
-'undeletecomment' => 'à¤\9fिपà¥\8dपणà¥\80 हà¤\9fाना',
-'undeletedrevisions' => '{{PLURAL:$1|à¤\8fà¤\95 रà¥\82पानà¥\8dतर वापस लाया à¤\97या|$1 रà¥\82पानà¥\8dतर वापस लायà¥\87 à¤\97यà¥\87}} हà¥\88',
-'undeletedrevisions-files' => '{{PLURAL:$1|1 à¤\85वतरण|$1 à¤\85वतरण}} à¤\94र {{PLURAL:$2|1 फ़ाà¤\88ल|$2 फ़ाà¤\87लà¥\87à¤\82}} पà¥\81नरà¥\8dसà¥\8dथापित à¤\95र दियà¥\87ं',
-'undeletedfiles' => '{{PLURAL:$1|1 फ़ाà¤\88ल|$1 फ़ाà¤\88लें}} पुनर्स्थापित',
+'undeletecomment' => 'à¤\95ारण:',
+'undeletedrevisions' => '{{PLURAL:$1|à¤\8fà¤\95 à¤\85वतरण पà¥\81नरà¥\8dसà¥\8dथापित à¤\95िया|$1 à¤\85वतरण पà¥\81नरà¥\8dसà¥\8dथापित à¤\95ियà¥\87}}',
+'undeletedrevisions-files' => '{{PLURAL:$1|1 à¤\85वतरण|$1 à¤\85वतरण}} à¤\94र {{PLURAL:$2|1 फ़ाà¤\87ल|$2 फ़ाà¤\87लà¥\87à¤\82}} पà¥\81नरà¥\8dसà¥\8dथापित à¤\95र दà¥\80ं',
+'undeletedfiles' => '{{PLURAL:$1|1 फ़ाà¤\87ल|$1 फ़ाà¤\87लें}} पुनर्स्थापित',
'cannotundelete' => 'पुनर्स्थापित नहीं कर सके:
$1',
'undeletedpage' => "'''$1 को पुनर्स्थापित कर दिया गया है'''
हाल में हटाये गये तथा पुनर्स्थापित किये गए पन्नों की जानकारी के लिये [[Special:Log/delete|हटाने की लॉग]] देखें।",
-'undelete-header' => 'हाल में हटाये गये पृष्ठ देखने के लियें [[Special:Log/delete|हटाने की सूची]] देखें।',
+'undelete-header' => 'हाल में हटाये गये पृष्ठ देखने के लिये [[Special:Log/delete|हटाने का लॉग]] देखें।',
'undelete-search-title' => 'हटाये गये पृष्ठ खोजें',
'undelete-search-box' => 'हटाये गये पृष्ठ खोजें',
'undelete-search-prefix' => 'शुरूआती शब्द अनुसार पृष्ठ खोजें:',
'undelete-search-submit' => 'खोजें',
-'undelete-no-results' => 'हà¤\9fायà¥\87à¤\82 à¤\97यà¥\87à¤\82 पनà¥\8dनà¥\8bà¤\82à¤\95à¥\87 à¤\86रà¥\8dà¤\9aिवà¥\8dहमà¥\87à¤\82 मà¥\87ल à¤\96ानà¥\87 वालà¥\87 पà¥\83षà¥\8dठमिलà¥\87 नहà¥\80à¤\82।',
-'undelete-filename-mismatch' => '$1 समयà¤\95à¥\87 फ़ाà¤\87लà¤\95à¥\87 हà¤\9fायà¥\87 à¤\97यà¥\87 à¤\85वतरणà¤\95à¥\8b पà¥\81नरà¥\8dसà¥\8dथापित नहà¥\80à¤\82 à¤\95िया à¤\9cा सà¤\95ता: फ़ाà¤\88ल का नाम मेल नहीं खाता',
-'undelete-bad-store-key' => '$1 समयà¤\95ा फ़ाà¤\88ल à¤\85वतरण पà¥\81नरà¥\8dसà¥\8dथापित नहà¥\80à¤\82 à¤\95र सà¤\95तà¥\87à¤\82 हà¥\88à¤\82: हà¤\9fानà¥\87 सà¥\87 पहलà¥\87 फ़ाà¤\88ल à¤\85सà¥\8dतितà¥\8dवमà¥\87à¤\82 नहीं थी।',
-'undelete-cleanup-error' => 'à¤\87सà¥\8dतà¥\87मालमà¥\87à¤\82 न लाà¤\88 à¤\97à¤\88 "$1" à¤\86रà¥\8dà¤\9aिवà¥\8dह फ़ाà¤\88ल हà¤\9fानà¥\87 मà¥\87à¤\82 समसà¥\8dया हà¥\81à¤\88 हà¥\88à¤\82।',
-'undelete-missing-filearchive' => 'सिà¤\9aिà¤\95ा पà¥\81रालà¥\87à¤\96 à¤\95à¥\8dरमाà¤\82à¤\95 $1 à¤\95à¥\8b पà¥\81नरà¥\8dसà¥\8dथापित à¤\95रनà¥\87 मà¥\87à¤\82 à¤\85सà¤\95à¥\8dषम हà¥\88à¤\82, à¤\95à¥\8dयà¥\8bà¤\82à¤\95ि यह à¤\86à¤\81à¤\95ड़ाà¤\95à¥\8bष में उपलब्ध नहीं है।
+'undelete-no-results' => 'हà¤\9fाà¤\8f à¤\97à¤\8f पà¥\83षà¥\8dठà¥\8bà¤\82 à¤\95à¥\87 लà¥\87à¤\96ाà¤\97ार मà¥\87à¤\82 मà¥\87ल à¤\96ातà¥\87 à¤\95à¥\8bà¤\88 पà¥\83षà¥\8dठनहà¥\80à¤\82 मिलà¥\87।',
+'undelete-filename-mismatch' => '$1 à¤\95à¥\87 फ़ाà¤\87ल à¤\95à¥\87 हà¤\9fायà¥\87 à¤\97यà¥\87 à¤\85वतरण à¤\95à¥\8b पà¥\81नरà¥\8dसà¥\8dथापित नहà¥\80à¤\82 à¤\95िया à¤\9cा सà¤\95ता: फ़ाà¤\87ल का नाम मेल नहीं खाता',
+'undelete-bad-store-key' => '$1 à¤\95ा फ़ाà¤\87ल à¤\85वतरण पà¥\81नरà¥\8dसà¥\8dथापित नहà¥\80à¤\82 à¤\95र सà¤\95तà¥\87 हà¥\88à¤\82: हà¤\9fानà¥\87 सà¥\87 पहलà¥\87 à¤à¥\80 फ़ाà¤\87ल मà¥\8cà¤\9cà¥\82द नहीं थी।',
+'undelete-cleanup-error' => 'पà¥\81रालà¥\87à¤\96 मà¥\87à¤\82 सà¥\87 à¤\85पà¥\8dरयà¥\81à¤\95à¥\8dत फ़ाà¤\87ल "$1" हà¤\9fानà¥\87 मà¥\87à¤\82 तà¥\8dरà¥\81à¤\9fि।',
+'undelete-missing-filearchive' => 'फ़ाà¤\87ल पà¥\81रालà¥\87à¤\96 à¤\86à¤\88॰डà¥\80 $1 à¤\95à¥\8b पà¥\81नरà¥\8dसà¥\8dथापित à¤\95रनà¥\87 मà¥\87à¤\82 à¤\85सà¤\95à¥\8dषम हà¥\88à¤\82, à¤\95à¥\8dयà¥\8bà¤\82à¤\95ि यह डाà¤\9fाबà¥\87स में उपलब्ध नहीं है।
या ऐसा भी हो सकता है कि इसे पहले से ही पुनर्स्थापित किया जा चुका हो।',
-'undelete-error' => 'पà¥\83षà¥\8dठà¤\85विलà¥\8bपन में त्रुटि',
-'undelete-error-short' => 'फ़ाà¤\88ल पà¥\81नरà¥\8dसà¥\8dथापित à¤\95रनà¥\87 मà¥\87à¤\82 समसà¥\8dया: $1',
-'undelete-error-long' => 'फ़ाà¤\88ल पà¥\81नरà¥\8dसà¥\8dथापित à¤\95रनà¥\87 मà¥\87à¤\82 à¤\86à¤\88 हà¥\81à¤\88 समसà¥\8dयाà¤\8fà¤\82:
+'undelete-error' => 'पà¥\83षà¥\8dठपà¥\81नरà¥\8dसà¥\8dथापन में त्रुटि',
+'undelete-error-short' => 'फ़ाà¤\87ल पà¥\81नरà¥\8dसà¥\8dथापन मà¥\87à¤\82 तà¥\8dरà¥\81à¤\9fि: $1',
+'undelete-error-long' => 'फ़ाà¤\87ल पà¥\81नरà¥\8dसà¥\8dथापन मà¥\87à¤\82 à¤\86à¤\88 तà¥\8dरà¥\81à¤\9fियाà¤\81:
$1',
'undelete-show-file-confirm' => 'क्या आप वाकई फ़ाइल "<nowiki>$1</nowiki>" के $2 को $3 बजे बने, हटाए जा चुके अवतरण को देखना चाहते हैं?',
'ipbenableautoblock' => 'इस सदस्यद्वारा इस्तेमाल किया गया आखिरी आईपी एड्रेस और यहां से आगे इस सदस्य द्वारा इस्तेमालमें लाये जाने वाले सभी एड्रेस ब्लॉक करें।',
'ipbsubmit' => 'इस सदस्य को और बदलाव करने से रोकें',
'ipbother' => 'अन्य समय:',
-'ipboptions' => '२ à¤\98à¤\82à¤\9fà¥\87:2 hours,१ दिन:1 day,३ दिन:3 days,१ हफà¥\8dता:1 week,२ हफà¥\8dतà¥\87:2 weeks,१ महिना:1 month,३ महिनà¥\87:3 months,६ महिनà¥\87:6 months,१ साल:1 year,हमेशा के लिये:infinite',
+'ipboptions' => 'दà¥\8b à¤\98à¤\82à¤\9fà¥\87:2 hours,à¤\8fà¤\95 दिन:1 day,तà¥\80न दिन:3 days,à¤\8fà¤\95 सपà¥\8dताह:1 week,दà¥\8b सपà¥\8dताह:2 weeks,à¤\8fà¤\95 महà¥\80ना:1 month,तà¥\80न महà¥\80नà¥\87:3 months,à¤\9bà¤\83 महà¥\80नà¥\87:6 months,à¤\8fà¤\95 साल:1 year,हमेशा के लिये:infinite',
'ipbotheroption' => 'अन्य',
'ipbotherreason' => 'अन्य/दूसरा कारण:',
'ipbhidename' => 'संपादन व सूचियों से सदस्य नाम छिपाएँ',
'tooltip-ca-protect' => 'इस पृष्ठको सुरक्षित किजीयें',
'tooltip-ca-unprotect' => 'इस पृष्ठ की सुरक्षा बदलें ।',
'tooltip-ca-delete' => 'इस पृष्ठ को हटाएं',
-'tooltip-ca-undelete' => 'इस पृष्ठको हटाने से पहले किये गये बदलाव पुनर्स्थापित करें',
+'tooltip-ca-undelete' => 'इस पृष्ठ को हटाने से पहले किये गये बदलाव पुनर्स्थापित करें',
'tooltip-ca-move' => 'यह पृष्ठ स्थानांतरित करें',
'tooltip-ca-watch' => 'इस पृष्ठ को अपनी ध्यानसूची में डालें',
'tooltip-ca-unwatch' => 'यह पृष्ठ अपने ध्यानसूचीसे हटाएं',
'underline-default' => 'Prema postavkama preglednika',
# Font style option in Special:Preferences
-'editfont-style' => 'Uredi područje font stila:',
+'editfont-style' => 'Font u okviru za uređivanje',
'editfont-default' => 'Prema postavkama preglednika',
'editfont-monospace' => 'Font s jednakim razmakom',
'editfont-sansserif' => 'Font Sans-serif',
'newwindow' => '(otvara se u novom prozoru)',
'cancel' => 'Odustani',
'moredotdotdot' => 'Više...',
+'morenotlisted' => 'Ovaj popis nije potpun.',
'mypage' => 'Stranica',
'mytalk' => 'Moj razgovor',
'anontalk' => 'Razgovor za ovu IP adresu',
'create-this-page' => 'Započni ovu stranicu',
'delete' => 'Izbriši',
'deletethispage' => 'Izbriši ovu stranicu',
+'undeletethispage' => 'Vrati ovu stranicu',
'undelete_short' => 'Vrati {{PLURAL:$1|$1 uređivanje|$1 uređivanja}}',
'viewdeleted_short' => 'Prikaži $1 {{plural: $1|izbrisano uređivanje|izbrisana uređivanja|izbrisanih uređivanja}}',
'protect' => 'Zaštiti',
'mailerror' => 'Pogrješka pri slanju e-pošte: $1',
'acct_creation_throttle_hit' => 'Posjetitelji ovog wikija koji rabe Vašu IP adresu napravili su {{PLURAL:$1|1 račun|$1 računa}} u posljednjem danu, što je najveći dopušteni broj u tom vremenskom razdoblju.
Zbog toga posjetitelji s ove IP adrese trenutačno ne mogu otvoriti nove suradničke račune.',
-'emailauthenticated' => 'Vaša e-mail adresa je ovjerena $2 u $3.',
+'emailauthenticated' => 'vaša e-mail adresa je ovjerena $2 u $3.',
'emailnotauthenticated' => 'Vaša e-mail adresa još nije ovjerena.
Ne možemo poslati e-mail ni u jednoj od sljedećih naredbi.',
'noemailprefs' => 'Nije navedena adresa elektroničke pošte, stoga sljedeće naredbe ne će raditi.',
'passwordreset-text-one' => 'Ispunite ovaj obrazac ako želite ponovno postaviti Vašu zaporku.',
'passwordreset-legend' => 'Poništi lozinku',
'passwordreset-disabled' => 'Poništavanje lozinke je onemogućeno na ovom wikiju.',
+'passwordreset-emaildisabled' => 'Funkcija e-pošte je onemogućena na ovom wikiju.',
'passwordreset-username' => 'Suradničko ime:',
'passwordreset-domain' => 'Domena:',
'passwordreset-capture' => 'Pogledati krajnju poruku?',
'stub-threshold-disabled' => 'Onemogućeno',
'recentchangesdays' => 'Broj dana prikazanih u nedavnim promjenama:',
'recentchangesdays-max' => '(maksimalno $1 {{PLURAL:$1|dan|dana}})',
-'recentchangescount' => 'Broj izmjena za prikaz kao zadano:',
+'recentchangescount' => 'Zadani broj izmjena koje se prikazuju:',
'prefs-help-recentchangescount' => 'Ovo uključuje nedavne promjene, stare izmjene, i evidencije.',
'savedprefs' => 'Vaše postavke su sačuvane.',
'timezonelegend' => 'Vremenska zona:',
'prefs-editor' => 'Uređivač',
'prefs-preview' => 'Prikaži kako će izgledati',
'prefs-advancedrc' => 'Napredne mogućnosti',
-'prefs-advancedrendering' => 'Napredne opcije',
-'prefs-advancedsearchoptions' => 'Napredne opcije',
-'prefs-advancedwatchlist' => 'Napredne opcije',
+'prefs-advancedrendering' => 'Napredne mogućnosti',
+'prefs-advancedsearchoptions' => 'Napredne mogućnosti',
+'prefs-advancedwatchlist' => 'Napredne mogućnosti',
'prefs-displayrc' => 'Prikaži opcije',
'prefs-displaysearchoptions' => 'Mogućnosti prikaza',
'prefs-displaywatchlist' => 'Mogućnosti prikaza',
# Recent changes
'nchanges' => '{{PLURAL:$1|$1 promjena|$1 promjene|$1 promjena}}',
+'enhancedrc-history' => 'povijest',
'recentchanges' => 'Nedavne promjene',
'recentchanges-legend' => 'Izbornik nedavnih promjena',
'recentchanges-summary' => 'Na ovoj stranici možete pratiti nedavne promjene u wikiju.',
'listfiles_size' => 'Veličina (u bajtovima)',
'listfiles_description' => 'Opis',
'listfiles_count' => 'Inačice',
+'listfiles-latestversion-yes' => 'Da',
+'listfiles-latestversion-no' => 'Ne',
# File description page
'file-anchor-link' => 'Slika',
'randompage' => 'Slučajna stranica',
'randompage-nopages' => 'Nema stranica u {{PLURAL:$2|imenskom prostoru|imenskim prostorima}}: $1.',
+# Random page in category
+'randomincategory-selectcategory-submit' => 'Idi',
+
# Random redirect
'randomredirect' => 'Slučajno preusmjeravanje',
'randomredirect-nopages' => 'Nema preusmjeravanja u imenskom prostoru "$1".',
# Spam protection
'spamprotectiontitle' => 'Filtrilo kontre spamo',
+# Info page
+'pageinfo-toolboxlink' => 'Informo di ca pagino',
+
# Browsing diffs
'previousdiff' => '← Plu anciena versiono',
'nextdiff' => 'Plu recenta versiono →',
* @author Dakrismeno
* @author Danmaz74
* @author Darth Kule
+ * @author DexterMorgan
* @author F. Cosoleto
* @author Felis
* @author FollowTheMedia
$messages = array(
# User preference toggles
'tog-underline' => 'Sottolinea i collegamenti:',
-'tog-justify' => 'Allineamento dei paragrafi giustificato',
+'tog-justify' => 'Allineamento giustificato dei paragrafi',
'tog-hideminor' => 'Nascondi le modifiche minori nelle ultime modifiche',
'tog-hidepatrolled' => 'Nascondi le modifiche verificate nelle ultime modifiche',
'tog-newpageshidepatrolled' => "Nascondi le pagine verificate dall'elenco delle pagine più recenti",
'deletecomment' => 'Motivo:',
'deleteotherreason' => 'Altra motivazione o motivazione aggiuntiva:',
'deletereasonotherlist' => 'Altra motivazione',
-'deletereason-dropdown' => "*Motivazioni più comuni per la cancellazione
-** Richiesta dell'autore
+'deletereason-dropdown' => "* Motivazioni più comuni per la cancellazione
+** Spam
+** Vandalismo
** Violazione di copyright
-** Vandalismo",
+** Richiesta dell'autore
+** Redirect rotto",
'delete-edit-reasonlist' => 'Modifica i motivi di cancellazione',
'delete-toobig' => 'La cronologia di questa pagina è molto lunga (oltre $1 {{PLURAL:$1|revisione|revisioni}}). La sua cancellazione è stata limitata per evitare di creare accidentalmente dei problemi di funzionamento al database di {{SITENAME}}.',
'delete-warning-toobig' => 'La cronologia di questa pagina è molto lunga (oltre $1 {{PLURAL:$1|revisione|revisioni}}). La sua cancellazione può creare dei problemi di funzionamento al database di {{SITENAME}}; procedere con cautela.',
'deleteotherreason' => '他の、または追加の理由:',
'deletereasonotherlist' => 'その他の理由',
'deletereason-dropdown' => '*よくある削除理由
-** 投稿者依頼
+** スパム
+** 荒らし
** 著作権侵害
-** 荒らし',
+** 投稿者依頼
+** 破損リダイレクト',
'delete-edit-reasonlist' => '削除理由を編集',
'delete-toobig' => 'このページには、$1版を超える編集履歴があります。
このようなページの削除は、{{SITENAME}}の偶発的な問題を避けるため、制限されています。',
'userlogin-noaccount' => '계정이 없나요?',
'userlogin-joinproject' => '{{SITENAME}}에 가입하세요',
'nologin' => '계정이 없나요? $1.',
-'nologinlink' => '계정을 만들 수 있습니다',
+'nologinlink' => '계정을 만들기',
'createaccount' => '계정 만들기',
'gotaccount' => '계정이 이미 있다면, $1.',
'gotaccountlink' => '로그인하세요',
'hiddencategories' => '이 문서는 다음 {{PLURAL:$1|숨은 분류 1개|숨은 분류 $1개}}에 속해 있습니다:',
'edittools' => '<!-- 이 문서는 편집 창과 파일 올리기 창에 출력됩니다. -->',
'nocreatetext' => '{{SITENAME}}에서 새로운 문서를 만드는 것은 제한되어 있습니다.
-이미 존재하는 다른 문서를 편집하거나, [[Special:UserLogin|로그인하거나 계정을 만들 수 있습니다]].',
+이미 존재하는 다른 문서를 편집하거나, [[Special:UserLogin|로그인하거나 계정을 만들]] 수 있습니다.',
'nocreate-loggedin' => '새 문서를 만들 권한이 없습니다.',
'sectioneditnotsupported-title' => '부분 편집 지원 안됨',
'sectioneditnotsupported-text' => '이 문서에서는 문단 편집을 지원하지 않습니다.',
'deletecomment' => '이유:',
'deleteotherreason' => '다른 이유/추가적인 이유:',
'deletereasonotherlist' => '다른 이유',
-'deletereason-dropdown' => '*일반적인 삭제 이유
-** 작성자의 요청
+'deletereason-dropdown' => '* 일반적인 삭제 이유
+** 스팸
+** 문서 훼손 행위
** 저작권 침해
-** 훼손 행위',
+** 작성자의 요청
+** 깨진 넘겨주기',
'delete-edit-reasonlist' => '삭제 이유 편집',
'delete-toobig' => '이 문서에는 {{PLURAL:$1|편집 역사}}가 $1개 있습니다.
편집 역사가 긴 문서를 삭제하면 {{SITENAME}}에 큰 혼란을 줄 수 있기 때문에 삭제할 수 없습니다.',
'deleteotherreason' => 'Causa alia vel explicatio:',
'deletereasonotherlist' => 'Causa alia',
'deletereason-dropdown' => '*Causae deletionum communes
-** Desiderium auctoris
+** Spam
+** Vandalismus
** Violatio verborum privatorum
-** Vandalismus',
+** Desiderium auctoris
+** Redirectio fracta',
'delete-edit-reasonlist' => 'Causas deletionum recensere',
# Rollback
'deletereason-dropdown' => '* Heefegst Grënn fir eng Säit ze läschen
** Wonsch vum Auteur
** Verletzung vun engem Copyright
-** Vandalismus',
+** Vandalismus
+** Futtis Viruleedung
+** Spam',
'delete-edit-reasonlist' => 'Läschgrënn änneren',
'delete-toobig' => "Dës Säit huet e laangen Historique, méi wéi $1 {{PLURAL:$1|Versioun|Versiounen}}.
D'Läsche vu sou Säite gouf limitéiert fir ongewollte Stéierungen op {{SITENAME}} ze verhënneren.",
'import-interwiki-templates' => 'Mat alle Schablounen',
'import-interwiki-submit' => 'Import',
'import-interwiki-namespace' => 'Zil-Nummraum:',
+'import-interwiki-rootpage' => 'Zil-Stamm-Säit (fakultativ):',
'import-upload-filename' => 'Numm vum Fichier:',
'import-comment' => 'Bemierkung:',
'importtext' => 'Exportéiert de Fichier w.e.g. vun der Source-Wiki mat der [[Special:Export|Export-Funktioun]].
* @author Siggis
* @author Tomasdd
* @author Urhixidur
+ * @author Vilius2001
* @author Vpovilaitis
* @author לערי ריינהארט
*/
'action-block' => 'neleisti šiam naudotojui redaguoti',
'action-protect' => 'pakeisti apsaugos lygius šiam puslapiui',
'action-rollback' => 'greitai atmesti paskutinio naudotojo atliktų tam tikro puslapio pakeitimų',
-'action-import' => 'importuoti šį puslapį iš kitos wiki',
-'action-importupload' => 'importuoti šį puslapį iš įkelto failo',
+'action-import' => 'importuoti puslapius iš kitos wiki',
+'action-importupload' => 'importuoti puslapius iš įkelto failo',
'action-patrol' => 'pažymėti kitų keitimus kaip patikrintus',
'action-autopatrol' => 'savo keitimų pažymėjimas patikrintais',
'action-unwatchedpages' => 'žiūrėti nestebimų puslapių sąrašą',
Paskutimas keitimas darytas naudotojo [[User:$3|$3]] ([[User talk:$3|Aptarimas]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
'editcomment' => "Redagavimo komentaras: „''$1''“.",
'revertpage' => 'Atmestas [[Special:Contributions/$2|$2]] ([[User talk:$2|Aptarimas]]) pakeitimas; sugrąžinta [[User:$1|$1]] versija',
-'revertpage-nouser' => 'Atmesti (naudotojo vardas pašalintas) pakeitimai, grąžinta prieš tai buvusi [[User:$1|$1]] versija',
+'revertpage-nouser' => 'Atversti pakeitimai paslėpto vartotojo, grąžino prieš tai buvusią versiją {{GENDER:$1|[[User:$1|$1]]}}',
'rollback-success' => 'Atmesti $1 pakeitimai;
grąžinta prieš tai buvusi $2 versija.',
'contributions' => '{{GENDER:$1|Naudotojo}} įndėlis',
'contributions-title' => '{{GENDER:$1|Naudotojo|Naudotojos}} $1 indėlis',
'mycontris' => 'Įnašai',
-'contribsub2' => 'Naudotojo $1 ($2)',
+'contribsub2' => 'Dėl {{GENDER:$3|$1}} ($2)',
'nocontribs' => 'Jokie keitimai neatitiko šių kriterijų.',
'uctop' => '(dabartinis)',
'month' => 'Nuo mėnesio (ir anksčiau):',
'right-move-rootuserpages' => 'Pārvietot saknes lietotāja lapas',
'right-movefile' => 'Pārvietot failus',
'right-suppressredirect' => 'Neveidot pāradresāciju no vecā nosaukuma, pārvietojot lapu',
-'right-upload' => 'Augšuplādēt failus',
+'right-upload' => 'Augšupielādēt failus',
'right-reupload' => 'Pārrakstīt esošu failu',
'right-reupload-own' => 'Pārrakstīt paša augšuplādētu esošu failu',
-'right-upload_by_url' => 'Augšuplādēt failu no URL',
+'right-upload_by_url' => 'Augšupielādēt failus no URL',
'right-autoconfirmed' => 'Izmainīt daļēji aizsargātas lapas',
'right-delete' => 'Dzēst lapas',
'right-bigdelete' => 'Dzēst lapas ar lielām hronoloģijām',
'recentchangeslinked-to' => 'Rādīt izmaiņas lapās, kurās ir saites uz šo lapu (nevis lapās uz kurām ir saites no šīs lapas)',
# Upload
-'upload' => 'Augšuplādēt failu',
-'uploadbtn' => 'Augšuplādēt',
+'upload' => 'Augšupielādēt failu',
+'uploadbtn' => 'Augšupielādēt',
'reuploaddesc' => 'Atcelt augšupielādi un atgriezties pie augšupielādes veidnes.',
'upload-tryagain' => 'Iesniegt izmainīto faila aprakstu',
'uploadnologin' => 'Neesi iegājis',
Dzēšanas un pārvietošanas reģistri šai lapai ir uzskaitīti šeit:",
'uploadtext' => "Pirms tu kaut ko augšupielādē, noteikti izlasi un ievēro [[Project:Attēlu izmantošanas noteikumi|attēlu izmantošanas noteikumus]].
-Lai aplūkotu vai meklētu agrāk augšuplādētus attēlus,
+Lai aplūkotu vai meklētu agrāk augšupielādētus attēlus,
dodies uz [[Special:FileList|augšupielādēto attēlu sarakstu]].
Augšupielādes un dzēšanas tiek reģistrētas [[Special:Log/upload|augšupielādes reģistrā]] un [[Special:Log/delete|dzēšanas reģistrā]].
Gandrīz visos pārlūkos tev vajadzētu redzēt pogu '''\"Choose...\",''' kuru spiežot parādīsies faila atvēršanas dialogs.
Izvēloties kādu failu, tā adrese parādīsies ailītē blakus šai pogai.
Tev ir arī jāatzīmē ailīte, kas apstiprina, ka tu nepārkāp nekādas autortiesības, augšupielādējot šo failu.
-Spied pogu '''Augšuplādēt''', lai pabeigtu augšupielādi.
+Spied pogu '''Augšupielādēt''', lai pabeigtu augšupielādi.
Tas var ieilgt, ja tavs interneta pieslēgums ir lēns.
Ieteicamie formāti ir:
vai skaņām
* '''<nowiki>[[</nowiki>{{ns:media}}<nowiki>:Fails.ogg]]</nowiki>'''
-Lūdzu, ņem vērā, ka tāpat kā citas wiki lapas arī tevis augšuplādētos failus citi var mainīt vai dzēst, ja uzskata, ka tas nāktu par labu šim projektam, kā arī atceries, ka tev var tikt liegta augšupielādes iespēja, ja tu šo sistēmu.",
+Lūdzu, ņem vērā, ka tāpat kā citas wiki lapas arī tevis augšupielādētos failus citi var mainīt vai dzēst, ja uzskata, ka tas nāktu par labu šim projektam, kā arī atceries, ka tev var tikt liegta augšupielādes iespēja, ja tu šo sistēmu.",
'upload-permitted' => 'Atļautie failu tipi: $1.',
'upload-preferred' => 'Ieteicamie failu tipi: $1.',
'upload-prohibited' => 'Aizliegtie failu tipi: $1.',
'ignorewarning' => 'Ignorēt brīdinājumu un saglabāt failu',
'ignorewarnings' => 'Ignorēt visus brīdinājumus',
'minlength1' => 'Failu vārdiem jābūt vismaz vienu simbolu gariem.',
-'illegalfilename' => 'Faila nosaukumā "$1" ir simboli, kas nav atļauti virsrakstos. Lūdzu, pārdēvē failu un mēģini to vēlreiz augšuplādēt.',
+'illegalfilename' => 'Faila nosaukumā "$1" ir simboli, kas nav atļauti virsrakstos. Lūdzu, pārdēvējiet failu un mēģiniet to vēlreiz augšupielādēt.',
'filename-toolong' => 'Failu nosaukumi nedrīkst pārsniegt 240 baitus.',
'badfilename' => 'Attēla nosaukums ir nomainīts, tagad tas ir "$1".',
'filetype-mime-mismatch' => 'Faila paplašinājums ".$1" neatbilst noteiktajam MIME tipam ($2).',
-'filetype-badmime' => 'Šeit nav atļauts augšuplādēt failus ar MIME tipu "$1".',
+'filetype-badmime' => 'Šeit nav atļauts augšupielādēt failus ar MIME tipu "$1".',
'filetype-bad-ie-mime' => 'Nevar augšupielādēt šo failu, jo Internet Explorer to uzskatītu kā "$1", kas ir neatļauts un potenciāli bīstams faila tips.',
'filetype-unwanted-type' => "'''\".\$1\"''' ir nevēlams failu tips. {{PLURAL:\$3|Ieteicamais faila tips|Ieteicamie failu tipi}} ir \$2.",
'filetype-banned-type' => "'''\".\$1\"''' nav atļautais failu tips. {{PLURAL:\$3|Atļautais faila tips|Atļautie failu tipi}} ir \$2.",
'large-file' => 'Ieteicams, lai faili nebūtu lielāki par $1;
šī faila izmērs ir $2.',
'largefileserver' => 'Šis fails ir lielāks nekā serveris ņem pretī.',
-'emptyfile' => 'Šķiet, ka tu esi augšuplādējis tukšu failu. Iespējams, faila nosaukumā esi pieļāvis kļūdu. Lūdzu, pārbaudi, vai tiešām tu vēlies augšuplādēt tieši šo failu.',
+'emptyfile' => 'Šķiet, ka jūs esat augšupielādējis tukšu failu.
+Iespējams, faila nosaukumā esat pieļāvis kļūdu.
+Lūdzu, pārbaudiet, vai tiešām jūs vēlaties augšupielādēt tieši šo failu.',
'windows-nonascii-filename' => 'Šī viki neatbalsta failu nosaukumus ar īpašām rakstzīmēm.',
'fileexists' => 'Fails ar šādu nosaukumu jau pastāv, lūdzu, pārbaudi <strong>[[:$1]]</strong>, ja neesi drošs, ka vēlies to mainīt.
[[$1|thumb]]',
Izskatās, ka šis ir samazināts attēls ''(thumbnail)''.
Ja tev ir šis pats attēls pilnā izmērā, augšuplādē to, ja nav, tad nomaini faila vārdu.",
'fileexists-forbidden' => 'Fails ar šādu nosaukumu jau eksistē un to nevar aizvietot ar jaunu.
-Ja tu joprojām gribi augšuplādēt šo failu, tad mēģini vēlreiz, ar citu faila vārdu. [[File:$1|thumb|center|$1]]',
+Ja jūs joprojām gribat augšupielādēt šo failu, tad mēģiniet vēlreiz ar citu faila nosaukumu.
+[[File:$1|thumb|center|$1]]',
'file-exists-duplicate' => 'Fails ir kopija {{PLURAL:$1|šim failam|šiem failiem}}:',
'uploadwarning' => 'Augšupielādes brīdinājums',
'uploadwarning-text' => 'Lūdzu, pārveido zemāk esošo faila aprakstu un mēģini vēlreiz.',
'upload-description' => 'Faila apraksts',
'upload-options' => 'Augšupielādes iestatījumi',
'watchthisupload' => 'Uzraudzīt šo failu',
-'filewasdeleted' => 'Fails ar šādu nosaukumu jau ir bijis augšuplādēts un pēc tam izdzēsts.
-Apskaties $1 pirms turpini šo failu augšuplādēt atkārtoti.',
+'filewasdeleted' => 'Fails ar šādu nosaukumu jau ir bijis augšupielādēts un pēc tam izdzēsts.
+Apskatiet $1 pirms turpiniet šo failu augšupielādēt atkārtoti.',
'filename-bad-prefix' => "Faila vārds failam, kuru tu mēģini augšpulādēt, sākas ar '''\"\$1\"''', kas ir neaprakstošs vārds, kādu parasti uzģenerē digitālais fotoaparāts.
Lūdzu izvēlies aprakstošāku vārdu šim failam.",
'upload-success-subj' => 'Augšupielāde veiksmīga',
'restriction-edit' => 'Izmainīt',
'restriction-move' => 'Pārvietot',
'restriction-create' => 'Izveidot',
-'restriction-upload' => 'Augšuplādēt',
+'restriction-upload' => 'Augšupielādēt',
# Restriction levels
'restriction-level-sysop' => 'pilnā aizsardzība',
'tooltip-feed-atom' => 'Šīs lapas Atom barotne',
'tooltip-t-contributions' => 'Apskatīt šā lietotāja ieguldījumu uzskaitījumu.',
'tooltip-t-emailuser' => 'Sūtīt e-pastu šim lietotājam',
-'tooltip-t-upload' => 'Augšuplādēt attēlus vai multimēdiju failus',
+'tooltip-t-upload' => 'Augšupielādēt failus',
'tooltip-t-specialpages' => 'Visu īpašo lapu uzskaitījums',
'tooltip-t-print' => 'Drukājama lapas versija',
'tooltip-t-permalink' => 'Paliekoša saite uz šo lapas versiju',
'histlegend' => "Таҥастарашлаш ӱлыл версийыште ойырымаш полдышым да Enter-ым темдал.<br />
Умылтарымаш: (кызыт) = кызытсе версий деч ойыртем, (ончычсо) = ончычсо версий деч ойыртем, '''и''' = изи тӧрлатымаш.",
'history-fieldset-title' => 'Эртымгорным ончыкташ',
-'histfirst' => 'Эн тошто',
-'histlast' => 'Эн у',
+'histfirst' => 'эн тошто',
+'histlast' => 'эн у',
'historyempty' => '(яра)',
# Revision feed
'notextmatches' => 'Лаштык-влакыште икгайлык возымо уке',
'prevn' => 'кодшо {{PLURAL:$1|$1}}',
'nextn' => 'весе {{PLURAL:$1|$1}}',
+'prevn-title' => 'Кодшо $1 {{PLURAL:$1|результат}}',
+'nextn-title' => 'Весе $1 {{PLURAL:$1|результат}}',
'shown-title' => 'Лаштыкыште $1 {{PLURAL:$1|возымаш|возымашым}} ончыкташ',
'viewprevnext' => 'Ончал ($1 {{int:pipe-separator}} $2) ($3)',
'searchmenu-new' => "'''Тиде вики-проектыште «[[:$1]]» лӱман лаштыкым ышташ!'''",
'search-interwiki-more' => '(эше)',
'searchrelated' => 'кылдалтше',
'searchall' => 'чыла',
+'showingresultsheader' => "'''$4'''лан {{PLURAL:$5|'''$3''' гыч '''$1''' результат|'''$3''' гыч '''$1 - $2''' результат}}",
'nonefound' => "'''Ешартыш''': Посна палемдыме огыл гын, кычалмаш южо лӱм-влак коклаште гына эрта. Чыла лаштык-влак коклаште кычалашлан (каҥашымаш, ямдылык-влак да т.м.) шке йодмашыштет ''all:'' префиксым кучылт, але кӱлешан лӱм-влакым палемде.",
'search-nonefound' => 'Тыйын йодышет почеш нимо муалтын огыл',
'powersearch' => 'Сайынрак кычал',
'minoreditletter' => 'и',
'newpageletter' => 'У',
'boteditletter' => 'б',
-'rc-enhanced-expand' => 'Ð\9fоказаÑ\82Ñ\8c деÑ\82али (JavaScript кӱлеÑ\88)',
+'rc-enhanced-expand' => 'ТиÑ\87маÑ\88Ñ\8bн онÑ\87Ñ\8bкÑ\82аÑ\88',
'rc-enhanced-hide' => 'Рашлык-влакым шылташ',
# Recent changes linked
'linksearch' => 'Ӧрдыж кылвер-влак',
'linksearch-ns' => 'Лӱм-влакын кумдыкышт:',
'linksearch-ok' => 'Кычал',
+'linksearch-line' => '$2 лаштыкыште $1 ончыкталтын',
# Special:ListUsers
'listusers-submit' => 'ончыкташ',
'watchthispage' => 'Тиде лаштыкым эскераш',
'unwatch' => 'Эскерыман огыл',
'unwatchthispage' => 'Эскерымым чарнаш',
-'watchlist-details' => 'Эскерымаш лӱмерыштет $1 {{PLURAL:$1|лаштык|лаштык}} (каҥашымаш лаштык-влакым шотлыде)',
+'watchlist-details' => 'Эскерымаш лӱмерыштет $1 {{PLURAL:$1|лаштык}}, каҥашымаш лаштык-влакым шотлыде',
'watchlistcontains' => 'Тыйын лӱмерыште $1 {{PLURAL:$1|лаштык|лаштык}}.',
'wlshowlast' => 'Пытартыш $1 шагат $2 кечылан $3 ончыкташ',
'watchlist-options' => 'Эскерыме лӱмерын келыштарымаш',
# Undelete
'undeletelink' => 'ончалаш/тӧрлатен шындаш',
+'undeleteviewlink' => 'ончыкташ',
'undelete-search-submit' => 'Кычал',
# Namespace form on various pages
# Thumbnails
'thumbnail-more' => 'Кугемдаш',
+'thumbnail_error' => 'Изи сӱретым ыштыме годым йоҥылыш: $1',
# Tooltip help for the actions
'tooltip-pt-userpage' => 'Тыйын лаштыкет',
'deletecomment' => 'Причина:',
'deleteotherreason' => 'Друга/дополнителна причина:',
'deletereasonotherlist' => 'Друга причина',
-'deletereason-dropdown' => '*Вообичаени причини за бришење
-** На барање на авторот
+'deletereason-dropdown' => '* Вообичаени причини за бришење
+** Спам
+** Вандализам
** Прекршување на авторски права
-** Вандализам',
+** На барање на авторот
+** Прекинато пренасочување',
'delete-edit-reasonlist' => 'Уреди причини за бришење',
'delete-toobig' => 'Оваа страница има долга историја на уредување, преку $1 {{PLURAL:$1|ревизија|ревизии}}.
Бришењето на ваквии страници е забрането со цел {{SITENAME}} да се заштити од оштетувања.',
'userlogin-resetpassword-link' => 'Joew wachtwoord opniej instellen',
'helplogin-url' => 'Help:Anmelden',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Hulpe bie t anmelden]]',
+'userlogin-loggedin' => 'Je bin al an-emeld as {{GENDER:$1|$1}}.
+Gebruuk t onderstaonde formulier um an te melden as n aandere gebruker.',
+'userlogin-createanother' => 'n Aandere gebrukerskonto anmaken',
'createacct-join' => 'Geef joew gegevens hieronder op.',
'createacct-another-join' => 'Vul hieronder de informasie van de nieje gebruker in.',
'createacct-emailrequired' => 'Netpostadres',
'deleteotherreason' => 'Andere reden:',
'deletereasonotherlist' => 'Andere reden',
'deletereason-dropdown' => '*Veel voorkomende verwijderredenen
-** Op aanvraag van auteur
+** Spam
+** Vandalisme
** Schending van auteursrechten
-** Vandalisme',
+** Op aanvraag van auteur
+** Kapotte doorverwijzing',
'delete-edit-reasonlist' => 'Redenen voor verwijderen bewerken',
'delete-toobig' => "Deze pagina heeft een lange bewerkingsgeschiedenis, meer dan $1 {{PLURAL:$1|versie|versies}}.
Het verwijderen van dit soort pagina's is met rechten beperkt om het per ongeluk verstoren van de werking van {{SITENAME}} te voorkomen.",
'tags-tag' => 'Merkenamn',
'tags-display-header' => 'Utsjånad på endringslister',
'tags-description-header' => 'Tyding',
+'tags-active-header' => 'Verksamt?',
'tags-hitcount-header' => 'Merkte endringar',
+'tags-active-yes' => 'Ja',
+'tags-active-no' => 'Nei',
'tags-edit' => 'endra',
'tags-hitcount' => '{{PLURAL:$1|éi endring|$1 endringar}}',
ନିଜର [[Special:Preferences|{{SITENAME}} ପସନ୍ଦସବୁକୁ]] ବଦଳାଇବାକୁ ଭୁଲିବେ ନାହିଁ ।',
'yourname' => 'ବ୍ୟବହାରକାରୀଙ୍କ ନାମ:',
'userlogin-yourname' => 'ବ୍ୟବହାରକାରୀଙ୍କ ନାମ',
+'userlogin-yourname-ph' => 'ଆପଣଙ୍କ ଇଉଜର ନାମ ଟାଇପ କରନ୍ତୁ',
+'createacct-another-username-ph' => 'ଆପଣଙ୍କ ଇଉଜର ନାମ ଟାଇପ କରନ୍ତୁ',
'yourpassword' => 'ପାସୱାର୍ଡ଼',
'userlogin-yourpassword' => 'ପାସୱାର୍ଡ଼',
'userlogin-yourpassword-ph' => 'ଆପଣଙ୍କ ପାସୱାର୍ଡ଼ ନିବେଶ କରନ୍ତୁ',
'createacct-yourpasswordagain' => 'ପାସୱର୍ଡ଼ ନିଶ୍ଚିତ କରିବେ',
'createacct-yourpasswordagain-ph' => 'ପୁଣି ପାସୱର୍ଡ଼ ନିବେଶ କରନ୍ତୁ',
'remembermypassword' => 'ଏହି ବ୍ରାଉଜରରେ (ସବୁଠୁ ଅଧିକ ହେଲେ $1 {{PLURAL:$1|day|ଦିନ}}) ପାଇଁ ମୋ ଲଗଇନ ମନେ ରଖିଥିବେ',
+'userlogin-remembermypassword' => 'ମୋତେ ଲଗ-ଇନ କରି ରଖିଥାନ୍ତୁ',
+'userlogin-signwithsecure' => 'ନିରାପଦ କନେକସନ ବ୍ୟବହାର କରନ୍ତୁ',
'yourdomainname' => 'ଆପଣଙ୍କ ଡୋମେନ:',
'password-change-forbidden' => 'ଆପଣ ଏହି ଉଇକିରେ ପାସୱାର୍ଡ ବଦଳାଇ ପାରିବେ ନାହିଁ ।',
'externaldberror' => 'ବୋଧ ହୁଏ ଚିହ୍ନଟ ଡାଟାବେସ ଭୁଲଟିଏ ହୋଇଥିଲା ବା ଆପଣଙ୍କୁ ନିଜର ବାହାର ଖାତା ଅପଡେଟ କରିବା ନିମନ୍ତେ ଅନୁମତି ମିଳିନାହିଁ ।',
'userlogout' => 'ଲଗ ଆଉଟ',
'notloggedin' => 'ଲଗ ଇନ କରିନାହାନ୍ତି',
'userlogin-noaccount' => 'ଖାତାଟିଏ ନାହିଁ?',
+'userlogin-joinproject' => '{{SITENAME}}ରେ ଯୋଗଦିଅନ୍ତୁ',
'nologin' => 'ଖାତାଟିଏ ନାହିଁ? $1।',
'nologinlink' => 'ନୂଆ ଖାତାଟିଏ ଖୋଲନ୍ତୁ',
'createaccount' => 'ନୂଆ ଖାତାଟିଏ ଖୋଲନ୍ତୁ',
'gotaccount' => 'ଆଗରୁ ଖାତାଟିଏ ଅଛି କି? $1.',
'gotaccountlink' => 'ଲଗ ଇନ (Log in)',
'userlogin-resetlink' => 'ଲଗଇନ ତଥ୍ୟ ସବୁ ଭୁଲିଗେଲେକି?',
+'userlogin-resetpassword-link' => 'ପାସୱାର୍ଡ଼ ରିସେଟ କରନ୍ତୁ',
+'helplogin-url' => 'Help:Logging_in',
+'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|ଲଗ-ଇନ କରିବାରେ ସହଯୋଗ]]',
'createacct-emailrequired' => 'ଇମେଲ ଠିକଣା',
'createacct-emailoptional' => 'ଇମେଲ ଠିକଣା (ଇଚ୍ଛାଧୀନ)',
'createacct-email-ph' => 'ଆପଣଙ୍କ ଇମେଲ ଠିକଣା ନିବେଶ କରନ୍ତୁ',
-'createaccountmail' => 'ଗୋଟିଏ ସାମୟିକ ଜାହିତାହି ପାସୱାର୍ଡ ବ୍ୟବହାର କରନ୍ତୁ ଏବଂ ଏହାକୁ ତଳେ ଦିଆଯାଇଥିବା ଇ-ମେଲ ଠିକଣାକୁ ପଠେଇ ଦିଅନ୍ତୁ',
+'createacct-another-email-ph' => 'ଆପଣଙ୍କ ଇ-ମେଲ ଠିକଣା ଦିଅନ୍ତୁ',
+'createaccountmail' => 'ଏକ ଅସ୍ଥାୟୀ ପାସୱାର୍ଡ଼ ବ୍ୟବହାର କରନ୍ତୁ ଏବଂ ଏହାକୁ ତଳେ ଦିଆଯାଇଥିବା ଇ-ମେଲ ଠିକଣାକୁ ପଠେଇ ଦିଅନ୍ତୁ',
'createacct-realname' => 'ପ୍ରକୃତ ନାମ (ଇଚ୍ଛାଧୀନ)',
'createaccountreason' => 'କାରଣ:',
'createacct-reason' => 'କାରଣ',
'createacct-reason-ph' => 'ଆପଣ ଅନ୍ୟଏକ ଖାତା କାହିଁକି ତିଆରି କରୁଛନ୍ତି',
+'createacct-imgcaptcha-ph' => 'ଉପରେ ଲେଖାଥିବା ଲେଖାଟି ଲେଖନ୍ତୁ',
+'createacct-submit' => 'ନିଜର ନୂଆ ଖାତାଟିଏ ଖୋଲନ୍ତୁ',
+'createacct-another-submit' => 'ଆଉ ଏକ ଖାତା ଖୋଲନ୍ତୁ',
+'createacct-benefit-heading' => '{{SITENAME}} ଆପଣଙ୍କ ଭଳି ଲୋକମାନଙ୍କ ଦ୍ୱାରା ଗଢ଼ା ।',
+'createacct-benefit-body1' => '{{PLURAL:$1|ସମ୍ପାଦନା|ସମ୍ପାଦନାମାନ}}',
'badretype' => 'ଆପଣ ଦେଇଥିବା ପାସବାର୍ଡ଼ଟି ମେଳଖାଉନାହିଁ ।',
'userexists' => 'ଆପଣ ଦେଇଥିବା ଇଉଜର ନାମ ଆଗରୁ ଅଛି ।
ଦୟାକରି ଅଲଗା ନାମଟିଏ ବାଛନ୍ତୁ ।',
$2
{{PLURAL:$3|Tymczasowego hasła|Tymczasowych haseł}} można użyć w ciągu {{PLURAL:$5|jednego dnia|$5 dni}}.
-Powinieneś zalogować się i zmienić hasło na nowe. Jeśli to ktoś inni poprosił o wysłanie przypomnienia lub jeśli pamiętasz aktualne hasło i nie chce go zmieniać wystarczy, że zignorujesz tę wiadomość i będziesz nadal korzystał ze swojego starego hasła.',
+Powinieneś zalogować się i zmienić hasło na nowe. Jeśli to ktoś inny poprosił o wysłanie przypomnienia lub jeśli pamiętasz aktualne hasło i nie chcesz go zmieniać wystarczy, że zignorujesz tę wiadomość i będziesz nadal korzystać ze swojego starego hasła.',
'passwordreset-emailtext-user' => 'Użytkownik $1 poprosił o zresetowanie twojego hasła w {{GRAMMAR:MS.lp{{SITENAME}}}} ($4). Z tym adresem e‐mailowym powiązane {{PLURAL:$3|jest konto użytkownika|są następujące konta użytkowników:}}
$2
{{PLURAL:$3|Tymczasowego hasła|Tymczasowych haseł}} można użyć w ciągu {{PLURAL:$5|jednego dnia|$5 dni}}.
-Powinieneś zalogować się i zmienić hasło na nowe. Jeśli to ktoś inni poprosił o wysłanie przypomnienia lub jeśli pamiętasz aktualne hasło i nie chce go zmieniać wystarczy, że zignorujesz tę wiadomość i będziesz nadal korzystał ze swojego starego hasła.',
+Powinieneś zalogować się i zmienić hasło na nowe. Jeśli to ktoś inny poprosił o wysłanie przypomnienia lub jeśli pamiętasz aktualne hasło i nie chcesz go zmieniać wystarczy, że zignorujesz tę wiadomość i będziesz nadal korzystać ze swojego starego hasła.',
'passwordreset-emailelement' => 'Nazwa użytkownika – $1
Tymczasowe hasło – $2',
'passwordreset-emailsent' => 'E‐mail pozwalający na zresetowanie hasła został wysłany.',
'deletecomment' => 'Powód',
'deleteotherreason' => 'Inny lub dodatkowy powód:',
'deletereasonotherlist' => 'Inny powód',
-'deletereason-dropdown' => '* Najczęstsze powody usunięcia
+'deletereason-dropdown' => '* Najczęstsze przyczyny usunięcia
+** Spam
+** Wandalizm
+** Naruszenia praw autorskich
** Prośba autora
-** Naruszenie praw autorskich
-** Wandalizm',
+** Zerwane przekierowanie',
'delete-edit-reasonlist' => 'Edytuj listę przyczyn usunięcia',
'delete-toobig' => 'Ta strona ma bardzo długą historię edycji – ponad $1 {{PLURAL:$1|zmianę|zmiany|zmian}}.<br />
Usuwanie jej zostało ograniczone ze względu na możliwość zakłócenia pracy {{GRAMMAR:D.lp|{{SITENAME}}}}.',
* '''Internet Explorer:''' Che a ten-a sgnacà ''Ctrl'' antramentre che a sgnaca col rat ansima a ''Agiorné'', ò pura che a sgnaca tut ansema ''Ctrl-F5''
*'''Opera''' Ch'a dësveuida soa memorisassion andrinta a ''Utiss → Gust''",
'usercssyoucanpreview' => "'''Drita:''' che a deuvra ël boton «{{int:showpreview}}» për controlé l'efet ëd sò neuv còdes CSS dnans ëd salvelo.",
-'userjsyoucanpreview' => "'''Drita:''' che a deuvra ël boton «{{int:showpreview}}» për controlé l'efet ëd sò còdes JS dnans ëd salvelo.",
-'usercsspreview' => "'''Che a varda che lòn che a s-ciàira a l'é nomach na preuva ëd sò CSS.'''
+'userjsyoucanpreview' => "'''Drita:''' che a deuvra ël boton «{{int:showpreview}}» për controlé l'efet ëd sò neuv còdes JavaScript dnans ëd salvelo.",
+'usercsspreview' => "'''Che a varda che lòn che a s-ciàira a l'é nomach na preuva ëd sò feuj CSS.'''
'''A l'é ancó nen stàit salvà!'''",
-'userjspreview' => "'''Che as visa che a l'é mach antramentr che as fa na preuva ëd sò Javascript, che a l'é ancó pa stàit salvà!'''",
-'sitecsspreview' => "'''Che a varda che a l'é mach an mente ch'a preuva sto CSS.'''
-'''A l'é ancó pa stàit salvà!'''",
-'sitejspreview' => "'''Che a varda che a l'é mach an mente ch'a preuva sto còdes JavaScript.'''
-'''A l'é ancó pa stàit salvà!'''",
-'userinvalidcssjstitle' => "'''Avis:''' A-i é pa gnun-a pel \"\$1\". Che as visa che le pàgine .css e .js che un as fa daspërchiel a deuvro tute minùscole për tìtol, pr'esempi {{ns:user}}:Scaramacaj/vector.css nopà che {{ns:user}}:Scaramacaj/Vector.css.",
+'userjspreview' => "'''Che as visa che a l'é mach antramentre che as fa na preuva ëd sò còdes Javascript e che a l'é ancó pa stàit salvà!'''",
+'sitecsspreview' => "'''Che a varda che a l'é mach an camin ch'a preuva cost CSS.'''
+'''A l'é pa ancora stàit salvà!'''",
+'sitejspreview' => "'''Che a varda che a l'é mach an camin ch'a preuva cost còdes JavaScript.'''
+'''A l'é pa ancora stàit salvà!'''",
+'userinvalidcssjstitle' => "'''Atension:''' A-i é gnun-a pel «$1». Che as visa che le pàgine .css e .js che un as fa daspërchiel a deuvro tute minùscole për tìtol, pr'esempi {{ns:user}}:Scaramacaj/vector.css nopà che {{ns:user}}:Scaramacaj/Vector.css.",
'updated' => '(Agiornà)',
-'note' => "'''NÃ\92TA:'''",
-'previewnote' => "'''Che a ten-a present che costa-sì a l'é mach na preuva.'''
-Ij sò cambi a son anco' pa stàit salvà!",
+'note' => "'''Nòta:'''",
+'previewnote' => "'''Che a ten-a da ment che costa-sì a l'é mach na preuva.'''
+Soe modìfiche a son pa ancora stàite salvà!",
'continue-editing' => 'Andé a la zòna ëd modìfica',
-'previewconflict' => "Costa preuva a-j mostra ël test dl'artìcol ambelessì dzora. Se a sërn dë salvelo, a l'é parèj che a lo s-ciairëran ëdcò tuti j'àutri Utent.",
-'session_fail_preview' => "'''Darmagi! I l'oma pa podù processé soa modìfica per via che a son përdusse për la stra ij dat ëd session.
-Për piasì che a preuva n'àutra vira. Se a dovèissa mai torna riveje sossì, che a preuva a seurte dal sistema e peuj torna a rintré.'''",
+'previewconflict' => "Costa preuva a mostra ël test dl'artìcol ambelessì-dzora. Se a sern dë salvelo, a l'é parèj che a lo s-ciairëran ëdcò tuti j'àutri Utent.",
+'session_fail_preview' => "'''Darmagi! I l'oma pa podù processé soa modìfica per via che a son përdusse për la stra ij dat ëd session.'''
+Për piasì che a preuva n'àutra vira. Se a dovèissa torna nen marcé, che a preuva a [[Special:UserLogout|seurte dal sistema]] e peuj torna a rintré.",
'session_fail_preview_html' => "'''Darmagi! I l'oma nen podù processé soa modìfica ën essend che a son përdusse për la stra ij dat ëd session.'''
''Për via che {{SITENAME}} a lassa mostré còdes HTML nen filtrà, la preuva a l'é stërmà coma precaussion contra a dij possìbij atach fàit an Javascript.''
-'''Se sòn a l'era na modìfica normal, për piasì che a preuva a fela n'àutra vira. Se a dovèissa mai torna deje dle gran-e, che a preuva a [[Special:UserLogout|seurte da 'nt ël sistema]] e peuj torna a rintré.'''",
+'''Se costa a l'era na modìfica normal, për piasì che a preuva a fela n'àutra vira. Se a dovèissa mai torna deje dle gran-e, che a preuva a [[Special:UserLogout|seurte da 'nt ël sistema]] e peuj torna a rintré.'''",
'token_suffix_mismatch' => "'''Soa modìfica a l'é nen stàita acetà përché sò navigator a l'hai fàit ciadel con ij pont e le vìrgole
-ant ël quàder ëd modìfica. La rason che a l'é nen stàit acetà a l'é për evité ch'a-i fasa darmagi al
-test ch'a-i é già. Sossì dle vire a riva quand un a deuvra un programa proxy ëd coj un pòch dla Bajòna.'''",
-'edit_form_incomplete' => "'''Quàich part dël formolari ëd modìfica a l'é pa rivà al sërvent; contròla doe vire che toe modìfiche a-i sio anco' e preuva torna.'''",
+ant ël quàder ëd modìfica.'''
+La rason che a l'é nen stàit acetà a l'é për evité ch'a-j fasa darmagi al
+test ch'a-i é già. Sossì dle vire a riva quand un a deuvra un servent anònim an sl'Aragnà ëd coj un pòch dla Bajòna.",
+'edit_form_incomplete' => "'''Chèiche part dël formolari ëd modìfica a son pa rivà al servent; ch'a contròla për da bin che soe modìfiche a-i sio ancora e ch'a preuva torna.'''",
'editing' => 'Modìfica ëd $1',
'creating' => 'Creé $1',
-'editingsection' => 'I soma dapress a modifiché $1 (session)',
-'editingcomment' => 'I soma dapress a modifiché $1 (neuva session)',
-'editconflict' => "Conflit d'edission: $1",
-'explainconflict' => "Cheidun d'àutr a l'ha salvà soa version dl'artìcol antramentré che chiel (chila) as prontava la soa.
+'editingsection' => 'Modìfica ëd $1 (session)',
+'editingcomment' => 'Modìfica ëd $1 (neuva session)',
+'editconflict' => 'Conflit ëd modìfica: $1',
+'explainconflict' => "Cheidun d'àutr a l'ha salvà soa version dl'artìcol antramentre che chiel as prontava la soa.
Ël quàder ëd modìfica dë dzora a mostra ël test ëd l'artìcol coma a resta adess (visadì, lòn che a-i é ant sla Ragnà). Soe modìfiche a stan ant ël quàder dë sota.
Ën volend a peul gionté soe modìfiche ant ël quàder dë dzora.
'''Mach''' ël test ant ël quàder dë dzora a sarà salvà, ën sgnacand ël boton \"{{int:savearticle}}\".",
'yourtext' => 'Sò test',
-'storedversion' => 'Version memorisà',
-'nonunicodebrowser' => "'''A L'EUJ! Sò programa ëd navigassion (browser) a travaja pa giust con lë stàndard unicode. I soma obligà a dovré dij truschin përchè a peula salvesse sò artìcoj sensa problema: ij caràter che a son nen ASCII a jë s-ciairerà ant ël quàder ëd modìfica test coma còdes esadecimaj.'''",
+'storedversion' => 'La version memorisà',
+'nonunicodebrowser' => "'''A L'EUJ! Sò programa ëd navigassion a marcia pa giust con lë stàndard Unicode. I soma obligà a dovré dij truschin përchè a peula salvesse sò artìcoj sensa problema: ij caràter che a son nen ASCII a jë s-ciairerà ant ël quàder ëd modìfica dël test coma còdes esadecimaj.'''",
'editingold' => "'''CHE A FASA MACH ATENSION: che a sta fasend-je dle modìfiche a na version nen agiornà dl'artìcol.<br />
Se a la salva parèj, lòn che a l'era stàit fàit dapress a sta revision-sì as perdrà d'autut.'''",
'yourdiff' => 'Diferense',
'deleteotherreason' => 'Rason àutra/adissional:',
'deletereasonotherlist' => 'Àutra rason',
'deletereason-dropdown' => "*Rason sòlite ch'a së scancela la ròba
-** A lo ciama l'àutor
+** Rumenta
+** Vandalism
** Violassion dij drit d'autor
-** Vandalism",
+** A lo ciama l'àutor
+** Ridiression cioca",
'delete-edit-reasonlist' => 'Modifiché la rason dlë scancelament',
'delete-toobig' => "Sta pàgina-sì a l'ha na stòria motobin longa, bele pì che $1 {{PLURAL:$1|revision|revision}}.
Lë scancelassion ëd pàgine parèj a l'é stàita limità për evité ch'as fasa darmagi për eror a {{SITENAME}}.",
# Human-readable timestamps
'minutes-ago' => '$1 {{PLURAL:$1|minuta|minute}} fa',
-'seconds-ago' => '$1 {{PLURAL:$1|second|second}} fa',
+'seconds-ago' => '$1 {{PLURAL:$1second}} fa',
'monday-at' => 'Lùn-es a $1',
'tuesday-at' => 'Màrtes a $1',
'wednesday-at' => 'Merco a $1',
'htmlform-submit' => 'Mandé',
'htmlform-reset' => 'Gavé le modìfiche',
'htmlform-selectorother-other' => 'Àutr',
+'htmlform-no' => 'Nò',
+'htmlform-yes' => 'É',
+'htmlform-chosen-placeholder' => "Serne n'opsion",
# SQLite database support
'sqlite-has-fts' => '$1 con arserca an test pien mantnùa',
'sqlite-no-fts' => '$1 sensa arserca an test pien mantnùa',
# New logging system
-'logentry-delete-delete' => "$1 a l'ha scancelà la pàgina $3",
-'logentry-delete-restore' => "$1 a l'ha ripristinà la pàgina $3",
-'logentry-delete-event' => "$1 a l'ha modificà la visibilità ëd {{PLURAL:$5|n'event dël registr|$5 event dël registr}} dzora $3: $4",
-'logentry-delete-revision' => "$1 a l'ha modificà la visibilità ëd {{PLURAL:$5|na revision|$5 revision}} dzora la pàgina $3: $4",
-'logentry-delete-event-legacy' => "$1 a l'ha modificà la visibilità dj'eveniment dël registr dzora $3",
-'logentry-delete-revision-legacy' => "$1 a l'ha modificà la visibilità dle revision dzora la pàgina $3",
-'logentry-suppress-delete' => "$1 a l'ha eliminà la pàgina $3",
-'logentry-suppress-event' => "$1 a l'ha modificà segretament la visibilità ëd {{PLURAL:$5|n'eveniment dël registr|$5 eveniment dël registr}} dzora $3: $4",
-'logentry-suppress-revision' => "$1 a l'ha modificà segretament la visibilità ëd {{PLURAL:$5|na revision|$5 revision}} dzora la pàgina $3: $4",
-'logentry-suppress-event-legacy' => "$1 l'ha modificà segretament la visibilità dj'evenimentt dël registr dzora $3",
-'logentry-suppress-revision-legacy' => "$1 a l'ha modificà segretament la visibilità dle revision dzora la pàgina $3",
+'logentry-delete-delete' => "$1 a l'ha {{GENDER:$2|scancelà}} la pàgina $3",
+'logentry-delete-restore' => "$1 {{GENDER:$2|a l'ha ripristinà}} la pàgina $3",
+'logentry-delete-event' => "$1 {{GENDER:$2|a l'ha modificà}} la visibilità ëd {{PLURAL:$5|n'event dël registr|$5 event dël registr}} dzora $3: $4",
+'logentry-delete-revision' => "$1 {{GENDER:$2|a l'ha modificà}} la visibilità ëd {{PLURAL:$5|na revision|$5 revision}} dzora la pàgina $3: $4",
+'logentry-delete-event-legacy' => "$1 {{GENDER:$2|a l'ha modificà}} la visibilità dj'eveniment dël registr dzora $3",
+'logentry-delete-revision-legacy' => "$1 {{GENDER:$2|a l'ha modificà}} la visibilità dle revision dzora la pàgina $3",
+'logentry-suppress-delete' => "$1 {{GENDER:$2|a l'ha eliminà}} la pàgina $3",
+'logentry-suppress-event' => "$1 {{GENDER:$2|a l'ha modificà}} da stërmà la visibilità ëd {{PLURAL:$5|n'eveniment dël registr|$5 eveniment dël registr}} dzora $3: $4",
+'logentry-suppress-revision' => "$1 {{GENDER:$2|a l'ha modificà}} da stërmà la visibilità ëd {{PLURAL:$5|na revision|$5 revision}} dzora la pàgina $3: $4",
+'logentry-suppress-event-legacy' => "$1 {{GENDER:$2|a l'ha modificà}} da stërmà la visibilità dj'eveniment dël registr dzora $3",
+'logentry-suppress-revision-legacy' => "$1 {{GENDER:$2|a l'ha modificà}} da stërmà la visibilità dle revision dzora la pàgina $3",
'revdelete-content-hid' => 'contnù stërmà',
'revdelete-summary-hid' => 'resumé dle modìfiche stërmà',
'revdelete-uname-hid' => 'stranòm stërmà',
'revdelete-uname-unhid' => 'stranòm dëscoatà',
'revdelete-restricted' => "restrission aplicà a j'aministrator",
'revdelete-unrestricted' => "restrission për j'aministrator gavà",
-'logentry-move-move' => "$1 a l'ha tramudà la pàgina $3 a $4",
-'logentry-move-move-noredirect' => "$1 a l'ha tramudà la pàgina $3 a $4 sensa lassé na ridiression",
-'logentry-move-move_redir' => "$1 a l'ha tramudà la pàgina $3 a $4 ansima a na ridiression",
-'logentry-move-move_redir-noredirect' => "$1 a l'ha tramudà la pàgina $3 a $4 ansima a na ridiression sensa lassé na ridiression",
-'logentry-patrol-patrol' => "$1 a l'ha marcà la revision $4 dla pàgina $3 'me controlà",
-'logentry-patrol-patrol-auto' => "$1 a l'ha marcà automaticament la revision $4 dla pàgina $3 'me controlà",
-'logentry-newusers-newusers' => "Ël cont utent $1 a l'é stàit creà",
-'logentry-newusers-create' => "Ël cont utent $1 a l'é stàit creà",
-'logentry-newusers-create2' => "Ël cont utent $3 a l'é stàit creà da $1",
-'logentry-newusers-autocreate' => "Ël cont $1 a l'é stàit creà an automàtich",
-'logentry-rights-rights' => "$1 a l'ha tramudà l'apartenesa a la partìa për $3 da $4 a $5",
-'logentry-rights-rights-legacy' => "$1 a l'ha tramudà l'apartenensa a la partìa për $3",
-'logentry-rights-autopromote' => "$1 a l'é stàit automaticament promovù da $4 a $5",
+'logentry-move-move' => "$1 {{GENDER:$2|a l'ha tramudà}} la pàgina $3 a $4",
+'logentry-move-move-noredirect' => "$1 {{GENDER:$2|a l'ha tramudà}} la pàgina $3 a $4 sensa lassé na ridiression",
+'logentry-move-move_redir' => "$1 {{GENDER:$2|a l'ha tramudà}} la pàgina $3 a $4 ansima a na ridiression",
+'logentry-move-move_redir-noredirect' => "$1 {{GENDER:$2|a l'ha tramudà}} la pàgina $3 a $4 ansima a na ridiression sensa lassé na ridiression",
+'logentry-patrol-patrol' => "$1 {{GENDER:$2|a l'ha marcà}} la revision $4 dla pàgina $3 'me controlà",
+'logentry-patrol-patrol-auto' => "$1 {{GENDER:$2|a l'ha marcà}} an automàtich la revision $4 dla pàgina $3 'me controlà",
+'logentry-newusers-newusers' => "Ël cont utent $1 {{GENDER:$2|a l'é stàit creà}}",
+'logentry-newusers-create' => "Ël cont utent $1 {{GENDER:$2|a l'é stàit creà}}",
+'logentry-newusers-create2' => "Ël cont utent $3 {{GENDER:$2|a l'é stàit creà}} da $1",
+'logentry-newusers-byemail' => "Ël cont utent $3 a l'é {{GENDER:$2|stàit creà}} da $1 e la ciav a l'é stàita mandà për pòsta eletrònica",
+'logentry-newusers-autocreate' => "Ël cont $1 a l'é {{GENDER:$2|stàit creà}} an automàtich",
+'logentry-rights-rights' => "$1 {{GENDER:$2|a l'ha modificà}} l'apartenensa a la partìa për $3 da $4 a $5",
+'logentry-rights-rights-legacy' => "$1 {{GENDER:$2|a l'ha modificà}} l'apartenensa a la partìa për $3",
+'logentry-rights-autopromote' => "$1 a l'é {{GENDER:$2|stàit promovù}} an automàtich da $4 a $5",
'rightsnone' => '(gnun)',
# Feedback
'api-error-ok-but-empty' => 'Eror antern: Gnun-a rispòsta dal servent.',
'api-error-overwrite' => "Dzorascrive ansima a n'archivi esistent a l'é nen përmëttù.",
'api-error-stashfailed' => "Eror antern: ël servent a l'ha pa podù memorisé l'archivi a temp.",
+'api-error-publishfailed' => "Eror antern: Ël servent a l'ha pa podù publiché l'archivi provisòri.",
'api-error-timeout' => "Ël servent a l'ha pa rëspondù ant ël temp ëspetà.",
'api-error-unclassified' => "A l'é capitaje n'eror nen conossù.",
'api-error-unknown-code' => 'Eror sconossù: «$1».',
'duration-centuries' => '$1 {{PLURAL:$1|sécol|sécoj}}',
'duration-millennia' => '$1 {{PLURAL:$1|milenari|milenari}}',
+# Image rotation
+'rotate-comment' => 'Plancià virà ëd $1 {{PLURAL:$1|gre}} an sens orari',
+
+# Limit report
+'limitreport-title' => "Dàit d'otimisassion ëd l'analisator:",
+'limitreport-cputime' => "Temp CPU d'utilisassion",
+'limitreport-cputime-value' => '$1 {{PLURAL:$1|second}}',
+'limitreport-walltime' => "Temp real d'utilisassion",
+'limitreport-walltime-value' => '$1 {{PLURAL:$1|second}}',
+'limitreport-ppvisitednodes' => 'Cont dij neu ëd prepocessor visità',
+'limitreport-ppgeneratednodes' => 'Cont dij neu ëd preprocessor generà',
+'limitreport-postexpandincludesize' => "Taja d'anclusion dòp espansion",
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
+'limitreport-templateargumentsize' => "Taja dl'argoment ëd lë stamp",
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
+'limitreport-expansiondepth' => "Pi granda përfondità d'espansion",
+'limitreport-expensivefunctioncount' => "Cont ëd le fonsion d'anàlisi care",
+
);
'tog-hidepatrolled' => 'په وروستيو بدلونونو کې څارل شوې سمونونه پټول',
'tog-newpageshidepatrolled' => 'د نوؤ مخونو په لړليک کې کتل شوي مخونه پټول',
'tog-extendwatchlist' => 'يوازې د وروستني بدلونونو د ښکاره کولو لپاره نه بلکه د ټولو بدلونونو د ښکاره کولو لپاره کتنلړ غځول',
-'tog-usenewrc' => 'په کتنلړ او وروستي بدلونو مخ باندې ډله ايز بدلونونه (جاوا سکرېپټ ته اړتيا ده)',
+'tog-usenewrc' => 'په کتنلړ او وروستي بدلونو مخ باندې ډله ايز بدلونونه',
'tog-numberheadings' => 'د سرليکونو خپلکاره شمېرايښودنه',
'tog-showtoolbar' => 'د سمون اوزارپټه ښکاره کول',
'tog-editondblclick' => 'په دوه کلېک سره د مخونو سمون',
'tog-showhiddencats' => 'پټې وېشنيزې ښکاره کول',
'tog-norollbackdiff' => 'پرشاتمبولو وروسته توپيرونه نه ښودل',
'tog-useeditwarning' => 'کله چې يو سمون مخ څخه د بدلونونو د خوندي کولو پرته وځم خبر دې شم',
+'tog-prefershttps' => 'د ننوتلو پر مهال تل يوه خوندي اړيکتيا کارول',
'underline-always' => 'تل',
'underline-never' => 'هېڅکله',
'compareselectedversions' => 'ټاکلې بڼې سره پرتلل',
'showhideselectedversions' => 'ټاکلې بڼې ښکاره کول/پټول',
'editundo' => 'ناکړ',
+'diff-empty' => '(بې توپيره)',
'diff-multi' => ' د ({{PLURAL:$2| يو کارن|$2 کارنانو}} لخوا {{PLURAL:$1|يوه منځګړې بڼه|$1 منځګړې بڼې}}د نه ده ښکاره شوې)',
# Search results
# Special:LinkSearch
'linksearch' => 'د باندنيو تړنو پلټنه',
-'linksearch-pat' => 'د Ù¾Ù\84Ù¼Ù\86Û\90 Ù\85خبÛ\90Ù\84Ú«ه:',
+'linksearch-pat' => 'د Ù¾Ù\84Ù¼Ù\86Û\90 Ù\85خبÛ\90Ù\84Ú¯ه:',
'linksearch-ns' => 'نوم-تشيال:',
'linksearch-ok' => 'پلټل',
'linksearch-line' => '$1 د $2 سره تړل شوی',
'month' => 'له مياشتې د (او پخواني):',
'year' => 'له کال د (او پخواني):',
-'sp-contributions-newbies' => 'د Ù\86Ù\88Ù\88 Ú«ڼونونو ونډې ښکاره کول',
-'sp-contributions-newbies-sub' => 'د Ù\86Ù\88Ù\88 Ú«ڼونونو لپاره',
+'sp-contributions-newbies' => 'د Ù\86Ù\88Ù\88 Ú¯ڼونونو ونډې ښکاره کول',
+'sp-contributions-newbies-sub' => 'د Ù\86Ù\88Ù\88 Ú¯ڼونونو لپاره',
'sp-contributions-blocklog' => 'د بنديز يادښت',
'sp-contributions-deleted' => 'ړنګې شوې ونډې',
'sp-contributions-uploads' => 'پورته کېدنې',
'exif-meteringmode-0' => 'ناجوت',
'exif-meteringmode-1' => 'منځالی',
-'exif-meteringmode-5' => 'Ù\85خبÛ\90Ù\84Ú«ه',
+'exif-meteringmode-5' => 'Ù\85خبÛ\90Ù\84Ú¯ه',
'exif-meteringmode-255' => 'نور',
'exif-lightsource-0' => 'ناجوت',
'databaseerror-text' => 'Ocorreu um erro na consulta à base de dados.
Isto pode indicar um defeito no programa.',
'databaseerror-textcl' => 'Ocorreu um erro na consulta à base de dados.',
+'databaseerror-query' => 'Consulta:$1',
'databaseerror-error' => 'Erro: $1',
'laggedslavemode' => "'''Aviso:''' A página pode não conter as atualizações mais recentes.",
'readonly' => 'Base de dados bloqueada (limitada a leituras)',
'undo-failure' => 'Não foi possível desfazer a edição por conflito com alterações intermédias.',
'undo-norev' => 'Não foi possível desfazer a edição porque ela não existe ou foi apagada.',
'undo-summary' => 'Desfeita a edição $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussão]])',
+'undo-summary-username-hidden' => 'Desfazer a revisão $1 por um usuário oculto',
# Account creation failure
'cantcreateaccounttitle' => 'Não é possível criar uma conta',
'badsig' => 'Assinatura inválida; verifique o código HTML utilizado.',
'badsiglength' => 'A sua assinatura é demasiado longa.
Não deverá conter mais de $1 {{PLURAL:$1|carácter|caracteres}}.',
-'yourgender' => 'Sexo:',
-'gender-unknown' => 'Não especificado',
-'gender-male' => 'Masculino',
-'gender-female' => 'Feminino',
-'prefs-help-gender' => 'Opcional: usado pelo programa para ajuste das mensagens ao género do utilizador.
+'yourgender' => 'Como prefere ser descrito?',
+'gender-unknown' => 'Prefiro não dizer',
+'gender-male' => 'Ele edita páginas wiki',
+'gender-female' => 'Ela edita páginas wiki',
+'prefs-help-gender' => 'Esta preferência é opcional.
+O software usa o seu valor para o endereçar e para o mencionar a outros usando o género gramatical apropriado.
Esta informação será pública.',
'email' => 'Correio electrónico',
'prefs-help-realname' => 'O fornecimento do nome verdadeiro é opcional.
'reuploaddesc' => 'Cancelar o envio e voltar ao formulário de carregamento',
'upload-tryagain' => 'Submeta a descrição do ficheiro modificado',
'uploadnologin' => 'Não autenticado',
-'uploadnologintext' => 'Tem de estar [[Special:UserLogin|autenticado]] para enviar ficheiros.',
+'uploadnologintext' => 'Tem de $1 para enviar ficheiros.',
'upload_directory_missing' => 'O diretório de carregamento de ficheiros ($1) não existe e o servidor de internet não conseguiu criá-lo.',
'upload_directory_read_only' => 'O servidor de internet não possui permissão de escrita no diretório de carregamento de ficheiros ($1).',
'uploaderror' => 'Erro ao carregar',
'deleteotherreason' => 'Outro/motivo adicional:',
'deletereasonotherlist' => 'Outro motivo',
'deletereason-dropdown' => '* Motivos de eliminação comuns
-** Pedido do autor
+** Spam
+** Vandalismo
** Violação de direitos de autor
-** Vandalismo',
+** Pedido do autor
+** Redirecionamento quebrado',
'delete-edit-reasonlist' => 'Editar motivos de eliminação',
'delete-toobig' => 'Esta página tem um histórico longo, com mais de $1 {{PLURAL:$1|edição|edições}}.
A eliminação de páginas como esta foi restringida na {{SITENAME}}, para evitar problemas acidentais.',
# Limit report
'limitreport-cputime-value' => '$1 {{PLURAL:$1|segundo|segundos}}',
-'limitreport-postexpandincludesize-value' => '$1/$2 bytes',
-'limitreport-templateargumentsize-value' => '$1/$2 bytes',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
);
'userlogin-resetpassword-link' => 'Troque sua senha',
'helplogin-url' => 'Help:Iniciar sessão',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Ajuda para iniciar sessão]]',
+'userlogin-loggedin' => 'Você já está conectado como {{GENDER:$1|$1}}.
+Use o formulário abaixo para iniciar sessão como outro usuário.',
'createacct-join' => 'Insira suas informações abaixo.',
'createacct-another-join' => 'Preeencha as informações para a nova conta',
'createacct-emailrequired' => 'Endereço de e-mail',
'revdelete-unsuppress' => 'Remover restrições das edições restauradas',
'revdelete-log' => 'Motivo:',
'revdelete-submit' => 'Aplicar {{PLURAL:$1|à revisão selecionada|às revisões selecionadas}}',
-'revdelete-success' => "'''A visibilidade da revisão foi definida com sucesso.'''",
+'revdelete-success' => "'''A visibilidade da revisão foi atualizada.'''",
'revdelete-failure' => "'''A visibilidade da revisão não foi atualizada:'''
$1",
'logdelete-success' => "'''Visibilidade de evento definida com sucesso.'''",
'deletecomment' => 'Motivo:',
'deleteotherreason' => 'Justificativa adicional:',
'deletereasonotherlist' => 'Outro motivo',
-'deletereason-dropdown' => '* Motivos de eliminação comuns
-** Pedido do autor
+'deletereason-dropdown' => '* Motivos comuns para eliminação
+** Spam
+** Vandalismo
** Violação de direitos de autor
-** Vandalismo',
+** A pedido do autor
+** Redirecionamento inválido',
'delete-edit-reasonlist' => 'Editar motivos de eliminação',
'delete-toobig' => 'Esta página possui um longo histórico de edições, com mais de $1 {{PLURAL:$1|edição|edições}}.
A eliminação de tais páginas foi restrita, a fim de se evitarem problemas acidentais em {{SITENAME}}.',
'contributions' => 'Contribuições {{GENDER:$1|do usuário|da usuária}}',
'contributions-title' => 'Contribuições {{GENDER:$1|do usuário|da usuária}} $1',
'mycontris' => 'Contribuições',
-'contribsub2' => 'Para $1 ($2)',
+'contribsub2' => 'Para {{GENDER:$3|$1}} ($2)',
'nocontribs' => 'Não foram encontradas mudanças com este critério.',
'uctop' => '(atual)',
'month' => 'Mês (inclusive anteriores):',
'tags-tag' => 'Nome da etiqueta',
'tags-display-header' => 'Aparência nas listas de modificações',
'tags-description-header' => 'Descrição completa do significado',
+'tags-active-header' => 'Ativo?',
'tags-hitcount-header' => 'Modificações etiquetadas',
+'tags-active-yes' => 'Sim',
+'tags-active-no' => 'Não',
'tags-edit' => 'editar',
'tags-hitcount' => '$1 {{PLURAL:$1|modificação|modificações}}',
'limitreport-walltime-value' => '$1 {{PLURAL:$1|segundo|segundos}}',
'limitreport-ppvisitednodes' => 'Número de nós visitados pelo pré-processador',
'limitreport-ppgeneratednodes' => 'Número de nós gerados pelo pré-processador',
+'limitreport-postexpandincludesize' => 'Tamanho de inclusão pós-expansão',
'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
+'limitreport-templateargumentsize' => 'Argumento do tamanho da predefinição',
'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
'limitreport-expansiondepth' => 'Máxima profundidade de expansão',
+'limitreport-expensivefunctioncount' => 'Conta da função expansiva do analizador',
);
'deletecomment' => 'Motiv:',
'deleteotherreason' => 'Motiv diferit/suplimentar:',
'deletereasonotherlist' => 'Alt motiv',
-'deletereason-dropdown' => '*Motive uzuale
-** La cererea autorului
+'deletereason-dropdown' => '*Motive uzuale de ștergere
+** Spam
+** Vandalism
** Violarea drepturilor de autor
-** Vandalism',
+** La cererea autorului
+** Redirecționare nefuncțională',
'delete-edit-reasonlist' => 'Modifică motivele ștergerii',
'delete-toobig' => 'Această pagină are un istoric al modificărilor important, cu mai mult de $1 {{PLURAL:$1|versiune|versiuni|de versiuni}}.
Ștergerea unei astfel de pagini a fost restricționată pentru a preveni apariția unor erori în {{SITENAME}}.',
'version-credits-summary' => 'Am dori să amintim următoarele persoane pentru contribuțiile aduse proiectului [[Special:Version|MediaWiki]].',
'version-license-info' => 'MediaWiki este un software liber pe care îl puteți redistribui și/sau modifica sub termenii Licenței Publice Generale GNU publicată de Free Software Foundation – fie a doua versiune a acesteia, fie, la alegerea dumneavoastră, orice altă versiune ulterioară.
-MediaWiki este distribuit în speranța că va fi folositor, dar FĂRĂ VREO GARANȚIE, nici măcar cea implicită de COMERCIALIZARE sau de ADAPTARE PENTRU UN UN SCOP ANUME. Vedeți Licența Publică Generală GNU pentru mai multe detalii.
+MediaWiki este distribuit în speranța că va fi folositor, dar FĂRĂ VREO GARANȚIE, nici măcar cea implicită de COMERCIALIZARE sau de ADAPTARE PENTRU UN SCOP ANUME. Vedeți Licența Publică Generală GNU pentru mai multe detalii.
În cazul în care nu ați primit [{{SERVER}}{{SCRIPTPATH}}/COPYING o copie a Licenței Publice Generale GNU] împreună cu acest program, scrieți la Free Software Foundation, Inc, 51, Strada Franklin, etajul cinci, Boston, MA 02110-1301, Statele Unite ale Americii sau [//www.gnu.org/licenses/old-licenses/gpl-2.0.html citiți-o online].',
'version-software' => 'Software instalat',
* @author Incnis Mrsi
* @author Iniquity
* @author Innv
+ * @author Ivan Shmakov
* @author Jackie
* @author JenVan
* @author Jl
'revdelete-concurrent-change' => 'Ошибка изменения записи от $2, $1: её статус был изменён кем-то другим, пока вы пытались изменить его.
Пожалуйста, проверьте журналы.',
'revdelete-only-restricted' => 'Ошибка сокрытия записи от $2 $1: вы не можете скрыть запись от просмотра администраторами без выбора одной из других настроек сокрытия.',
-'revdelete-reason-dropdown' => 'Стандартные причины удаления
+'revdelete-reason-dropdown' => '* Стандартные причины удаления
** Нарушение авторских прав
** Неуместные личные сведения
+** Неуместное имя участника
** Потенциально клеветнические сведения',
'revdelete-otherreason' => 'Другая/дополнительная причина:',
'revdelete-reasonotherlist' => 'Другая причина',
# Recent changes
'nchanges' => '$1 {{PLURAL:$1|изменение|изменения|изменений}}',
-'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|с последнего посещения}}',
+'enhancedrc-since-last-visit' => '$1 с последнего посещения',
'enhancedrc-history' => 'история',
'recentchanges' => 'Свежие правки',
'recentchanges-legend' => 'Настройки свежих правок',
'deleteotherreason' => 'Другая причина/дополнение:',
'deletereasonotherlist' => 'Другая причина',
'deletereason-dropdown' => '* Типовые причины удаления
+** спам
** вандализм
+** нарушение авторских прав
** по запросу автора
-** наÑ\80Ñ\83Ñ\88ение авÑ\82оÑ\80Ñ\81киÑ\85 пÑ\80ав',
+** неÑ\80абоÑ\82аÑ\8eÑ\89ее пеÑ\80енапÑ\80авление',
'delete-edit-reasonlist' => 'Править список причин',
'delete-toobig' => 'У этой страницы очень длинная история изменений, более $1 {{PLURAL:$1|версии|версий|версий}}.
Удаление таких страниц было запрещено во избежание нарушений в работе сайта {{SITENAME}}.',
* @author OchAyeTheNoo
* @author Omnipaedista
* @author Purodha
+ * @author Shirayuki
* @author The Evil IP address
* @author Urhixidur
* @author Ushanka
'tog-extendwatchlist' => 'Rozšíriť zoznam sledovaných stránok, aby zobrazoval všetky zmeny, nie len posledné',
'tog-usenewrc' => 'Zoskupiť v posledných úpravách a na zozname sledovaných stránok podľa stránky (vyžaduje JavaScript)',
'tog-numberheadings' => 'Automaticky číslovať nadpisy',
-'tog-showtoolbar' => 'Zobraziť panel nástrojov úprav (vyžaduje JavaSkript)',
-'tog-editondblclick' => 'Upravovať stránky po dvojitom kliknutí (vyžaduje JavaScript)',
+'tog-showtoolbar' => 'Zobraziť panel nástrojov úprav',
+'tog-editondblclick' => 'Upravovať stránky po dvojitom kliknutí',
'tog-editsection' => 'Umožniť upravovanie sekcie prostredníctvom odkazov [upraviť]',
-'tog-editsectiononrightclick' => 'Umožniť upravovanie sekcie pravým kliknutím na nadpisy sekcií (vyžaduje JavaScript)',
+'tog-editsectiononrightclick' => 'Umožniť upravovanie sekcie pravým kliknutím na nadpisy sekcií',
'tog-showtoc' => 'Zobrazovať tabuľku s obsahom (pre stránky s viac ako 3 nadpismi)',
'tog-rememberpassword' => 'Zapamätať si prihlásenie na tomto počítači (najviac $1 {{PLURAL:$1|deň|dni|dní}})',
'tog-watchcreations' => 'Pridávať stránky, ktoré vytvorím a súbory, ktoré nahrám medzi sledované',
'tog-shownumberswatching' => 'Zobraziť počet používateľov sledujúcich stránku',
'tog-oldsig' => 'Súčasný podpis:',
'tog-fancysig' => 'Považovať podpisy za wikitext (bez automatických odkazov)',
-'tog-uselivepreview' => 'Používať živý náhľad (JavaScript) (experimentálna funkcia)',
+'tog-uselivepreview' => 'Používať živý náhľad(experimentálna funkcia)',
'tog-forceeditsummary' => 'Upozoriť ma, keď nevyplním zhrnutie úprav',
'tog-watchlisthideown' => 'Skryť moje úpravy zo zoznamu sledovaných',
'tog-watchlisthidebots' => 'Skryť úpravy botov zo zoznamu sledovaných',
'tog-noconvertlink' => 'Vypnúť konverziu názvov odkazov',
'tog-norollbackdiff' => 'Vynechať rozdiel po vykonaní rollbacku',
'tog-useeditwarning' => 'Upozorniť ma, keď opúšťam upravovaciu stránku s neuloženými zmenami',
+'tog-prefershttps' => 'Po prihlásení používať vždy zabezpečené pripojenie',
'underline-always' => 'Vždy',
'underline-never' => 'Nikdy',
'newwindow' => '(otvorí v novom okne)',
'cancel' => 'Zrušiť',
'moredotdotdot' => 'Viac...',
-'morenotlisted' => 'Ďalšie neuvedené...',
+'morenotlisted' => 'Tento zoznam nie je úplný.',
'mypage' => 'Stránka',
'mytalk' => 'Diskusia',
'anontalk' => 'Diskusia k tejto IP adrese',
# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
'aboutsite' => 'O {{GRAMMAR:lokál|{{SITENAME}}}}',
'aboutpage' => 'Project:Úvod',
-'copyright' => 'Obsah je k dispozícii za licenčných podmienok $1.',
+'copyright' => 'Obsah je dostupný pod $1, pokiaľ nie je uvedené inak.',
'copyrightpage' => '{{ns:project}}:Autorské práva',
'currentevents' => 'Aktuality',
'currentevents-url' => 'Project:Aktuality',
# General errors
'error' => 'Chyba',
'databaseerror' => 'Chyba v databáze',
+'databaseerror-text' => 'Došlo k chybe pri otázke do databázy.
+Môže to byť spôsobené chybou v softvéri.',
+'databaseerror-query' => 'Otázka: $1',
+'databaseerror-function' => 'Funkcia: $1',
+'databaseerror-error' => 'Chyba: $1',
'laggedslavemode' => 'Upozornenie: Je možné, že stránka neobsahuje posledné aktualizácie.',
'readonly' => 'Databáza je zamknutá',
'enterlockreason' => 'Zadajte dôvod požadovaného zamknutia vrátane odhadu, kedy očakávate odomknutie',
'cannotdelete-title' => 'Nemôžete zmazať stránku „$1“',
'delete-hook-aborted' => 'Zmazanie zrušila prídavná funkcia (prípojný bod syntaktického analyzátora).
Neudala vysvetlenie.',
+'no-null-revision' => 'Nepodarilo sa vytvoriť novú prázdnu revíziu stránky „$1“',
'badtitle' => 'Neplatný nadpis',
'badtitletext' => 'Požadovaný nadpis bol neplatný, nezadaný, alebo nesprávne odkazovaný z inej jazykovej verzie {{GRAMMAR:genitív|{{SITENAME}}}}. Mohol tiež obsahovať jeden alebo viac znakov, ktoré nie je možné použiť v nadpisoch.',
'perfcached' => 'Nasledujúce údaje pochádzajú z vyrovnávacej pamäte a nemusia byť úplne aktuálne. Vo vyrovnávacej pamäti {{PLURAL:$1|je dostupný|sú dostupné|je dostupných}} najviac {{PLURAL:$1|jeden výsledok|$1 výsledky|$1 výsledkov}}.',
# Login and logout pages
'logouttext' => "'''Práve ste sa odhlásili.'''
-Odteraz môžete používať {{GRAMMAR:akuzatív|{{SITENAME}}}} ako anonymný používateľ alebo sa môžete opäť <span class='plainlinks'>[$1 prihlásiť]</span> pod rovnakým alebo odlišným používateľským menom.
Uvedomte si, že niektoré stránky sa môžu naďalej zobrazovať ako keby ste boli prihlásený, až kým nevymažete vyrovnávaciu pamäť vášho prehliadača.",
'welcomeuser' => 'Vitajte, $1 !',
'welcomecreation-msg' => 'Váš účet bol vytvorený.
'userlogin-resetpassword-link' => 'Obnoviť heslo',
'helplogin-url' => 'Help:Prihlasovanie',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoc s prihlásením]]',
+'userlogin-loggedin' => 'Ste už {{GENDER:$1|prihĺasený|prihlásená}} ako $1.
+Pomocou formulára nižšie sa môžete prihlásiť ako iný redaktor.',
+'userlogin-createanother' => 'Vytvoriť ďalší účet',
'createacct-join' => 'Vyplňte svoje údaje.',
'createacct-another-join' => 'Vyplňte údaje nového účtu.',
'createacct-emailrequired' => 'E-mailová adresa',
'createacct-emailoptional' => 'E-mailová adresa (nepovinné)',
'createacct-email-ph' => 'Zadajte vašu e-mailovú adresu',
'createacct-another-email-ph' => 'Zadajte vašu e-mailovú adresu',
-'createaccountmail' => 'Použiť dočasné náhodné heslo a poslať ho na nižšie uvedenú emailovú adresu',
+'createaccountmail' => 'Použiť dočasné náhodné heslo a poslať ho na uvedenú emailovú adresu',
'createacct-realname' => 'Skutočné meno (nepovinné)',
'createaccountreason' => 'Dôvod:',
'createacct-reason' => 'Dôvod',
'cannotchangeemail' => 'Na tejto wiki nie je možné meniť e-mailové adresy používateľského účtu.',
'emaildisabled' => 'Táto lokalita nedokáže posielať emaily.',
'accountcreated' => 'Účet vytvorený',
-'accountcreatedtext' => 'Používateľský účet $1 bol vytvorený.',
+'accountcreatedtext' => 'Používateľský účet [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|diskusia]]) bol vytvorený.',
'createaccount-title' => 'Vytvorenie účtu na {{GRAMMAR:lokál|{{SITENAME}}}}',
'createaccount-text' => 'Niekto vytvoril účet pre vašu emailovú adresu na {{GRAMMAR:lokál|{{SITENAME}}}}
($4) s názvom „$2“, s heslom „$3“. Mali by ste sa prihlásiť a svoje heslo teraz zmeniť.
Ak bol účet vytvorený omylom, túto správu môžete ignorovať.',
'usernamehasherror' => 'Používateľské meno nemôže obsahovať znak mriežky.',
-'login-throttled' => 'Nedávno ste uskutočnili príliš mnoho neúspešných pokusov o prihlásenie.
-Prosím, počkajte predtým, než to skúsite znova.',
+'login-throttled' => 'Uskutočnili ste príliš mnoho neúspešných pokusov o prihlásenie.
+Prosím, počkajte $1 predtým, než to skúsite znova.',
'login-abort-generic' => 'Vaše prihlásenie nebolo úspešné - zrušené',
'loginlanguagelabel' => 'Jazyk: $1',
'suspicious-userlogout' => 'Vaša požiadavka odhlásiť sa bola zamietnutá, pretože to vyzerá, že ju poslal pokazený prehliadač alebo proxy server.',
# Recent changes
'nchanges' => '$1 {{PLURAL:$1|zmena|zmeny|zmien}}',
+'enhancedrc-history' => 'história',
'recentchanges' => 'Posledné úpravy',
'recentchanges-legend' => 'Možnosti posledných zmien',
'recentchanges-summary' => 'Pomocou tejto stránky sledujete posledné úpravy wiki.',
+'recentchanges-noresult' => 'V danom období nie sú zmeny spĺňajúce tieto kritériá.',
'recentchanges-feed-description' => 'Sledovať posledné úpravy tejto wiki týmto kanálom.',
'recentchanges-label-newpage' => 'Táto úprava vytvorila novú stránku.',
'recentchanges-label-minor' => 'Toto je drobná úprava',
'upload_source_file' => ' (súbor na vašom počítači)',
# Special:ListFiles
-'listfiles-summary' => 'Táto špeciálna stránka zobrazuje všetky nahrané súbory.
-Pri filtrovaní podľa používateľa sa zobrazia iba súbory, ktorých najnovšiu verziu nahral dotyčný používateľ.',
+'listfiles-summary' => 'Táto špeciálna stránka zobrazuje všetky nahrané súbory.',
'listfiles_search_for' => 'Hľadať názov súboru:',
'imgfile' => 'súbor',
'listfiles' => 'Zoznam obrázkov',
'listfiles_size' => 'Veľkosť (v bajtoch)',
'listfiles_description' => 'Popis',
'listfiles_count' => 'Verzie',
+'listfiles-show-all' => 'Vrátane starších verzií obrázkov',
+'listfiles-latestversion' => 'Aktuálna verzia',
+'listfiles-latestversion-yes' => 'Áno',
+'listfiles-latestversion-no' => 'Nie',
# File description page
'file-anchor-link' => 'Súbor',
'randompage' => 'Náhodná stránka',
'randompage-nopages' => '{{PLURAL:$2|V mennom priestore „$1“ nie sú žiadne stránky.|V nasledovných menných priestoroch nie sú žiadne stránky: $1}}',
+# Random page in category
+'randomincategory' => 'Náhodná stránka v kategórii',
+'randomincategory-invalidcategory' => '"$1" nie je platný názov kategórie.',
+'randomincategory-nopages' => 'V [[:Category:$1|kategórii $1]] nie sú žiadne stránky.',
+'randomincategory-selectcategory' => 'Získať náhodnú stránku z kategórie: $1 $2',
+'randomincategory-selectcategory-submit' => 'Ísť na',
+
# Random redirect
'randomredirect' => 'Náhodná presmerovacia stránka',
'randomredirect-nopages' => 'V mennom „$1“ priestore nie sú žiadne presmerovania.',
'tags-display-header' => 'Vzhľad v zoznamoch úprav',
'tags-description-header' => 'Úplný popis významu',
'tags-hitcount-header' => 'Označené úpravy',
+'tags-active-yes' => 'Áno',
+'tags-active-no' => 'Nie',
'tags-edit' => 'upraviť',
'tags-hitcount' => '$1 {{PLURAL:$1|zmena|zmeny|zmien}}',
'dberr-problems' => 'Prepáčte! Táto stránka má práve technické problémy.',
'dberr-again' => 'Skúste niekoľko minút počkať a potom opäť načítať stránku.',
'dberr-info' => '(Spojenie s databázovým serverom neúspešné: $1)',
+'dberr-info-hidden' => '(Nie je možné kontaktovať databázový server)',
'dberr-usegoogle' => 'Zatiaľ môžete skúsiť hľadať pomocou Google.',
'dberr-outofdate' => 'Pamätajte, že ich indexy nemusia byť aktuálne.',
'dberr-cachederror' => 'Toto je kópia požadovanej stránky z vyrovnávacej pamäte a nemusí byť aktuálna.',
'logentry-delete-event-legacy' => '$1 {{GENDER:$2|zmenil|zmenila}} viditeľnosť záznamov udalostí k stránke $3',
'logentry-delete-revision-legacy' => '$1 {{GENDER:$2|zmenil|zmenila}} viditeľnosť revízií na stránke $3',
'logentry-suppress-delete' => '$1 {{GENDER:$2|utajil|utajila}} stránku $3',
-'logentry-suppress-event' => '$1 utajene zmenil viditeľnosť {{PLURAL:$5|záznamu udalostí|$5 záznamov udalostí}} k stránke $3: $4',
+'logentry-suppress-event' => '$1 utajene {{GENDER:$2|zmenil|zmenila}} viditeľnosť {{PLURAL:$5|záznamu udalostí|$5 záznamov udalostí}} k stránke $3: $4',
'logentry-suppress-revision' => '$1 utajene zmenil viditeľnosť {{PLURAL:$5|revízie|$5 revízií}} na stránke $3: $4',
-'logentry-suppress-event-legacy' => '$1 utajene zmenil viditeľnosť záznamov udalostí k stránke $3',
-'logentry-suppress-revision-legacy' => '$1 utajene zmenil viditeľnosť revízií na stránke $3',
+'logentry-suppress-event-legacy' => '$1 utajene {{GENDER:$2|zmenil|zmenila}} viditeľnosť záznamov udalostí k stránke $3',
+'logentry-suppress-revision-legacy' => '$1 utajene {{GENDER:$2|zmenil|zmenila}} viditeľnosť revízií na stránke $3',
'revdelete-content-hid' => 'obsah skrytý',
'revdelete-summary-hid' => 'zhrnutie editácie skryté',
'revdelete-uname-hid' => 'používateľské meno skryté',
'logentry-move-move-noredirect' => '$1 premiestnil stránku $3 na $4, ale neponechal presmerovanie',
'logentry-move-move_redir' => '$1 premiestnil stránku $3 na $4 prostredníctvom presmerovania',
'logentry-move-move_redir-noredirect' => '$1 premiestnil stránku $3 na $4 prostredníctvom presmerovania, ale neponechal presmerovanie',
-'logentry-patrol-patrol' => '$1 označil revíziu $4 stránky $3 ako stráženú',
-'logentry-patrol-patrol-auto' => '$1 automaticky označil revíziu $4 stránky $3 ako stráženú',
-'logentry-newusers-newusers' => 'Bol vytvorený používateľský účet $1',
+'logentry-patrol-patrol' => '$1 {{GENDER:$2|označil|označila}} revíziu $4 stránky $3 ako overenú',
+'logentry-patrol-patrol-auto' => '$1 automaticky {{GENDER:$2|označil|označila}} revíziu $4 stránky $3 ako overenú',
+'logentry-newusers-newusers' => 'Bol {{GENDER:$2|vytvorený}} používateľský účet $1',
'logentry-newusers-create' => 'Bol vytvorený používateľský účet $1',
'logentry-newusers-create2' => '$1 vytvoril používateľský účet $3',
'logentry-newusers-byemail' => '$1 vytvoril používateľský účet $3 a heslo bolo poslané emailom',
-'logentry-newusers-autocreate' => 'Používateľský účet $1 bol vytvorený automaticky',
+'logentry-newusers-autocreate' => 'Používateľský účet $1 bol {{GENDER:$2|vytvorený}} automaticky',
'logentry-rights-rights' => '$1 zmenil členstvo $3 v skupinách z $4 na $5',
'logentry-rights-rights-legacy' => '$1 zmenil členstvo $3 v skupinách',
'logentry-rights-autopromote' => '$1 bol automaticky povýšený z $4 na $5',
# Image rotation
'rotate-comment' => 'Obrázok otočený o $1 {{PLURAL:$1|stupeň|stupne|stupňov}} v smere hodinových ručičiek',
+# Limit report
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|bajt|bajty|bajtov}}',
+'limitreport-templateargumentsize' => 'Veľkosť argumentov šablón',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|bajt|bajty|bajtov}}',
+'limitreport-expansiondepth' => 'Najväčšia hĺbka expanzie',
+'limitreport-expensivefunctioncount' => 'Počet náročných funkcií parseru',
+
);
'userlogin-resetpassword-link' => 'Ponastavite svoje geslo',
'helplogin-url' => 'Help:Prijava',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoč pri prijavi]]',
+'userlogin-loggedin' => 'Ste že prijavljeni kot {{GENDER:$1|$1}}.
+Uporabite spodnji obrazec, da se prijavite kot drug uporabnik.',
+'userlogin-createanother' => 'Ustvari drug račun',
'createacct-join' => 'Spodaj vnesite svoje informacije.',
'createacct-another-join' => 'Spodaj vnesite informacije o novem računu.',
'createacct-emailrequired' => 'E-poštni naslov',
'sectioneditnotsupported-title' => 'Urejanje razdelkov ni podprto',
'sectioneditnotsupported-text' => 'Urejanje razdelkov ni podprto na tej strani.',
'permissionserrors' => 'Napaka dovoljenja',
-'permissionserrorstext' => 'Za izvedbo dejanja nimate dovoljenja zaradi {{PLURAL:$1|naslednjega razloga|naslednjih razlogov|naslednjih razlogov|naslednjih razlogov|naslednjih razlogov}}:',
+'permissionserrorstext' => 'Za izvedbo dejanja nimate dovoljenja zaradi {{PLURAL:$1|naslednjega razloga|naslednjih razlogov}}:',
'permissionserrorstext-withaction' => 'Za $2 zaradi {{PLURAL:$1|naslednjega razloga|naslednjih razlogov}} nimate dovoljenja:',
'recreate-moveddeleted-warn' => "'''Opozorilo: Pišete stran, ki je bila nekoč že izbrisana.'''
'deleteotherreason' => 'Drugi/dodatni razlogi:',
'deletereasonotherlist' => 'Drug razlog',
'deletereason-dropdown' => '* Pogosti razlogi za brisanje
-** zahteva avtorja
+** smetenje
+** vandalizem
** kršitev avtorskih pravic
-** vandalizem',
+** zahteva avtorja
+** pretrgana preusmeritev',
'delete-edit-reasonlist' => 'Uredi razloge za brisanje',
'delete-toobig' => 'Ta stran ima obsežno zgodovino urejanja, tj. čez $1 {{PLURAL:$1|redakcijo|redakciji|redakcije|redakcij}}.
Izbris takšnih strani je bil omejen v izogib neželenim motnjam {{GRAMMAR:dative|{{SITENAME}}}}.',
'contributions' => '{{GENDER:$1|Uporabnikovi|Uporabničini}} prispevki',
'contributions-title' => 'Prispevki uporabnika $1',
'mycontris' => 'Prispevki',
-'contribsub2' => 'Uporabnik: $1 ($2)',
+'contribsub2' => 'Za {{GENDER:$3|$1}} ($2)',
'nocontribs' => 'Ne najdem nobene merilom ustrezajoče spremembe.',
'uctop' => '(trenutno)',
'month' => 'Od meseca (in prej):',
'tags-tag' => 'Ime oznake',
'tags-display-header' => 'Prikaz na seznamu sprememb',
'tags-description-header' => 'Polni opis pomena',
+'tags-active-header' => 'Dejavno?',
'tags-hitcount-header' => 'Etiketirane spremembe',
+'tags-active-yes' => 'Da',
+'tags-active-no' => 'Ne',
'tags-edit' => 'uredi',
'tags-hitcount' => '$1 {{PLURAL:$1|sprememba|spremembi|spremembe|sprememb|sprememb}}',
'tog-newpageshidepatrolled' => 'Göm patrullerade sidor från listan över nya sidor',
'tog-extendwatchlist' => 'Utöka bevakningslistan till att visa alla ändringar, inte bara den senaste',
'tog-usenewrc' => 'Gruppera ändringar efter sida i senaste ändringar och bevakningslistan',
-'tog-numberheadings' => 'Numrerade rubriker',
+'tog-numberheadings' => 'Automatisk numrerade rubriker',
'tog-showtoolbar' => 'Visa redigerings-verktygsraden',
'tog-editondblclick' => 'Redigera sidor med dubbelklick',
'tog-editsection' => 'Aktivera redigering av avsnitt genom [redigera]-länkar',
'deletecomment' => 'Anledning:',
'deleteotherreason' => 'Annan/ytterligare anledning:',
'deletereasonotherlist' => 'Annan anledning',
-'deletereason-dropdown' => '*Vanliga anledningar till radering
-** Författarens begäran
+'deletereason-dropdown' => '* Vanliga anledningar till radering
+** Spam
+** Vandalism
** Upphovsrättsbrott
-** Vandalism',
+** Författarens begäran
+** Trasig omdirigering',
'delete-edit-reasonlist' => 'Redigera anledningar för radering',
'delete-toobig' => 'Denna sida har en lång redigeringshistorik med mer än $1 {{PLURAL:$1|sidversion|sidversioner}}. Borttagning av sådana sidor har begränsats för att förhindra oavsiktliga driftstörningar på {{SITENAME}}.',
'delete-warning-toobig' => 'Denna sida har en lång redigeringshistorik med mer än $1 {{PLURAL:$1|sidversion|sidversioner}}. Att radera sidan kan skapa problem med hanteringen av databasen på {{SITENAME}}; var försiktig.',
'tog-hidepatrolled' => 'Son değişikliklerde gözden geçirilen düzenlemeleri gizle',
'tog-newpageshidepatrolled' => 'Kontrol edilmiş sayfaları yeni sayfalar listesinde gizle',
'tog-extendwatchlist' => 'İzleme listesini sadece en son değil, tüm değişiklikleri göstermek için genişlet',
-'tog-usenewrc' => 'Son değişiklikler sayfasındaki ve izleme listesindeki değişiklikleri gruplandırma',
+'tog-usenewrc' => 'Son değişiklikler sayfasındaki ve izleme listesindeki değişiklikleri gruplandır',
'tog-numberheadings' => 'Başlıkları otomatik numaralandır',
'tog-showtoolbar' => 'Düzenleme yaparken araç çubuğunu göster',
'tog-editondblclick' => 'Çift tıklayarak sayfaları düzenle',
'right-editusercssjs' => 'Diğer kullanıcıların CSS ve JS dosyalarında değişiklik yap',
'right-editusercss' => 'Diğer kullanıcıların CSS dosyalarında değişiklik yap',
'right-edituserjs' => 'Diğer kullanıcıların JS dosyalarında değişiklik yap',
+'right-editmyoptions' => 'tercihlerini düzenle',
'right-rollback' => 'Belirli bir sayfayı değiştiren son kullanıcının değişikliklerini hızlıca geri döndür',
'right-markbotedits' => 'Geri döndürülen değişiklikleri, bot değişiklikleri olarak işaretle',
'right-noratelimit' => 'Derecelendirme sınırlamalarından etkilenme',
'action-userrights-interwiki' => 'diğer vikilerde kullanıcıların, kullanıcı haklarını değiştirmeye',
'action-siteadmin' => 'veritabanını kilitleyip açmaya',
'action-sendemail' => 'e-posta gönder',
+'action-editmywatchlist' => 'izleme listeni düzenle',
+'action-viewmywatchlist' => 'izleme listeni gör',
# Recent changes
'nchanges' => '$1 {{PLURAL:$1|değişiklik|değişiklik}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|son ziyaretten bu yana}}',
+'enhancedrc-history' => 'geçmiş',
'recentchanges' => 'Son değişiklikler',
'recentchanges-legend' => 'Son değişiklikler seçenekleri',
'recentchanges-summary' => 'Yapılan en son değişiklikleri bu sayfadan izleyin.',
'rc_categories_any' => 'Herhangi',
'rc-change-size-new' => '$1 {{PLURAL:$1|bayt|bayt}} değişiklikten sonra',
'newsectionsummary' => '/* $1 */ yeni başlık',
-'rc-enhanced-expand' => 'Ayrıntıları göster (JavaScript gerekir)',
+'rc-enhanced-expand' => 'Ayrıntıları göster',
'rc-enhanced-hide' => 'Ayrıntıları gizle',
'rc-old-title' => 'ilk olarak oluşturulan "$1"',
'reuploaddesc' => 'Yükleme formuna geri dön.',
'upload-tryagain' => 'Değiştirilmiş dosya açıklamasını gönder',
'uploadnologin' => 'Oturum açık değil',
-'uploadnologintext' => 'Dosya yükleyebilmek için [[Special:UserLogin|oturum aç]]manız gerekiyor.',
+'uploadnologintext' => 'Dosya yükleyebilmek için [[$1|oturum aç]]manız gerekiyor.',
'upload_directory_missing' => 'Yükleme dizini ($1) kayıp ve websunucusu tarafından oluşturulamıyor.',
'upload_directory_read_only' => 'Dosya yükleme dizinine ($1) web sunucusunun yazma izni yok.',
'uploaderror' => 'Yükleme hatası',
'listfiles_size' => 'Boyut (bayt)',
'listfiles_description' => 'Tanım',
'listfiles_count' => 'Sürümler',
+'listfiles-latestversion' => 'Geçerli sürüm',
+'listfiles-latestversion-yes' => 'Evet',
+'listfiles-latestversion-no' => 'Hayır',
# File description page
'file-anchor-link' => 'Dosya',
# Random page in category
'randomincategory' => 'Kategoriye göre rastgele sayfa',
'randomincategory-selectcategory' => 'Rastgele sayfa alınacak kategori: $1 $2.',
+'randomincategory-selectcategory-submit' => 'Getir',
# Random redirect
'randomredirect' => 'Rastgele yönlendirme',
'tags-tag' => 'Etiket adı',
'tags-display-header' => 'Değişiklik listelerindeki görünüm',
'tags-description-header' => 'Anlamının tam açıklaması',
+'tags-active-header' => 'Etkin?',
'tags-hitcount-header' => 'Etiketli değişiklikler',
+'tags-active-yes' => 'Evet',
+'tags-active-no' => 'Hayır',
'tags-edit' => 'değiştir',
'tags-hitcount' => '$1 {{PLURAL:$1|değişiklik|değişiklik}}',
'deleteotherreason' => 'Інша/додаткова причина:',
'deletereasonotherlist' => 'Інша причина',
'deletereason-dropdown' => '* Типові причини вилучення
+** спам
** вандалізм
+** порушення авторських прав
** за запитом автора
-** поÑ\80Ñ\83Ñ\88еннÑ\8f авÑ\82оÑ\80Ñ\81Ñ\8cкиÑ\85 пÑ\80ав',
+** Ð\97ламанÑ\96 пеÑ\80енапÑ\80авленнÑ\8f',
'delete-edit-reasonlist' => 'Редагувати причини вилучення',
'delete-toobig' => 'У цієї сторінки дуже довга історія редагувань, більше $1 {{PLURAL:$1|версії|версій|версій}}.
Вилучення таких сторінок було заборонене з метою уникнення порушень у роботі сайту {{SITENAME}}.',
'accmailtitle' => 'Đã gửi mật khẩu.',
'accmailtext' => "Một mật khẩu được tạo ngẫu nhiên cho [[User talk:$1|$1]] đã được gửi đến $2. Có thể đổi mật khẩu tại trang ''[[Special:ChangePassword|đổi mật khẩu]]'' sau khi đã đăng nhập.",
'newarticle' => '(Mới)',
-'newarticletext' => "Bạn đi đến đây từ một liên kết đến một trang chưa tồn tại. Để tạo trang, hãy bắt đầu gõ vào ô bên dưới (xem [[{{MediaWiki:Helppage}}|trang trợ giúp]] để có thêm thông tin). Nếu bạn đến đây do nhầm lẫn, chỉ cần nhấn vào nút '''Lùi''' (''Back'') trong trình duyệt của bạn.",
+'newarticletext' => '<div style="margin-top: 0px;" class="emptymwmsg mediawiki_newarticletext"></div>',
'anontalkpagetext' => "----''Đây là trang thảo luận của một người dùng vô danh chưa tạo tài khoản hoặc có tài khoản nhưng không đăng nhập.
Do đó chúng ta phải dùng một dãy số gọi là địa chỉ IP để xác định anh/chị ta.
Một địa chỉ IP như vậy có thể có nhiều người cùng dùng chung.
'ipbreason' => 'Lý do:',
'ipbreasonotherlist' => 'Lý do khác',
'ipbreason-dropdown' => '*Một số lý do cấm thường gặp
-** Phá hoại
-** Thêm thông tin nội dung sai lệch
-** Tẩy trống nội dung trang
-** Quảng cáo vớ vẩn
-** Đăng liên kết thư rác đến trang web bên ngoài
+** Thêm thông tin sai lệch
+** Xóa nội dung trang
+** Đăng liên kết thư rác đến trang Web bên ngoài
** Cho thông tin rác vào trang
** Có thái độ dọa dẫm/quấy rối
-** Tên thành viên không được chấp nhận
-** Tạo nhiều trang mới vi phạm bản quyền, bỏ qua thảo luận và cảnh báo
-** Truyền nhiều hình ảnh thiếu nguồn gốc hoặc bản quyền
-** Con rối của thành viên bị cấm',
+** Lạm dụng nhiều tài khoản
+** Tên thành viên không thể chấp nhận',
'ipb-hardblock' => 'Ngăn không cho thành viên đã đăng nhập sửa đổi từ địa chỉ IP này',
'ipbcreateaccount' => 'Cấm mở tài khoản',
'ipbemailban' => 'Không cho gửi thư điện tử',
'tog-showhiddencats' => 'Fârschdegde ghadegoriin dsajchn',
# Dates
-'sunday' => 'Sundooch',
+'sunday' => 'Sunndooch',
+'monday' => 'Monndooch',
+'tuesday' => 'Dinnsdooch',
+'wednesday' => 'Miidwoch',
+'thursday' => 'Dunnerschdooch',
+'friday' => 'Freidooch',
+'saturday' => 'Samsdooch',
'sun' => 'Su',
+'thu' => 'Du',
'january' => 'Januaar',
'february' => 'Feebruaar',
'march' => 'Märds',
'category-subcat-count' => 'Di ghadegorii umfasd {{PLURAL:$2|bloos a undâr-ghadegorii|dsam $2 undâr-ghadegoriâ, wofoo {{PLURAL:$1|nôr ôône| $1}}}} undn ôôdsajchd wärn.',
'category-article-count' => 'Di ghadegorii umfasd {{PLURAL:$2|bloos a sajdn|$2 sajdn, wofoo hiir {{PLURAL:$1|aane undn ôôdsajchd wärd|l$1 ôôdsajchd undn wärn}}}}.',
'listingcontinuesabbrev' => '(Fôrdsedsung)',
+'noindex-category' => 'Seidn, wou net indexierd sin',
+'about' => 'Ieber',
'newwindow' => '(Wärd in am najn fenschdâ daargschdeld)',
'cancel' => 'Abbrechn',
'mytalk' => 'Disghusjoonssajdn',
'vector-view-history' => 'Wärsjoonsfolche',
'vector-view-view' => 'Leesn',
'vector-view-viewsource' => 'Gwäl-dhägsd ôôgugn',
-'actions' => 'Aggdsione',
+'actions' => 'Agdsiona',
'namespaces' => 'Nôômsrajm',
'variants' => 'Warjandn',
Wen's des ned is, bisd womeeglich iwa ân feela in dr sofdwäâr gschdolbäd. In dämm Fall melds´däs, bidde mid där URL, am [[Special:ListUsers/sysop|Administrator]].",
'missingarticle-rev' => '(wärsjoonsnumâr: $1)',
+'badtitle' => 'Ungüldicher Addigl',
'badtitletext' => "Dii fârlangde sajdn gibd's ned, odâr sii had ân uugildichn sajdnnôôma ghabd, odâr s'wôôr â gschlambdâr fârwajs fonâm andârn wighi häär. Filajchd is aa â buuchschdôôb drin'n, däär in sajdnnôôm gôôr ned schdena däf.",
'viewsource' => 'Gwäl-dhägsd ôôgugn',
'remembermypassword' => 'Af dem ghombjuudâr schdändich ôôgmäld blajm (for a maximum of $1 {{PLURAL:$1|day|days}})',
'login' => 'Ôômeldn',
'nav-login-createaccount' => 'Oomeldn / Ghondoo ooleeng',
+'loginprompt' => 'Zum Omelldn mäin Guggies agdivierd sei.',
'userlogin' => 'Ôômeldn / Als Bajdräächâr ajschrajm',
'logout' => 'Abmeldn',
'userlogout' => 'Abmeldn',
+'nologin' => 'Du hast ka Nutzergonto? $1',
'nologinlink' => 'Sich als najâr Ôôgmeldâr ôômäldn',
-'gotaccountlink' => 'Ôômeldn',
+'gotaccountlink' => 'Omeldn',
'mailmypassword' => 'Â najs passwôrd iwâr iimejl dsuschign lasn',
'loginlanguagelabel' => 'Sproch: $1',
'permissionserrorstext-withaction' => 'Du däfsd ned $2, des{{PLURAL:$1||}}dâsweechn:',
# Parser/template warnings
-'post-expand-template-inclusion-warning' => "'''Obachd:''' dii Gräiß vo dii ajbundna Vorlââng is zgrouß, ajniche Vorlââng kenna ned ajbundn wärrn.",
-'post-expand-template-inclusion-category' => 'Sajdn, ba denne vo dii ajbundna Vorlââng dii Gräiß üba da gräißdmöchlichn Gräiß iss',
+'post-expand-template-inclusion-warning' => "'''Wannung''': Däi Gräiss vo eibundne Vuurloong is zu grouss, einiche Vuurloong könna net eibundn werrn.",
+'post-expand-template-inclusion-category' => 'Seidn, in dena wou däi maximale Gräiss vo eibundne Vuurloong ieberschriddn is.',
# History pages
'viewpagelogs' => 'Logbicher fär dii sajdn dsajchn',
* '''({{int:cur}})''' = undârschiid dsur geechnwärdichn wärsjoon, '''({{int:last}})''' = undârschiid dsur foorichn wärsjoon
* Uurdsajd/Daadum = wärsjoon dsu dära dsajd, '''{{int:minoreditletter}}''' = glane ändärung.",
'history-fieldset-title' => 'Suchng in där wärsjoonsfolche',
-'histfirst' => 'Ã\84ldâschde',
-'histlast' => 'Najsde',
+'histfirst' => 'älldsde',
+'histlast' => 'neisde',
# Revision deletion
'rev-delundel' => 'ôôdsajng/fârbärng',
'revdel-restore' => 'Ändârn, was oodsajchd wäd',
+'revdel-restore-deleted' => 'glöschde Versiona',
+'revdel-restore-visible' => 'sichdbore Versiona',
# Merge log
'revertmerge' => 'Dsrig fôr dii fârajnichung',
'notextmatches' => 'Närchnds gfundn.',
'prevn' => '{{PLURAL:$1|foorichâr|fooriche $1}}',
'nextn' => '{{PLURAL:$1|nägschdâr|nägschde $1}}',
+'prevn-title' => '{{PLURAL:$1|Vuurherichs Ergebnis|Vuurheriche $1 Ergebniss}}',
+'shown-title' => 'Zeich mer $1 {{PLURAL:$1|Ergebnis|Ergebniss}} bro Seidn',
'viewprevnext' => 'Dsajch ($1 {{int:pipe-separator}} $2) ($3)',
-'searchprofile-articles' => 'Sajdn dii ann Inhald zajng',
-'searchprofile-images' => 'Muldimedjâ',
-'searchprofile-everything' => 'Âlls',
-'searchprofile-advanced' => 'Erwajderd',
+'searchmenu-new' => "'''Derschdell dai Seidn „[[:$1]]“ in diesn Wigi.'''",
+'searchprofile-articles' => 'Inhaldsseidn',
+'searchprofile-project' => 'Hilf- un Brojegdseidn',
+'searchprofile-images' => 'Muldimedia',
+'searchprofile-everything' => 'Alls',
+'searchprofile-advanced' => 'Erweiderd',
+'searchprofile-articles-tooltip' => 'Soung in $1',
+'searchprofile-project-tooltip' => 'Soung in $1',
+'searchprofile-images-tooltip' => 'Nach Daddein soung',
+'searchprofile-everything-tooltip' => 'Gsamdn Inhald durchsoung (aa Disgussionsseidn)',
+'searchprofile-advanced-tooltip' => 'Soung in weidere Namensraim',
'search-result-size' => '$1 ({{PLURAL:$2|1 wôrd|$2 wärdâr}})',
'search-result-score' => 'Âjschleechich: $1 %',
'search-redirect' => '(Wajdalajdung fon „$1“ häa)',
'timezoneregion-pacific' => 'Bhadsiifischâr Oodseaan',
'allowemail' => 'Iimejl-embfang fon andrâ ôôschdeln',
'youremail' => 'E-mail:',
+'yourrealname' => 'Bürcherlicher Noma:',
# Groups
'group-sysop' => 'Adminisdradoorn',
# Recent changes linked
'recentchangeslinked' => 'Ändärunga af sajdn, af dii fo hiir fârwiisn wäd',
-'recentchangeslinked-toolbox' => 'Ändärunga af sajdn, af dii fo hiir fârwiisn wäd',
+'recentchangeslinked-toolbox' => 'Änderunga an velingde Seidn',
'recentchangeslinked-title' => 'Ändrunga an sajdn, af dii fo „$1“ aus fârwiisn wärd.',
'recentchangeslinked-summary' => "Dii sôndârsajdn fiird di ledsdn ändrunga fon sajdn af, dii wo an däär hiir drôôhänga. Alles, was de dâfoo in daj [[Special:Watchlist|beoobachdunglisdn]] aufgnumma hasd, wäd aa no '''fäd''' ôôdsajchd.",
'recentchangeslinked-page' => 'Sajdn:',
# Upload
'upload' => 'Nauflôôdn',
'uploadlogpage' => 'Brodoghol fom dadaj-hoochlôôdn',
+'filedesc' => 'Bschreibung',
'uploadedimage' => 'had „[[$1]]“ naufglôôdn',
+'license' => 'Lizenz',
+
# File description page
-'file-anchor-link' => 'Dadhaj',
+'file-anchor-link' => 'Daddei',
'filehist' => 'Wärsjoona bis eds',
'filehist-help' => 'Glig af ân dsajdbhungd, um dii dôômôôliche fasung ôôdsuschaua',
'filehist-current' => 'agduäl',
'filehist-filesize' => 'Dadajgräâs',
'filehist-comment' => 'Sembf dâdsuâ',
'filehist-missing' => 'Dadaj fääld',
-'imagelinks' => 'Dsajchn, wo dii dadaj als benudsd wärd',
+'imagelinks' => 'Daddeiverwendung',
'linkstoimage' => 'Dii dadaj wäd fo {{PLURAL:$1|därâ |denâ $1 }} sajdn benudsd:',
'linkstoimage-more' => "Määr wii {{PLURAL:$1|ane |$1 }} sajdn fârwajsn uf diâ dadaj.
Dii lisdn undn dsajch dâfâu nôr äärschd môôl {{PLURAL:$1|an|$1}} fârwajs.
'duplicatesoffile' => 'Dii {{PLURAL:$1|folchende dadaj is â dublighaad|folchende $1 dadajâ sn dublighaade}} fon dâr dadaj ([[Special:FileDuplicateSearch/$2|wajdâre ôôndlshajdâ]]):',
'sharedupload' => 'Dii dadaj ghumd fo $1, un mär däf se fär annäre brojägd aa ´heernemâ.',
'sharedupload-desc-there' => 'Dii dadaj ghumd fon $1, un mr däf se fir andârâ brojägd aa nemâ. Genauârs schded uf dr [$2 beschrajwungssajdâ fon dr dadaj].',
+'sharedupload-desc-here' => 'Däi Daddei schdamm aus $1 un ko vo andre Brojegde verwendt werrn. Däi Beschreibung vo dera ihr [$2 Daddeibeschreibungsseidn] wärrd undn ozeichd.',
'uploadnewversion-linktext' => 'Â naje wärsjoon fo derä dadaj nauflôôdn',
# Random page
-'randompage' => 'Zufälliche Sajdn',
+'randompage' => 'Zoufälliche Seidn',
# Statistics
'statistics' => 'Schdadisdig',
'allarticles' => 'Ale sajdn',
'allpagessubmit' => "Loos gäd's.",
+# Special:Categories
+'categories' => 'Gadegorien',
+
# Special:LinkSearch
'linksearch' => 'Linggs nach ausârhalb',
'emailuser' => 'Dem ôôgmeldn â iimejl schign',
# Watchlist
-'watchlist' => 'Maj beoobachdungs-lisdn',
+'watchlist' => 'Beoobachdungslisdn',
'mywatchlist' => 'Beoobachdungslisdn',
'addedwatchtext' => "Di sajdn „[[:$1]]“ schdäd eds mid af dajnâr [[Special:Watchlist|beoobachdungs-lisdn]] .
# Undelete
'undeletelink' => 'ôôgugn/dsrighooln',
+'undeleteviewlink' => 'oschaun',
# Namespace form on various pages
'namespace' => 'Nôômâraum:',
'contributions-title' => 'Bajdrääch fo „$1“',
'mycontris' => 'Bajdreech',
'contribsub2' => 'Fär $1 ($2)',
-'uctop' => '(ledsdâr schdand)',
+'uctop' => '(agduell)',
'month' => 'bis moonad:',
'year' => 'bis dsum jôôr:',
'sp-contributions-newbies' => 'Bloos bajdrääch fo naj Ôôgmeldâ dsajchn',
'sp-contributions-blocklog' => 'Schbär-brodoghol',
-'sp-contributions-talk' => 'Disghusjoon',
+'sp-contributions-uploads' => 'Houchglodne Daddein',
+'sp-contributions-logs' => 'Logbäicher',
+'sp-contributions-talk' => 'Disgussion',
'sp-contributions-search' => 'Bajdreech suchng',
'sp-contributions-username' => 'IP-adresn odär nôômâ fom Ôôgmeldn:',
'sp-contributions-submit' => 'Suchng',
'linkshere' => "Dii afgfiirdn sajdn fârwajsn af ''„[[:$1]]“''':",
'isredirect' => 'Wajdârlajdungssajdn',
'istemplate' => 'Foorlaachn-ajbindung',
-'isimage' => 'fârwajs af des bild hiir',
+'isimage' => 'Daddeilink',
'whatlinkshere-prev' => '{{PLURAL:$1|vorhäärichâr|vorhääriche $1}}',
'whatlinkshere-next' => '{{PLURAL:$1|nägschdâr|nägschde $1}}',
'whatlinkshere-links' => '← fârwajse hiirhäär',
'blockip-title' => 'Bearbajdâr aus-schbärn',
'blockip-legend' => 'IP-Adresn odr Bearbajdâr aus-schbärn',
'ipboptions' => '2 schdund:2 hours,1 dooch:1 day,3 dooch:3 days,1 wochng:1 week,2 wochng:2 weeks,1 moonad:1 month,3 moonad:3 months,6 moonad:6 months,1 jôôr:1 year,oone dsajdschrangng:infinite',
-'ipblocklist' => 'Gschbärde IP-adresn un Ôôgmelde',
+'ipblocklist' => 'Gschberrder Nutzer',
'blocklink' => 'Schbärn',
'unblocklink' => 'frajgeem',
'change-blocklink' => 'Schbärn ändârn',
# Export
'export' => 'Sajdn ägsbhôrdiirn',
+# Namespace 8 related
+'allmessagesname' => 'Noma',
+'allmessagesdefault' => 'Schdandaddexd',
+
# Thumbnails
'thumbnail-more' => 'Grässär machng',
'tooltip-upload' => 'Loos midm nauflaadn',
'tooltip-rollback' => 'Hiir glign machd mid am môl alâs riggängich, was däär benudsâr dsledschd af där sajdn gmachd had.',
'tooltip-undo' => 'Hiir glign machd dii aane ändärung riggängich un dsajchd dan ôô, wiis dan ausschaua dääd. Dann koosd aa no â dsamfassung wisoo un warum dâdsuuschrajm.',
-'tooltip-summary' => 'Dibb schnell a glaane Zusammafassung nei.',
+'tooltip-summary' => 'Gib a korze Zammfassung ei.',
# Stylesheets
'common.css' => '/* CSS hiir beâjflusd ale schelfn */',
'metadata-help' => 'Dii dadaj umfasd annäre ôôgam, dii normaalârwajs fo där digidaal-ghamâraa odär fo am sghänâr häärghumma. Wen dii dadaj indswischn fârändârd wôrn is, meechn dii nimä dsum bild basn.',
'metadata-expand' => 'Ajdslhajdn dsajchn',
'metadata-collapse' => 'Ajdslhajdn ausblendn',
-'metadata-fields' => 'Hiir afgfiirde fäldâr fo dâ EXIF-medha-daadn wärn af alle bildbeschrajwungs-sajdn afgfiird, aa wen dii medhadaadn-dabelln ajgfalded is. Annäre sin ärschdâmôôl fârschdegd.
+'metadata-fields' => 'Folgnde Felder vo däi EXIF-Medadaden, däi wou in den MediaWigi-Sysdemdexd ogeem sin, werrn af Bildbeschreibungsseidn miid eiglabbder Medadadndabelln ozeichd. Weidere werrn schdandaddmäßich net ozeichd.
* make
* model
* datetimeoriginal
# Info page
'pageinfo-header-edits' => 'Jenotem redakamas',
+'pageinfo-toolboxlink' => 'Nüns pada',
'pageinfo-contentpage-yes' => 'Si',
'pageinfo-protect-cascading-yes' => 'Si',
'userlogin-resetpassword-link' => 'צוריקשטעלן אײַער פאַסווארט',
'helplogin-url' => 'Help:אריינלאגירן',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|הילף מיט אריינלאגירן]]',
+'userlogin-loggedin' => 'איר זענט שוין אריינלאגירט ווי {{GENDER:$1|$1}}.
+ניצט די פארעם אונטן כדי אריינלאגירן ווי אן אנדער באניצער.',
'userlogin-createanother' => 'שאפֿן נאך א קאנטע',
'createacct-join' => 'גיט ארײַן אײַער אינפֿארמאציע אונטן.',
'createacct-another-join' => 'ארײַנגעבן דער נײַער קאנטעס אינפארמאציע אונטן.',
'deleteotherreason' => 'אנדער/נאך אן אורזאך:',
'deletereasonotherlist' => 'אנדער אורזאך',
'deletereason-dropdown' => '* געוויינטלעכע אויסמעקן אורזאכן
+** ספאַם
** פֿארלאנג פֿון שרייבער
** קאפירעכט ברעכונג
** וואנדאליזם
-** נישט יידיש',
+** נישט יידיש
+** צעבראכענע ווייטערפירונג',
'delete-edit-reasonlist' => 'רעדאַקטירן די אויסמעקן סיבות',
'delete-toobig' => 'דער בלאַט האט א גרויסע רעדאקטירונג היסטאריע, מער ווי $1 {{PLURAL:$1|רעוויזיע|רעוויזיעס}}. אויסמעקן אזעלכע בלעטער איז באַגרענעצט געווארן בכדי צו פֿאַרמײַדן א צופֿעליגע פֿאַרשטערונג פֿון {{SITENAME}}.',
'delete-warning-toobig' => 'דער בלאַט האט א גרויסע רעדאקטירונג היסטאריע, מער ווי $1 {{PLURAL:$1|רעוויזיע|רעוויזיעס}}. אויסמעקן אים קען פֿאַרשטערן דאַטנבאַזע אפעראַציעס פֿון {{SITENAME}}; זײַט פֿארזיכטיג איידער איר מעקט אויס.',
'loginreqlink' => '登录',
'loginreqpagetext' => '您必须$1才能查看其它页面。',
'accmailtitle' => '密码已寄出',
-'accmailtext' => "为[[User talk:$1|$1]]随机生成的密码已送至$2。登陆后可以在''[[Special:ChangePassword|更改密码]]''页面中修改。",
+'accmailtext' => "为[[User talk:$1|$1]]随机生成的密码已送至$2。登录后可以在''[[Special:ChangePassword|更改密码]]''页面中修改。",
'newarticle' => '(新页面)',
'newarticletext' => '您进入了一个尚未创建的页面。
要创建该页面,请在下面的编辑框中输入内容(详情参见[[{{MediaWiki:Helppage}}|帮助页]])。
'recentchanges-label-bot' => '该编辑由机器人进行',
'recentchanges-label-unpatrolled' => '该编辑尚未巡查',
'rcnote' => "下面是过去'''$2'''天的最后'''$1'''个更改,截至$4 $5。",
-'rcnotefrom' => "下面是自'''$2'''起的更改(最多显示'''$1'''个)。",
-'rclistfrom' => '显示自$1起的新更改',
+'rcnotefrom' => "下面是'''$2'''之后的更改(最多显示'''$1'''个)。",
+'rclistfrom' => '显示$1之后的新更改',
'rcshowhideminor' => '$1小编辑',
'rcshowhidebots' => '$1机器人的编辑',
'rcshowhideliu' => '$1登录用户的编辑',
'deleteotherreason' => '其他/附加原因:',
'deletereasonotherlist' => '其他原因',
'deletereason-dropdown' => '*常见删除原因
-** 作者申请
+** 广告
+** 破坏行为
** 侵犯著作权
-** 破坏行为',
+** 作者申请
+** 损坏的重定向',
'delete-edit-reasonlist' => '编辑删除原因',
'delete-toobig' => '这个页面有一个十分大量的编辑历史,超过$1次修订。删除此类页面的动作已经被限制,以防止在{{SITENAME}}上的意外扰乱。',
'delete-warning-toobig' => '这个页面有一个十分大量的编辑历史,超过$1次修订。删除它可能会扰乱{{SITENAME}}的数据库操作;在继续此动作前请小心。',
* @author Liangent
* @author Liflon
* @author Littletung
+ * @author Liuxinyu970226
* @author Mark85296341
* @author Oapbtommy
* @author Openerror
'userlogin-resetpassword-link' => '重設密碼',
'helplogin-url' => 'Help:登入',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|登入幫助]]',
+'userlogin-loggedin' => '您已作為{{GENDER:$1|$1}}登錄。
+利用以下表單以作為另一賬戶登錄。',
+'userlogin-createanother' => '建立另一賬戶',
'createacct-join' => '輸入您的基本資料:',
'createacct-another-join' => '在下方輸入新帳號的資訊。',
'createacct-emailrequired' => '電子郵件',
該頁最後的編輯者是[[User:$3|$3]]([[User talk:$3|討論]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])。',
'editcomment' => "編輯摘要: \"''\$1''\"。",
'revertpage' => '已恢復由[[Special:Contributions/$2|$2]]([[User talk:$2|對話]])的編輯至[[User:$1|$1]]的最後一個修訂版本',
-'revertpage-nouser' => '已由隱藏的使用者恢復編輯到上個 {{GENDER:$1|[[使用者:$1|$1]]}} 的修訂版本',
+'revertpage-nouser' => '已由隱藏的使用者恢復編輯到上個{{GENDER:$1|[[User:$1|$1]]}}的修訂版本',
'rollback-success' => '已恢復 $1 的編輯;
更變更回 $2 的最後修訂版本。',
'tags-tag' => '標籤名稱',
'tags-display-header' => '在更改清單中的出現方式',
'tags-description-header' => '解釋完整描述',
+'tags-active-header' => '存檔?',
'tags-hitcount-header' => '已加上標籤的更改',
'tags-edit' => '編輯',
'tags-hitcount' => '$1次更改',
--- /dev/null
+--
+-- patch-archive-ar_id.sql
+--
+-- Bug 39675. Add archive.ar_id.
+
+ALTER TABLE /*$wgDBprefix*/archive
+ ADD COLUMN ar_id int unsigned NOT NULL AUTO_INCREMENT FIRST,
+ ADD PRIMARY KEY (ar_id);
CREATE UNIQUE INDEX /*i*/change_tag_rev_tag ON /*_*/change_tag (ct_rev_id,ct_tag);
-- Covering index, so we can pull all the info only out of the index.
CREATE INDEX /*i*/change_tag_tag_id ON /*_*/change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id);
-
--- Rollup table to pull a LIST of tags simply without ugly GROUP_CONCAT that only works on MySQL 4.1+
-CREATE TABLE /*_*/tag_summary (
- ts_rc_id int NULL,
- ts_log_id int NULL,
- ts_rev_id int NULL,
- ts_tags BLOB NOT NULL
-) /*$wgDBTableOptions*/;
-
-CREATE UNIQUE INDEX /*i*/tag_summary_rc_id ON /*_*/tag_summary (ts_rc_id);
-CREATE UNIQUE INDEX /*i*/tag_summary_log_id ON /*_*/tag_summary (ts_log_id);
-CREATE UNIQUE INDEX /*i*/tag_summary_rev_id ON /*_*/tag_summary (ts_rev_id);
-
-
-CREATE TABLE /*_*/valid_tag (
- vt_tag varchar(255) NOT NULL PRIMARY KEY
-) /*$wgDBTableOptions*/;
--- /dev/null
+--
+-- patch-extenallinks-el_id.sql
+--
+-- Bug 15441. Add externallinks.el_id.
+
+ALTER TABLE /*$wgDBprefix*/externallinks
+ ADD COLUMN el_id int unsigned NOT NULL AUTO_INCREMENT FIRST,
+ ADD PRIMARY KEY (el_id);
--- /dev/null
+-- Rollup table to pull a LIST of tags simply without ugly GROUP_CONCAT that only works on MySQL 4.1+
+-- Andrew Garrett, 2009-01
+CREATE TABLE /*_*/tag_summary (
+ ts_rc_id int NULL,
+ ts_log_id int NULL,
+ ts_rev_id int NULL,
+ ts_tags BLOB NOT NULL
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/tag_summary_rc_id ON /*_*/tag_summary (ts_rc_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_log_id ON /*_*/tag_summary (ts_log_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_rev_id ON /*_*/tag_summary (ts_rev_id);
--- /dev/null
+-- Andrew Garrett, 2009-01
+CREATE TABLE /*_*/valid_tag (
+ vt_tag varchar(255) NOT NULL PRIMARY KEY
+) /*$wgDBTableOptions*/;
$this->addOption( 'prestat', 'Stat the destination files first (try to use listings)' );
$this->addOption( 'skiphash', 'Skip SHA-1 sync checks for files' );
$this->addOption( 'missingonly', 'Only copy files missing from destination listing' );
+ $this->addOption( 'syncviadelete', 'Delete destination files missing from source listing' );
$this->addOption( 'utf8only', 'Skip source files that do not have valid UTF-8 names' );
$this->setBatchSize( 50 );
}
$this->error( "Cannot check for UTF-8, mbstring extension missing.", 1 ); // die
}
- $count = 0;
foreach ( $containers as $container ) {
if ( $subDir != '' ) {
$backendRel = "$container/$subDir";
$this->output( "Doing container '$container'...\n" );
}
- $srcPathsRel = $src->getFileList( array(
- 'dir' => $src->getRootStoragePath() . "/$backendRel",
- 'adviseStat' => !$this->hasOption( 'missingonly' ) // avoid HEADs
- ) );
- if ( $srcPathsRel === null ) {
- $this->error( "Could not list files in $container.", 1 ); // die
- }
-
if ( $this->hasOption( 'missingonly' ) ) {
- $dstPathsRel = $dst->getFileList( array(
- 'dir' => $dst->getRootStoragePath() . "/$backendRel" ) );
- if ( $dstPathsRel === null ) {
+ $this->output( "\tBuilding list of missing files..." );
+ $srcPathsRel = $this->getListingDiffRel( $src, $dst, $backendRel );
+ $this->output( count( $srcPathsRel ) . " file(s) need to be copied.\n" );
+ } else {
+ $srcPathsRel = $src->getFileList( array(
+ 'dir' => $src->getRootStoragePath() . "/$backendRel",
+ 'adviseStat' => true // avoid HEADs
+ ) );
+ if ( $srcPathsRel === null ) {
$this->error( "Could not list files in $container.", 1 ); // die
}
- // Get the list of destination files
- $relFilesDstSha1 = array();
- foreach ( $dstPathsRel as $dstPathRel ) {
- $relFilesDstSha1[sha1( $dstPathRel )] = 1;
- }
- unset( $dstPathsRel ); // free
- // Get the list of missing files
- $missingPathsRel = array();
- foreach ( $srcPathsRel as $srcPathRel ) {
- if ( !isset( $relFilesDstSha1[sha1( $srcPathRel )] ) ) {
- $missingPathsRel[] = $srcPathRel;
- }
- }
- unset( $srcPathsRel ); // free
- // Only copy the missing files over in the next loop
- $srcPathsRel = $missingPathsRel;
- $this->output( count( $srcPathsRel ) . " file(s) need to be copied.\n" );
- } elseif ( $this->getOption( 'prestat' ) ) {
+ }
+
+ if ( $this->getOption( 'prestat' ) && !$this->hasOption( 'missingonly' ) ) {
// Build the stat cache for the destination files
- $this->output( "Building destination stat cache..." );
+ $this->output( "\tBuilding destination stat cache..." );
$dstPathsRel = $dst->getFileList( array(
'dir' => $dst->getRootStoragePath() . "/$backendRel",
'adviseStat' => true // avoid HEADs
$this->output( "done [" . count( $this->statCache ) . " file(s)]\n" );
}
+ $this->output( "\tCopying file(s)...\n" );
+ $count = 0;
$batchPaths = array();
foreach ( $srcPathsRel as $srcPathRel ) {
// Check up on the rate file periodically to adjust the concurrency
if ( $rateFile && ( !$count || ( $count % 500 ) == 0 ) ) {
$this->mBatchSize = max( 1, (int)file_get_contents( $rateFile ) );
- $this->output( "Batch size is now {$this->mBatchSize}.\n" );
+ $this->output( "\tBatch size is now {$this->mBatchSize}.\n" );
}
$batchPaths[$srcPathRel] = 1; // remove duplicates
if ( count( $batchPaths ) >= $this->mBatchSize ) {
$this->copyFileBatch( array_keys( $batchPaths ), $backendRel, $src, $dst );
$batchPaths = array(); // done
}
+ $this->output( "\tCopied $count file(s).\n" );
+
+ if ( $this->hasOption( 'syncviadelete' ) ) {
+ $this->output( "\tBuilding list of excess destination files..." );
+ $delPathsRel = $this->getListingDiffRel( $dst, $src, $backendRel );
+ $this->output( count( $delPathsRel ) . " file(s) need to be deleted.\n" );
+
+ $this->output( "\tDeleting file(s)...\n" );
+ $count = 0;
+ $batchPaths = array();
+ foreach ( $delPathsRel as $delPathRel ) {
+ // Check up on the rate file periodically to adjust the concurrency
+ if ( $rateFile && ( !$count || ( $count % 500 ) == 0 ) ) {
+ $this->mBatchSize = max( 1, (int)file_get_contents( $rateFile ) );
+ $this->output( "\tBatch size is now {$this->mBatchSize}.\n" );
+ }
+ $batchPaths[$delPathRel] = 1; // remove duplicates
+ if ( count( $batchPaths ) >= $this->mBatchSize ) {
+ $this->delFileBatch( array_keys( $batchPaths ), $backendRel, $dst );
+ $batchPaths = array(); // done
+ }
+ ++$count;
+ }
+ if ( count( $batchPaths ) ) { // left-overs
+ $this->delFileBatch( array_keys( $batchPaths ), $backendRel, $dst );
+ $batchPaths = array(); // done
+ }
+
+ $this->output( "\tDeleted $count file(s).\n" );
+ }
if ( $subDir != '' ) {
$this->output( "Finished container '$container', directory '$subDir'.\n" );
}
}
- $this->output( "Done [$count file(s)].\n" );
+ $this->output( "Done.\n" );
}
+ /**
+ * @param FileBackend $src
+ * @param FileBackend $dst
+ * @param string $backendRel
+ * @return array (rel paths in $src minus those in $dst)
+ */
+ protected function getListingDiffRel( FileBackend $src, FileBackend $dst, $backendRel ) {
+ $srcPathsRel = $src->getFileList( array(
+ 'dir' => $src->getRootStoragePath() . "/$backendRel" ) );
+ if ( $srcPathsRel === null ) {
+ $this->error( "Could not list files in source container.", 1 ); // die
+ }
+ $dstPathsRel = $dst->getFileList( array(
+ 'dir' => $dst->getRootStoragePath() . "/$backendRel" ) );
+ if ( $dstPathsRel === null ) {
+ $this->error( "Could not list files in destination container.", 1 ); // die
+ }
+ // Get the list of destination files
+ $relFilesDstSha1 = array();
+ foreach ( $dstPathsRel as $dstPathRel ) {
+ $relFilesDstSha1[sha1( $dstPathRel )] = 1;
+ }
+ unset( $dstPathsRel ); // free
+ // Get the list of missing files
+ $missingPathsRel = array();
+ foreach ( $srcPathsRel as $srcPathRel ) {
+ if ( !isset( $relFilesDstSha1[sha1( $srcPathRel )] ) ) {
+ $missingPathsRel[] = $srcPathRel;
+ }
+ }
+ unset( $srcPathsRel ); // free
+
+ return $missingPathsRel;
+ }
+
+ /**
+ * @param array $srcPathsRel
+ * @param string $backendRel
+ * @param FileBackend $src
+ * @param FileBackend $dst
+ * @return void
+ */
protected function copyFileBatch(
array $srcPathsRel, $backendRel, FileBackend $src, FileBackend $dst
) {
$t_start = microtime( true );
$fsFiles = $src->getLocalReferenceMulti( array( 'srcs' => $srcPaths, 'latest' => 1 ) );
$ellapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
- $this->output( "\nDownloaded these file(s) [{$ellapsed_ms}ms]:\n" .
- implode( "\n", $srcPaths ) . "\n\n" );
+ $this->output( "\n\tDownloaded these file(s) [{$ellapsed_ms}ms]:\n\t" .
+ implode( "\n\t", $srcPaths ) . "\n\n" );
}
// Determine what files need to be copied over...
} elseif ( !$this->hasOption( 'missingonly' )
&& $this->filesAreSame( $src, $dst, $srcPath, $dstPath ) )
{
- $this->output( "Already have $srcPathRel.\n" );
+ $this->output( "\tAlready have $srcPathRel.\n" );
continue; // assume already copied...
}
$fsFile = array_key_exists( $srcPath, $fsFiles )
$this->error( print_r( $status->getErrorsArray(), true ) );
$this->error( "$wikiId: Could not copy file batch.", 1 ); // die
} elseif ( count( $copiedRel ) ) {
- $this->output( "\nCopied these file(s) [{$ellapsed_ms}ms]:\n" .
- implode( "\n", $copiedRel ) . "\n\n" );
+ $this->output( "\n\tCopied these file(s) [{$ellapsed_ms}ms]:\n\t" .
+ implode( "\n\t", $copiedRel ) . "\n\n" );
+ }
+ }
+
+ /**
+ * @param array $dstPathsRel
+ * @param string $backendRel
+ * @param FileBackend $dst
+ * @return void
+ */
+ protected function delFileBatch(
+ array $dstPathsRel, $backendRel, FileBackend $dst
+ ) {
+ $ops = array();
+ $deletedRel = array(); // for output message
+ $wikiId = $dst->getWikiId();
+
+ // Determine what files need to be copied over...
+ foreach ( $dstPathsRel as $dstPathRel ) {
+ $dstPath = $dst->getRootStoragePath() . "/$backendRel/$dstPathRel";
+ $ops[] = array( 'op' => 'delete', 'src' => $dstPath );
+ $deletedRel[] = $dstPathRel;
+ }
+
+ // Delete the batch of source files...
+ $t_start = microtime( true );
+ $status = $dst->doQuickOperations( $ops, array( 'bypassReadOnly' => 1 ) );
+ if ( !$status->isOK() ) {
+ sleep( 10 ); // wait and retry copy again
+ $status = $dst->doQuickOperations( $ops, array( 'bypassReadOnly' => 1 ) );
+ }
+ $ellapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
+ if ( !$status->isOK() ) {
+ $this->error( print_r( $status->getErrorsArray(), true ) );
+ $this->error( "$wikiId: Could not delete file batch.", 1 ); // die
+ } elseif ( count( $deletedRel ) ) {
+ $this->output( "\n\tDeleted these file(s) [{$ellapsed_ms}ms]:\n\t" .
+ implode( "\n\t", $deletedRel ) . "\n\n" );
}
}
+ /**
+ * @param FileBackend $src
+ * @param FileBackend $dst
+ * @param string $sPath
+ * @param string $dPath
+ * @return bool
+ */
protected function filesAreSame( FileBackend $src, FileBackend $dst, $sPath, $dPath ) {
$skipHash = $this->hasOption( 'skiphash' );
$srcStat = $src->getFileStat( array( 'src' => $sPath ) );
$default = wfMessage( $key )->inLanguage( $langCode )->useDatabase( false )->plain();
$messageInfo['relevantPages']++;
- if ( $actual === $default ) {
+
+ if (
+ // Exclude messages that are empty by default, such as sitenotice, specialpage
+ // summaries and accesskeys.
+ $default !== '' && $default !== '-' &&
+ $actual === $default
+ ) {
$hasTalk = isset( $statuses['talks'][$key] );
$messageInfo['results'][] = array(
'title' => $key . $titleSuffix,
} else {
$props = FSFile::getPropsFromPath( $file );
$flags = 0;
- $options = array();
+ $publishOptions = array();
$handler = MediaHandler::getHandler( $props['mime'] );
if ( $handler ) {
- $options['headers'] = $handler->getStreamHeaders( $props['metadata'] );
+ $publishOptions['headers'] = $handler->getStreamHeaders( $props['metadata'] );
} else {
- $options['headers'] = array();
+ $publishOptions['headers'] = array();
}
- $archive = $image->publish( $file, $flags, $options );
+ $archive = $image->publish( $file, $flags, $publishOptions );
if ( !$archive->isGood() ) {
echo "failed. (" .
$archive->getWikiText() .
}
$commentText = SpecialUpload::getInitialPageText( $commentText, $license );
- if ( !$summary ) {
+ if ( !isset( $options['summary'] ) ) {
$summary = $commentText;
}
-- Cannot reasonably create views on this table, due to the presence of TEXT
-- columns.
CREATE TABLE /*$wgDBprefix*/archive (
+ ar_id NOT NULL PRIMARY KEY clustered IDENTITY,
ar_namespace SMALLINT NOT NULL DEFAULT 0,
ar_title NVARCHAR(255) NOT NULL DEFAULT '',
ar_text NVARCHAR(MAX) NOT NULL,
-- Track links to external URLs
-- IE >= 4 supports no more than 2083 characters in a URL
CREATE TABLE /*$wgDBprefix*/externallinks (
+ el_id INT NOT NULL PRIMARY KEY clustered IDENTITY,
el_from INT NOT NULL DEFAULT '0',
el_to VARCHAR(2083) NOT NULL,
el_index VARCHAR(896) NOT NULL,
--- /dev/null
+define mw_prefix='{$wgDBprefix}';
+
+ALTER TABLE &mw_prefix.archive ADD (
+ar_id NUMBER NOT NULL,
+);
+ALTER TABLE &mw_prefix.archive ADD CONSTRAINT &mw_prefix.archive_pk PRIMARY KEY (ar_id);
--- /dev/null
+define mw_prefix='{$wgDBprefix}';
+
+ALTER TABLE &mw_prefix.externallinks ADD el_id NUMBER NOT NULL;
+ALTER TABLE &mw_prefix.externallinks ADD CONSTRAINT &mw_prefix.externallinks_pk PRIMARY KEY (el_id);
\ No newline at end of file
);
ALTER TABLE &mw_prefix.pagecontent ADD CONSTRAINT &mw_prefix.pagecontent_pk PRIMARY KEY (old_id);
+CREATE SEQUENCE archive_ar_id_seq;
CREATE TABLE &mw_prefix.archive (
+ ar_id NUMBER NOT NULL,
ar_namespace NUMBER DEFAULT 0 NOT NULL,
ar_title VARCHAR2(255) NOT NULL,
ar_text CLOB,
ar_content_model VARCHAR2(32),
ar_content_format VARCHAR2(64)
);
+ALTER TABLE &mw_prefix.archive ADD CONSTRAINT &mw_prefix.archive_pk PRIMARY KEY (ar_id);
ALTER TABLE &mw_prefix.archive ADD CONSTRAINT &mw_prefix.archive_fk1 FOREIGN KEY (ar_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX &mw_prefix.archive_i01 ON &mw_prefix.archive (ar_namespace,ar_title,ar_timestamp);
CREATE INDEX &mw_prefix.archive_i02 ON &mw_prefix.archive (ar_user_text,ar_timestamp);
CREATE UNIQUE INDEX &mw_prefix.category_u01 ON &mw_prefix.category (cat_title);
CREATE INDEX &mw_prefix.category_i01 ON &mw_prefix.category (cat_pages);
+CREATE SEQUENCE externallinks_el_id_seq;
CREATE TABLE &mw_prefix.externallinks (
+ el_id NUMBER NOT NULL,
el_from NUMBER NOT NULL,
el_to VARCHAR2(2048) NOT NULL,
el_index VARCHAR2(2048) NOT NULL
);
+ALTER TABLE &mw_prefix.externallinks ADD CONSTRAINT &mw_prefix.externallinks_pk PRIMARY KEY (el_id);
ALTER TABLE &mw_prefix.externallinks ADD CONSTRAINT &mw_prefix.externallinks_fk1 FOREIGN KEY (el_from) REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX &mw_prefix.externallinks_i01 ON &mw_prefix.externallinks (el_from, el_to);
CREATE INDEX &mw_prefix.externallinks_i02 ON &mw_prefix.externallinks (el_to, el_from);
CREATE TABLE profiling (
pf_count INTEGER NOT NULL DEFAULT 0,
- pf_time NUMERIC(18,10) NOT NULL DEFAULT 0,
+ pf_time FLOAT NOT NULL DEFAULT 0,
+ pf_memory FLOAT NOT NULL DEFAULT 0,
pf_name TEXT NOT NULL,
pf_server TEXT NULL
);
DROP SEQUENCE IF EXISTS logging_log_id_seq CASCADE;
DROP SEQUENCE IF EXISTS job_job_id_seq CASCADE;
DROP SEQUENCE IF EXISTS category_cat_id_seq CASCADE;
+DROP SEQUENCE IF EXISTS archive_ar_id_seq CASCADE;
+DROP SEQUENCE IF EXISTS externallinks_el_id_seq CASCADE;
DROP FUNCTION IF EXISTS page_deleted() CASCADE;
DROP FUNCTION IF EXISTS ts2_page_title() CASCADE;
DROP FUNCTION IF EXISTS ts2_page_text() CASCADE;
CREATE INDEX page_props_propname ON page_props (pp_propname);
CREATE UNIQUE INDEX pp_propname_page ON page_props (pp_propname,pp_page);
+CREATE SEQUENCE archive_ar_id_seq;
CREATE TABLE archive (
+ ar_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('archive_ar_id_seq'),
ar_namespace SMALLINT NOT NULL,
ar_title TEXT NOT NULL,
ar_text TEXT, -- technically should be bytea, but not used anymore
CREATE UNIQUE INDEX cl_from ON categorylinks (cl_from, cl_to);
CREATE INDEX cl_sortkey ON categorylinks (cl_to, cl_sortkey, cl_from);
+CREATE SEQUENCE externallinks_id_seq;
CREATE TABLE externallinks (
+ el_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('externallinks_id_seq'),
el_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
el_to TEXT NOT NULL,
el_index TEXT NOT NULL
-- This table is not used unless profiling is turned on
CREATE TABLE profiling (
pf_count INTEGER NOT NULL DEFAULT 0,
- pf_time NUMERIC(18,10) NOT NULL DEFAULT 0,
- pf_memory NUMERIC(18,10) NOT NULL DEFAULT 0,
+ pf_time FLOAT NOT NULL DEFAULT 0,
+ pf_memory FLOAT NOT NULL DEFAULT 0,
pf_name TEXT NOT NULL,
pf_server TEXT NULL
);
$this->addOption( 'starttime', 'Starting timestamp', true, true );
$this->addOption( 'endtime', 'Ending timestamp', true, true );
$this->addOption( 'htcp-dest', 'HTCP announcement destination (IP:port)', false, true );
+ $this->addOption( 'sleep-per-batch', 'Milliseconds to sleep between batches', false, true );
$this->addOption( 'dry-run', 'Do not send purge requests' );
$this->addOption( 'verbose', 'Show more output', false, false, 'v' );
$this->setBatchSize( 100 );
}
// Send batch of purge requests out to squids
- $squid = new SquidUpdate( $urls );
+ $squid = new SquidUpdate( $urls, count( $urls ) );
$squid->doUpdate();
+
+ if ( $this->hasOption( 'sleep-per-batch' ) ) {
+ // sleep-per-batch is milliseconds, usleep wants micro seconds.
+ usleep( 1000 * (int)$this->getOption( 'sleep-per-batch' ) );
+ }
}
$this->output( "Done!\n" );
$pending = $queue->getSize();
$claimed = $queue->getAcquiredCount();
$abandoned = $queue->getAbandonedCount();
- $active = $claimed - $abandoned;
+ $active = max( 0, $claimed - $abandoned );
if ( ( $pending + $claimed ) > 0 ) {
$this->output(
"{$type}: $pending queued; " .
DROP TABLE IF EXISTS /*_*/page_restrictions_tmp;
DROP TABLE IF EXISTS /*_*/protected_titles_tmp;
DROP TABLE IF EXISTS /*_*/page_props_tmp;
+DROP TABLE IF EXISTS /*_*/archive_tmp;
+DROP TABLE IF EXISTS /*_*/externallinks_tmp;
--------------------------------------------------------------------------------
-- Create new tables
);
CREATE UNIQUE INDEX /*i*/pp_page_propname ON /*_*/page_props_tmp (pp_page,pp_propname);
+--
+-- Holding area for deleted articles, which may be viewed
+-- or restored by admins through the Special:Undelete interface.
+-- The fields generally correspond to the page, revision, and text
+-- fields, with several caveats.
+-- Cannot reasonably create views on this table, due to the presence of TEXT
+-- columns.
+CREATE TABLE /*$wgDBprefix*/archive_tmp (
+ ar_id NOT NULL PRIMARY KEY clustered IDENTITY,
+ ar_namespace SMALLINT NOT NULL DEFAULT 0,
+ ar_title NVARCHAR(255) NOT NULL DEFAULT '',
+ ar_text NVARCHAR(MAX) NOT NULL,
+ ar_comment NVARCHAR(255) NOT NULL,
+ ar_user INT NULL REFERENCES /*$wgDBprefix*/[user](user_id) ON DELETE SET NULL,
+ ar_user_text NVARCHAR(255) NOT NULL,
+ ar_timestamp DATETIME NOT NULL DEFAULT GETDATE(),
+ ar_minor_edit BIT NOT NULL DEFAULT 0,
+ ar_flags NVARCHAR(255) NOT NULL,
+ ar_rev_id INT,
+ ar_text_id INT,
+ ar_deleted BIT NOT NULL DEFAULT 0,
+ ar_len INT DEFAULT NULL,
+ ar_page_id INT NULL,
+ ar_parent_id INT NULL
+);
+CREATE INDEX /*$wgDBprefix*/ar_name_title_timestamp ON /*$wgDBprefix*/archive_tmp(ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX /*$wgDBprefix*/ar_usertext_timestamp ON /*$wgDBprefix*/archive_tmp(ar_user_text,ar_timestamp);
+CREATE INDEX /*$wgDBprefix*/ar_user_text ON /*$wgDBprefix*/archive_tmp(ar_user_text);
+
+--
+-- Track links to external URLs
+-- IE >= 4 supports no more than 2083 characters in a URL
+CREATE TABLE /*$wgDBprefix*/externallinks_tmp (
+ el_id INT NOT NULL PRIMARY KEY clustered IDENTITY,
+ el_from INT NOT NULL DEFAULT '0',
+ el_to VARCHAR(2083) NOT NULL,
+ el_index VARCHAR(896) NOT NULL,
+);
+-- Maximum key length ON SQL Server is 900 bytes
+CREATE INDEX /*$wgDBprefix*/externallinks_index ON /*$wgDBprefix*/externallinks_tmp(el_index);
+
--------------------------------------------------------------------------------
-- Populate the new tables using INSERT SELECT
--------------------------------------------------------------------------------
INSERT OR IGNORE INTO /*_*/page_restrictions_tmp SELECT * FROM /*_*/page_restrictions;
INSERT OR IGNORE INTO /*_*/protected_titles_tmp SELECT * FROM /*_*/protected_titles;
INSERT OR IGNORE INTO /*_*/page_props_tmp SELECT * FROM /*_*/page_props;
+INSERT OR IGNORE INTO /*_*/archive_tmp SELECT * FROM /*_*/archive;
+INSERT OR IGNORE INTO /*_*/externallinks_tmp SELECT * FROM /*_*/externallinks;
--------------------------------------------------------------------------------
-- Do the table renames
ALTER TABLE /*_*/protected_titles_tmp RENAME TO /*_*/protected_titles;
DROP TABLE /*_*/page_props;
ALTER TABLE /*_*/page_props_tmp RENAME TO /*_*/page_props;
+DROP TABLE /*_*/archive;
+ALTER TABLE /*_*/archive_tmp RENAME TO /*_*/archive;
+DROP TABLE /*_*/externalllinks;
+ALTER TABLE /*_*/externallinks_tmp RENAME TO /*_*/externallinks;
--------------------------------------------------------------------------------
-- Drop and create tables with unique indexes but no valuable data
--- /dev/null
+DROP TABLE IF EXISTS /*_*/archive_tmp;
+
+CREATE TABLE /*$wgDBprefix*/archive_tmp (
+ ar_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ ar_namespace int NOT NULL default 0,
+ ar_title varchar(255) binary NOT NULL default '',
+ ar_text mediumblob NOT NULL,
+ ar_comment tinyblob NOT NULL,
+ ar_user int unsigned NOT NULL default 0,
+ ar_user_text varchar(255) binary NOT NULL,
+ ar_timestamp binary(14) NOT NULL default '',
+ ar_minor_edit tinyint NOT NULL default 0,
+ ar_flags tinyblob NOT NULL,
+ ar_rev_id int unsigned,
+ ar_text_id int unsigned,
+ ar_deleted tinyint unsigned NOT NULL default 0,
+ ar_len int unsigned,
+ ar_page_id int unsigned,
+ ar_parent_id int unsigned default NULL,
+ ar_sha1 varbinary(32) NOT NULL default '',
+ ar_content_model varbinary(32) DEFAULT NULL,
+ ar_content_format varbinary(64) DEFAULT NULL
+);
+
+INSERT OR IGNORE INTO /*_*/archive_tmp (
+ ar_namespace, ar_title, ar_title, ar_text, ar_comment, ar_user, ar_user_text, ar_timestamp,
+ ar_minor_edit, ar_flags, ar_rev_id, ar_text_id, ar_deleted, ar_len, ar_page_id, ar_parent_id )
+ SELECT
+ ar_namespace, ar_title, ar_title, ar_text, ar_comment, ar_user, ar_user_text, ar_timestamp,
+ ar_minor_edit, ar_flags, ar_rev_id, ar_text_id, ar_deleted, ar_len, ar_page_id, ar_parent_id
+ FROM /*_*/archive;
+
+DROP TABLE /*_*/archive;
+
+ALTER TABLE /*_*/archive_tmp RENAME TO /*_*/archive;
+
+CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
+CREATE INDEX /*i*/ar_revid ON /*_*/archive (ar_rev_id);
--- /dev/null
+DROP TABLE IF EXISTS /*_*/externallinks_tmp;
+
+CREATE TABLE /*$wgDBprefix*/externallinks_tmp (
+ el_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ el_from int unsigned NOT NULL default 0,
+ el_to blob NOT NULL,
+ el_index blob NOT NULL
+);
+
+INSERT OR IGNORE INTO /*_*/externallinks_tmp (el_from, el_to, el_index) SELECT
+ el_from, el_to, el_index FROM /*_*/externallinks;
+
+DROP TABLE /*_*/externallinks;
+
+ALTER TABLE /*_*/externallinks_tmp RENAME TO /*_*/externallinks;
+
+CREATE INDEX /*i*/el_from ON /*_*/externallinks (el_from, el_to(40));
+CREATE INDEX /*i*/el_to ON /*_*/externallinks (el_to(60), el_from);
+CREATE INDEX /*i*/el_index ON /*_*/externallinks (el_index(60));
\ No newline at end of file
-- fields, with several caveats.
--
CREATE TABLE /*_*/archive (
+ -- Primary key
+ ar_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
ar_namespace int NOT NULL default 0,
ar_title varchar(255) binary NOT NULL default '',
-- content format, see CONTENT_FORMAT_XXX constants
ar_content_format varbinary(64) DEFAULT NULL
-
) /*$wgDBTableOptions*/;
CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
-- Track links to external URLs
--
CREATE TABLE /*_*/externallinks (
+ -- Primary key
+ el_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+
-- page_id of the referring page
el_from int unsigned NOT NULL default 0,
-- Visibility of recent changes items, bitfield
rc_deleted tinyint unsigned NOT NULL default 0,
- -- Value corresonding to log_id, specific log entries
+ -- Value corresponding to log_id, specific log entries
rc_logid int unsigned NOT NULL default 0,
-- Store log type info here, or null
rc_log_type varbinary(255) NULL default NULL,
'common/commonElements.css' => array( 'media' => 'screen' ),
'common/commonContent.css' => array( 'media' => 'screen' ),
'common/commonInterface.css' => array( 'media' => 'screen' ),
- 'vector/screen.less' => array( 'media' => 'screen' ),
- 'vector/externalLinks.less' => array( 'media' => 'screen' ),
- 'vector/screen-hd.css' => array( 'media' => 'screen and (min-width: 982px)' ),
+ 'vector/styles.less',
+ ),
+ 'remoteBasePath' => $GLOBALS['wgStylePath'],
+ 'localBasePath' => $GLOBALS['wgStyleDirectory'],
+ ),
+ 'skins.vector.beta' => array(
+ // Keep in sync with skins.vector
+ 'styles' => array(
+ 'common/commonElements.css' => array( 'media' => 'screen' ),
+ 'common/commonContent.css' => array( 'media' => 'screen' ),
+ 'common/commonInterface.css' => array( 'media' => 'screen' ),
+ 'vector/styles-beta.less' => array( 'media' => 'screen' ),
),
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
),
'skins.vector.collapsibleNav' => array(
- 'styles' => array(
- 'vector/collapsibleNav.less',
- ),
'scripts' => array(
'vector/collapsibleNav.js',
),
*/
( function ( mw, $ ) {
- // Cache token so we don't have to keep fetching new ones for every single request.
- var cachedToken = null;
-
$.extend( mw.Api.prototype, {
/**
* @return {jQuery.Promise} See #post
*/
postWithEditToken: function ( params, ok, err ) {
- var useTokenToPost, getTokenIfBad,
- api = this;
- if ( cachedToken === null ) {
- // We don't have a valid cached token, so get a fresh one and try posting.
- // We do not trap any 'badtoken' or 'notoken' errors, because we don't want
- // an infinite loop. If this fresh token is bad, something else is very wrong.
- useTokenToPost = function ( token ) {
- params.token = token;
- api.post( params, { ok: ok, err: err } );
- };
- return api.getEditToken( useTokenToPost, err );
- } else {
- // We do have a token, but it might be expired. So if it is 'bad' then
- // start over with a new token.
- params.token = cachedToken;
- getTokenIfBad = function ( code, result ) {
- if ( code === 'badtoken' ) {
- // force a new token, clear any old one
- cachedToken = null;
- api.postWithEditToken( params, ok, err );
- } else {
- err( code, result );
- }
- };
- return api.post( params, { ok: ok, err: getTokenIfBad } );
- }
+ return this.postWithToken( 'edit', params ).done( ok ).fail( err );
},
/**
* @return {string} return.done.token Received token.
*/
getEditToken: function ( ok, err ) {
- var d = $.Deferred(),
- apiPromise;
-
- // Backwards compatibility (< MW 1.20)
- d.done( ok ).fail( err );
-
- apiPromise = this.get( {
- action: 'tokens',
- type: 'edit'
- }, {
- // Due to the API assuming we're logged out if we pass the callback-parameter,
- // we have to disable jQuery's callback system, and instead parse JSON string,
- // by setting 'jsonp' to false.
- // TODO: This concern seems genuine but no other module has it. Is it still
- // needed and/or should we pass this by default?
- jsonp: false
- } )
- .done( function ( data ) {
- var token;
- // If token type is not available for this user,
- // key 'edittoken' is missing or can contain Boolean false
- if ( data.tokens && data.tokens.edittoken ) {
- token = data.tokens.edittoken;
- cachedToken = token;
- d.resolve( token );
- } else {
- d.reject( 'token-missing', data );
- }
- } )
- .fail( d.reject );
-
- return d.promise( { abort: apiPromise.abort } );
+ return this.getToken( 'edit' ).done( ok ).fail( err );
},
/**
text: message
}, ok, err );
}
-
- } );
+ } );
/**
* @class mw.Api
dataType: 'json'
}
- };
+ },
+ tokenCache = {};
/**
* Constructor to create an object to interact with the API of a particular MediaWiki server.
return apiDeferred.promise( { abort: xhr.abort } ).fail( function ( code, details ) {
mw.log( 'mw.Api error: ', code, details );
} );
- }
+ },
+
+ /**
+ * Post to API with specified type of token. If we have no token, get one and try to post.
+ * If we have a cached token try using that, and if it fails, blank out the
+ * cached token and start over. For example to change an user option you could do:
+ *
+ * new mw.Api().postWithToken( 'options', {
+ * action: 'options',
+ * optionname: 'gender',
+ * optionvalue: 'female'
+ * } );
+ *
+ * @param {string} tokenType The name of the token, like options or edit.
+ * @param {Object} params API parameters
+ * @return {jQuery.Promise} See #post
+ */
+ postWithToken: function ( tokenType, params ) {
+ var api = this, hasOwn = tokenCache.hasOwnProperty;
+ if ( hasOwn.call( tokenCache, tokenType ) && tokenCache[tokenType] !== undefined ) {
+ params.token = tokenCache[tokenType];
+ return api.post( params ).then(
+ null,
+ function ( code ) {
+ if ( code === 'badtoken' ) {
+ // force a new token, clear any old one
+ tokenCache[tokenType] = params.token = undefined;
+ return api.post( params );
+ }
+ }
+ );
+ } else {
+ return api.getToken( tokenType ).then( function ( token ) {
+ tokenCache[tokenType] = params.token = token;
+ return api.post( params );
+ } );
+ }
+ },
+ /**
+ * Api helper to grab any token.
+ *
+ * @param {string} type Token type.
+ * @return {jQuery.Promise}
+ * @return {Function} return.done
+ * @return {string} return.done.token Received token.
+ */
+ getToken: function ( type ) {
+ var apiPromise,
+ d = $.Deferred();
+
+ apiPromise = this.get( {
+ action: 'tokens',
+ type: type
+ }, {
+ // Due to the API assuming we're logged out if we pass the callback-parameter,
+ // we have to disable jQuery's callback system, and instead parse JSON string,
+ // by setting 'jsonp' to false.
+ // TODO: This concern seems genuine but no other module has it. Is it still
+ // needed and/or should we pass this by default?
+ } )
+ .done( function ( data ) {
+ // If token type is not available for this user,
+ // key '...token' is missing or can contain Boolean false
+ if ( data.tokens && data.tokens[type + 'token'] ) {
+ d.resolve( data.tokens[type + 'token'] );
+ } else {
+ d.reject( 'token-missing', data );
+ }
+ } )
+ .fail( d.reject );
+
+ return d.promise( { abort: apiPromise.abort } );
+ }
};
/**
+@charset "UTF-8";
/**
* Provide Agora appearance for mw-ui-* classes when using a skin other than
* Vector.
box-sizing: border-box;
width: 290px;
}
-/* line 19, sourcefiles/scss/components/default/_forms.scss */
+/* line 20, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-vform > div {
display: block;
margin: 0 0 15px 0;
padding: 0;
width: 100%;
}
-/* line 27, sourcefiles/scss/components/default/_forms.scss */
+/* line 28, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-vform > div input,
.mw-ui-vform > div .mw-ui-button {
display: block;
margin: 0;
width: 100%;
}
-/* line 36, sourcefiles/scss/components/default/_forms.scss */
+/* line 37, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]) {
border-style: solid;
border-width: 1px;
.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus:not([type=checkbox]):not([type=radio]) {
outline: 0;
}
-/* line 40, sourcefiles/scss/components/default/_forms.scss */
+/* line 41, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-vform > div label {
display: block;
-webkit-box-sizing: border-box;
.mw-ui-vform > div label * {
font-weight: normal;
}
-/* line 51, sourcefiles/scss/components/default/_forms.scss */
+/* line 52, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-vform > div input[type="checkbox"],
.mw-ui-vform > div input[type="radio"] {
display: inline;
box-sizing: content-box;
width: auto;
}
+/* line 63, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform .error {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ font-size: 0.9em;
+ margin: 0 0 1em 0;
+ padding: 0.5em;
+ color: #cc0000;
+ border: 1px solid #fac5c5;
+ background-color: #fae3e3;
+ text-shadow: 0 1px #fae3e3;
+ word-wrap: break-word;
+}
+
+/* line 86, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform-div {
+ display: block;
+ margin: 0 0 15px 0;
+ padding: 0;
+ width: 100%;
+}
-/* line 67, sourcefiles/scss/components/default/_forms.scss */
+/* line 96, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-input {
border-style: solid;
border-width: 1px;
outline: 0;
}
-/* line 74, sourcefiles/scss/components/default/_forms.scss */
+/* line 103, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-label {
font-size: 0.9em;
color: #4a4a4a;
font-weight: normal;
}
-/* line 83, sourcefiles/scss/components/default/_forms.scss */
+/* line 112, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-checkbox-label, .mw-ui-radio-label {
margin-bottom: 0.5em;
cursor: pointer;
+@charset "UTF-8";
/**
* Provide Agora appearance for mw-ui-* classes when using the Vector skin.
* Compass builds these Agora styles from source Sass files in
box-sizing: border-box;
width: 290px;
}
-/* line 19, sourcefiles/scss/components/default/_forms.scss */
+/* line 20, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-vform > div {
display: block;
margin: 0 0 15px 0;
padding: 0;
width: 100%;
}
-/* line 27, sourcefiles/scss/components/default/_forms.scss */
+/* line 28, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-vform > div input,
.mw-ui-vform > div .mw-ui-button {
display: block;
margin: 0;
width: 100%;
}
-/* line 36, sourcefiles/scss/components/default/_forms.scss */
+/* line 37, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]) {
border-style: solid;
border-width: 1px;
.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus:not([type=checkbox]):not([type=radio]) {
outline: 0;
}
-/* line 40, sourcefiles/scss/components/default/_forms.scss */
+/* line 41, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-vform > div label {
display: block;
-webkit-box-sizing: border-box;
.mw-ui-vform > div label * {
font-weight: normal;
}
-/* line 51, sourcefiles/scss/components/default/_forms.scss */
+/* line 52, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-vform > div input[type="checkbox"],
.mw-ui-vform > div input[type="radio"] {
display: inline;
box-sizing: content-box;
width: auto;
}
+/* line 63, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform .error {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ font-size: 0.9em;
+ margin: 0 0 1em 0;
+ padding: 0.5em;
+ color: #cc0000;
+ border: 1px solid #fac5c5;
+ background-color: #fae3e3;
+ text-shadow: 0 1px #fae3e3;
+ word-wrap: break-word;
+}
+
+/* line 86, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform-div {
+ display: block;
+ margin: 0 0 15px 0;
+ padding: 0;
+ width: 100%;
+}
-/* line 67, sourcefiles/scss/components/default/_forms.scss */
+/* line 96, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-input {
border-style: solid;
border-width: 1px;
outline: 0;
}
-/* line 74, sourcefiles/scss/components/default/_forms.scss */
+/* line 103, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-label {
font-size: 0.9em;
color: #4a4a4a;
font-weight: normal;
}
-/* line 83, sourcefiles/scss/components/default/_forms.scss */
+/* line 112, sourcefiles/scss/components/default/_forms.scss */
.mw-ui-checkbox-label, .mw-ui-radio-label {
margin-bottom: 0.5em;
cursor: pointer;
width: $defaultFormWidth;
+ // Immediate divs in a vform are block and spaced-out.
& > div {
display: block;
margin: 0 0 15px 0;
}
}
+
+ // HTMLForm uses error, SpecialUserlogin (login and create account) uses
+ // errorbox.
+ // TODO move errorbox from mediawiki.special.vforms.css into here.
+ .error {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ font-size: 0.9em;
+ margin: 0 0 1em 0;
+ padding: 0.5em;
+ color: #cc0000;
+ border: 1px solid #fac5c5;
+ background-color: #fae3e3;
+ text-shadow: 0 1px #fae3e3;
+ word-wrap: break-word;
+ }
}
// --------------------------------------------------------------------------
// Elements
// --------------------------------------------------------------------------
+// Apply this to individual elements to style them.
+// You generally don't need to use this class on divs within an Agora
+// form container such as mw-ui-vform
+// XXX DRY: This repeats earlier styling, use an @include agora-div-styling ?
+.mw-ui-vform-div {
+ display: block;
+ margin: 0 0 15px 0;
+ padding: 0;
+ width: 100%;
+}
+
// Apply mw-ui-input to individual input fields to style them.
// You generally don't need to use this class if <input> is within an Agora
// form container such as mw-ui-vform
* var title = mw.Title.newFromImg( $( 'img:first' ) );
*
* @static
- * @param {HTMLImageElement|jQuery} img The image to use as a base.
- * @return {mw.Title|null} The file title - null if unsuccessful.
+ * @param {HTMLElement|jQuery} img The image to use as a base
+ * @return {mw.Title|null} The file title or null if unsuccessful
*/
Title.newFromImg = function ( img ) {
var matches, i, regex, src, decodedSrc,
recount = regexes.length;
- img = img.jquery ? img.get( 0 ) : img;
-
- src = img.src;
+ src = img.jquery ? img[0].src : img.src;
matches = src.match( thumbPhpRegex );
* @param {HTMLElement|jQuery|mw.Message|string} message
* @param {Object} options The options to use for the notification.
* See #defaults for details.
+ * @return {Object} Object with a close function to close the notification
*/
notify: function ( message, options ) {
var notif;
} else {
preReadyNotifQueue.push( notif );
}
+ return { close: $.proxy( notif.close, notif ) };
},
/**
/**
* @class mw.plugin.notify
*/
-( function ( mw ) {
+( function ( mw, $ ) {
'use strict';
/**
* @see mw.notification#notify
* @param message
* @param options
+ * @return {jQuery.Promise}
*/
mw.notify = function ( message, options ) {
+ var d = $.Deferred();
// Don't bother loading the whole notification system if we never use it.
mw.loader.using( 'mediawiki.notification', function () {
- // Don't bother calling mw.loader.using a second time after we've already loaded mw.notification.
- mw.notify = mw.notification.notify;
// Call notify with the notification the user requested of us.
- mw.notify( message, options );
- } );
+ d.resolve( mw.notification.notify( message, options ) );
+ }, d.reject );
+ return d.promise();
};
/**
* @mixins mw.plugin.notify
*/
-}( mediaWiki ) );
+}( mediaWiki, jQuery ) );
*/
function setupSkinUserCss( OutputPage $out ) {
parent::setupSkinUserCss( $out );
- $out->addModuleStyles( 'skins.vector' );
+
+ $styles = array( 'skins.vector' );
+ wfRunHooks( 'SkinVectorStyleModules', array( &$this, &$styles ) );
+ $out->addModuleStyles( $styles );
}
/**
--- /dev/null
+/* Content */
+#content {
+ line-height: 1.5em;
+ .mw-editsection {
+ font-family: @content-font-family;
+ }
+
+ h1,
+ #firstHeading {
+ font-family: @content-heading-font-family;
+ font-size: 1.833em;
+ line-height: 22pt;
+ padding: 0;
+ margin-bottom: 4pt;
+ }
+
+ h2 {
+ font-size: 1.5em;
+ line-height: 22pt;
+ }
+
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ font-family: @content-heading-font-family;
+ padding: 0;
+ margin-bottom: 4pt;
+ margin-top: 14pt;
+ }
+
+ h3 {
+ font-size: 1.17em;
+ line-height: 22pt;
+ }
+
+ h3,
+ h4 {
+ font-weight: bold;
+ }
+
+ h4,
+ h5,
+ h6 {
+ font-size: 100%; /* (reset) */
+ }
+
+ h6 {
+ font-style: italic;
+ }
+
+ p {
+ margin-bottom: 8pt;
+ }
+
+ // FIXME: this is hacky
+ #toc h2 {
+ font-size: 100%;
+ }
+}
+
+/* Personal menu */
+#p-personal a {
+ color: #555;
+}
+
+/* Main menu */
+div#mw-panel div.portal {
+ margin-left: 1.25em;
+ h3 {
+ margin: 0;
+ line-height: 1;
+ }
+}
--- /dev/null
+@html-font-size: 90%;
+
+@body-font-size: inherit;
+@body-font-color: inherit;
+
+// Page content
+@content-font-family: "Helvetica Neue", "Helvetica", "Nimbus Sans L", "Arial", "Liberation Sans", sans-serif;
+@content-font-size: 0.9em;
+@content-line-height: inherit;
+@content-padding: 1em;
+@content-heading-font-size: 1.6em;
+@content-heading-font-family: Georgia, "DejaVu Serif", serif;
+
+// Common menu
+@menu-link-color: #555;
+
+// Main menu
+@menu-main-font-size: 0.82em;
+@menu-main-heading-font-size: 100%;
+@menu-main-heading-padding: 5px 0;
+
+@menu-main-body-font-size: inherit;
+@menu-main-body-link-color: inherit;
+@menu-main-body-link-visited-color: inherit;
+@menu-main-body-margin: 0;
+@menu-main-body-padding: 0 0 10px;
+@menu-main-logo-left: 1.6em;
+
+// Personal menu
+@menu-personal-font-size: 0.75em;
+
+// Collapsible nav
+@collapsible-nav-heading-color: #555;
+@collapsible-nav-heading-collapsed-color: inherit;
+
+@collapsible-nav-heading-padding: 4px 0 3px 1.5em;
+@collapsible-nav-body-margin: 0 0 0 1.25em;
margin: -11px 9px 10px 11px;
h3 {
- color: #4D4D4D;
+ font-size: @menu-main-heading-font-size;
+ color: @collapsible-nav-heading-color;
font-weight: normal;
background-position: left center;
background-repeat: no-repeat;
.background-image-svg('images/arrow-expanded.svg', 'images/arrow-expanded.png');
- padding: 4px 0 3px 1.5em;
+ padding: @collapsible-nav-heading-padding;
margin-bottom: 0;
&:hover {
}
a {
- color: #4D4D4D;
+ color: @collapsible-nav-heading-color;
text-decoration: none;
}
}
.body {
+ margin: @collapsible-nav-body-margin;
background-image: none !important;
padding-top: 0;
display: none;
/* Collapsed */
&.collapsed {
h3 {
- color: #0645AD;
+ color: @collapsible-nav-heading-collapsed-color;
background-position: left center;
background-repeat: no-repeat;
.background-image-svg('images/arrow-collapsed-ltr.svg', 'images/arrow-collapsed-ltr.png');
}
a {
- color: #0645AD;
+ color: @collapsible-nav-heading-collapsed-color;
}
}
}
+++ /dev/null
-/* Vector screen styles for high definition displays */
-
-div#content {
- margin-left: 11em;
- padding: 1.25em 1.5em 1.5em 1.5em;
-}
-#p-logo {
- left: 0.5em;
-}
-div#footer {
- margin-left: 11em;
- padding: 1.25em;
-}
-#mw-panel {
- padding-left: 0.5em;
-}
-#p-search {
- margin-right: 1em;
-}
-#left-navigation {
- margin-left: 11em;
-}
-#p-personal {
- right: 1em;
-}
-#mw-head-base {
- margin-left: 11em;
-}
--- /dev/null
+/* Vector screen styles for high definition displays */
+
+div#content {
+ margin-left: 11em;
+ padding: 1.25em 1.5em 1.5em 1.5em;
+}
+#p-logo {
+ left: @menu-main-logo-left;
+}
+div#footer {
+ margin-left: 11em;
+ padding: 1.25em;
+}
+#mw-panel {
+ padding-left: 0.5em;
+}
+#p-search {
+ margin-right: 1em;
+}
+#left-navigation {
+ margin-left: 11em;
+}
+#p-personal {
+ right: 1em;
+}
+#mw-head-base {
+ margin-left: 11em;
+}
/*
* Any rules which should not be flipped automatically in right-to-left situations should be
- * prepended with @noflip in a comment block. Images that should be embedded as base64 data-URLs
- * should be prepended with @embed in a comment block.
+ * prepended with @noflip in a comment block.
*
- * This style-sheet employs a few CSS trick to accomplish compatibility with a wide range of web
+ * This stylesheet employs a few CSS trick to accomplish compatibility with a wide range of web
* browsers. The most common trick is to use some styles in IE6 only. This is accomplished by using
* a rule that makes things work in IE6, and then following it with a rule that begins with
* "html > body" or use a child selector ">", which is ignored by IE6 because it does not support
* the child selector. You can spot this by looking for the "OVERRIDDEN BY COMPLIANT BROWSERS" and
* "IGNORED BY IE6" comments.
*/
-@import "mediawiki.mixins.less";
+@import "mediawiki.mixins";
/* Framework */
+html {
+ font-size: @html-font-size;
+}
html,
body {
height: 100%;
margin: 0;
padding: 0;
- font-family: sans-serif;
- font-size: 1em;
+ font-family: @content-font-family;
}
body {
background-color: #f6f6f6;
+ font-size: @body-font-size;
}
/* Content */
div#content {
+ line-height: @content-line-height;
margin-left: 10em;
- padding: 1em;
+ padding: @content-padding;
/* Border on top, left, and bottom side */
border: 1px solid #a7d7f9;
border-right-width: 0;
/* Merge the border with tabs' one (in their background image) */
margin-top: -1px;
background-color: white;
- color: black;
+ color: @body-font-color;
direction: ltr;
}
/* Hide, but keep accessible for screen-readers */
margin: 0;
padding-left: 10em; /* Keep from overlapping logo */
}
-/* @noflip */
#p-personal li {
line-height: 1.125em;
+ /* @noflip */
float: left;
-}
-/* This one flips! */
-#p-personal li {
margin-left: 0.75em;
margin-top: 0.5em;
- font-size: 0.75em;
+ font-size: @menu-personal-font-size;
white-space: nowrap;
}
/* Navigation Containers */
display: none;
}
/* Namespaces and Views */
-/* @noflip */
div.vectorTabs {
+ /* @noflip */
float: left;
height: 2.5em;
}
background-repeat: no-repeat;
padding-left: 1px;
}
-/* @noflip */
div.vectorTabs ul {
+ /* @noflip */
float: left;
-}
-div.vectorTabs ul {
height: 100%;
list-style-type: none;
list-style-image: none;
margin: 0;
padding: 0;
}
-/* @noflip */
-div.vectorTabs ul li {
- float: left;
-}
/* OVERRIDDEN BY COMPLIANT BROWSERS */
div.vectorTabs ul li {
+ /* @noflip */
+ float: left;
line-height: 1.125em;
display: inline-block;
height: 100%;
height: 1.9em;
padding-left: 0.5em;
padding-right: 0.5em;
- color: #0645ad;
+ color: @menu-link-color;
cursor: pointer;
font-size: 0.8em;
}
padding-top: 1.25em;
}
/* IGNORED BY IE6 */
-/* @noflip */
div.vectorTabs span > a {
+ /* @noflip */
float: left;
display: block;
}
color: #a55858;
}
/* Variants and Actions */
-/* @noflip */
div.vectorMenu {
+ /* @noflip */
direction: ltr;
+ /* @noflip */
float: left;
/* SVG support using a transparent gradient to guarantee cross-browser
* compatibility (browsers able to understand gradient syntax support also SVG) */
.background-image-svg('images/arrow-down-icon.svg', 'images/arrow-down-icon.png');
+ /* @noflip */
background-position: 100% 60%;
background-repeat: no-repeat;
cursor: pointer;
.background-image-svg('images/arrow-down-focus-icon.svg', 'images/arrow-down-focus-icon.png');
background-position: 100% 60%;
}
-/* @noflip */
body.rtl div.vectorMenu {
+ /* @noflip */
direction: rtl;
}
/* OVERRIDDEN BY COMPLIANT BROWSERS */
-/* @noflip */
div#mw-head div.vectorMenu h3 {
+ /* @noflip */
float: left;
.background-image('images/tab-break.png');
background-repeat: no-repeat;
-}
-/* This will be flipped - unlike the one above it */
-div#mw-head div.vectorMenu h3 {
background-position: bottom left;
margin-left: -1px;
}
border: none;
}
/* OVERRIDDEN BY COMPLIANT BROWSERS */
-/* @noflip */
div.vectorMenu h3 a {
display: inline-block;
width: 24px;
text-decoration: none;
.background-image('images/tab-break.png');
background-repeat: no-repeat;
-}
-/* This will be flipped - unlike the one above it */
-div.vectorMenu h3 a {
background-position: bottom right;
}
/* IGNORED BY IE6 */
text-align: left;
}
/* OVERRIDDEN BY COMPLIANT BROWSERS */
-/* @noflip */
body.rtl div.vectorMenu div.menu {
+ /* @noflip */
margin-left: 24px;
}
/* IGNORED BY IE6 */
-/* @noflip */
body.rtl div.vectorMenu > div.menu {
+ /* @noflip */
margin-left: auto;
}
/* IGNORED BY IE6 */
/* Also fixes old versions of FireFox */
-/* @noflip */
body.rtl div.vectorMenu > div.menu,
x:-moz-any-link {
+ /* @noflip */
margin-left: 23px;
}
/* Enable forcing showing of the menu for accessibility */
display: inline-block;
padding: 0.5em;
white-space: nowrap;
- color: #0645ad;
+ color: @menu-link-color;
cursor: pointer;
font-size: 0.8em;
}
#p-search h3 {
display: none;
}
-/* @noflip */
#p-search {
+ /* @noflip */
float: left;
}
#p-search {
}
/* Panel */
div#mw-panel {
+ font-size: @menu-main-font-size;
position: absolute;
top: 160px;
padding-top: 1em;
div#mw-panel div.portal h3 {
font-weight: normal;
color: #444;
- padding: 0.25em;
- padding-top: 0;
- padding-left: 1.75em;
+ padding: @menu-main-heading-padding;
cursor: default;
border: none;
- font-size: 0.75em;
+ font-size: @menu-main-heading-font-size;
}
div#mw-panel div.portal div.body {
- margin: 0;
padding-top: 0.5em;
- margin-left: 1.25em;
+ margin: @menu-main-body-margin;
+
.background-image('images/portal-break.png');
background-repeat: no-repeat;
background-position: top left;
div#mw-panel div.portal div.body ul {
list-style-type: none;
list-style-image: none;
- padding: 0;
+ padding: @menu-main-body-padding;
margin: 0;
}
div#mw-panel div.portal div.body ul li {
padding: 0;
padding-bottom: 0.5em;
margin: 0;
- font-size: 0.75em;
+ font-size: @menu-main-body-font-size;
word-wrap: break-word;
}
div#mw-panel div.portal div.body ul li a {
- color: #0645ad;
-}
-div#mw-panel div.portal div.body ul li a:visited {
- color: #0b0080;
+ color: @menu-main-body-link-color;
+ &:visited {
+ color: @menu-main-body-link-visited-color;
+ }
}
+
/* Footer */
div#footer {
margin-left: 10em;
div#footer #footer-icons {
float: right;
}
-/* @noflip */
+
body.ltr div#footer #footer-places {
+ /* @noflip */
float: left;
}
div#footer #footer-info li {
#preftoc a:active {
display: inline-block;
position: relative;
- color: #0645ad;
+ color: @menu-link-color;
padding: 0.5em;
text-decoration: none;
background-image: none;
margin-right: 0.25em;
}
-/**
- * The following code is slightly modified from monobook
- */
-div#content {
- line-height: 1.5em;
-}
-#bodyContent {
- font-size: 0.8em;
-}
-
ul {
list-style-type: disc;
.list-style-image('images/bullet-icon.png');
#firstHeading {
padding-top: 0;
margin-top: 0;
- font-size: 1.6em;
+ font-size: @content-heading-font-size;
}
/* Icon for Usernames */
#bodyContent {
position: relative;
width: 100%;
-}
-div#bodyContent {
line-height: 1.5em;
+ font-size: @content-font-size;
}
/* mediawiki.notification */
div#content,
div#footer,
#left-navigation {
- .transition( margin-left 250ms, padding 250ms; );
+ .transition(margin-left 250ms, padding 250ms;);
}
+
#p-logo {
- .transition( left 250ms );
+ .transition(left 250ms);
}
+
#mw-panel {
- .transition( padding-right 250ms );
+ .transition(padding-right 250ms);
}
+
#p-search {
- .transition( margin-right 250ms );
+ .transition(margin-right 250ms);
}
+
#p-personal {
- .transition( right 250ms );
+ .transition(right 250ms);
}
+
#mw-head-base {
- .transition( margin-left 250ms );
+ .transition(margin-left 250ms);
}
}
--- /dev/null
+@import "variables.less";
+@import "beta/variables.less";
+@import "screen.less";
+@import "beta/screen.less";
+@import "externalLinks.less";
+@import "collapsibleNav.less";
+@media screen and (min-width: 982px) {
+ @import "screen-hd.less";
+}
--- /dev/null
+@import "variables.less";
+@import "screen.less";
+@import "externalLinks.less";
+@import "collapsibleNav.less";
+
+@media screen and (min-width: 982px) {
+ @import "screen-hd.less";
+}
--- /dev/null
+@html-font-size: 1em;
+
+@body-font-size: 1em;
+@body-font-color: #252525;
+
+// Page content
+@content-font-family: sans-serif;
+@content-font-size: 0.8em;
+@content-line-height: 1.5em;
+@content-padding: 1.5em 1.5em 1.5em 1.75em;
+@content-heading-font-size: 1.6em;
+@content-heading-font-family: sans-serif;
+
+// Common menu
+@menu-link-color: #0645ad;
+
+// Main menu
+@menu-main-font-size: inherit;
+@menu-main-heading-font-size: 0.75em;
+@menu-main-heading-padding: 0 1.75em 0.25em 0.25em;
+
+@menu-main-body-font-size: 0.75em;
+@menu-main-body-link-color: #0645ad;
+@menu-main-body-link-visited-color: #0b0080;
+@menu-main-body-margin: 0 0 0 1.25em;
+@menu-main-body-padding: 0;
+@menu-main-logo-left: 0.5em;
+
+// Personal menu
+@menu-personal-font-size: 0.75em;
+
+// Collapsible nav
+@collapsible-nav-heading-color: #4D4D4D;
+@collapsible-nav-heading-collapsed-color: #0645AD;
+
+@collapsible-nav-heading-padding: 4px 0 3px 1.5em;
+@collapsible-nav-body-margin: 0 0 0 1.25em;
$po->addLink( Title::newFromText( "linksupdatetest:Foo" ) ); // interwiki link should be ignored
$po->addLink( Title::newFromText( "#Foo" ) ); // hash link should be ignored
- $this->assertLinksUpdate( $t, $po, 'pagelinks', 'pl_namespace, pl_title', 'pl_from = 111', array(
+ $update = $this->assertLinksUpdate( $t, $po, 'pagelinks', 'pl_namespace, pl_title', 'pl_from = 111', array(
array( NS_MAIN, 'Foo' ),
) );
+ $this->assertArrayEquals( array(
+ Title::makeTitle( NS_MAIN, 'Foo' ), // newFromText doesn't yield the same internal state....
+ ), $update->getAddedLinks() );
$po = new ParserOutput();
$po->setTitleText( $t->getPrefixedText() );
$po->addLink( Title::newFromText( "Bar" ) );
+ $po->addLink( Title::newFromText( "Talk:Bar" ) );
- $this->assertLinksUpdate( $t, $po, 'pagelinks', 'pl_namespace, pl_title', 'pl_from = 111', array(
+ $update = $this->assertLinksUpdate( $t, $po, 'pagelinks', 'pl_namespace, pl_title', 'pl_from = 111', array(
array( NS_MAIN, 'Bar' ),
+ array( NS_TALK, 'Bar' ),
) );
+ $this->assertArrayEquals( array(
+ Title::makeTitle( NS_MAIN, 'Bar' ),
+ Title::makeTitle( NS_TALK, 'Bar' ),
+ ), $update->getAddedLinks() );
+ $this->assertArrayEquals( array(
+ Title::makeTitle( NS_MAIN, 'Foo' ),
+ ), $update->getRemovedLinks() );
}
public function testUpdate_externallinks() {
$update->commitTransaction();
$this->assertSelect( $table, $fields, $condition, $expectedRows );
+ return $update;
}
}
$this->assertEquals( 'abcdefghijka2', $msg->params( $params )->plain(), 'Params > 9 are replaced correctly' );
}
+ /**
+ * FIXME: This should not need database, but Language#formatExpiry does (bug 55912)
+ * @group Database
+ */
+ function testMessageParamTypes() {
+ $lang = Language::factory( 'en' );
+
+ $msg = new RawMessage( '$1' );
+ $this->assertEquals(
+ $lang->formatNum( 123456.789 ),
+ $msg->inLanguage( $lang )->numParams( 123456.789 )->plain(),
+ 'numParams is handled correctly'
+ );
+
+ $msg = new RawMessage( '$1' );
+ $this->assertEquals(
+ $lang->formatDuration( 1234 ),
+ $msg->inLanguage( $lang )->durationParams( 1234 )->plain(),
+ 'durationParams is handled correctly'
+ );
+
+ $msg = new RawMessage( '$1' );
+ $this->assertEquals(
+ $lang->formatExpiry( wfTimestampNow() ),
+ $msg->inLanguage( $lang )->expiryParams( wfTimestampNow() )->plain(),
+ 'expiryParams is handled correctly'
+ );
+
+ $msg = new RawMessage( '$1' );
+ $this->assertEquals(
+ $lang->formatTimePeriod( 1234 ),
+ $msg->inLanguage( $lang )->timeperiodParams( 1234 )->plain(),
+ 'timeperiodParams is handled correctly'
+ );
+
+ $msg = new RawMessage( '$1' );
+ $this->assertEquals(
+ $lang->formatSize( 123456 ),
+ $msg->inLanguage( $lang )->sizeParams( 123456 )->plain(),
+ 'sizeParams is handled correctly'
+ );
+
+ $msg = new RawMessage( '$1' );
+ $this->assertEquals(
+ $lang->formatBitrate( 123456 ),
+ $msg->inLanguage( $lang )->bitrateParams( 123456 )->plain(),
+ 'bitrateParams is handled correctly'
+ );
+ }
+
function testInContentLanguageDisabled() {
$this->setMwGlobals( 'wgLang', Language::factory( 'fr' ) );
* test usernames
*
* @dataProvider provideUserGenders
+ * @covers GenderCache::getGenderOf
*/
function testUserName( $username, $expectedGender ) {
$genderCache = GenderCache::singleton();
* genderCache should work with user objects, too
*
* @dataProvider provideUserGenders
+ * @covers GenderCache::getGenderOf
*/
function testUserObjects( $username, $expectedGender ) {
$genderCache = GenderCache::singleton();
* against the never existing username
*
* @dataProvider provideStripSubpages
+ * @covers GenderCache::getGenderOf
*/
function testStripSubpages( $pageWithSubpage, $expectedGender ) {
$genderCache = GenderCache::singleton();
/**
* @group Database
* @group Cache
+ * @covers MessageCache
*/
class MessageCacheTest extends MediaWikiLangTestCase {
/**
* @dataProvider dataGetDefaultModelFor
+ * @covers ContentHandler::getDefaultModelFor
*/
public function testGetDefaultModelFor( $title, $expectedModelId ) {
$title = Title::newFromText( $title );
/**
* @dataProvider dataGetDefaultModelFor
+ * @covers ContentHandler::getForTitle
*/
public function testGetForTitle( $title, $expectedContentModel ) {
$title = Title::newFromText( $title );
/**
* @dataProvider dataGetLocalizedName
+ * @covers ContentHandler::getLocalizedName
*/
public function testGetLocalizedName( $id, $expected ) {
$name = ContentHandler::getLocalizedName( $id );
/**
* @dataProvider dataGetPageLanguage
+ * @covers ContentHandler::getPageLanguage
*/
public function testGetPageLanguage( $title, $expected ) {
if ( is_string( $title ) ) {
/**
* @dataProvider dataGetContentText_Null
+ * @covers ContentHandler::getContentText
*/
public function testGetContentText_Null( $contentHandlerTextFallback ) {
$this->setMwGlobals( 'wgContentHandlerTextFallback', $contentHandlerTextFallback );
/**
* @dataProvider dataGetContentText_TextContent
+ * @covers ContentHandler::getContentText
*/
public function testGetContentText_TextContent( $contentHandlerTextFallback ) {
$this->setMwGlobals( 'wgContentHandlerTextFallback', $contentHandlerTextFallback );
/**
* ContentHandler::getContentText should have thrown an exception for non-text Content object
* @expectedException MWException
+ * @covers ContentHandler::getContentText
*/
public function testGetContentText_NonTextContent_fail() {
$this->setMwGlobals( 'wgContentHandlerTextFallback', 'fail' );
ContentHandler::getContentText( $content );
}
+ /**
+ * @covers ContentHandler::getContentText
+ */
public function testGetContentText_NonTextContent_serialize() {
$this->setMwGlobals( 'wgContentHandlerTextFallback', 'serialize' );
$this->assertEquals( $content->serialize(), $text );
}
+ /**
+ * @covers ContentHandler::getContentText
+ */
public function testGetContentText_NonTextContent_ignore() {
$this->setMwGlobals( 'wgContentHandlerTextFallback', 'ignore' );
/**
* @dataProvider dataMakeContent
+ * @covers ContentHandler::makeContent
*/
public function testMakeContent( $data, $title, $modelId, $format, $expectedModelId, $expectedNativeData, $shouldFail ) {
$title = Title::newFromText( $title );
}
*/
+ /**
+ * @covers ContentHandler::runLegacyHooks
+ */
public function testRunLegacyHooks() {
Hooks::register( 'testRunLegacyHooks', __CLASS__ . '::dummyHookHandler' );
);
}
+ /**
+ * @covers CssContent::getModel
+ */
public function testGetModel() {
$content = $this->newContent( 'hello world.' );
$this->assertEquals( CONTENT_MODEL_CSS, $content->getModel() );
}
+ /**
+ * @covers CssContent::getContentHandler
+ */
public function testGetContentHandler() {
$content = $this->newContent( 'hello world.' );
/**
* @dataProvider dataEquals
+ * @covers CssContent::equals
*/
public function testEquals( Content $a, Content $b = null, $equal = false ) {
$this->assertEquals( $equal, $a->equals( $b ) );
);
}
+ /**
+ * @covers JavaScriptContent::addSectionHeader
+ */
public function testAddSectionHeader() {
$content = $this->newContent( 'hello world' );
$c = $content->addSectionHeader( 'test' );
);
}
+ /**
+ * @covers JavaScriptContent::matchMagicWord
+ */
public function testMatchMagicWord() {
$mw = MagicWord::get( "staticredirect" );
$this->assertFalse( $content->matchMagicWord( $mw ), "should not have matched magic word, since it's not wikitext" );
}
+ /**
+ * @covers JavaScriptContent::updateRedirect
+ */
public function testUpdateRedirect() {
$target = Title::newFromText( "testUpdateRedirect_target" );
$this->assertTrue( $content->equals( $newContent ), "content should be unchanged since it's not wikitext" );
}
+ /**
+ * @covers JavaScriptContent::getModel
+ */
public function testGetModel() {
$content = $this->newContent( "hello world." );
$this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $content->getModel() );
}
+ /**
+ * @covers JavaScriptContent::getContentHandler
+ */
public function testGetContentHandler() {
$content = $this->newContent( "hello world." );
/**
* @dataProvider dataGetParserOutput
+ * @covers TextContent::getParserOutput
*/
public function testGetParserOutput( $title, $model, $text, $expectedHtml, $expectedFields = null ) {
$title = Title::newFromText( $title );
/**
* @dataProvider dataPreSaveTransform
+ * @covers TextContent::preSaveTransform
*/
public function testPreSaveTransform( $text, $expected ) {
global $wgContLang;
/**
* @dataProvider dataPreloadTransform
+ * @covers TextContent::preloadTransform
*/
public function testPreloadTransform( $text, $expected ) {
global $wgContLang;
/**
* @dataProvider dataGetRedirectTarget
+ * @covers TextContent::getRedirectTarget
*/
public function testGetRedirectTarget( $text, $expected ) {
$content = $this->newContent( $text );
/**
* @dataProvider dataGetRedirectTarget
+ * @covers TextContent::isRedirect
*/
public function testIsRedirect( $text, $expected ) {
$content = $this->newContent( $text );
/**
* @dataProvider dataIsCountable
* @group Database
+ * @covers TextContent::isCountable
*/
public function testIsCountable( $text, $hasLinks, $mode, $expected ) {
$this->setMwGlobals( 'wgArticleCountMethod', $mode );
/**
* @dataProvider dataGetTextForSummary
+ * @covers TextContent::getTextForSummary
*/
public function testGetTextForSummary( $text, $maxlength, $expected ) {
$content = $this->newContent( $text );
$this->assertEquals( $expected, $content->getTextForSummary( $maxlength ) );
}
+ /**
+ * @covers TextContent::getTextForSearchIndex
+ */
public function testGetTextForSearchIndex() {
$content = $this->newContent( 'hello world.' );
$this->assertEquals( 'hello world.', $content->getTextForSearchIndex() );
}
+ /**
+ * @covers TextContent::copy
+ */
public function testCopy() {
$content = $this->newContent( 'hello world.' );
$copy = $content->copy();
$this->assertEquals( 'hello world.', $copy->getNativeData() );
}
+ /**
+ * @covers TextContent::getSize
+ */
public function testGetSize() {
$content = $this->newContent( 'hello world.' );
$this->assertEquals( 12, $content->getSize() );
}
+ /**
+ * @covers TextContent::getNativeData
+ */
public function testGetNativeData() {
$content = $this->newContent( 'hello world.' );
$this->assertEquals( 'hello world.', $content->getNativeData() );
}
+ /**
+ * @covers TextContent::getWikitextForTransclusion
+ */
public function testGetWikitextForTransclusion() {
$content = $this->newContent( 'hello world.' );
$this->assertEquals( 'hello world.', $content->getWikitextForTransclusion() );
}
+ /**
+ * @covers TextContent::getModel
+ */
public function testGetModel() {
$content = $this->newContent( "hello world." );
$this->assertEquals( CONTENT_MODEL_TEXT, $content->getModel() );
}
+ /**
+ * @covers TextContent::getContentHandler
+ */
public function testGetContentHandler() {
$content = $this->newContent( "hello world." );
/**
* @dataProvider dataIsEmpty
+ * @covers TextContent::isEmpty
*/
public function testIsEmpty( $text, $empty ) {
$content = $this->newContent( $text );
/**
* @dataProvider dataEquals
+ * @covers TextContent::equals
*/
public function testEquals( Content $a, Content $b = null, $equal = false ) {
$this->assertEquals( $equal, $a->equals( $b ) );
/**
* @dataProvider dataGetDeletionUpdates
+ * @covers TextContent::getDeletionUpdates
*/
public function testDeletionUpdates( $title, $model, $text, $expectedStuff ) {
$ns = $this->getDefaultWikitextNS();
/**
* @dataProvider provideConvert
+ * @covers TextContent::convert
*/
public function testConvert( $text, $model, $lossy, $expectedNative ) {
$content = $this->newContent( $text );
$this->handler = ContentHandler::getForModelID( CONTENT_MODEL_WIKITEXT );
}
+ /**
+ * @covers WikitextContentHandler::serializeContent
+ */
public function testSerializeContent() {
$content = new WikitextContent( 'hello world' );
}
}
+ /**
+ * @covers WikitextContentHandler::unserializeContent
+ */
public function testUnserializeContent() {
$content = $this->handler->unserializeContent( 'hello world' );
$this->assertEquals( 'hello world', $content->getNativeData() );
}
}
+ /**
+ * @covers WikitextContentHandler::makeEmptyContent
+ */
public function testMakeEmptyContent() {
$content = $this->handler->makeEmptyContent();
* @dataProvider provideMakeRedirectContent
* @param Title|string $title Title object or string for Title::newFromText()
* @param string $expected Serialized form of the content object built
+ * @covers WikitextContentHandler::makeRedirectContent
*/
public function testMakeRedirectContent( $title, $expected ) {
global $wgContLang;
/**
* @dataProvider dataIsSupportedFormat
+ * @covers WikitextContentHandler::isSupportedFormat
*/
public function testIsSupportedFormat( $format, $supported ) {
$this->assertEquals( $supported, $this->handler->isSupportedFormat( $format ) );
/**
* @dataProvider dataMerge3
+ * @covers WikitextContentHandler::merge3
*/
public function testMerge3( $old, $mine, $yours, $expected ) {
$this->checkHasDiff3();
/**
* @dataProvider dataGetAutosummary
+ * @covers WikitextContentHandler::getAutosummary
*/
public function testGetAutosummary( $old, $new, $flags, $expected ) {
$oldContent = is_null( $old ) ? null : new WikitextContent( $old );
/**
* @dataProvider dataGetSecondaryDataUpdates
* @group Database
+ * @covers WikitextContent::getSecondaryDataUpdates
*/
public function testGetSecondaryDataUpdates( $title, $model, $text, $expectedStuff ) {
$ns = $this->getDefaultWikitextNS();
/**
* @dataProvider dataGetSection
+ * @covers WikitextContent::getSection
*/
public function testGetSection( $text, $sectionId, $expectedText ) {
$content = $this->newContent( $text );
/**
* @dataProvider dataReplaceSection
+ * @covers WikitextContent::replaceSection
*/
public function testReplaceSection( $text, $section, $with, $sectionTitle, $expected ) {
$content = $this->newContent( $text );
$this->assertEquals( $expected, is_null( $c ) ? null : $c->getNativeData() );
}
+ /**
+ * @covers WikitextContent::addSectionHeader
+ */
public function testAddSectionHeader() {
$content = $this->newContent( 'hello world' );
$content = $content->addSectionHeader( 'test' );
);
}
+ /**
+ * @covers WikitextContent::matchMagicWord
+ */
public function testMatchMagicWord() {
$mw = MagicWord::get( "staticredirect" );
$this->assertFalse( $content->matchMagicWord( $mw ), "should not have matched magic word" );
}
+ /**
+ * @covers WikitextContent::updateRedirect
+ */
public function testUpdateRedirect() {
$target = Title::newFromText( "testUpdateRedirect_target" );
$this->assertEquals( $target->getFullText(), $newContent->getRedirectTarget()->getFullText() );
}
+ /**
+ * @covers WikitextContent::getModel
+ */
public function testGetModel() {
$content = $this->newContent( "hello world." );
$this->assertEquals( CONTENT_MODEL_WIKITEXT, $content->getModel() );
}
+ /**
+ * @covers WikitextContent::getContentHandler
+ */
public function testGetContentHandler() {
$content = $this->newContent( "hello world." );
/**
* @dataProvider provideDiapers
+ * @covers DatabaseMysqlBase::addIdentifierQuotes
*/
function testAddIdentifierQuotes( $expected, $in ) {
$db = new FakeDatabaseMysqlBase();
*/
class DatabaseSQLTest extends MediaWikiTestCase {
+ /**
+ * @var DatabaseTestHelper
+ */
private $database;
protected function setUp() {
/**
* @dataProvider provideSelect
+ * @covers DatabaseBase::select
*/
function testSelect( $sql, $sqlText ) {
$this->database->select(
/**
* @dataProvider provideUpdate
+ * @covers DatabaseBase::update
*/
function testUpdate( $sql, $sqlText ) {
$this->database->update(
/**
* @dataProvider provideDelete
+ * @covers DatabaseBase::delete
*/
function testDelete( $sql, $sqlText ) {
$this->database->delete(
/**
* @dataProvider provideUpsert
+ * @covers DatabaseBase::upsert
*/
function testUpsert( $sql, $sqlText ) {
$this->database->upsert(
/**
* @dataProvider provideDeleteJoin
+ * @covers DatabaseBase::deleteJoin
*/
function testDeleteJoin( $sql, $sqlText ) {
$this->database->deleteJoin(
/**
* @dataProvider provideInsert
+ * @covers DatabaseBase::insert
*/
function testInsert( $sql, $sqlText ) {
$this->database->insert(
/**
* @dataProvider provideInsertSelect
+ * @covers DatabaseBase::insertSelect
*/
function testInsertSelect( $sql, $sqlText ) {
$this->database->insertSelect(
/**
* @dataProvider provideReplace
+ * @covers DatabaseBase::replace
*/
function testReplace( $sql, $sqlText ) {
$this->database->replace(
/**
* @dataProvider provideNativeReplace
+ * @covers DatabaseBase::nativeReplace
*/
function testNativeReplace( $sql, $sqlText ) {
$this->database->nativeReplace(
/**
* @dataProvider provideConditional
+ * @covers DatabaseBase::conditional
*/
function testConditional( $sql, $sqlText ) {
$this->assertEquals( trim( $this->database->conditional(
/**
* @dataProvider provideBuildConcat
+ * @covers DatabaseBase::buildConcat
*/
function testBuildConcat( $stringList, $sqlText ) {
$this->assertEquals( trim( $this->database->buildConcat(
/**
* @dataProvider provideBuildLike
+ * @covers DatabaseBase::buildLike
*/
function testBuildLike( $array, $sqlText ) {
$this->assertEquals( trim( $this->database->buildLike(
/**
* @dataProvider provideUnionQueries
+ * @covers DatabaseBase::unionQueries
*/
function testUnionQueries( $sql, $sqlText ) {
$this->assertEquals( trim( $this->database->unionQueries(
);
}
+ /**
+ * @covers DatabaseBase::commit
+ */
function testTransactionCommit() {
$this->database->begin( __METHOD__ );
$this->database->commit( __METHOD__ );
$this->assertLastSql( 'BEGIN; COMMIT' );
}
+ /**
+ * @covers DatabaseBase::rollback
+ */
function testTransactionRollback() {
$this->database->begin( __METHOD__ );
$this->database->rollback( __METHOD__ );
$this->assertLastSql( 'BEGIN; ROLLBACK' );
}
+ /**
+ * @covers DatabaseBase::dropTable
+ */
function testDropTable() {
$this->database->setExistingTables( array( 'table' ) );
$this->database->dropTable( 'table', __METHOD__ );
$this->assertLastSql( 'DROP TABLE table' );
}
+ /**
+ * @covers DatabaseBase::dropTable
+ */
function testDropNonExistingTable() {
$this->assertFalse(
$this->database->dropTable( 'non_existing', __METHOD__ )
* @group medium
*/
class DatabaseSqliteTest extends MediaWikiTestCase {
+
+ /**
+ * @var MockDatabaseSqlite
+ */
var $db;
protected function setUp() {
/**
* @dataProvider provideAddQuotes()
+ * @covers DatabaseSqlite::addQuotes
*/
public function testAddQuotes( $value, $expected ) {
// check quoting
}
}
+ /**
+ * @covers DatabaseSqlite::replaceVars
+ */
public function testReplaceVars() {
$this->assertEquals( 'foo', $this->replaceVars( 'foo' ), "Don't break anything accidentally" );
);
}
+ /**
+ * @covers DatabaseSqlite::tableName
+ */
public function testTableName() {
// @todo Moar!
$db = new DatabaseSqliteStandalone( ':memory:' );
$this->assertEquals( 'foobar', $db->tableName( 'bar' ) );
}
+ /**
+ * @covers DatabaseSqlite::duplicateTableStructure
+ */
public function testDuplicateTableStructure() {
$db = new DatabaseSqliteStandalone( ':memory:' );
$db->query( 'CREATE TABLE foo(foo, barfoo)' );
);
}
+ /**
+ * @covers DatabaseSqlite::duplicateTableStructure
+ */
public function testDuplicateTableStructureVirtual() {
$db = new DatabaseSqliteStandalone( ':memory:' );
if ( $db->getFulltextSearchModule() != 'FTS3' ) {
);
}
+ /**
+ * @covers DatabaseSqlite::deleteJoin
+ */
public function testDeleteJoin() {
$db = new DatabaseSqliteStandalone( ':memory:' );
$db->query( 'CREATE TABLE a (a_1)', __METHOD__ );
}
}
+ /**
+ * @covers DatabaseSqlite::insertId
+ */
public function testInsertIdType() {
$db = new DatabaseSqliteStandalone( ':memory:' );
- $this->assertInstanceOf( 'ResultWrapper',
- $db->query( 'CREATE TABLE a ( a_1 )', __METHOD__ ), "Database creationg" );
- $this->assertTrue( $db->insert( 'a', array( 'a_1' => 10 ), __METHOD__ ),
- "Insertion worked" );
+
+ $databaseCreation = $db->query( 'CREATE TABLE a ( a_1 )', __METHOD__ );
+ $this->assertInstanceOf( 'ResultWrapper', $databaseCreation, "Database creation" );
+
+ $insertion = $db->insert( 'a', array( 'a_1' => 10 ), __METHOD__ );
+ $this->assertTrue( $insertion, "Insertion worked" );
+
$this->assertInternalType( 'integer', $db->insertId(), "Actual typecheck" );
$this->assertTrue( $db->close(), "closing database" );
}
* @group DatabaseBase
*/
class DatabaseTest extends MediaWikiTestCase {
- var $db, $functionTest = false;
+ /**
+ * @var DatabaseBase
+ */
+ var $db;
+ var $functionTest = false;
protected function setUp() {
parent::setUp();
$this->functionTest = false;
}
}
-
+ /**
+ * @covers DatabaseBase::dropTable
+ */
function testAddQuotesNull() {
$check = "NULL";
if ( $this->db->getType() === 'sqlite' || $this->db->getType() === 'oracle' ) {
return;
}
+ $user = RequestContext::getMain()->getUser();
+ if ( $user->pingLimiter( 'renderfile' ) ) {
+ wfThumbError( 500, wfMessage( 'actionthrottledtext' ) );
+ return;
+ }
+
// Thumbnail isn't already there, so create the new thumbnail...
try {
$thumb = $img->transform( $params, File::RENDER_NOW );