* $wgXhtmlDefaultNamespace is no longer used by core. Setting it will no longer change the
xmlns used by MediaWiki. Reliance on this variable by extensions is deprecated.
* $wgHandheldStyle was removed.
+* $wgHandheldForIPhone was removed.
* $wgJsMimeType is no longer used by core. Most usage has been removed since
HTML output is now exclusively HTML5.
* $wgDBOracleDRCP added. True enables persistent connection with DRCP on Oracle.
handlers can take further action based on the status of the patrol footer
* LinkCache singleton can now be altered or cleared, letting one to specify
another instance that does not rely on a database backend.
+* MediaWiki's PHPUnit tests can now use PHPUnit installed using composer --dev.
=== Bug fixes in 1.22 ===
* Disable Special:PasswordReset when $wgEnableEmail is false. Previously one
is disabled, and AJAX patrolling and watching are now disabled when use of
the write API is not allowed.
* (bug 48294) API: Fix chunk upload async mode.
+* (bug 46749) Broken files tracking category removed from pages if an image
+ with that name is uploaded.
=== API changes in 1.22 ===
* (bug 46626) xmldoublequote parameter was removed. Because of a bug, the
* prop=info now adds the content model and page language of the title.
* New upload log entries will now contain information on the relavent
image (sha1 and timestamp).
+* (bug 49239) action=parse now can parse in preview mode.
=== Languages updated in 1.22===
$modeName: the requested content model name
&$handler: set this to a ContentHandler object, if desired.
+'ContentGetParserOutput': Customize parser output for a given content object,
+called by AbstractContent::getParserOutput. May be used to override the normal
+model-specific rendering of page content.
+$content: The Content to render
+$title: Title of the page, as context
+$revId: The revision ID, as context
+$options: ParserOptions for rendering. To avoid confusing the parser cache,
+the output can only depend on parameters provided to this hook function, not on global state.
+$generateHtml: boolean, indicating whether full HTML should be generated. If false,
+generation of HTML may be skipped, but other information should still be present in the
+ParserOutput object.
+&$output: ParserOutput, to manipulate or replace
+
'ConvertContent': Called by AbstractContent::convert when a conversion to another
content model is requested.
$content: The Content object to be converted.
$article: The article object corresponding to the page
'ShowRawCssJs': Customise the output of raw CSS and JavaScript in page views.
-DEPRECATED, use the ContentHandler facility to handle CSS and JavaScript!
+DEPRECATED, use the ContentGetParserOutput hook instead!
$text: Text being shown
$title: Title of the custom script/stylesheet page
$output: Current OutputPage object
$this->rcCacheIndex = 0;
$this->lastdate = '';
$this->rclistOpen = false;
- $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
+ $this->getOutput()->addModules( 'mediawiki.special.changeslist' );
return '';
}
* @param $watched
*/
public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
- # If it's a new article, there is no diff link, but if it hasn't been
- # patrolled yet, we need to give users a way to do so
+ global $wgUseRCPatrol;
+
$params = array();
+ // In case we got a page creation which is yet unpatrolled and
+ // recent changes patrolling is enabled, the user probably rather
+ // wants to patrol the whole page (first revision) instead of seeing
+ // a patrollink for the current revision.
+ if ( $wgUseRCPatrol && $unpatrolled && $rc->getAttribute( 'rc_type' ) == RC_NEW ) {
+ $params['patrolpage'] = 1;
+ }
+
$articlelink = Linker::linkKnown(
$rc->getTitle(),
null,
* @param $rc RecentChange
*/
public function insertRollback( &$s, &$rc ) {
- if ( $rc->mAttribs['rc_type'] != RC_NEW && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
+ 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 */
$this->rcCacheIndex = 0;
$this->lastdate = '';
$this->rclistOpen = false;
- $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
+ $this->getOutput()->addModules( 'mediawiki.special.changeslist' );
return '';
}
/**
implode( $this->message['semicolon-separator'], $users )
)->escaped() . '</span>';
- $tl = '<span class="mw-collapsible-toggle mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>';
+ $tl = '<span class="mw-collapsible-toggle mw-collapsible-arrow mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>';
$r .= "<td>$tl</td>";
# Main line
/** Array for more like $wgSkipSkin. */
$wgSkipSkins = array();
-/**
- * If set, 'screen' and 'handheld' media specifiers for stylesheets are
- * transformed such that they apply to the iPhone/iPod Touch Mobile Safari,
- * which doesn't recognize 'handheld' but does support media queries on its
- * screen size.
- *
- * Consider only using this if you have a *really good* handheld stylesheet,
- * as iPhone users won't have any way to disable it and use the "grown-up"
- * styles instead.
- */
-$wgHandheldForIPhone = false;
-
/**
* Allow user Javascript page?
* This enables a lot of neat customizations, but may
* The value is the replacement for the key (it can contain $1, etc.)
* %h will be replaced by the short SHA-1 (7 first chars) and %H by the
* full SHA-1 of the HEAD revision.
+ * %r will be replaced with a URL-encoded version of $1.
*
* @since 1.20
*/
$wgGitRepositoryViewers = array(
- 'https://gerrit.wikimedia.org/r/p/(.*)' => 'https://gerrit.wikimedia.org/r/gitweb?p=$1;h=%H',
- 'ssh://(?:[a-z0-9_]+@)?gerrit.wikimedia.org:29418/(.*)' => 'https://gerrit.wikimedia.org/r/gitweb?p=$1;h=%H',
+ 'https://gerrit.wikimedia.org/r/p/(.*)' => 'https://git.wikimedia.org/commit/%r/%H',
+ 'ssh://(?:[a-z0-9_]+@)?gerrit.wikimedia.org:29418/(.*)' => 'https://git.wikimedia.org/commit/%r/%H',
);
/** @} */ # End of maintenance }
*/
$wgHTTPProxy = false;
+/**
+ * Timeout for connections done internally (in seconds)
+ * Only works for curl
+ */
+$wgHTTPConnectTimeout = 5e0;
+
/** @} */ # End HTTP client }
/************************************************************************//**
}
foreach ( self::getViewers() as $repo => $viewer ) {
$pattern = '#^' . $repo . '$#';
- if ( preg_match( $pattern, $url ) ) {
+ if ( preg_match( $pattern, $url, $matches ) ) {
$viewerUrl = preg_replace( $pattern, $viewer, $url );
$headSHA1 = $this->getHeadSHA1();
$replacements = array(
'%h' => substr( $headSHA1, 0, 7 ),
- '%H' => $headSHA1
+ '%H' => $headSHA1,
+ '%r' => urlencode( $matches[1] ),
);
return strtr( $viewerUrl, $replacements );
}
// In text/html, initial <html> and <head> tags can be omitted under
// pretty much any sane circumstances, if they have no attributes. See:
- // <http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags>
+ // <http://www.whatwg.org/html/syntax.html#optional-tags>
if ( !$wgWellFormedXml && !$attribs
&& in_array( $element, array( 'html', 'head' ) ) ) {
return '';
$element = strtolower( $element );
// Reference:
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags
+ // http://www.whatwg.org/html/syntax.html#optional-tags
if ( !$wgWellFormedXml && in_array( $element, array(
'html',
'head',
* @return Boolean
*/
public static function isXmlMimeType( $mimetype ) {
- # http://www.whatwg.org/specs/web-apps/current-work/multipage/infrastructure.html#xml-mime-type
+ # http://www.whatwg.org/html/infrastructure.html#xml-mime-type
# * text/xml
# * application/xml
# * Any mimetype with a subtype ending in +xml (this implicitly includes application/xhtml+xml)
$candidates = array();
foreach ( $urls as $density => $url ) {
// Image candidate syntax per current whatwg live spec, 2012-09-23:
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1.html#attr-img-srcset
+ // http://www.whatwg.org/html/embedded-content-1.html#attr-img-srcset
$candidates[] = "{$url} {$density}x";
}
return implode( ", ", $candidates );
* @param array $options options to pass to MWHttpRequest object.
* Possible keys for the array:
* - timeout Timeout length in seconds
+ * - connectTimeout Timeout for connection, in seconds (curl only)
* - postData An array of key-value pairs or a url-encoded form data
* - proxy The proxy to use.
* Otherwise it will use $wgHTTPProxy (if set)
if ( !isset( $options['timeout'] ) ) {
$options['timeout'] = 'default';
}
+ if( !isset( $options['connectTimeout'] ) ) {
+ $options['connectTimeout'] = 'default';
+ }
$req = MWHttpRequest::factory( $url, $options );
$status = $req->execute();
* @param array $options (optional) extra params to pass (see Http::request())
*/
protected function __construct( $url, $options = array() ) {
- global $wgHTTPTimeout;
+ global $wgHTTPTimeout, $wgHTTPConnectTimeout;
$this->url = wfExpandUrl( $url, PROTO_HTTP );
$this->parsedUrl = wfParseUrl( $this->url );
} else {
$this->timeout = $wgHTTPTimeout;
}
+ if ( isset( $options['connectTimeout'] ) && $options['connectTimeout'] != 'default' ) {
+ $this->connectTimeout = $options['connectTimeout'];
+ } else {
+ $this->connectTimeout = $wgHTTPConnectTimeout;
+ }
if ( isset( $options['userAgent'] ) ) {
$this->setUserAgent( $options['userAgent'] );
}
$this->curlOptions[CURLOPT_PROXY] = $this->proxy;
$this->curlOptions[CURLOPT_TIMEOUT] = $this->timeout;
+ $this->curlOptions[CURLOPT_CONNECTTIMEOUT_MS] = $this->connectTimeout * 1000;
$this->curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
$this->curlOptions[CURLOPT_WRITEFUNCTION] = $this->callback;
$this->curlOptions[CURLOPT_HEADERFUNCTION] = array( $this, "readHeader" );
} else {
$params = array( 'page' => $page );
}
+
+ $renderLang = $request->getVal( 'lang' );
+ if ( !is_null( $renderLang ) ) {
+ $params['lang'] = $renderLang;
+ }
+
$width_orig = $this->displayImg->getWidth( $page );
$width = $width_orig;
$height_orig = $this->displayImg->getHeight( $page );
wfProfileOut( __METHOD__ );
}
+ /**
+ * Queue recursive jobs for this page
+ *
+ * Which means do LinksUpdate on all templates
+ * that include the current page, using the job queue.
+ */
function queueRecursiveJobs() {
- wfProfileIn( __METHOD__ );
+ self::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' );
+ }
- if ( $this->mTitle->getBacklinkCache()->hasLinks( 'templatelinks' ) ) {
+ /**
+ * Queue a RefreshLinks job for any table.
+ *
+ * @param Title $title Title to do job for
+ * @param String $table Table to use (e.g. 'templatelinks')
+ */
+ public static function queueRecursiveJobsForTable( Title $title, $table ) {
+ wfProfileIn( __METHOD__ );
+ if ( $title->getBacklinkCache()->hasLinks( $table ) ) {
$job = new RefreshLinksJob2(
- $this->mTitle,
+ $title,
array(
- 'table' => 'templatelinks',
+ 'table' => $table,
) + Job::newRootJobParams( // "overall" refresh links job info
- "refreshlinks:templatelinks:{$this->mTitle->getPrefixedText()}"
+ "refreshlinks:{$table}:{$title->getPrefixedText()}"
)
);
JobQueueGroup::singleton()->push( $job );
JobQueueGroup::singleton()->deduplicateRootJob( $job );
}
-
wfProfileOut( __METHOD__ );
}
// The spec recommends defining XHTML5's charset using the XML declaration
// instead of meta.
// Our XML declaration is output by Html::htmlHeader.
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#attr-meta-http-equiv-content-type
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#charset
+ // http://www.whatwg.org/html/semantics.html#attr-meta-http-equiv-content-type
+ // http://www.whatwg.org/html/semantics.html#charset
$ret .= Html::element( 'meta', array( 'charset' => 'UTF-8' ) );
}
* this stylesheet
*/
public static function transformCssMedia( $media ) {
- global $wgRequest, $wgHandheldForIPhone;
+ global $wgRequest;
// http://www.w3.org/TR/css3-mediaqueries/#syntax
$screenMediaQueryRegex = '/^(?:only\s+)?screen\b/i';
}
}
- // Expand longer media queries as iPhone doesn't grok 'handheld'
- if ( $wgHandheldForIPhone ) {
- $mediaAliases = array(
- 'screen' => 'screen and (min-device-width: 481px)',
- 'handheld' => 'handheld, only screen and (max-device-width: 480px)',
- );
-
- if ( isset( $mediaAliases[$media] ) ) {
- $media = $mediaAliases[$media];
- }
- }
-
return $media;
}
}
/**
+ * @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;
/**
* 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,
# WAI-ARIA
# http://www.w3.org/TR/wai-aria/
- # http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#wai-aria
+ # http://www.whatwg.org/html/elements.html#wai-aria
# For now we only support role="presentation" until we work out what roles should be
# usable by content and we ensure that our code explicitly rejects patterns that
# violate HTML5's ARIA restrictions.
* in the id and
* name attributes
* @see http://www.w3.org/TR/html401/struct/links.html#h-12.2.3 Anchors with the id attribute
- * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-id-attribute
+ * @see http://www.whatwg.org/html/elements.html#the-id-attribute
* HTML5 definition of id attribute
*
* @param string $id id to escape
}
if ( $wgAllowMicrodataAttributes ) {
- # add HTML5 microdata tags as specified by http://www.whatwg.org/specs/web-apps/current-work/multipage/microdata.html#the-microdata-model
+ # add HTML5 microdata tags as specified by http://www.whatwg.org/html/microdata.html#the-microdata-model
$common = array_merge( $common, array(
'itemid', 'itemprop', 'itemref', 'itemscope', 'itemtype'
) );
'hr' => array_merge( $common, array( 'noshade', 'size', 'width' ) ),
# HTML Ruby annotation text module, simple ruby only.
- # http://www.whatwg.org/specs/web-apps/current-work/multipage/text-level-semantics.html#the-ruby-element
+ # http://www.whatwg.org/html/text-level-semantics.html#the-ruby-element
'ruby' => $common,
# rbc
# rtc
'bdi' => $common,
# HTML5 elements, defined by:
- # http://www.whatwg.org/specs/web-apps/current-work/multipage/
+ # http://www.whatwg.org/html/
'data' => array_merge( $common, array( 'value' ) ),
'time' => array_merge( $common, array( 'datetime' ) ),
'mark' => $common,
* Does a string look like an e-mail address?
*
* This validates an email address using an HTML5 specification found at:
- * http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
+ * http://www.whatwg.org/html/states-of-the-type-attribute.html#valid-e-mail-address
* Which as of 2011-01-24 says:
*
* A valid e-mail address is a string that matches the ABNF production
* @return Bool true if the update succeeded
*/
public function invalidateCache() {
- global $wgMemc;
-
if ( wfReadOnly() ) {
return false;
}
}
/**
- * @return String
+ * @return string
*/
function __toString() {
return $this->getName();
}
wfProfileIn( __METHOD__ );
- # Set it now to avoid infinite recursion in accessors
+ // Set it now to avoid infinite recursion in accessors
$this->mLoadedItems = true;
switch ( $this->mFrom ) {
case 'name':
$this->mId = self::idFromName( $this->mName );
if ( !$this->mId ) {
- # Nonexistent user placeholder object
+ // Nonexistent user placeholder object
$this->loadDefaults( $this->mName );
} else {
$this->loadFromId();
/**
* Load user table data, given mId has already been set.
- * @return Bool false if the ID does not exist, true otherwise
+ * @return bool false if the ID does not exist, true otherwise
*/
public function loadFromId() {
global $wgMemc;
return false;
}
- # Try cache
+ // Try cache
$key = wfMemcKey( 'user', 'id', $this->mId );
$data = $wgMemc->get( $key );
if ( !is_array( $data ) || $data['mVersion'] < MW_USER_VERSION ) {
- # Object is expired, load from DB
+ // Object is expired, load from DB
$data = false;
}
if ( !$data ) {
wfDebug( "User: cache miss for user {$this->mId}\n" );
- # Load from DB
+ // Load from DB
if ( !$this->loadFromDatabase() ) {
- # Can't load from ID, user is anonymous
+ // Can't load from ID, user is anonymous
return false;
}
$this->saveToCache();
} else {
wfDebug( "User: got user {$this->mId} from cache\n" );
- # Restore from cache
+ // Restore from cache
foreach ( self::$mCacheVars as $name ) {
$this->$name = $data[$name];
}
* you have both an ID and a name handy.
*
* @param string $name Username, validated by Title::newFromText()
- * @param string|Bool $validate Validate username. Takes the same parameters as
- * User::getCanonicalName(), except that true is accepted as an alias
- * for 'valid', for BC.
+ * @param string|bool $validate Validate username. Takes the same parameters as
+ * User::getCanonicalName(), except that true is accepted as an alias
+ * for 'valid', for BC.
*
* @return User|bool User object, or false if the username is invalid
- * (e.g. if it contains illegal characters or is an IP address). If the
- * username is not present in the database, the result will be a user object
- * with a name, zero user ID and default settings.
+ * (e.g. if it contains illegal characters or is an IP address). If the
+ * username is not present in the database, the result will be a user object
+ * with a name, zero user ID and default settings.
*/
public static function newFromName( $name, $validate = 'valid' ) {
if ( $validate === true ) {
if ( $name === false ) {
return false;
} else {
- # Create unloaded user object
+ // Create unloaded user object
$u = new User;
$u->mName = $name;
$u->mFrom = 'name';
* If the code is invalid or has expired, returns NULL.
*
* @param string $code Confirmation code
- * @return User object, or null
+ * @return User|null
*/
public static function newFromConfirmationCode( $code ) {
$dbr = wfGetDB( DB_SLAVE );
* Create a new user object using data from session or cookies. If the
* login credentials are invalid, the result is an anonymous user.
*
- * @param $request WebRequest object to use; $wgRequest will be used if omitted.
+ * @param WebRequest $request Object to use; $wgRequest will be used if omitted.
* @return User object
*/
public static function newFromSession( WebRequest $request = null ) {
/**
* Get the username corresponding to a given user ID
* @param int $id User ID
- * @return String|bool The corresponding username
+ * @return string|bool The corresponding username
*/
public static function whoIs( $id ) {
return UserCache::singleton()->getProp( $id, 'name' );
* Get the real name of a user given their user ID
*
* @param int $id User ID
- * @return String|bool The corresponding user's real name
+ * @return string|bool The corresponding user's real name
*/
public static function whoIsReal( $id ) {
return UserCache::singleton()->getProp( $id, 'real_name' );
/**
* Get database id given a user name
* @param string $name Username
- * @return Int|Null The corresponding user's ID, or null if user is nonexistent
+ * @return int|null The corresponding user's ID, or null if user is nonexistent
*/
public static function idFromName( $name ) {
$nt = Title::makeTitleSafe( NS_USER, $name );
if ( is_null( $nt ) ) {
- # Illegal name
+ // Illegal name
return null;
}
* addresses like this, if we allowed accounts like this to be created
* new users could get the old edits of these anonymous users.
*
- * @param string $name to match
- * @return Bool
+ * @param string $name Name to match
+ * @return bool
*/
public static function isIP( $name ) {
return preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/', $name ) || IP::isIPv6( $name );
* is longer than the maximum allowed username size or doesn't begin with
* a capital letter.
*
- * @param string $name to match
- * @return Bool
+ * @param string $name Name to match
+ * @return bool
*/
public static function isValidUserName( $name ) {
global $wgContLang, $wgMaxNameChars;
* If an account already exists in this form, login will be blocked
* by a failure to pass this function.
*
- * @param string $name to match
- * @return Bool
+ * @param string $name Name to match
+ * @return bool
*/
public static function isUsableName( $name ) {
global $wgReservedUsernames;
* isValidUserName() to avoid disrupting existing accounts.
*
* @param string $name to match
- * @return Bool
+ * @return bool
*/
public static function isCreatableName( $name ) {
global $wgInvalidUsernameCharacters;
* Is the input a valid password for this user?
*
* @param string $password Desired password
- * @return Bool
+ * @return bool
*/
public function isValidPassword( $password ) {
//simple boolean wrapper for getPasswordValidity
* Does a string look like an e-mail address?
*
* This validates an email address using an HTML5 specification found at:
- * http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
+ * http://www.whatwg.org/html/states-of-the-type-attribute.html#valid-e-mail-address
* Which as of 2011-01-24 says:
*
* A valid e-mail address is a string that matches the ABNF production
* pass validation here.
*
* @param string $addr E-mail address
- * @return Bool
+ * @return bool
* @deprecated since 1.18 call Sanitizer::isValidEmail() directly
*/
public static function isValidEmailAddr( $addr ) {
* Given unvalidated user input, return a canonical username, or false if
* the username is invalid.
* @param string $name User input
- * @param string|Bool $validate type of validation to use:
+ * @param string|bool $validate type of validation to use:
* - false No validation
* - 'valid' Valid for batch processes
* - 'usable' Valid for batch processes and login
* @return bool|string
*/
public static function getCanonicalName( $name, $validate = 'valid' ) {
- # Force usernames to capital
+ // Force usernames to capital
global $wgContLang;
$name = $wgContLang->ucfirst( $name );
return false;
}
- # Clean up name according to title rules
+ // Clean up name according to title rules
$t = ( $validate === 'valid' ) ?
Title::newFromText( $name ) : Title::makeTitle( NS_USER, $name );
- # Check for invalid titles
+ // Check for invalid titles
if ( is_null( $t ) ) {
return false;
}
- # Reject various classes of invalid names
+ // Reject various classes of invalid names
global $wgAuth;
$name = $wgAuth->getCanonicalName( $t->getText() );
* Count the number of edits of a user
*
* @param int $uid User ID to check
- * @return Int the user's edit count
+ * @return int The user's edit count
*
* @deprecated since 1.21 in favour of User::getEditCount
*/
/**
* Return a random password.
*
- * @return String new random password
+ * @return string New random password
*/
public static function randomPassword() {
global $wgMinimalPasswordLength;
* @param string $all 'all' to check if the whole object has been loaded
* or any other string to check if only the item is available (e.g.
* for optimisation)
- * @return Boolean
+ * @return boolean
*/
public function isItemLoaded( $item, $all = 'all' ) {
return ( $this->mLoadedItems === true && $all === 'all' ) ||
/**
* Set that an item has been loaded
*
- * @param $item String
+ * @param string $item
*/
protected function setItemLoaded( $item ) {
if ( is_array( $this->mLoadedItems ) ) {
/**
* Load user data from the session or login cookie.
- * @return Bool True if the user is logged in, false otherwise.
+ * @return bool True if the user is logged in, false otherwise.
*/
private function loadFromSession() {
$result = null;
$proposedUser = User::newFromId( $sId );
if ( !$proposedUser->isLoggedIn() ) {
- # Not a valid ID
+ // Not a valid ID
return false;
}
global $wgBlockDisablesLogin;
if ( $wgBlockDisablesLogin && $proposedUser->isBlocked() ) {
- # User blocked and we've disabled blocked user logins
+ // User blocked and we've disabled blocked user logins
return false;
}
$passwordCorrect = ( strlen( $token ) && $token === $request->getCookie( 'Token' ) );
$from = 'cookie';
} else {
- # No session or persistent login cookie
+ // No session or persistent login cookie
return false;
}
wfDebug( "User: logged in from $from\n" );
return true;
} else {
- # Invalid credentials
+ // Invalid credentials
wfDebug( "User: can't log in from $from, invalid credentials\n" );
return false;
}
* Load user and user_group data from the database.
* $this->mId must be set, this is how the user is identified.
*
- * @return Bool True if the user exists, false if the user is anonymous
+ * @return bool True if the user exists, false if the user is anonymous
*/
public function loadFromDatabase() {
- # Paranoia
+ // Paranoia
$this->mId = intval( $this->mId );
- /** Anonymous user */
+ // Anonymous user
if ( !$this->mId ) {
$this->loadDefaults();
return false;
wfRunHooks( 'UserLoadFromDatabase', array( $this, &$s ) );
if ( $s !== false ) {
- # Initialise user table data
+ // Initialise user table data
$this->loadFromRow( $s );
$this->mGroups = null; // deferred
$this->getEditCount(); // revalidation for nulls
return true;
} else {
- # Invalid user_id
+ // Invalid user_id
$this->mId = 0;
$this->loadDefaults();
return false;
* Clear various cached data stored in this object. The cache of the user table
* data (i.e. self::$mCacheVars) is not cleared unless $reloadFrom is given.
*
- * @param bool|String $reloadFrom Reload user and user_groups table data from a
+ * @param bool|string $reloadFrom Reload user and user_groups table data from a
* given source. May be "name", "id", "defaults", "session", or false for
* no reload.
*/
}
$defOpt = $wgDefaultUserOptions;
- # default language setting
+ // Default language setting
$defOpt['language'] = $defOpt['variant'] = $wgContLang->getCode();
foreach ( SearchEngine::searchableNamespaces() as $nsnum => $nsname ) {
$defOpt['searchNs' . $nsnum] = !empty( $wgNamespacesToBeSearchedDefault[$nsnum] );
* Get a given default option value.
*
* @param string $opt Name of option to retrieve
- * @return String Default option value
+ * @return string Default option value
*/
public static function getDefaultOption( $opt ) {
$defOpts = self::getDefaultOptions();
$ip = null;
}
- # User/IP blocking
+ // User/IP blocking
$block = Block::newFromTarget( $this, $ip, !$bFromSlave );
- # Proxy blocking
+ // Proxy blocking
if ( !$block instanceof Block && $ip !== null && !$this->isAllowed( 'proxyunbannable' )
&& !in_array( $ip, $wgProxyWhitelist ) )
{
- # Local list
+ // Local list
if ( self::isLocallyBlockedProxy( $ip ) ) {
$block = new Block;
$block->setBlocker( wfMessage( 'proxyblocker' )->text() );
}
}
- # (bug 23343) Apply IP blocks to the contents of XFF headers, if enabled
+ // (bug 23343) Apply IP blocks to the contents of XFF headers, if enabled
if ( !$block instanceof Block
&& $wgApplyIpBlocksToXff
&& $ip !== null
*
* @param string $ip IP to check
* @param bool $checkWhitelist whether to check the whitelist first
- * @return Bool True if blacklisted.
+ * @return bool True if blacklisted.
*/
public function isDnsBlacklisted( $ip, $checkWhitelist = false ) {
global $wgEnableSorbs, $wgEnableDnsBlacklist,
*
* @param string $ip IP to check
* @param string|array $bases of Strings: URL of the DNS blacklist
- * @return Bool True if blacklisted.
+ * @return bool True if blacklisted.
*/
public function inDnsBlacklist( $ip, $bases ) {
wfProfileIn( __METHOD__ );
$found = false;
// @todo FIXME: IPv6 ??? (http://bugs.php.net/bug.php?id=33170)
if ( IP::isIPv4( $ip ) ) {
- # Reverse IP, bug 21255
+ // Reverse IP, bug 21255
$ipReversed = implode( '.', array_reverse( explode( '.', $ip ) ) );
foreach ( (array)$bases as $base ) {
- # Make hostname
- # If we have an access key, use that too (ProjectHoneypot, etc.)
+ // Make hostname
+ // If we have an access key, use that too (ProjectHoneypot, etc.)
if ( is_array( $base ) ) {
if ( count( $base ) >= 2 ) {
- # Access key is 1, base URL is 0
+ // Access key is 1, base URL is 0
$host = "{$base[1]}.$ipReversed.{$base[0]}";
} else {
$host = "$ipReversed.{$base[0]}";
$host = "$ipReversed.$base";
}
- # Send query
+ // Send query
$ipList = gethostbynamel( $host );
if ( $ipList ) {
wfProfileIn( __METHOD__ );
if ( !is_array( $wgProxyList ) ) {
- # Load from the specified file
+ // Load from the specified file
$wgProxyList = array_map( 'trim', file( $wgProxyList ) );
}
} elseif ( array_search( $ip, $wgProxyList ) !== false ) {
$ret = true;
} elseif ( array_key_exists( $ip, $wgProxyList ) ) {
- # Old-style flipped proxy list
+ // Old-style flipped proxy list
$ret = true;
} else {
$ret = false;
/**
* Is this user subject to rate limiting?
*
- * @return Bool True if rate limited
+ * @return bool True if rate limited
*/
public function isPingLimitable() {
global $wgRateLimitsExcludedIPs;
* last-hit counters will be shared across wikis.
*
* @param string $action Action to enforce; 'edit' if unspecified
- * @return Bool True if a rate limiter was tripped
+ * @return bool True if a rate limiter was tripped
*/
public function pingLimiter( $action = 'edit' ) {
- # Call the 'PingLimiter' hook
+ // Call the 'PingLimiter' hook
$result = false;
if ( !wfRunHooks( 'PingLimiter', array( &$this, $action, &$result ) ) ) {
return $result;
return false;
}
- # Some groups shouldn't trigger the ping limiter, ever
+ // Some groups shouldn't trigger the ping limiter, ever
if ( !$this->isPingLimitable() ) {
return false;
}
* Check if user is blocked
*
* @param bool $bFromSlave Whether to check the slave database instead of the master
- * @return Bool True if blocked, false otherwise
+ * @return bool True if blocked, false otherwise
*/
public function isBlocked( $bFromSlave = true ) { // hacked from false due to horrible probs on site
return $this->getBlock( $bFromSlave ) instanceof Block && $this->getBlock()->prevents( 'edit' );
/**
* Check if user is blocked from editing a particular article
*
- * @param $title Title to check
+ * @param Title $title Title to check
* @param bool $bFromSlave whether to check the slave database instead of the master
- * @return Bool
+ * @return bool
*/
function isBlockedFrom( $title, $bFromSlave = false ) {
global $wgBlockAllowsUTEdit;
$blocked = $this->isBlocked( $bFromSlave );
$allowUsertalk = ( $wgBlockAllowsUTEdit ? $this->mAllowUsertalk : false );
- # If a user's name is suppressed, they cannot make edits anywhere
+ // If a user's name is suppressed, they cannot make edits anywhere
if ( !$this->mHideName && $allowUsertalk && $title->getText() === $this->getName() &&
$title->getNamespace() == NS_USER_TALK ) {
$blocked = false;
/**
* If user is blocked, return the name of the user who placed the block
- * @return String name of blocker
+ * @return string Name of blocker
*/
public function blockedBy() {
$this->getBlockedStatus();
/**
* If user is blocked, return the specified reason for the block
- * @return String Blocking reason
+ * @return string Blocking reason
*/
public function blockedFor() {
$this->getBlockedStatus();
/**
* If user is blocked, return the ID for the block
- * @return Int Block ID
+ * @return int Block ID
*/
public function getBlockId() {
$this->getBlockedStatus();
* This is intended for quick UI checks.
*
* @param string $ip IP address, uses current client if none given
- * @return Bool True if blocked, false otherwise
+ * @return bool True if blocked, false otherwise
*/
public function isBlockedGlobally( $ip = '' ) {
if ( $this->mBlockedGlobally !== null ) {
/**
* Check if user account is locked
*
- * @return Bool True if locked, false otherwise
+ * @return bool True if locked, false otherwise
*/
public function isLocked() {
if ( $this->mLocked !== null ) {
/**
* Check if user account is hidden
*
- * @return Bool True if hidden, false otherwise
+ * @return bool True if hidden, false otherwise
*/
public function isHidden() {
if ( $this->mHideName !== null ) {
/**
* Get the user's ID.
- * @return Int The user's ID; 0 if the user is anonymous or nonexistent
+ * @return int The user's ID; 0 if the user is anonymous or nonexistent
*/
public function getId() {
if ( $this->mId === null && $this->mName !== null && User::isIP( $this->mName ) ) {
/**
* Get the user name, or the IP of an anonymous user
- * @return String User's name or IP address
+ * @return string User's name or IP address
*/
public function getName() {
if ( $this->isItemLoaded( 'name', 'only' ) ) {
- # Special case optimisation
+ // Special case optimisation
return $this->mName;
} else {
$this->load();
if ( $this->mName === false ) {
- # Clean up IPs
+ // Clean up IPs
$this->mName = IP::sanitizeIP( $this->getRequest()->getIP() );
}
return $this->mName;
/**
* Get the user's name escaped by underscores.
- * @return String Username escaped by underscores.
+ * @return string Username escaped by underscores.
*/
public function getTitleKey() {
return str_replace( ' ', '_', $this->getName() );
/**
* Check if the user has new messages.
- * @return Bool True if the user has new messages
+ * @return bool True if the user has new messages
*/
public function getNewtalk() {
$this->load();
- # Load the newtalk status if it is unloaded (mNewtalk=-1)
+ // Load the newtalk status if it is unloaded (mNewtalk=-1)
if ( $this->mNewtalk === -1 ) {
$this->mNewtalk = false; # reset talk page status
- # Check memcached separately for anons, who have no
- # entire User object stored in there.
+ // Check memcached separately for anons, who have no
+ // entire User object stored in there.
if ( !$this->mId ) {
global $wgDisableAnonTalk;
if ( $wgDisableAnonTalk ) {
/**
* Return the revision and link for the oldest new talk page message for
* this user.
- * Note: This function was designed to accomodate multiple talk pages, but
+ * @note This function was designed to accomodate multiple talk pages, but
* currently only returns a single link and revision.
* @return Array
*/
/**
* Get the revision ID for the oldest new talk page message for this user
- * @return Integer or null if there are no new messages
+ * @return int|null Revision id or null if there are no new messages
*/
public function getNewMessageRevisionId() {
$newMessageRevisionId = null;
*
* @see getNewtalk()
* @param string $field 'user_ip' for anonymous users, 'user_id' otherwise
- * @param string|Int $id User's IP address for anonymous users, User ID otherwise
+ * @param string|int $id User's IP address for anonymous users, User ID otherwise
* @param bool $fromMaster true to fetch from the master, false for a slave
- * @return Bool True if the user has new messages
+ * @return bool True if the user has new messages
*/
protected function checkNewtalk( $field, $id, $fromMaster = false ) {
if ( $fromMaster ) {
/**
* Add or update the new messages flag
* @param string $field 'user_ip' for anonymous users, 'user_id' otherwise
- * @param string|Int $id User's IP address for anonymous users, User ID otherwise
+ * @param string|int $id User's IP address for anonymous users, User ID otherwise
* @param $curRev Revision new, as yet unseen revision of the user talk page. Ignored if null.
- * @return Bool True if successful, false otherwise
+ * @return bool True if successful, false otherwise
*/
protected function updateNewtalk( $field, $id, $curRev = null ) {
// Get timestamp of the talk page revision prior to the current one
/**
* Clear the new messages flag for the given user
* @param string $field 'user_ip' for anonymous users, 'user_id' otherwise
- * @param string|Int $id User's IP address for anonymous users, User ID otherwise
- * @return Bool True if successful, false otherwise
+ * @param string|int $id User's IP address for anonymous users, User ID otherwise
+ * @return bool True if successful, false otherwise
*/
protected function deleteNewtalk( $field, $id ) {
$dbw = wfGetDB( DB_MASTER );
/**
* Generate a current or new-future timestamp to be stored in the
* user_touched field when we update things.
- * @return String Timestamp in TS_MW format
+ * @return string Timestamp in TS_MW format
*/
private static function newTouchedTimestamp() {
global $wgClockSkewFudge;
/**
* Validate the cache for this account.
* @param string $timestamp A timestamp in TS_MW format
- *
* @return bool
*/
public function validateCache( $timestamp ) {
/**
* Get the user touched timestamp
- * @return String timestamp
+ * @return string timestamp
*/
public function getTouched() {
$this->load();
* Set the password and reset the random token unconditionally.
*
* @param string|null $str New password to set or null to set an invalid
- * password hash meaning that the user will not be able to log in
- * through the web interface.
+ * password hash meaning that the user will not be able to log in
+ * through the web interface.
*/
public function setInternalPassword( $str ) {
$this->load();
/**
* Get the user's current token.
* @param bool $forceCreation Force the generation of a new token if the user doesn't have one (default=true for backwards compatibility)
- * @return String Token
+ * @return string Token
*/
public function getToken( $forceCreation = true ) {
$this->load();
/**
* Has password reminder email been sent within the last
* $wgPasswordReminderResendTime hours?
- * @return Bool
+ * @return bool
*/
public function isPasswordReminderThrottled() {
global $wgPasswordReminderResendTime;
/**
* Get the user's e-mail address
- * @return String User's email address
+ * @return string User's email address
*/
public function getEmail() {
$this->load();
/**
* Get the timestamp of the user's e-mail authentication
- * @return String TS_MW timestamp
+ * @return string TS_MW timestamp
*/
public function getEmailAuthenticationTimestamp() {
$this->load();
$this->setEmail( $str );
if ( $str !== '' && $wgEmailAuthentication ) {
- # Send a confirmation request to the new address if needed
+ // Send a confirmation request to the new address if needed
$type = $oldaddr != '' ? 'changed' : 'set';
$result = $this->sendConfirmationMail( $type );
if ( $result->isGood() ) {
- # Say the the caller that a confirmation mail has been sent
+ // Say the the caller that a confirmation mail has been sent
$result->value = 'eauth';
}
} else {
/**
* Get the user's real name
- * @return String User's real name
+ * @return string User's real name
*/
public function getRealName() {
if ( !$this->isItemLoaded( 'realname' ) ) {
*
* @param string $oname The option to check
* @param string $defaultOverride A default value returned if the option does not exist
- * @param bool $ignoreHidden = whether to ignore the effects of $wgHiddenPrefs
- * @return String User's current value for the option
+ * @param bool $ignoreHidden Whether to ignore the effects of $wgHiddenPrefs
+ * @return string User's current value for the option
* @see getBoolOption()
* @see getIntOption()
*/
* Get the user's current setting for a given option, as a boolean value.
*
* @param string $oname The option to check
- * @return Bool User's current value for the option
+ * @return bool User's current value for the option
* @see getOption()
*/
public function getBoolOption( $oname ) {
*
* @param string $oname The option to check
* @param int $defaultOverride A default value returned if the option does not exist
- * @return Int User's current value for the option
+ * @return int User's current value for the option
* @see getOption()
*/
public function getIntOption( $oname, $defaultOverride = 0 ) {
* Set the given option for a user.
*
* @param string $oname The option to set
- * @param $val mixed New value to set
+ * @param mixed $val New value to set
*/
public function setOption( $oname, $val ) {
$this->loadOptions();
* and 'all', which forces a reset of *all* preferences and overrides everything else.
*
* @param array|string $resetKinds which kinds of preferences to reset. Defaults to
- * array( 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' )
- * for backwards-compatibility.
+ * array( 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' )
+ * for backwards-compatibility.
* @param $context IContextSource|null context source used when $resetKinds
- * does not contain 'all', passed to getOptionKinds().
- * Defaults to RequestContext::getMain() when null.
+ * does not contain 'all', passed to getOptionKinds().
+ * Defaults to RequestContext::getMain() when null.
*/
public function resetOptions(
$resetKinds = array( 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' ),
/**
* Get the user's preferred date format.
- * @return String User's preferred date format
+ * @return string User's preferred date format
*/
public function getDatePreference() {
// Important migration for old data rows
global $wgMaxArticleSize; # Maximum article size, in Kb
$threshold = $this->getIntOption( 'stubthreshold' );
if ( $threshold > $wgMaxArticleSize * 1024 ) {
- # If they have set an impossible value, disable the preference
- # so we can use the parser cache again.
+ // If they have set an impossible value, disable the preference
+ // so we can use the parser cache again.
$threshold = 0;
}
return $threshold;
$this->getGroups(), // explicit groups
$this->getAutomaticGroups( $recache ) // implicit groups
) );
- # Hook for additional groups
+ // Hook for additional groups
wfRunHooks( 'UserEffectiveGroups', array( &$this, &$this->mEffectiveGroups ) );
// Force reindexation of groups when a hook has unset one of them
$this->mEffectiveGroups = array_values( array_unique( $this->mEffectiveGroups ) );
) );
}
if ( $recache ) {
- # Assure data consistency with rights/groups,
- # as getEffectiveGroups() depends on this function
+ // Assure data consistency with rights/groups,
+ // as getEffectiveGroups() depends on this function
$this->mEffectiveGroups = null;
}
wfProfileOut( __METHOD__ );
/**
* Get the user's edit count.
- * @return Int
+ * @return int
*/
public function getEditCount() {
if ( !$this->getId() ) {
/**
* Get whether the user is logged in
- * @return Bool
+ * @return bool
*/
public function isLoggedIn() {
return $this->getID() != 0;
/**
* Get whether the user is anonymous
- * @return Bool
+ * @return bool
*/
public function isAnon() {
return !$this->isLoggedIn();
* Check if user is allowed to access a feature / make an action
*
* @internal param \String $varargs permissions to test
- * @return Boolean: True if user is allowed to perform *any* of the given actions
+ * @return boolean: True if user is allowed to perform *any* of the given actions
*
* @return bool
*/
/**
* Internal mechanics of testing a permission
- * @param $action String
+ * @param string $action
* @return bool
*/
public function isAllowed( $action = '' ) {
if ( $action === '' ) {
return true; // In the spirit of DWIM
}
- # Patrolling may not be enabled
+ // Patrolling may not be enabled
if ( $action === 'patrol' || $action === 'autopatrol' ) {
global $wgUseRCPatrol, $wgUseNPPatrol;
if ( !$wgUseRCPatrol && !$wgUseNPPatrol ) {
return false;
}
}
- # Use strict parameter to avoid matching numeric 0 accidentally inserted
- # by misconfiguration: 0 == 'foo'
+ // Use strict parameter to avoid matching numeric 0 accidentally inserted
+ // by misconfiguration: 0 == 'foo'
return in_array( $action, $this->getRights(), true );
}
/**
* Check whether to enable recent changes patrol features for this user
- * @return Boolean: True or false
+ * @return boolean: True or false
*/
public function useRCPatrol() {
global $wgUseRCPatrol;
/**
* Check whether to enable new pages patrol features for this user
- * @return Bool True or false
+ * @return bool True or false
*/
public function useNPPatrol() {
global $wgUseRCPatrol, $wgUseNPPatrol;
- return ( ( $wgUseRCPatrol || $wgUseNPPatrol ) && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) ) );
+ return (
+ ( $wgUseRCPatrol || $wgUseNPPatrol )
+ && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
+ );
}
/**
/**
* Check the watched status of an article.
* @param $title Title of the article to look at
- * @return Bool
+ * @return bool
*/
public function isWatched( $title ) {
return $this->getWatchedItem( $title )->isWatched();
public function clearNotification( &$title ) {
global $wgUseEnotif, $wgShowUpdatedMarker;
- # Do nothing if the database is locked to writes
+ // Do nothing if the database is locked to writes
if ( wfReadOnly() ) {
return;
}
* @param string $value Value to set
* @param int $exp Expiration time, as a UNIX time value;
* if 0 or not specified, use the default $wgCookieExpiration
- * @param $secure Bool
+ * @param bool $secure
* true: Force setting the secure attribute when setting the cookie
* false: Force NOT setting the secure attribute when setting the cookie
* null (default): Use the default ($wgCookieSecure) to set the secure attribute
$this->clearCookie( 'Token' );
$this->clearCookie( 'forceHTTPS' );
- # Remember when user logged out, to prevent seeing cached pages
+ // Remember when user logged out, to prevent seeing cached pages
$this->setCookie( 'LoggedOut', wfTimestampNow(), time() + 86400 );
}
/**
* If only this user's username is known, and it exists, return the user ID.
- * @return Int
+ * @return int
*/
public function idForName() {
$s = trim( $this->getName() );
* settings.
*
* @deprecated since 1.17 use the ParserOptions object to get the relevant options
- * @return String Page rendering hash
+ * @return string Page rendering hash
*/
public function getPageRenderingHash() {
wfDeprecated( __METHOD__, '1.17' );
/**
* Get whether the user is explicitly blocked from account creation.
- * @return Bool|Block
+ * @return bool|Block
*/
public function isBlockedFromCreateAccount() {
$this->getBlockedStatus();
/**
* Get whether the user is blocked from using Special:Emailuser.
- * @return Bool
+ * @return bool
*/
public function isBlockedFromEmailuser() {
$this->getBlockedStatus();
/**
* Get whether the user is allowed to create an account.
- * @return Bool
+ * @return bool
*/
function isAllowedToCreateAccount() {
return $this->isAllowed( 'createaccount' ) && !$this->isBlockedFromCreateAccount();
/**
* Determine whether the user is a newbie. Newbies are either
* anonymous IPs, or the most recently created accounts.
- * @return Bool
+ * @return bool
*/
public function isNewbie() {
return !$this->isAllowed( 'autoconfirmed' );
/**
* Check to see if the given clear-text password is one of the accepted passwords
* @param string $password user password.
- * @return Boolean: True if the given password is correct, otherwise False.
+ * @return boolean: True if the given password is correct, otherwise False.
*/
public function checkPassword( $password ) {
global $wgAuth, $wgLegacyEncoding;
if ( $wgAuth->authenticate( $this->getName(), $password ) ) {
return true;
} elseif ( $wgAuth->strict() ) {
- /* Auth plugin doesn't allow local authentication */
+ // Auth plugin doesn't allow local authentication
return false;
} elseif ( $wgAuth->strictUserAuth( $this->getName() ) ) {
- /* Auth plugin doesn't allow local authentication for this user name */
+ // Auth plugin doesn't allow local authentication for this user name
return false;
}
if ( self::comparePasswords( $this->mPassword, $password, $this->mId ) ) {
return true;
} elseif ( $wgLegacyEncoding ) {
- # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
- # Check for this with iconv
+ // Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
+ // Check for this with iconv
$cp1252Password = iconv( 'UTF-8', 'WINDOWS-1252//TRANSLIT', $password );
if ( $cp1252Password != $password &&
self::comparePasswords( $this->mPassword, $cp1252Password, $this->mId ) )
*
* @param $plaintext string
*
- * @return Boolean: True if matches, false otherwise
+ * @return boolean: True if matches, false otherwise
*/
public function checkTemporaryPassword( $plaintext ) {
global $wgNewPasswordExpiry;
*
* @param string|array $salt of Strings Optional function-specific data for hashing
* @param $request WebRequest object to use or null to use $wgRequest
- * @return String The new edit token
+ * @return string The new edit token
*/
public function editToken( $salt = '', $request = null ) {
wfDeprecated( __METHOD__, '1.19' );
*
* @param string|array $salt of Strings Optional function-specific data for hashing
* @param $request WebRequest object to use or null to use $wgRequest
- * @return String The new edit token
+ * @return string The new edit token
*/
public function getEditToken( $salt = '', $request = null ) {
if ( $request == null ) {
/**
* Generate a looking random token for various uses.
*
- * @return String The new random token
- * @deprecated since 1.20; Use MWCryptRand for secure purposes or wfRandomString for pseudo-randomness
+ * @return string The new random token
+ * @deprecated since 1.20: Use MWCryptRand for secure purposes or wfRandomString for pseudo-randomness
*/
public static function generateToken() {
return MWCryptRand::generateHex( 32 );
*
* @param string $val Input value to compare
* @param string $salt Optional function-specific data for hashing
- * @param $request WebRequest object to use or null to use $wgRequest
- * @return Boolean: Whether the token matches
+ * @param WebRequest $request Object to use or null to use $wgRequest
+ * @return boolean: Whether the token matches
*/
public function matchEditToken( $val, $salt = '', $request = null ) {
$sessionToken = $this->getEditToken( $salt, $request );
*
* @param string $val Input value to compare
* @param string $salt Optional function-specific data for hashing
- * @param $request WebRequest object to use or null to use $wgRequest
- * @return Boolean: Whether the token matches
+ * @param WebRequest $request object to use or null to use $wgRequest
+ * @return boolean: Whether the token matches
*/
public function matchEditTokenNoSuffix( $val, $salt = '', $request = null ) {
$sessionToken = $this->getEditToken( $salt, $request );
* this change to the database.
*
* @param &$expiration \mixed Accepts the expiration time
- * @return String New token
+ * @return string New token
*/
protected function confirmationToken( &$expiration ) {
global $wgUserEmailConfirmationTokenExpiry;
/**
* Return a URL the user can use to confirm their email address.
* @param string $token Accepts the email confirmation token
- * @return String New token URL
+ * @return string New token URL
*/
protected function confirmationTokenUrl( $token ) {
return $this->getTokenUrl( 'ConfirmEmail', $token );
/**
* Return a URL the user can use to invalidate their email address.
* @param string $token Accepts the email confirmation token
- * @return String New token URL
+ * @return string New token URL
*/
protected function invalidationTokenUrl( $token ) {
return $this->getTokenUrl( 'InvalidateEmail', $token );
*
* @param string $page Special page
* @param string $token Token
- * @return String Formatted URL
+ * @return string Formatted URL
*/
protected function getTokenUrl( $page, $token ) {
// Hack to bypass localization of 'Special:'
/**
* Is this user allowed to send e-mails within limits of current
* site configuration?
- * @return Bool
+ * @return bool
*/
public function canSendEmail() {
global $wgEnableEmail, $wgEnableUserEmail;
/**
* Is this user allowed to receive e-mails within limits of current
* site configuration?
- * @return Bool
+ * @return bool
*/
public function canReceiveEmail() {
return $this->isEmailConfirmed() && !$this->getOption( 'disablemail' );
* confirmed their address by returning a code or using a password
* sent to the address from the wiki.
*
- * @return Bool
+ * @return bool
*/
public function isEmailConfirmed() {
global $wgEmailAuthentication;
/**
* Check whether there is an outstanding request for e-mail confirmation.
- * @return Bool
+ * @return bool
*/
public function isEmailConfirmationPending() {
global $wgEmailAuthentication;
/**
* Get the timestamp of account creation.
*
- * @return String|Bool|Null Timestamp of account creation, false for
- * non-existent/anonymous user accounts, or null if existing account
- * but information is not in database.
+ * @return string|bool|null Timestamp of account creation, false for
+ * non-existent/anonymous user accounts, or null if existing account
+ * but information is not in database.
*/
public function getRegistration() {
if ( $this->isAnon() ) {
/**
* Get the timestamp of the first edit
*
- * @return String|Bool Timestamp of first edit, or false for
- * non-existent/anonymous user accounts.
+ * @return string|bool Timestamp of first edit, or false for
+ * non-existent/anonymous user accounts.
*/
public function getFirstEditTimestamp() {
if ( $this->getId() == 0 ) {
* Get the localized descriptive name for a group, if it exists
*
* @param string $group Internal group name
- * @return String Localized descriptive group name
+ * @return string Localized descriptive group name
*/
public static function getGroupName( $group ) {
$msg = wfMessage( "group-$group" );
*
* @param string $group Internal group name
* @param string $username Username for gender (since 1.19)
- * @return String Localized name for group member
+ * @return string Localized name for group member
*/
public static function getGroupMember( $group, $username = '#' ) {
$msg = wfMessage( "group-$group-member", $username );
* Get the title of a page describing a particular group
*
* @param string $group Internal group name
- * @return Title|Bool Title of the page if it exists, false otherwise
+ * @return Title|bool Title of the page if it exists, false otherwise
*/
public static function getGroupPage( $group ) {
$msg = wfMessage( 'grouppage-' . $group )->inContentLanguage();
*
* @param string $group Internal name of the group
* @param string $text The text of the link
- * @return String HTML link to the group
+ * @return string HTML link to the group
*/
public static function makeGroupLinkHTML( $group, $text = '' ) {
if ( $text == '' ) {
*
* @param string $group Internal name of the group
* @param string $text The text of the link
- * @return String Wikilink to the group
+ * @return string Wikilink to the group
*/
public static function makeGroupLinkWiki( $group, $text = '' ) {
if ( $text == '' ) {
* Initialize user_editcount from data out of the revision table
*
* @param $add Integer Edits to add to the count from the revision table
- * @return Integer Number of edits
+ * @return integer Number of edits
*/
protected function initEditCount( $add = 0 ) {
// Pull from a slave to be less cruel to servers
* Get the description of a given right
*
* @param string $right Right to query
- * @return String Localized description of the right
+ * @return string Localized description of the right
*/
public static function getRightDescription( $right ) {
$key = "right-$right";
*
* @param string $password Plain-text password
* @param string $userId User ID
- * @return String Password hash
+ * @return string Password hash
*/
public static function oldCrypt( $password, $userId ) {
global $wgPasswordSalt;
*
* @param string $password Plain-text password
* @param bool|string $salt Optional salt, may be random or the user ID.
-
- * If unspecified or false, will generate one automatically
- * @return String Password hash
+ * If unspecified or false, will generate one automatically
+ * @return string Password hash
*/
public static function crypt( $password, $salt = false ) {
global $wgPasswordSalt;
* @param string $password Plain-text password to compare
* @param string|bool $userId User ID for old-style password salt
*
- * @return Boolean
+ * @return boolean
*/
public static function comparePasswords( $hash, $password, $userId = false ) {
$type = substr( $hash, 0, 3 );
}
if ( $type == ':A:' ) {
- # Unsalted
+ // Unsalted
return md5( $password ) === substr( $hash, 3 );
} elseif ( $type == ':B:' ) {
- # Salted
+ // Salted
list( $salt, $realHash ) = explode( ':', substr( $hash, 3 ), 2 );
return md5( $salt . '-' . md5( $password ) ) === $realHash;
} else {
- # Old-style
+ // Old-style
return self::oldCrypt( $password, $userId ) === $hash;
}
}
$userId = $this->getId();
$insert_rows = array();
foreach ( $saveOptions as $key => $value ) {
- # Don't bother storing default values
+ // Don't bother storing default values
$defaultOption = self::getDefaultOption( $key );
if ( ( is_null( $defaultOption ) &&
!( $value === false || is_null( $value ) ) ) ||
}
}
+# Load composer's autoloader if present
+if ( is_readable( "$IP/vendor/autoload.php" ) ) {
+ require_once "$IP/vendor/autoload.php";
+}
+
# Get MWInit class
require_once "$IP/includes/Init.php";
wfShellExec( "$cmd &", $retVal );
wfProfileOut( __METHOD__ . '-exec' );
} else {
- // Fallback to running the jobs here while the user waits
- $group = JobQueueGroup::singleton();
- do {
- $job = $group->pop( JobQueueGroup::USE_CACHE ); // job from any queue
- if ( $job ) {
- $output = $job->toString() . "\n";
- $t = - microtime( true );
- wfProfileIn( __METHOD__ . '-' . get_class( $job ) );
- $success = $job->run();
- wfProfileOut( __METHOD__ . '-' . get_class( $job ) );
- $group->ack( $job ); // done
- $t += microtime( true );
- $t = round( $t * 1000 );
- if ( $success === false ) {
- $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n";
- } else {
- $output .= "Success, Time: $t ms\n";
+ try {
+ // Fallback to running the jobs here while the user waits
+ $group = JobQueueGroup::singleton();
+ do {
+ $job = $group->pop( JobQueueGroup::USE_CACHE ); // job from any queue
+ if ( $job ) {
+ $output = $job->toString() . "\n";
+ $t = - microtime( true );
+ wfProfileIn( __METHOD__ . '-' . get_class( $job ) );
+ $success = $job->run();
+ wfProfileOut( __METHOD__ . '-' . get_class( $job ) );
+ $group->ack( $job ); // done
+ $t += microtime( true );
+ $t = round( $t * 1000 );
+ if ( $success === false ) {
+ $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n";
+ } else {
+ $output .= "Success, Time: $t ms\n";
+ }
+ wfDebugLog( 'jobqueue', $output );
}
- wfDebugLog( 'jobqueue', $output );
- }
- } while ( --$n && $job );
+ } while ( --$n && $job );
+ } catch ( MWException $e ) {
+ // We don't want exceptions thrown during job execution to
+ // be reported to the user since the output is already sent.
+ // Instead we just log them.
+ wfDebugLog( 'exception', $e->getLogMessage() );
+ }
}
}
}
function() use ( $dbw, $that, $method, $added, $deleted ) {
$ns = $that->getTitle()->getNamespace();
- // First make sure the rows exist. If one of the "deleted" ones didn't
- // exist, we might legitimately not create it, but it's simpler to just
- // create it and then give it a negative value, since the value is bogus
- // anyway.
- //
- // Sometimes I wish we had INSERT ... ON DUPLICATE KEY UPDATE.
- $insertCats = array_merge( $added, $deleted );
- if ( !$insertCats ) {
- // Okay, nothing to do
- return;
- }
-
- $insertRows = array();
- foreach ( $insertCats as $cat ) {
- $insertRows[] = array(
- 'cat_id' => $dbw->nextSequenceValue( 'category_cat_id_seq' ),
- 'cat_title' => $cat
- );
- }
- $dbw->insert( 'category', $insertRows, $method, 'IGNORE' );
-
$addFields = array( 'cat_pages = cat_pages + 1' );
$removeFields = array( 'cat_pages = cat_pages - 1' );
-
if ( $ns == NS_CATEGORY ) {
$addFields[] = 'cat_subcats = cat_subcats + 1';
$removeFields[] = 'cat_subcats = cat_subcats - 1';
$removeFields[] = 'cat_files = cat_files - 1';
}
- if ( $added ) {
- $dbw->update(
+ if ( count( $added ) ) {
+ $insertRows = array();
+ foreach ( $added as $cat ) {
+ $insertRows[] = array(
+ 'cat_title' => $cat,
+ 'cat_pages' => 1,
+ 'cat_subcats' => ( $ns == NS_CATEGORY ) ? 1 : 0,
+ 'cat_files' => ( $ns == NS_FILE ) ? 1 : 0,
+ );
+ }
+ $dbw->upsert(
'category',
+ $insertRows,
+ array( 'cat_title' ),
$addFields,
- array( 'cat_title' => $added ),
$method
);
}
- if ( $deleted ) {
+ if ( count( $deleted ) ) {
$dbw->update(
'category',
$removeFields,
$cat = Category::newFromName( $catName );
wfRunHooks( 'CategoryAfterPageAdded', array( $cat, $that ) );
}
+
foreach ( $deleted as $catName ) {
$cat = Category::newFromName( $catName );
wfRunHooks( 'CategoryAfterPageRemoved', array( $cat, $that ) );
public $rootElement = '';
/**
- * @param string $file filename
- * @param $filterCallback callable (optional)
+ * @param string $input a filename or string containing the XML element
+ * @param callable $filterCallback (optional)
* Function to call to do additional custom validity checks from the
* SAX element handler event. This gives you access to the element
* namespace, name, and attributes, but not to text contents.
* Filter should return 'true' to toggle on $this->filterMatch
+ * @param boolean $isFile (optional) indicates if the first parameter is a
+ * filename (default, true) or if it is a string (false)
*/
- function __construct( $file, $filterCallback = null ) {
+ function __construct( $input, $filterCallback = null, $isFile = true ) {
$this->filterCallback = $filterCallback;
- $this->run( $file );
+ if ( $isFile ) {
+ $this->validateFromFile( $input );
+ } else {
+ $this->validateFromString( $input );
+ }
+ }
+
+ /**
+ * Alternative constructor: from filename
+ *
+ * @param string $fname the filename of an XML document
+ * @param callable $filterCallback (optional)
+ * Function to call to do additional custom validity checks from the
+ * SAX element handler event. This gives you access to the element
+ * namespace, name, and attributes, but not to text contents.
+ * Filter should return 'true' to toggle on $this->filterMatch
+ * @return XmlTypeCheck
+ */
+ public static function newFromFilename( $fname, $filterCallback = null ) {
+ return new self( $fname, $filterCallback, true );
+ }
+
+ /**
+ * Alternative constructor: from string
+ *
+ * @param string $string a string containing an XML element
+ * @param callable $filterCallback (optional)
+ * Function to call to do additional custom validity checks from the
+ * SAX element handler event. This gives you access to the element
+ * namespace, name, and attributes, but not to text contents.
+ * Filter should return 'true' to toggle on $this->filterMatch
+ * @return XmlTypeCheck
+ */
+ public static function newFromString( $string, $filterCallback = null ) {
+ return new self( $string, $filterCallback, false );
}
/**
}
/**
- * @param $fname
+ * Get an XML parser with the root element handler.
+ * @see XmlTypeCheck::rootElementOpen()
+ * @return resource a resource handle for the XML parser
*/
- private function run( $fname ) {
+ private function getParser() {
$parser = xml_parser_create_ns( 'UTF-8' );
-
// case folding violates XML standard, turn it off
xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
-
xml_set_element_handler( $parser, array( $this, 'rootElementOpen' ), false );
+ return $parser;
+ }
+
+ /**
+ * @param string $fname the filename
+ */
+ private function validateFromFile( $fname ) {
+ $parser = $this->getParser();
if ( file_exists( $fname ) ) {
$file = fopen( $fname, "rb" );
$chunk = fread( $file, 32768 );
$ret = xml_parse( $parser, $chunk, feof( $file ) );
if ( $ret == 0 ) {
- // XML isn't well-formed!
+ $this->wellFormed = false;
fclose( $file );
xml_parser_free( $parser );
return;
fclose( $file );
}
}
-
$this->wellFormed = true;
xml_parser_free( $parser );
}
+ /**
+ *
+ * @param string $string the XML-input-string to be checked.
+ */
+ private function validateFromString( $string ) {
+ $parser = $this->getParser();
+ $ret = xml_parse( $parser, $string, true );
+ xml_parser_free( $parser );
+ if ( $ret == 0 ) {
+ $this->wellFormed = false;
+ return;
+ }
+ $this->wellFormed = true;
+ }
+
/**
* @param $parser
* @param $name
// Clear page info.
$revision = WikiPage::factory( $title )->getRevision();
if ( $revision !== null ) {
- $memcKey = wfMemcKey( 'infoaction', $title->getPrefixedText(), $revision->getId() );
- $wgMemc->delete( $memcKey );
+ $key = wfMemcKey( 'infoaction', sha1( $title->getPrefixedText() ), $revision->getId() );
+ $wgMemc->delete( $key );
}
}
* @return array
*/
protected function pageInfo() {
- global $wgContLang, $wgRCMaxAge, $wgMemc, $wgUnwatchedPageThreshold, $wgPageInfoTransclusionLimit;
+ global $wgContLang, $wgRCMaxAge, $wgMemc,
+ $wgUnwatchedPageThreshold, $wgPageInfoTransclusionLimit;
$user = $this->getUser();
$lang = $this->getLanguage();
$title = $this->getTitle();
$id = $title->getArticleID();
- $memcKey = wfMemcKey( 'infoaction', sha1( $title->getPrefixedText() ), $this->page->getLatest() );
+ $memcKey = wfMemcKey( 'infoaction',
+ sha1( $title->getPrefixedText() ), $this->page->getLatest() );
$pageCounts = $wgMemc->get( $memcKey );
if ( $pageCounts === false ) {
// Get page information that would be too "expensive" to retrieve by normal means
$titleObj = $rev->getTitle();
$wgTitle = $titleObj;
$pageObj = WikiPage::factory( $titleObj );
- $popts = $pageObj->makeParserOptions( $this->getContext() );
- $popts->enableLimitReport( !$params['disablepp'] );
+ $popts = $this->makeParserOptions( $pageObj, $params );
// If for some reason the "oldid" is actually the current revision, it may be cached
if ( $rev->isCurrent() ) {
$oldid = $pageObj->getLatest();
}
- $popts = $pageObj->makeParserOptions( $this->getContext() );
- $popts->enableLimitReport( !$params['disablepp'] );
+ $popts = $this->makeParserOptions( $pageObj, $params );
// Potentially cached
$p_result = $this->getParsedContent( $pageObj, $popts, $pageid,
$wgTitle = $titleObj;
$pageObj = WikiPage::factory( $titleObj );
- $popts = $pageObj->makeParserOptions( $this->getContext() );
- $popts->enableLimitReport( !$params['disablepp'] );
+ $popts = $this->makeParserOptions( $pageObj, $params );
if ( is_null( $text ) ) {
$this->dieUsage( 'The text parameter should be passed with the title parameter. Should you be using the "page" parameter instead?', 'params' );
}
}
+ /**
+ * Constructs a ParserOptions object
+ *
+ * @param WikiPage $pageObj
+ * @param array $params
+ *
+ * @return ParserOptions
+ */
+ protected function makeParserOptions( WikiPage $pageObj, array $params ) {
+ wfProfileIn( __METHOD__ );
+
+ $popts = $pageObj->makeParserOptions( $this->getContext() );
+ $popts->enableLimitReport( !$params['disablepp'] );
+ $popts->setIsPreview( $params['preview'] );
+ $popts->setIsSectionPreview( $params['preview'] && $this->section !== false );
+
+ wfProfileOut( __METHOD__ );
+ return $popts;
+ }
+
/**
* @param $page WikiPage
* @param $popts ParserOptions
'section' => null,
'disablepp' => false,
'generatexml' => false,
+ 'preview' => false,
'contentformat' => array(
ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
),
'section' => 'Only retrieve the content of this section number',
'disablepp' => 'Disable the PP Report from the parser output',
'generatexml' => 'Generate XML parse tree (requires prop=wikitext)',
+ 'preview' => 'Parse in preview mode',
'contentformat' => 'Content serialization format used for the input text',
'contentmodel' => 'Content model of the new content',
);
wfRunHooks( 'ConvertContent', array( $this, $toModel, $lossy, &$result ) );
return $result;
}
+
+ /**
+ * Returns a ParserOutput object, filled by a call to fillParserOutput().
+ * Subclasses that want to control the parser output may override this, or
+ * they can override fillParserOutput(). If they override getParserOutput(),
+ * itself, they should take care to call the ContentGetParserOutput hook.
+ *
+ * @param $title Title Context title for parsing
+ * @param $revId int|null Revision ID (for {{REVISIONID}})
+ * @param $options ParserOptions|null Parser options
+ * @param $generateHtml bool Whether or not to generate HTML
+ *
+ * @return ParserOutput representing the HTML form of the text
+ */
+ public function getParserOutput( Title $title, $revId = null,
+ ParserOptions $options = null, $generateHtml = true
+ ) {
+ # Generic implementation, relying on $this->getHtml()
+
+ if ( $options === null ) {
+ $options = $this->getContentHandler()->makeParserOptions( 'canonical' );
+ }
+
+ $po = new ParserOutput();
+
+ if ( wfRunHooks( 'ContentGetParserOutput',
+ array( $this, $title, $revId, $options, $generateHtml, &$po ) ) ) {
+
+ $this->fillParserOutput( $title, $revId, $options, $generateHtml, $po );
+ }
+
+ return $po;
+ }
+
+ /**
+ * Fills the provided ParserOutput object with the HTML returned by getHtml().
+ * Subclasses should override this (or getParserOutput) appropriately.
+ * This placeholder implementation always throws an exception.
+ *
+ * @param $title Title Context title for parsing
+ * @param $revId int|null Revision ID (for {{REVISIONID}})
+ * @param $options ParserOptions|null Parser options
+ * @param $generateHtml bool Whether or not to generate HTML
+ * @param &$output ParserOutput The output object to fill (reference).
+ *
+ * @throws MWException
+ */
+ protected function fillParserOutput( Title $title, $revId,
+ ParserOptions $options, $generateHtml, ParserOutput &$output
+ ) {
+ // Don't make abstract, so subclasses that override getParserOutput() directly don't fail.
+ throw new MWException( 'Subclasses of AbstractContent must override fillParserOutput!' );
+ }
}
protected function getHtml() {
$html = "";
$html .= "<pre class=\"mw-code mw-css\" dir=\"ltr\">\n";
- $html .= $this->getHighlightHtml();
+ $html .= htmlspecialchars( $this->getNativeData() );
$html .= "\n</pre>\n";
return $html;
protected function getHtml() {
$html = "";
$html .= "<pre class=\"mw-code mw-js\" dir=\"ltr\">\n";
- $html .= $this->getHighlightHtml();
+ $html .= htmlspecialchars( $this->getNativeData() );
$html .= "\n</pre>\n";
return $html;
}
/**
- * Returns a generic ParserOutput object, wrapping the HTML returned by
- * getHtml().
+ * Fills the provided ParserOutput object with the HTML returned by getHtml().
+ *
+ * Content models in $wgTextModelsToParse will be parsed as wikitext to process links,
+ * magic words, etc.
+ *
+ * Subclasses may override this to provide custom rendering.
*
* @param $title Title Context title for parsing
* @param int|null $revId Revision ID (for {{REVISIONID}})
* @param $options ParserOptions|null Parser options
* @param bool $generateHtml Whether or not to generate HTML
- *
- * @return ParserOutput representing the HTML form of the text
+ * @param $output ParserOutput The output object to fill (reference).
*/
- public function getParserOutput( Title $title,
- $revId = null,
- ParserOptions $options = null, $generateHtml = true
+ protected function fillParserOutput( Title $title, $revId,
+ ParserOptions $options, $generateHtml, ParserOutput &$output
) {
global $wgParser, $wgTextModelsToParse;
- if ( !$options ) {
- //NOTE: use canonical options per default to produce cacheable output
- $options = $this->getContentHandler()->makeParserOptions( 'canonical' );
- }
-
if ( in_array( $this->getModel(), $wgTextModelsToParse ) ) {
- // parse just to get links etc into the database
- $po = $wgParser->parse( $this->getNativeData(), $title, $options, true, true, $revId );
- } else {
- $po = new ParserOutput();
+ // parse just to get links etc into the database, HTML is replaced below.
+ $output = $wgParser->parse( $this->getNativeData(), $title, $options, true, true, $revId );
}
if ( $generateHtml ) {
$html = '';
}
- $po->setText( $html );
- return $po;
+ $output->setText( $html );
}
/**
* Generates an HTML version of the content, for display. Used by
* getParserOutput() to construct a ParserOutput object.
*
- * This default implementation just calls getHighlightHtml(). Content
- * models that have another mapping to HTML (as is the case for markup
- * languages like wikitext) should override this method to generate the
- * appropriate HTML.
+ * This default implementation runs the text returned by $this->getNativeData()
+ * through htmlspecialchars and tried to convert line breaks and indentation to HTML..
*
* @return string An HTML representation of the content
*/
- protected function getHtml() {
- return $this->getHighlightHtml();
- }
-
- /**
- * Generates a syntax-highlighted version of the content, as HTML.
- * Used by the default implementation of getHtml().
- *
- * @return string an HTML representation of the content's markup
- */
- protected function getHighlightHtml() {
- # TODO: make Highlighter interface, use highlighter here, if available
- return htmlspecialchars( $this->getNativeData() );
+ public static function convertWhiteSpaceToHTML( $msg ) {
+ $msg = htmlspecialchars( $msg );
+ $msg = preg_replace( '/^ /m', ' ', $msg );
+ $msg = preg_replace( '/ $/m', ' ', $msg );
+ $msg = preg_replace( '/ /', '  ', $msg );
+ $msg = str_replace( "\n", '<br />', $msg );
+ return $msg;
}
/**
* using $wgParser.
*
* @since 1.21
+ * @see AbstractContent::fillParserOutput().
*
* @param $title Title
* @param int $revId Revision to pass to the parser (default: null)
* @param $options ParserOptions (default: null)
* @param bool $generateHtml (default: false)
- *
- * @internal param \IContextSource|null $context
- * @return ParserOutput representing the HTML form of the text
+ * @param &$output ParserOutput representing the HTML form of the text,
+ * may be manipulated or replaced.
*/
- public function getParserOutput( Title $title,
- $revId = null,
- ParserOptions $options = null, $generateHtml = true
+ protected function fillParserOutput( Title $title, $revId,
+ ParserOptions $options, $generateHtml, ParserOutput &$output
) {
global $wgParser;
- if ( !$options ) {
- //NOTE: use canonical options per default to produce cacheable output
- $options = $this->getContentHandler()->makeParserOptions( 'canonical' );
- }
-
- $po = $wgParser->parse( $this->getNativeData(), $title, $options, true, true, $revId );
- return $po;
+ $output = $wgParser->parse( $this->getNativeData(), $title, $options, true, true, $revId );
}
protected function getHtml() {
return $this->query( $sql, $fname );
}
+ /**
+ * INSERT ON DUPLICATE KEY UPDATE wrapper, upserts an array into a table.
+ *
+ * This updates any conflicting rows (according to the unique indexes) using
+ * the provided SET clause and inserts any remaining (non-conflicted) rows.
+ *
+ * $rows may be either:
+ * - A single associative array. The array keys are the field names, and
+ * the values are the values to insert. The values are treated as data
+ * and will be quoted appropriately. If NULL is inserted, this will be
+ * converted to a database NULL.
+ * - An array with numeric keys, holding a list of associative arrays.
+ * This causes a multi-row INSERT on DBMSs that support it. The keys in
+ * each subarray must be identical to each other, and in the same order.
+ *
+ * It may be more efficient to leave off unique indexes which are unlikely
+ * to collide. However if you do this, you run the risk of encountering
+ * errors which wouldn't have occurred in MySQL.
+ *
+ * Usually throws a DBQueryError on failure. If errors are explicitly ignored,
+ * returns success.
+ *
+ * @param string $table Table name. This will be passed through DatabaseBase::tableName().
+ * @param array $rows A single row or list of rows to insert
+ * @param array $uniqueIndexes List of single field names or field name tuples
+ * @param array $set An array of values to SET. For each array element,
+ * the key gives the field name, and the value gives the data
+ * to set that field to. The data will be quoted by
+ * DatabaseBase::addQuotes().
+ * @param string $fname Calling function name (use __METHOD__) for logs/profiling
+ * @param array $options of options
+ *
+ * @return bool
+ * @since 1.22
+ */
+ public function upsert(
+ $table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__
+ ) {
+ if ( !count( $rows ) ) {
+ return true; // nothing to do
+ }
+ $rows = is_array( reset( $rows ) ) ? $rows : array( $rows );
+
+ if ( count( $uniqueIndexes ) ) {
+ $clauses = array(); // list WHERE clauses that each identify a single row
+ foreach ( $rows as $row ) {
+ foreach ( $uniqueIndexes as $index ) {
+ $index = is_array( $index ) ? $index : array( $index ); // columns
+ $rowKey = array(); // unique key to this row
+ foreach ( $index as $column ) {
+ $rowKey[$column] = $row[$column];
+ }
+ $clauses[] = $this->makeList( $rowKey, LIST_AND );
+ }
+ }
+ $where = array( $this->makeList( $clauses, LIST_OR ) );
+ } else {
+ $where = false;
+ }
+
+ $useTrx = !$this->mTrxLevel;
+ if ( $useTrx ) {
+ $this->begin( $fname );
+ }
+ try {
+ # Update any existing conflicting row(s)
+ if ( $where !== false ) {
+ $ok = $this->update( $table, $set, $where, $fname );
+ } else {
+ $ok = true;
+ }
+ # Now insert any non-conflicting row(s)
+ $ok = $this->insert( $table, $rows, $fname, array( 'IGNORE' ) ) && $ok;
+ } catch ( Exception $e ) {
+ if ( $useTrx ) {
+ $this->rollback( $fname );
+ }
+ throw $e;
+ }
+ if ( $useTrx ) {
+ $this->commit( $fname );
+ }
+
+ return $ok;
+ }
+
/**
* DELETE where the condition is a join.
*
return $this->nativeReplace( $table, $rows, $fname );
}
+ /**
+ * @param string $table
+ * @param array $rows
+ * @param array $uniqueIndexes
+ * @param array $set
+ * @param string $fname
+ * @param array $options
+ * @return bool
+ */
+ public function upsert(
+ $table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__
+ ) {
+ if ( !count( $rows ) ) {
+ return true; // nothing to do
+ }
+ $rows = is_array( reset( $rows ) ) ? $rows : array( $rows );
+
+ $table = $this->tableName( $table );
+ $columns = array_keys( $rows[0] );
+
+ $sql = "INSERT INTO $table (" . implode( ',', $columns ) . ') VALUES ';
+ $rowTuples = array();
+ foreach ( $rows as $row ) {
+ $rowTuples[] = '(' . $this->makeList( $row ) . ')';
+ }
+ $sql .= implode( ',', $rowTuples );
+ $sql .= " ON DUPLICATE KEY UPDATE " . $this->makeList( $set, LIST_SET );
+
+ return (bool)$this->query( $sql, $fname );
+ }
+
/**
* Estimate rows in dataset
* Returns estimated count, based on EXPLAIN output
$names = array();
$storageDir = rtrim( $params['dir'], '/' );
$suffixStart = ( $dir === '' ) ? 0 : strlen( $dir ) + 1; // size of "path/to/dir/"
- foreach ( $cfObjects as $object ) {
+ // Iterate over the list *backwards* as this primes the stat cache, which is LRU.
+ // If this fills the cache and the caller stats an uncached file before stating
+ // the ones on the listing, there would be zero cache hits if this went forwards.
+ for ( end( $cfObjects ); key( $cfObjects ) !== null; prev( $cfObjects ) ) {
+ $object = current( $cfObjects );
$path = "{$storageDir}/" . substr( $object->name, $suffixStart );
$val = array(
// Convert dates like "Tue, 03 Jan 2012 22:01:04 GMT" to TS_MW
$this->cheapCache->set( $path, 'stat', $val );
$names[] = $object->name;
}
- return $names;
+ return array_reverse( $names ); // keep the paths in original order
}
/**
protected $domain; // string; domain (usually wiki ID)
protected $lockTTL; // integer; maximum time locks can be held
- /* Lock types; stronger locks have higher values */
+ /** Lock types; stronger locks have higher values */
const LOCK_SH = 1; // shared lock (for reads)
const LOCK_UW = 2; // shared lock (for reads used to write elsewhere)
const LOCK_EX = 3; // exclusive lock (for writes)
* @return Status
*/
final public function lock( array $paths, $type = self::LOCK_EX, $timeout = 0 ) {
+ return $this->lockByType( array( $type => $paths ), $timeout );
+ }
+
+ /**
+ * Lock the resources at the given abstract paths
+ *
+ * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+ * @param integer $timeout Timeout in seconds (0 means non-blocking) (since 1.21)
+ * @return Status
+ * @since 1.22
+ */
+ final public function lockByType( array $pathsByType, $timeout = 0 ) {
wfProfileIn( __METHOD__ );
+ $status = Status::newGood();
+ $pathsByType = $this->normalizePathsByType( $pathsByType );
$msleep = array( 0, 50, 100, 300, 500 ); // retry backoff times
$start = microtime( true );
do {
- $status = $this->doLock( array_unique( $paths ), $this->lockTypeMap[$type] );
+ $status = $this->doLockByType( $pathsByType );
$elapsed = microtime( true ) - $start;
if ( $status->isOK() || $elapsed >= $timeout || $elapsed < 0 ) {
break; // success, timeout, or clock set back
* @return Status
*/
final public function unlock( array $paths, $type = self::LOCK_EX ) {
+ return $this->unlockByType( array( $type => $paths ) );
+ }
+
+ /**
+ * Unlock the resources at the given abstract paths
+ *
+ * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+ * @return Status
+ * @since 1.22
+ */
+ final public function unlockByType( array $pathsByType ) {
wfProfileIn( __METHOD__ );
- $status = $this->doUnlock( array_unique( $paths ), $this->lockTypeMap[$type] );
+ $pathsByType = $this->normalizePathsByType( $pathsByType );
+ $status = $this->doUnlockByType( $pathsByType );
wfProfileOut( __METHOD__ );
return $status;
}
return sha1( "{$this->domain}:{$path}" );
}
+ /**
+ * Normalize the $paths array by converting LOCK_UW locks into the
+ * appropriate type and removing any duplicated paths for each lock type.
+ *
+ * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+ * @return Array
+ * @since 1.22
+ */
+ final protected function normalizePathsByType( array $pathsByType ) {
+ $res = array();
+ foreach ( $pathsByType as $type => $paths ) {
+ $res[$this->lockTypeMap[$type]] = array_unique( $paths );
+ }
+ return $res;
+ }
+
+ /**
+ * @see LockManager::lockByType()
+ * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+ * @return Status
+ * @since 1.22
+ */
+ protected function doLockByType( array $pathsByType ) {
+ $status = Status::newGood();
+ $lockedByType = array(); // map of (type => paths)
+ foreach ( $pathsByType as $type => $paths ) {
+ $status->merge( $this->doLock( $paths, $type ) );
+ if ( $status->isOK() ) {
+ $lockedByType[$type] = $paths;
+ } else {
+ // Release the subset of locks that were acquired
+ foreach ( $lockedByType as $type => $paths ) {
+ $status->merge( $this->doUnlock( $paths, $type ) );
+ }
+ break;
+ }
+ }
+ return $status;
+ }
+
/**
* Lock resources with the given keys and lock type
*
*/
abstract protected function doLock( array $paths, $type );
+ /**
+ * @see LockManager::unlockByType()
+ * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+ * @return Status
+ * @since 1.22
+ */
+ protected function doUnlockByType( array $pathsByType ) {
+ $status = Status::newGood();
+ foreach ( $pathsByType as $type => $paths ) {
+ $status->merge( $this->doUnlock( $paths, $type ) );
+ }
+ return $status;
+ }
+
/**
* Unlock resources with the given keys and lock type
*
protected $manager;
/** @var Status */
protected $status;
- /** @var Array List of resource paths*/
- protected $paths;
-
- protected $type; // integer lock type
+ /** @var Array Map of lock types to resource paths */
+ protected $pathsByType;
/**
- * @param $manager LockManager
- * @param array $paths List of storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @param $status Status
+ * @param LockManager $manager
+ * @param array $pathsByType Map of lock types to path lists
+ * @param Status $status
*/
- protected function __construct(
- LockManager $manager, array $paths, $type, Status $status
- ) {
+ protected function __construct( LockManager $manager, array $pathsByType, Status $status ) {
$this->manager = $manager;
- $this->paths = $paths;
+ $this->pathsByType = $pathsByType;
$this->status = $status;
- $this->type = $type;
}
/**
* Any locks are released once this object goes out of scope.
* The status object is updated with any errors or warnings.
*
- * @param $manager LockManager
- * @param array $paths List of storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @param $status Status
+ * $type can be "mixed" and $paths can be a map of types to paths (since 1.22).
+ * Otherwise $type should be an integer and $paths should be a list of paths.
+ *
+ * @param LockManager $manager
+ * @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
+ * @param integer $timeout Timeout in seconds (0 means non-blocking) (since 1.22)
* @return ScopedLock|null Returns null on failure
*/
public static function factory(
- LockManager $manager, array $paths, $type, Status $status
+ LockManager $manager, array $paths, $type, Status $status, $timeout = 0
) {
- $lockStatus = $manager->lock( $paths, $type );
+ $pathsByType = is_integer( $type ) ? array( $type => $paths ) : $paths;
+ $lockStatus = $manager->lockByType( $pathsByType, $timeout );
$status->merge( $lockStatus );
if ( $lockStatus->isOK() ) {
- return new self( $manager, $paths, $type, $status );
+ return new self( $manager, $pathsByType, $status );
}
return null;
}
$lock = null;
}
+ /**
+ * Release the locks when this goes out of scope
+ */
function __destruct() {
$wasOk = $this->status->isOK();
- $this->status->merge( $this->manager->unlock( $this->paths, $this->type ) );
+ $this->status->merge( $this->manager->unlockByType( $this->pathsByType ) );
if ( $wasOk ) {
// Make sure status is OK, despite any unlockFiles() fatals
$this->status->setResult( true, $this->status->value );
# Invalidate cache for all pages using this file
$update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' );
$update->doUpdate();
+ if ( !$reupload ) {
+ LinksUpdate::queueRecursiveJobsForTable( $this->getTitle(), 'imagelinks' );
+ }
# Invalidate cache for all pages that redirects on this page
$redirs = $this->getTitle()->getRedirectsHere();
foreach ( $redirs as $redir ) {
+ if ( !$reupload && $redir->getNamespace() === NS_FILE ) {
+ LinksUpdate::queueRecursiveJobsForTable( $redir, 'imagelinks' );
+ }
$update = new HTMLCacheUpdate( $redir, 'imagelinks' );
$update->doUpdate();
}
$patterns['rtl_in_url'] = "/{$patterns['lookbehind_not_letter']}(rtl){$patterns['lookahead_for_closing_paren']}/i";
$patterns['cursor_east'] = "/{$patterns['lookbehind_not_letter']}([ns]?)e-resize/";
$patterns['cursor_west'] = "/{$patterns['lookbehind_not_letter']}([ns]?)w-resize/";
- $patterns['four_notation_quantity'] = "/{$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}/i";
- $patterns['four_notation_color'] = "/(-color\s*:\s*){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}/i";
+ $patterns['four_notation_quantity'] = "/(:\s*){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s*[;}])/i";
+ $patterns['four_notation_color'] = "/(-color\s*:\s*){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}(\s*[;}])/i";
// The two regexes below are parenthesized differently then in the original implementation to make the
// callback's job more straightforward
$patterns['bg_horizontal_percentage'] = "/(background(?:-position)?\s*:\s*[^%]*?)(-?{$patterns['num']})(%\s*(?:{$patterns['quantity']}|{$patterns['ident']}))/";
* @return string
*/
private static function fixFourPartNotation( $css ) {
- $css = preg_replace( self::$patterns['four_notation_quantity'], '$1$2$7$4$5$6$3', $css );
- $css = preg_replace( self::$patterns['four_notation_color'], '$1$2$3$8$5$6$7$4', $css );
+ $css = preg_replace( self::$patterns['four_notation_quantity'], '$1$2$3$8$5$6$7$4$9', $css );
+ $css = preg_replace( self::$patterns['four_notation_color'], '$1$2$3$8$5$6$7$4$9', $css );
return $css;
}
/**
* Get the thumbnail extension and MIME type for a given source MIME type
+ *
+ * @param String $ext Extension of original file
+ * @param String $mime Mime type of original file
+ * @param Array $params Handler specific rendering parameters
* @return array thumbnail extension and MIME type
*/
function getThumbType( $ext, $mime, $params = null ) {
*/
var $file;
- var $width, $height, $url, $page, $path;
+ var $width, $height, $url, $page, $path, $lang;
/**
* @var array Associative array mapping optional supplementary image files
/**
* @param $title string
- * @param $params array
+ * @param $params string|array Query parameters to add
* @return array
*/
- public function getDescLinkAttribs( $title = null, $params = '' ) {
- $query = '';
+ public function getDescLinkAttribs( $title = null, $params = array() ) {
+ if ( is_array( $params ) ) {
+ $query = $params;
+ } else {
+ $query = array();
+ }
if ( $this->page && $this->page !== 1 ) {
- $query = 'page=' . urlencode( $this->page );
+ $query['page'] = $this->page;
}
- if ( $params ) {
- $query .= $query ? '&' . $params : $params;
+ if( $this->lang ) {
+ $query['lang'] = $this->lang;
}
+
+ if ( is_string( $params ) && $params !== '' ) {
+ $query = $params . '&' . wfArrayToCgi( $query );
+ }
+
$attribs = array(
'href' => $this->file->getTitle()->getLocalURL( $query ),
'class' => 'image',
# Previous parameters:
# $file, $url, $width, $height, $path = false, $page = false
+ $defaults = array(
+ 'page' => false,
+ 'lang' => false
+ );
+
if ( is_array( $parameters ) ) {
- $defaults = array(
- 'page' => false
- );
$actualParams = $parameters + $defaults;
} else {
# Using old format, should convert. Later a warning could be added here.
'width' => $path,
'height' => $parameters,
'page' => ( $numArgs > 5 ) ? func_get_arg( 5 ) : false
- );
+ ) + $defaults;
$path = ( $numArgs > 4 ) ? func_get_arg( 4 ) : false;
}
$this->height = round( $actualParams['height'] );
$this->page = $actualParams['page'];
+ $this->lang = $actualParams['lang'];
}
/**
$clientHeight = $params['height'];
$physicalWidth = $params['physicalWidth'];
$physicalHeight = $params['physicalHeight'];
+ $lang = isset( $params['lang'] ) ? $params['lang'] : 'en';
if ( $flags & self::TRANSFORM_LATER ) {
return new ThumbnailImage( $image, $dstUrl, $dstPath, $params );
}
$srcPath = $image->getLocalRefPath();
- $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight );
+ $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight, $lang );
if ( $status === true ) {
return new ThumbnailImage( $image, $dstUrl, $dstPath, $params );
} else {
* @param string $dstPath
* @param string $width
* @param string $height
+ * @param string $lang Language code of the language to render the SVG in
* @throws MWException
* @return bool|MediaTransformError
*/
- public function rasterize( $srcPath, $dstPath, $width, $height ) {
+ public function rasterize( $srcPath, $dstPath, $width, $height, $lang = false ) {
global $wgSVGConverters, $wgSVGConverter, $wgSVGConverterPath;
$err = false;
$retval = '';
if ( is_array( $wgSVGConverters[$wgSVGConverter] ) ) {
// This is a PHP callable
$func = $wgSVGConverters[$wgSVGConverter][0];
- $args = array_merge( array( $srcPath, $dstPath, $width, $height ),
+ $args = array_merge( array( $srcPath, $dstPath, $width, $height, $lang ),
array_slice( $wgSVGConverters[$wgSVGConverter], 1 ) );
if ( !is_callable( $func ) ) {
throw new MWException( "$func is not callable" );
wfEscapeShellArg( $dstPath ) ),
$wgSVGConverters[$wgSVGConverter]
) . " 2>&1";
+
+ $env = array();
+ if( $lang !== false ) {
+ $env['LANG'] = $lang;
+ }
+
wfProfileIn( 'rsvg' );
wfDebug( __METHOD__ . ": $cmd\n" );
- $err = wfShellExec( $cmd, $retval );
+ $err = wfShellExec( $cmd, $retval, $env );
wfProfileOut( 'rsvg' );
}
}
}
return $result;
}
+
+
+ /**
+ * @param string $name Parameter name
+ * @param $string $value Parameter value
+ * @return bool Validity
+ */
+ function validateParam( $name, $value ) {
+ if ( in_array( $name, array( 'width', 'height' ) ) ) {
+ // Reject negative heights, widths
+ return ( $value > 0 );
+ } elseif( $name == 'lang' ) {
+ // Validate $code
+ if( !Language::isValidBuiltinCode( $value ) ) {
+ wfDebug( "Invalid user language code\n" );
+ return false;
+ }
+ return true;
+ }
+ // Only lang, width and height are acceptable keys
+ return false;
+ }
+
+ /**
+ * @param array $params name=>value pairs of parameters
+ * @return string Filename to use
+ */
+ function makeParamString( $params ) {
+ $lang = '';
+ if( isset( $params['lang'] ) && $params['lang'] !== 'en' ) {
+ $params['lang'] = mb_strtolower( $params['lang'] );
+ $lang = "lang{$params['lang']}-";
+ }
+ if ( !isset( $params['width'] ) ) {
+ return false;
+ }
+ return "$lang{$params['width']}px";
+ }
+
+ function parseParamString( $str ) {
+ $m = false;
+ if ( preg_match( '/^lang([a-z]+(?:-[a-z]+)*)-(\d+)px$/', $str, $m ) ) {
+ return array( 'width' => array_pop( $m ), 'lang' => $m[1] );
+ } elseif( preg_match( '/^(\d+)px$/', $str, $m ) ) {
+ return array( 'width' => $m[1], 'lang' => 'en' );
+ } else {
+ return false;
+ }
+ }
+
+ function getParamMap() {
+ return array( 'img_lang' => 'lang', 'img_width' => 'width' );
+ }
+
+ /**
+ * @param $params
+ * @return array
+ */
+ function getScriptParams( $params ) {
+ return array(
+ 'width' => $params['width'],
+ 'lang' => $params['lang'],
+ );
+ }
}
There's an experimental PHP extension module which wraps the ICU library's
normalization functions. This is *MUCH* faster than doing this work in pure
-PHP code. This is at https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/extensions/normal.git;a=summary.
+PHP code. This is at https://git.wikimedia.org/summary/mediawiki%2Fextensions%2Fnormal.git.
It is used by the WMF, which currently runs PHP 5.3.10 on Linux. It hasn't been
thoroughly tested on other configurations, but may work.
if ( !$found && $title ) {
if ( !Profiler::instance()->isPersistent() ) {
# Too many unique items can kill profiling DBs/collectors
- $titleProfileIn = __METHOD__ . "-title-" . $title->getDBkey();
+ $titleProfileIn = __METHOD__ . "-title-" . $title->getPrefixedDBkey();
wfProfileIn( $titleProfileIn ); // template in
}
wfProfileIn( __METHOD__ . '-loadtpl' );
$doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
}
?>
- <div>
- <label><?php $this->msg( 'yourdomainname' ); ?></label>
+ <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">
}
?>
<div id="mw-user-domain-section">
- <label for='wpDomain' class="pos-above"><?php $this->msg( 'yourdomain' ); ?></label>
+ <label for='wpDomain'><?php $this->msg( 'yourdomainname' ); ?></label>
<select name="wpDomain" value="<?php $this->text( 'domain' ); ?>"
tabindex="3">
<?php echo $doms ?>
'youhavenewmessagesmanyusers' => 'لك $1 من مستخدمين كثُر ($2).',
'newmessageslinkplural' => '{{PLURAL:$1|رسالة جديدة|رسائل جديدة}}',
'newmessagesdifflinkplural' => 'أحدث {{PLURAL:$1|تغيير|تغييرات}}',
-'youhavenewmessagesmulti' => 'لك رسائل جديدة في $1',
+'youhavenewmessagesmulti' => 'لديك رسائل جديدة في $1',
'editsection' => 'عدّل',
'editold' => 'تعديل',
'viewsourceold' => 'استعرض المصدر',
# Special:PasswordReset
'passwordreset' => 'ܣܘܡ ܡܠܬܐ ܕܥܠܠܐ ܙܒܢ ܐܚܪܝܢ',
'passwordreset-legend' => 'ܣܘܡ ܡܠܬܐ ܕܥܠܠܐ ܙܒܢ ܐܚܪܝܢ',
-'passwordreset-pretext' => '{{PLURAL:$1||ܐܥܠ ܚܕ ܡܢ ܡܢܘܬ̈ܐ ܕܝܕ̈ܥܬܐ ܠܬܬܚܬ}}',
'passwordreset-username' => 'ܫܡܐ ܕܡܦܠܚܢܐ:',
'passwordreset-domain' => 'ܪܘܚܬܐ:',
'passwordreset-email' => 'ܡܘܢܥܐ ܕܒܝܠܕܪܐ ܐܠܩܛܪܘܢܝܐ:',
'accmailtitle' => 'ܡܠܬܐ ܕܥܠܠܐ ܫܕܪܬ',
'newarticle' => '(ܚܕܬܐ)',
'newarticletext' => "ܐܬܬ ܒܬܪ ܐܣܪܐ ܕܦܐܬܐ ܕܠܐ ܐܬܬܣܝܡܬ ܥܕܡܫ.
-ܠܣܘܝܡܐ ܕܦܐܬܐ ܗܕܐ, ܫܪܝ ܠܟܬܒܬܐ ܒܣܢܕܘܩܐ ܠܬܚܬ (ܚܙܝ [[{{MediaWiki:Helppage}}|ܦܐܬܐ ܕܥܘܕܪܢܐ]] ܠܐܚܪܢܐ ܝܕ̈ܥܬܐ).
-ܐܢ ܐܬܬ ܠܗܪܟܐ ܦܘܕܐܝܬ, ܕܘܫ ܠܦܪܡܝܬܐ ܕ '''ܠܒܣܬܪ back''' ܒܡܦܐܬܢܐ ܕܝܠܟ.",
+ܠܣܘܝܡܐ ܕܦܐܬܐ ܗܕܐ، ܫܪܝ ܠܟܬܒܬܐ ܒܣܢܕܘܩܐ ܠܬܚܬ (ܚܙܝ [[{{MediaWiki:Helppage}}|ܦܐܬܐ ܕܥܘܕܪܢܐ]] ܠܐܚܪܢܐ ܝܕ̈ܥܬܐ).
+ܐܢ ܐܬܬ ܠܗܪܟܐ ܦܘܕܐܝܬ، ܟܒܘܫ ܥܠ ܦܪܡܝܬܐ ܕ '''ܠܒܣܬܪ back''' ܒܡܦܐܬܢܐ ܕܝܠܟ.",
'updated' => '(ܐܬܚܕܬ)',
'note' => "'''ܡܥܝܪܢܘܬܐ:'''",
'previewnote' => "'''ܕܟܪ ܕܗܢܘ ܚܝܪܐ ܩܕܡܝܐ ܒܠܚܘܕ'''.
# Special:PasswordReset
'passwordreset' => 'Reaniciar contraseña',
-'passwordreset-text' => 'Complete esti formulariu pa reaniciar la contraseña.',
+'passwordreset-text-one' => 'Complete esti formulariu pa reaniciar la contraseña.',
+'passwordreset-text-many' => '{{PLURAL:$1|Escriba una de les partes de los datos pa reaniciar la contraseña.}}',
'passwordreset-legend' => 'Reaniciar contraseña',
'passwordreset-disabled' => 'Los reanicios de contraseña tán desactivaos nesta wiki.',
'passwordreset-emaildisabled' => 'Les funciones de corréu electrónicu tan desactivaes nesta wiki.',
-'passwordreset-pretext' => "{{PLURAL:$1||Escribi unu de los elementos de los datos d'abaxo}}",
'passwordreset-username' => "Nome d'usuariu:",
'passwordreset-domain' => 'Dominiu:',
'passwordreset-capture' => '¿Ver el corréu electrónicu resultante?',
Paez que se desanició.',
'edit-conflict' => "Conflictu d'edición.",
'edit-no-change' => "S'inoró la to edición, porque nun se fizo nengún cambéu nel testu.",
+'postedit-confirmation' => 'Guardose la edición.',
'edit-already-exists' => 'Nun pudo crease una páxina nueva.
Esta yá esiste.',
'defaultmessagetext' => 'Testu predetermináu',
'sp-contributions-newbies' => 'Паказваць толькі ўклады з новых рахункаў',
'sp-contributions-newbies-sub' => 'З новых рахункаў',
'sp-contributions-newbies-title' => 'Уклады ўдзельнікаў з новых рахункаў',
-'sp-contributions-blocklog' => 'Ð\96Ñ\83Ñ\80нал забаÑ\80онаÑ\9e',
+'sp-contributions-blocklog' => 'блакÑ\96Ñ\80оÑ\9eкÑ\96',
'sp-contributions-deleted' => 'сцёрты ўклад удзельніка',
'sp-contributions-uploads' => 'Загрузкі',
'sp-contributions-logs' => 'журналы',
'oct' => 'кас',
'nov' => 'ліс',
'dec' => 'сьн',
+'january-date' => '$1 студзеня',
+'february-date' => '$1 лютага',
+'march-date' => '$1 сакавіка',
+'april-date' => '$1 красавіка',
+'may-date' => '$1 траўня',
+'june-date' => '$1 чэрвеня',
+'july-date' => '$1 ліпеня',
+'august-date' => '$1 жніўня',
+'september-date' => '$1 верасьня',
+'october-date' => '$1 кастрычніка',
+'november-date' => '$1 лістапада',
+'december-date' => '$1 сьнежня',
# Categories related messages
'pagecategories' => '{{PLURAL:$1|Катэгорыя|Катэгорыі|Катэгорыі}}',
# Special:PasswordReset
'passwordreset' => 'Ачыстка паролю',
+'passwordreset-text-one' => 'Запоўніце гэтую форму, каб скінуць пароль.',
+'passwordreset-text-many' => '{{PLURAL:$1|Увядзіце гэтыя зьвесткі, каб скінуць пароль.}}',
'passwordreset-legend' => 'Ачысьціць пароль',
'passwordreset-disabled' => 'Магчымасьць ачысткі паролю была забароненая ў {{GRAMMAR:месны|{{SITENAME}}}}.',
'passwordreset-emaildisabled' => 'Функцыі e-mail у гэтай вікі былі адключаныя.',
'content-failed-to-parse' => 'Зьмест «$2» не адпавядае тыпу $1: $3.',
'invalid-content-data' => 'Няслушныя зьвесткі',
'content-not-allowed-here' => 'Зьмест тыпу «$1» на старонцы [[$2]] не дазволены',
-'editwarning-warning' => 'УÑ\81е зÑ\80обленÑ\8bÑ\8f Ð\92амÑ\96 зÑ\8cменÑ\8b могÑ\83Ñ\86Ñ\8c бÑ\8bÑ\86Ñ\8c Ñ\81Ñ\82Ñ\80аÑ\87анÑ\8bÑ\8f, калÑ\96 Ð\92Ñ\8b пакÑ\96неÑ\86е гÑ\8dÑ\82Ñ\83Ñ\8e Ñ\81Ñ\82аÑ\80онкÑ\83.
-Ð\9aалÑ\96 Ð\92Ñ\8b Ñ\9eвайÑ\88лÑ\96 Ñ\9e Ñ\81Ñ\8bÑ\81Ñ\82Ñ\8dмÑ\83, Ð\92Ñ\8b можаÑ\86е адклÑ\8eÑ\87Ñ\8bÑ\86Ñ\8c гÑ\8dÑ\82ае папÑ\8fÑ\80Ñ\8dджанÑ\8cне Ñ\83 Ñ\81Ñ\8dкÑ\86Ñ\8bÑ\96 «{{int:prefs-editing}}» Ð\92ашых наладаў.',
+'editwarning-warning' => 'Ð\9fакÑ\96нÑ\83Ñ\9eÑ\88Ñ\8b гÑ\8dÑ\82Ñ\83Ñ\8e Ñ\81Ñ\82аÑ\80онкÑ\83, вÑ\8b можаÑ\86е Ñ\81Ñ\82Ñ\80аÑ\86Ñ\96Ñ\86Ñ\8c Ñ\83Ñ\81е Ñ\9eнеÑ\81енÑ\8bÑ\8f зÑ\8cменÑ\8b.
+Ð\9aалÑ\96 вÑ\8b Ñ\9eвайÑ\88лÑ\96 Ñ\9e Ñ\81Ñ\8bÑ\81Ñ\82Ñ\8dмÑ\83, Ð\92Ñ\8b можаÑ\86е адклÑ\8eÑ\87Ñ\8bÑ\86Ñ\8c гÑ\8dÑ\82ае папÑ\8fÑ\80Ñ\8dджанÑ\8cне Ñ\9e Ñ\81Ñ\8dкÑ\86Ñ\8bÑ\96 «РÑ\8dдагаванÑ\8cне» вашых наладаў.',
# Content models
'content-model-wikitext' => 'вікі-тэкст',
'notvisiblerev' => 'Вэрсія была выдаленая',
'watchnochange' => 'Нічога з Вашага сьпісу назіраньня не зьмянілася за паказаны пэрыяд.',
'watchlist-details' => 'У Вашым сьпісе назіраньня $1 {{PLURAL:$1|старонка|старонкі|старонак}} за выключэньнем старонак абмеркаваньня.',
-'wlheader-enotif' => 'Ð\94аÑ\81Ñ\8bлка паведамленÑ\8cнÑ\8fÑ\9e па Ñ\8dлекÑ\82Ñ\80оннай поÑ\88Ñ\86е Ñ\9eклÑ\8eÑ\87анаÑ\8f.',
-'wlheader-showupdated' => "Старонкі, якія былі зьмененыя пасьля Вашага апошняга візыту, вылучаныя '''тоўстым''' шрыфтам",
+'wlheader-enotif' => 'Ð\90павÑ\8fÑ\88Ñ\87Ñ\8dнÑ\8cне па e-mail Ñ\83клÑ\8eÑ\87анае.',
+'wlheader-showupdated' => "Старонкі, зьмененыя з часу вашага апошняга візыту, вылучаныя '''тоўстым''' шрыфтам.",
'watchmethod-recent' => 'прагляд апошніх зьменаў у старонках са сьпісу назіраньня',
'watchmethod-list' => 'прагляд старонак са сьпісу назіраньня дзеля апошніх зьменах',
'watchlistcontains' => 'Ваш сьпіс назіраньня зьмяшчае $1 {{PLURAL:$1|старонка|старонкі|старонак}}.',
'yourpassword' => 'Šifra:',
'userlogin-yourpassword' => 'Šifra',
'userlogin-yourpassword-ph' => 'Unesite Vašu šifru/lozinku',
+'createacct-yourpassword-ph' => 'Unesite šifru/lozinku',
'yourpasswordagain' => 'Ponovite šifru:',
+'createacct-yourpasswordagain-ph' => 'Unesite šifru/lozinku opet',
'remembermypassword' => 'Zapamti moju šifru na ovom računaru (najviše $1 {{PLURAL:$1|dan|dana|dana}})',
'userlogin-signwithsecure' => 'Koristite sigurnu konekciju',
'securelogin-stick-https' => 'Ostani povezan na HTTPS nakon prijave',
'gotaccountlink' => 'Prijavi se',
'userlogin-resetlink' => 'Zaboravili ste detalje vaše prijave?',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoć pri prijavljivanju]]',
-'createaccountmail' => 'e-poštom',
+'createacct-join' => 'Unesite svoje podatke ispod.',
+'createacct-emailrequired' => 'Adresa e-pošte',
+'createacct-emailoptional' => 'Adresa e-pošte (opcionalno)',
+'createaccountmail' => 'Koristite privremenu, slučajno stvorenu šifru/lozinku i pošaljite na dole navedenu adrеsu e-pošte',
+'createacct-realname' => 'Pravo ime (opcionalno)',
'createaccountreason' => 'Razlog:',
+'createacct-reason' => 'Razlog',
+'createacct-captcha' => 'Sigurnosna provjera',
+'createacct-imgcaptcha-ph' => 'Unesite tekst koji vidite iznad',
+'createacct-submit' => 'Napravite svoj korisnički račun',
'badretype' => 'Šifre koje ste unijeli se ne poklapaju.',
'userexists' => 'Korisničko ime koje ste unijeli je već u upotrebi.
Molimo Vas da izaberete drugo ime.',
'passwordreset-capture-help' => 'Ako označite ovu kućicu, e-poruka (s privremenom lozinkom) će biti prikazana i poslata korisniku.',
'passwordreset-email' => 'E-mail adresa:',
'passwordreset-emailtitle' => 'Detalji računa na {{SITENAME}}',
-'passwordreset-emailtext-ip' => 'Neto (vjerovatno Vi, s IP adrese $1) je zatražio podsjetnik Vaših detalja računa
-za {{SITENAME}} ($4). Sljedeći {{PLURAL:$3|račun korisnika je|računi korisnika su}}
-povezani s ovom e-mail adresom:
+'passwordreset-emailtext-ip' => 'Neko (vjerovatno Vi, s IP adrese $1) je zatražio podsjetnik Vaših detalja računa za {{SITENAME}} ($4). Sljedeći {{PLURAL:$3|račun korisnika je|računi korisnika su}} povezani s ovom e-mail adresom:
$2
{{PLURAL:$3|Ova privremena šifra|Ove privremene šifre}} će isteći za {{PLURAL:$5|jedan dan|$5 dana}}.
-Trebate se prijaviti i odabrati novu šifru. Ako je neko drugi napravio ovaj
-zahtjev, ili ako ste se sjetili Vaše početne šifre, a ne želite je promijeniti,
-možete zanemariti ovu poruku i nastaviti koristiti staru šifru.',
+Trebate se prijaviti i odabrati novu šifru. Ako je neko drugi napravio ovaj zahtjev, ili ako ste se sjetili Vaše početne šifre, a ne želite je promijeniti, možete zanemariti ovu poruku i nastaviti koristiti staru šifru.',
'passwordreset-emailtext-user' => 'Korisnik $1 na {{SITENAME}} je zatražio podsjetnik o detaljima Vašeg računa za {{SITENAME}}
($4). Sljedeći {{PLURAL:$3|korisnički račun je|korisnički računi su}} povezani s ovom e-mail adresom:
'disclaimerpage' => 'Project:Avís general',
'edithelp' => 'Ajuda',
'edithelppage' => "Help:Com s'edita una pàgina",
-'helppage' => 'Viquipèdia:Ajuda',
+'helppage' => 'Help:Índex',
'mainpage' => 'Pàgina principal',
'mainpage-description' => 'Pàgina principal',
'policy-url' => 'Project:Polítiques',
'createacct-yourpasswordagain' => 'Confirmeu la contrasenya',
'createacct-yourpasswordagain-ph' => 'Introduïu de nou la contrasenya',
'remembermypassword' => 'Recorda la contrasenya entre sessions (per un màxim de $1 {{PLURAL:$1|dia|dies}})',
-'userlogin-remembermypassword' => "Recorda'm",
+'userlogin-remembermypassword' => 'Mantén-me connectat',
'userlogin-signwithsecure' => 'Connexió segura',
'securelogin-stick-https' => "Roman connectat via HTTPS desprès d'autenticar-se",
'yourdomainname' => 'El vostre domini',
'createacct-imgcaptcha-ph' => 'Introduïu el text que apareix a dalt',
'createacct-submit' => 'Crea el meu compte',
'createacct-benefit-heading' => '{{SITENAME}} és feta per gent com tu.',
-'createacct-benefit-body1' => 'edicions',
-'createacct-benefit-body2' => 'pàgines',
-'createacct-benefit-body3' => 'col·laboradors aquest mes',
+'createacct-benefit-body1' => '{{PLURAL:$1|edició|edicions}}',
+'createacct-benefit-body2' => '{{PLURAL:$1|pàgina|pàgines}}',
+'createacct-benefit-body3' => '{{PLURAL:$1|col·laborador recent|col·laboradors recents}}',
'badretype' => 'Les contrasenyes que heu introduït no coincideixen.',
'userexists' => 'El nom que heu entrat ja és en ús.
Escolliu-ne un de diferent.',
'invalid-content-data' => 'Dades de contingut no vàlides',
'content-not-allowed-here' => 'No és permés el contingut "$1" a la pàgina [[$2]]',
'editwarning-warning' => "Si sortiu d'aquesta pàgina perdreu tots els canvis que hàgiu fet.
-Si teniu un compte d'usuari, podeu eliminar aquest avís a la secció «{{int:prefs-editing}}» de les vostres preferències.",
+Si teniu un compte d'usuari, podeu eliminar aquest avís a la secció «Caixa d'edició» de les vostres preferències.",
# Content models
'content-model-wikitext' => 'wikitext',
'notvisiblerev' => 'La versió ha estat esborrada',
'watchnochange' => "No s'ha editat cap dels elements que vigileu en el període de temps que es mostra.",
'watchlist-details' => 'Teniu $1 {{PLURAL:$1|pàgina vigilada|pàgines vigilades}}, sense comptar les pàgines de discussió.',
-'wlheader-enotif' => "S'ha habilitat la notificació per correu electrònic.",
-'wlheader-showupdated' => "Les pàgines que s'han canviat des de la vostra darrera visita es mostren '''en negreta'''",
+'wlheader-enotif' => 'La notificació per correu electrònic està habilitada.',
+'wlheader-showupdated' => "Les pàgines que s'han canviat des de la vostra darrera visita es mostren en '''negreta'''.",
'watchmethod-recent' => "s'està comprovant si hi ha pàgines vigilades en les edicions recents",
'watchmethod-list' => "s'està comprovant si hi ha edicions recents en les pàgines vigilades",
'watchlistcontains' => 'La vostra llista de seguiment conté {{PLURAL:$1|una única pàgina|$1 pàgines}}.',
'logentry-move-move-noredirect' => '$1 ha mogut $3 a $4 sense deixar una redirecció',
'logentry-move-move_redir' => '$1 ha mogut $3 a $4 sobre una redirecció',
'logentry-move-move_redir-noredirect' => '$1 {{GENDER:$2|ha desplaçat}} la pàgina $3 a $4 on hi havia una redirecció i sense crear una nova redirecció',
-'logentry-patrol-patrol' => '1$ {{GENDER:$2|ha marcat}} la revisió $4 de la pàgina «$3» com a patrullada',
+'logentry-patrol-patrol' => '$1 {{GENDER:$2|ha marcat}} la revisió $4 de la pàgina «$3» com a patrullada',
'logentry-patrol-patrol-auto' => '$1 {{GENDER:$2|ha marcat}} automàticament la versió $4 de la pàgina $3 com a patrullada',
'logentry-newusers-newusers' => "El compte d'usuari $1 {{GENDER:$2|ha estat creat}}",
'logentry-newusers-create' => "El compte d'usuari $1 {{GENDER:$2|ha estat creat}}",
'userlogin-remembermypassword' => 'Дагахь латт ве со',
'yourdomainname' => 'Хьан машан меттиг:',
'login' => 'Чу валар/ялар',
-'nav-login-createaccount' => 'Вовзийта хьой / дlавазло',
+'nav-login-createaccount' => 'Вовзийта/Йовзийта хьой / дӀавазло/дӀаязло',
'loginprompt' => 'Ахьа бакъо йала йеза оцу «cookies» хьайна вовзийта лаахь гlирсан.',
'userlogin' => 'Чу валар/ялар я декъашхон дӀаяздар кхоллар',
-'userloginnocreate' => 'Ð\92овзийÑ\82а Ñ\85Ñ\8cой',
+'userloginnocreate' => 'ЧÑ\83валаÑ\80/Ñ\8fлаÑ\80',
'logout' => 'Аравалар/ялар',
-'userlogout' => 'Ара валар',
+'userlogout' => 'Аравалар/ялар',
'notloggedin' => 'Хьо вовзийта/йовзийта гlирсан',
'nologin' => "Хlинца дlа вазвина/язйина вац/яц? '''$1'''.",
'nologinlink' => 'Кхолла декъашхон дӀаяздар',
'createaccount' => 'Кхолла декъашхон дӀаяздар',
'gotaccount' => "Дlавазвелла вуй хьо? '''$1'''.",
-'gotaccountlink' => 'Ð\92овзийÑ\82а Ñ\85Ñ\8cой',
+'gotaccountlink' => 'ЧÑ\83валаÑ\80/Ñ\8fлаÑ\80',
'createaccountmail' => 'хааман зlене хула',
'createaccountreason' => 'Бахьан:',
'createacct-reason' => 'Бахьна',
'newarticletext' => "Хьо веана хьажоригци хlокху агlон тlе, хlара агlо хlинца йоцаш ю.
Нагахь иза кхолла лаахь, хlотта де лахо гуш долу корехь йоза (мадарра хьажа. [[{{MediaWiki:Helppage}}|гlон агlон чу]]).
Нагахь гlалат даьлла нисвелляхь кхузе, атта тlе тlаlа йе '''юха йоккхуриг''' хьай гlирса тlяхь.",
-'anontalkpagetext' => "----''Хlара дийцаре агIо къайлаха волу декъашхочуьна ю, хlинца дlавазвина воцуш, йа лелош воцуш.
-Цундела иза вовзийта лелош ду терахьца IP-долу метаг.
+'anontalkpagetext' => "----''Хlара дийцаре агIо къайлаха волу декъашхочуьна ю, хlинца дӀавазвина/дӀаязйина воцуш/йоцуш, я лелош воцуш/йоцуш.
+Цундела иза вовзийта/йовзийта лелош ду терахьца IP-долу метаг.
Иза терахь долу меттиг хила мега кхечу декъашхойчух терра.
-Нагахь хьо къайлах волу декъашхо валахь хьайна хаам кхаьчна аьлла хеташн, хьуна хьажийна доцуш, дехар до, кхолла хьай меттиг кху чохь[[Special:UserLogin/signup|дlавазло]] йа [[Special:UserLogin|хьой вовзийта]],",
+Нагахь хьо къайлах волу декъашхо валахь хьайна хаам кхаьчна аьлла хеташн, хьуна хьажийна доцуш, дехар до, кхолла хьай меттиг кху чохь[[Special:UserLogin/signup|дlавазло]] йа [[Special:UserLogin|хьой вовзийта/йовзийта]],",
'noarticletext' => "Хlокх хан чохь кху агlонца йоза дац.
Шуьга далундерг [[Special:Search/{{PAGENAME}}|лахар ишта хьехош йолу цlе]] кхечу яззамашкахь,
<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} лахар кхечу тептаршкахь],
Шуьга далундерг [[Special:Search/{{PAGENAME}}|лахар ишта хьехош йолу цӀе]] кхечу яззамашкахь,
йа <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} лаха оцуьнах терадерг кхечу тептаршкахь].</span>',
'userpage-userdoesnotexist' => 'Ишта дlайазвар «<nowiki>$1</nowiki>» хlинца дац. Хьажа билгал, хьуна бакъалла лаьи кхолла йа хийцам ба хlокху агlон.',
+'sitecsspreview' => "'''ХӀара хьалх хьажар ду, хӀара CSS.'''
+'''хӀинца Ӏалашдина дац!'''",
+'sitejspreview' => "'''ХӀара хьалх хьажар ду, хӀара JavaScript-ишар.'''
+'''хӀинца Ӏалашдина дац!'''",
'updated' => '(Карла йаькхина)',
'note' => "'''Билгалдаккхар:'''",
'previewnote' => "'''Хlара хьалх хьажар ду, йоза хlинца язданза ду!'''",
* <strong class="mw-specialpagerestricted">Кlеззиг таронаш йолу леррина агlонаш.</strong>',
'specialpages-group-maintenance' => 'Жамlаш гlирса хьашташ кхочушдар',
'specialpages-group-other' => 'Кхин гlуллакхан агlонаш',
-'specialpages-group-login' => 'Хьой вовзийта / Дlавазло',
+'specialpages-group-login' => 'Хьой вовзийта/йовзийта / дӀавазло/дӀаязло',
'specialpages-group-changes' => 'Керла нисдарш а тéптарш',
'specialpages-group-media' => 'Жамlаш оцу медиа-гlирсашан а чуяхарш',
'specialpages-group-users' => 'Декъашхой а бакъонаш',
# Special:PasswordReset
'passwordreset' => 'Ailosod cyfrinair',
+'passwordreset-text-one' => 'Cwblhewch y ffurflen hon er mwyn ailosod eich cyfrinair.',
+'passwordreset-text-many' => "{{PLURAL:$1|Rhowch un o'r dewis o fanylion derbyniol er mwyn ailosod eich cyfrinair.}}",
'passwordreset-legend' => 'Ailosod y cyfrinair',
'passwordreset-disabled' => 'Analluogwyd ailosod cyfrineiriau ar y wici hwn.',
'passwordreset-emaildisabled' => 'Analluogwyd offer e-bost ar y wici hwn.',
Ymddengys iddi gael ei dileu.",
'edit-conflict' => 'Cyd-ddigwyddiad golygu.',
'edit-no-change' => 'Anwybyddwyd eich golygiad, gan na newidiwyd y testun.',
+'postedit-confirmation' => 'Rhoddwyd eich golygiad ar gadw.',
'edit-already-exists' => 'Ni ellid creu tudalen newydd.
Mae ar gael yn barod.',
'defaultmessagetext' => 'Y testun rhagosodedig',
# Special:PasswordReset
'passwordreset' => 'Nulstil adgangskode',
-'passwordreset-text' => 'Udfyld denne formular for at nulstille din adgangskode.',
'passwordreset-legend' => 'Nulstil adgangskode',
'passwordreset-disabled' => 'Nulstilling af kodeord er slået fra på denne wiki.',
'passwordreset-emaildisabled' => 'E-mailfunktioner er slået fra på denne wiki.',
-'passwordreset-pretext' => '{{PLURAL:$1||Indtast en af de nedenstående oplysninger}}',
'passwordreset-username' => 'Brugernavn:',
'passwordreset-domain' => 'Domæne:',
'passwordreset-capture' => 'Se den resulterende email?',
'img_center' => array( 1, 'center', 'centre' ),
'img_framed' => array( 1, 'framed', 'enframed', 'frame' ),
'img_frameless' => array( 1, 'frameless' ),
+ 'img_lang' => array( 1, 'lang=$1' ),
'img_page' => array( 1, 'page=$1', 'page $1' ),
'img_upright' => array( 1, 'upright', 'upright=$1', 'upright $1' ),
'img_border' => array( 1, 'border' ),
'create-this-page' => 'Create this page',
'delete' => 'Delete',
'deletethispage' => 'Delete this page',
+'undeletethispage' => 'Undelete this page',
'undelete_short' => 'Undelete {{PLURAL:$1|one edit|$1 edits}}',
'viewdeleted_short' => 'View {{PLURAL:$1|one deleted edit|$1 deleted edits}}',
'protect' => 'Protect',
'views' => 'Uunsichten',
'toolbox' => 'Werktjüügen',
'userpage' => 'Brükersidj uunwise',
-'projectpage' => 'Prujäktsid wise',
+'projectpage' => 'Projektsidj wise',
'imagepage' => 'Dateisidj uunwise',
'mediawikipage' => 'Mädialangssidj uunwise',
'templatepage' => 'Föörlaagensidj uunwise',
'disclaimers' => 'Disclaimers',
'disclaimerpage' => 'Project:Disclaimers',
'edithelp' => "Halep bi't bewerkin",
-'edithelppage' => 'Help:Beårbe',
+'edithelppage' => 'Help:Bewerke',
'helppage' => 'Help:Auersicht',
'mainpage' => 'Hoodsidj',
'mainpage-description' => 'Hoodsidj',
'newmessageslinkplural' => '{{PLURAL:$1|ian nei nooracht|nei noorachten}}',
'newmessagesdifflinkplural' => 'leetst {{PLURAL:$1|feranrang|feranrangen}}',
'youhavenewmessagesmulti' => 'Dü heest nei bööd üüb $1',
-'editsection' => 'Beårbe',
+'editsection' => 'Bewerke',
'editsection-brackets' => '[$1]',
-'editold' => 'Beårbe',
+'editold' => 'Bewerke',
'viewsourceold' => 'kweltekst uunwise',
'editlink' => 'bewerke',
'viewsourcelink' => 'kweltekst uunwise',
-'editsectionhint' => 'Säksjoon beårbe: $1',
+'editsectionhint' => 'Kirew bewerke: $1',
'toc' => 'Inhåltsfertiiknis',
'showtoc' => 'Wise',
'hidetoc' => 'Ferbärje',
'internalerror_info' => 'Süsteemfeeler: $1',
'fileappenderrorread' => '"$1" köö wilert dåt baitufäigen ai leesen wårde.',
'fileappenderror' => 'Köö ai "$1" tu "$2" önjhinge.',
-'filecopyerror' => 'Jü dootäi "$1" köö ai eefter "$2" kopiiird wårde.',
-'filerenameerror' => 'Jü dootäi "$1" köö ai eefter "$2" amnååmd wårde.',
-'filedeleteerror' => 'Jü dootäi "$1" köö ai straagen wårde.',
+'filecopyerror' => 'Det datei $1 küd ei efter $2 kopiaret wurd.',
+'filerenameerror' => 'Det datei $1 küd ei efter $2 amnäämd wurd.',
+'filedeleteerror' => 'Det datei $1 küd ei stregen wurd.',
'directorycreateerror' => 'Dåt fertiiknis "$1" köö ai mååged wårde.',
-'filenotfound' => 'Köö dootäi "$1" ai fine.',
-'fileexistserror' => 'Ai möölik, eefter dootäi "$1" tu schriwen: dootäi bestoont ål',
+'filenotfound' => 'Det datei $1 küd ei fünjen wurd.',
+'fileexistserror' => 'Uun det datei "$1" küd ei skrewen wurd: Hat as al diar.',
'unexpected' => 'Ünfermousene wjart: "$1"="$2".',
'formerror' => 'Fäägel: köö jü form ai lääwere',
'badarticleerror' => 'Jüdeer aksjoon koon ai aw jüdeer sid mååged wårde.',
-'cannotdelete' => 'Jü sid unti dootäi "$1" köö ai straagen wårde.
-Dåt as flicht ål straagen foon huum ouders.',
+'cannotdelete' => 'Det sidj of datei "$1" küd ei stregen wurd.
+Det as ferlicht al faan hoker ööders stregen wurden.',
'cannotdelete-title' => 'Sidj „$1“ koon ei stregen wurd.',
'delete-hook-aborted' => 'Det striken as faan en software-feranerang faan MediaWiki ferhanert wurden. Di grünj as ei bekäänd.',
'badtitle' => 'Ferkiard tiitel',
'login-throttled' => 'Dü heest tu oofding fersoocht, di önjtumälden.
Wees sü gödj än täif, bit dü wider ferseechst.',
'login-abort-generic' => 'Din önjmälding wus ei erfolchrik - Oufbräägen',
-'loginlanguagelabel' => 'Spräke: $1',
+'loginlanguagelabel' => 'Spriak: $1',
'suspicious-userlogout' => 'Dan Oufmäldönjfrååge wörd ferwaigred, deer ja fermouslik foon en defäkte browser unti en cache-proxy sånd wörd.',
# Email sending
Dåt koon ferschääwen unti wächhååld weese, wilt dü jü sid bekiikedest.',
'loginreqtitle' => 'Dü skel di uunmelde.',
'loginreqlink' => 'Uunmelde',
-'loginreqpagetext' => 'Dü möist $1 am oudere side tu bekiiken.',
+'loginreqpagetext' => 'Dü skel di $1, am ööder sidjen uuntulukin.',
'accmailtitle' => 'Pååsuurd sånd.',
'accmailtext' => "En tufäli generiird pååsuurd for [[User talk:$1|$1]] wörd tu $2 fersånd.
'''Dåt as nuch ai spiikerd!'''",
'sitejspreview' => "'''Påås aw dåt dü jüdeer JavaScript code bloot forbekiikest.'''
'''Dåt as nuch ai spiikerd!'''",
-'userinvalidcssjstitle' => "''Woorschauing:''' Skin \"\$1\"jeeft dåt ai. Betånk, dåt brükerspetsiifische .css- än .js-side ma en latj bökstääw önjfånge mönje, ålsü biispelswise ''{{ns:user}}:Münsterkjarl/vector.css'' önj stää foon ''{{ns:user}}:Münsterkjarl/Vector.css''.",
+'userinvalidcssjstitle' => "''Paase üüb:''' Skak \"\$1\" jaft at ei.
+Seenk diaram, dat faan en brüker iinracht .css- an .js-sidjen mä en letjen buksteew began skel. Bispal:
+''{{ns:user}}:Münsterkjarl/vector.css'' uunsteed faan ''{{ns:user}}:Münsterkjarl/Vector.css''.",
'updated' => '(Änred)',
'note' => "'''Påås aw:'''",
'previewnote' => "'''Heer könst dü sä, hü det sidj wurd skal.'''
Det komt flooksis föör, wan Dan anonym Proxy-siinst ei rocht werket.",
'edit_form_incomplete' => "'''Enkelt dialen faan det formulaar san ei rocht uunkimen.'''
Wees so gud an kontroliare ales noch ans.",
-'editing' => 'Beårbe foon $1',
+'editing' => 'Bewerkin faan $1',
'creating' => 'Maage $1',
'editingsection' => 'Beårben foon $1 (oufsnaas)',
'editingcomment' => 'Beårben foon $1 (naien oufsnaas)',
Bluas wat '''boowen''' stäänt, woort seekert. Diaram kopiare din feranrangen boowen iin.
An do trak „{{int:savearticle}}“.",
'yourtext' => 'Dan tekst',
-'storedversion' => 'Spiikerd färsjoon',
-'nonunicodebrowser' => "'''Påås aw:''' Dan browser koon unicode-tiikne ai rucht ferårbe. Brük hål en oudern browser am side tu ferårben.",
+'storedversion' => 'Seekert werjuun',
+'nonunicodebrowser' => "'''Paase üüb:''' Dan browser komt ei mä unicode-tiakens turocht. Wees so gud an brük en öödern browser.",
'editingold' => "'''Paase üüb: Dü bewerkest en ual werjuun faan detdiar sidj.
Wan dü det seekerst, wurd aal a nei werjuunen auerskrewen.'''",
'yourdiff' => 'Ferskeeler',
'titleprotectedwarning' => "'''Paase üüb: \"Detdiar sidj mä didiar nööm koon ei faan arken bewerket wurd. Bluas enkelt brükern mä [[Special:ListGroupRights|was brükerrochten]] kön detdiar sidj nei maage of bewerke.'''
Uun't logbuk stäänt muar diartu:",
'templatesused' => '{{PLURAL:$1|Jü füliend forlååg wårt|Da füliende forlååge wårde}} foon jüdeer sid ferwånd:',
-'templatesusedpreview' => '{{PLURAL:$1|Jü füliend forlååg wårt|Da füliende forlååge wårde}} foon diheere sideforlök ferwånd:',
+'templatesusedpreview' => '{{PLURAL:$1|Detdiar föörlaag woort|Jodiar föörlaagen wurd}} uun detdiar sidjenföörskau brükt:',
'templatesusedsection' => '{{PLURAL:$1|Jü füliend forlååg wårt|Da füliende forlååge wårde}} foon dideer oufsnaas ferwånd:',
'template-protected' => '(seekert)',
'template-semiprotected' => '(hualew-seekert)',
'hiddencategories' => 'Jüdeer sid as lasmoot foon {{PLURAL:$1|1 ferstäägen kategorii|$1 ferstäägene kategoriie}}:',
-'nocreatetext' => 'Aw {{SITENAME}} wörd dåt måågen foon naie side begränsed.
-Dü koost bestönjene side änre unti de [[Special:UserLogin|önjmälde unti mååg en account]].',
-'nocreate-loggedin' => 'Dü heest niinj beruchtiging, naie side tu måågen.',
+'nocreatetext' => 'Üüb {{SITENAME}} könst dü nian nei sidjen maage, oober dü könst sidjen feranre. [[Special:UserLogin|Melde di uun of racht en brükerkonto iin]].',
+'nocreate-loggedin' => 'Dü mutst nian nei sidjen maage.',
'sectioneditnotsupported-title' => 'Jü beårbing foon oufsnaase wårt ai unerstüted',
'sectioneditnotsupported-text' => 'Jü beårbing foon oufsnaase wårt aw jüdeer beårbingssid ai stiped.',
'permissionserrors' => 'Beruchtigingsfäägel',
'expensive-parserfunction-warning' => 'Woorschauing: Jüdeer sid önjthålt tu fool apteele foon widluftie parserfunksjoone.
Deer {{PLURAL:$2|mötj ai mör ås 1 apteel|mönje ai mör ås $1 apteele}} weese.',
-'expensive-parserfunction-category' => 'Side, da widluftie parserfunksjoone tu oofding apteele',
+'expensive-parserfunction-category' => 'Sidjen mä tuföl parser-funktjuunen.',
'post-expand-template-inclusion-warning' => "'''Woorschauing:''' Jü grutelse foon da önjbünene forlååge as tu grut, hu forlååge koone ai önjbünen wårde.",
-'post-expand-template-inclusion-category' => 'Side, önj da jü maksimoole grutelse foon önjbünene forlååge ouerschran as',
+'post-expand-template-inclusion-category' => 'Sidjen mä muar üs det maksimaal taal faan iinbünjen föörlaagen.',
'post-expand-template-argument-warning' => 'Påås aw: Jüdeer sid enthålt tumanst en argumänt önj en forlååge, dåt äkspandiird tu grut as. Dadeere argumänte wårde ignoriird.',
-'post-expand-template-argument-category' => 'Side, da ignoriirde forlååge-argumänte önjthüülje',
+'post-expand-template-argument-category' => 'Sidjen mä föörlaagen, huar ei arke iindrach brükt wurd koon.',
'parser-template-loop-warning' => 'Forlåågesloif önjtdäkt: [[$1]]',
'parser-template-recursion-depth-warning' => 'Forloagerekursjoonsdiipgränse ouerschran ($1)',
-'language-converter-depth-warning' => 'Spräkekonwärsjoonsdiipdegränse ouerschren ($1)',
+'language-converter-depth-warning' => 'Spriakenkonwerter auerläästet ($1)',
'node-count-exceeded-category' => 'Jodiar sidjen haa tuföl ferbinjangen (nodes)',
'node-count-exceeded-warning' => 'Detdiar sidj hää tuföl ferbinjangen (nodes)',
'expansion-depth-exceeded-category' => 'Jodiar sidjen haa tuföl ütjwidjangen (expansion)',
# History pages
'viewpagelogs' => 'Logbuken faan detdiar sidj uunwise',
'nohistory' => 'Deer as niinj beårbingshistoori for jüdeer sid.',
-'currentrev' => 'Aktäle färsjoon.',
-'currentrev-asof' => 'Aktuäl färsjoon foon $2 am e klook $3',
-'revisionasof' => 'Färsjoon foon e klook $2, $3',
-'revision-info' => 'Färsjoon foon di $4 am e klook $5 foon $2',
-'previousrevision' => '← Näistålere färsjoon',
-'nextrevision' => 'Näistjunger färsjoon →',
-'currentrevisionlink' => 'Aktuäle färsjoon',
+'currentrev' => 'Leetst werjuun',
+'currentrev-asof' => 'Leetst werjuun faan di $2, am e klook $3',
+'revisionasof' => 'Werjuun faan $2, klook $3',
+'revision-info' => 'Werjuun faan di $4 am a klook $5 faan $2',
+'previousrevision' => '← Naistääler werjuun',
+'nextrevision' => 'Naistjonger werjuun →',
+'currentrevisionlink' => 'Leetst werjuun',
'cur' => 'aktuäl',
'next' => 'näist',
'last' => 'leest',
'history-feed-title' => 'Färsjoonshistoori',
'history-feed-description' => 'Färsjoonshistoori for jüdeer sid önj {{SITENAME}}',
'history-feed-item-nocomment' => '$1 bit $2',
-'history-feed-empty' => 'Jü önjfordied sid bestoont ai. Flicht wörd jü sleeked unti ferschääwen. [[Special:Search|Döörsäk]] {{SITENAME}} aw pååsende naie side.',
+'history-feed-empty' => 'Son sidj jaft at ei. Ferlicht as det stregen of fersköwen wurden. [[Special:Search|Schük]] üüb {{SITENAME}} efter nei sidjen, diar diartu paase.',
# Revision deletion
'rev-deleted-comment' => '(Tuhuupefooting wächnümen)',
'rev-deleted-user' => '(Brükernoome wächhååld)',
'rev-deleted-event' => '(Logbökaksjoon wächhååld)',
'rev-deleted-user-contribs' => '[Benjüternoome unti IP-adräs wächhååld - beårbing üt baidråge ferstäägen]',
-'rev-deleted-text-permission' => "Judeer Färsjoon wörd '''straagen'''.
-Näre önjgoowen tu di strikforgung ås uk en begrüning fant huum önj dåt [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbök].",
-'rev-deleted-text-unhide' => "Jüdeer färsjoon as '''straagen''' wörden.
-Ainkelthäide fant huum önj dåt [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbök].
-Dü koost nuch [$1 jüdeer färsjoon bekiike], wan dü wider gunge mååst.",
-'rev-suppressed-text-unhide' => "Jüdeer färsjoon as '''unerdrükt''' wörden.
-Ainkelthäide fant huum önj dåt [{{fullurl:{{#special:Log}}/suppress|page={{FULLPAGENAMEE}}}} strik-logbök].
-Dü koost nuch [$1 jüdeer färsjoon bekiike], wan dü wider gunge mååst.",
-'rev-deleted-text-view' => "Jüdeer Färsjoon as '''straagen''' wörden.
-Dü koost da wider önjkiike. Näre önjgoowen tu di sleekforgung ås uk en begrüning fant huum önj dåt [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbök].",
-'rev-suppressed-text-view' => "Jüdeer färsjoon as '''unerdrükd''' wörden.
-Dü koost da önjkiike. Ainkelthäide stönje önj dåt [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} unerdrukings-logbök].",
+'rev-deleted-text-permission' => "Detdiar werjuun as '''stregen''' wurden.
+Uun't [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbuk] stäänt muar diartu.",
+'rev-deleted-text-unhide' => "Detdiar werjuun as '''stregen''' wurden.
+Uun't [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbuk] stäänt muar diartu.
+Dü könst [$1 detdiar werjuun uunluke], wan dü wel.",
+'rev-suppressed-text-unhide' => "Detdiar werjuun as '''ferbürgen''' wurden.
+Uun't [{{fullurl:{{#special:Log}}/suppress|page={{FULLPAGENAMEE}}}} fersteeg-logbuk] stäänt muar diartu.
+Dü könst [$1 detdiar werjuun uunluke], wan dü wel.",
+'rev-deleted-text-view' => "Detdiar werjuun as '''stregen''' wurden.
+Dü könst det noch uunluke. Uun't [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbuk] stäänt muar diartu.",
+'rev-suppressed-text-view' => "Detdiar werjuun as '''ferbürgen''' wurden.
+Dü könst det noch uunluke. Uun't [{{fullurl:{{#special:Log}}/suppress|page={{FULLPAGENAMEE}}}} fersteeg-logbuk] stäänt muar diartu.",
'rev-deleted-no-diff' => "Dü könst di ferskeel ei uunluke, auer ian werjuun '''stregen''' wurden as.
Wan dü muar wed wel, luke iin uun't [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbuk].",
'rev-suppressed-no-diff' => "Dü könst di ferskeel ei uunluke, auer ian werjuun '''stregen''' wurden as.",
Dü könst [$1 di ferskeel uunluke], wan dü wel.",
'rev-suppressed-unhide-diff' => "Ian faan jodiar werjuunen as '''ferbürgen''' wurden. Wan dü muar wed wel, luke iin uun't [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} fersteeg-logbuk].
Dü könst [$1 di ferskeel uunluke], wan dü wel.",
-'rev-deleted-diff-view' => "En Färsjoon foon dåtdeer färsjoonsferschääl wörd '''straagen'''.
-Dü koost dåt färsjoonsferschääl siinj.
-Näre önjgoowen fant huum önj dåt [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbök].",
+'rev-deleted-diff-view' => "Ian faan jodiar werjuunen as '''stregen''' wurden.
+Dü könst di ferskeel uunluke. Wan dü muar wed wel, luke iin uun't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbuk].",
'rev-suppressed-diff-view' => "Ian faan jodiar werjuunen as '''ferbürgen''' wurden. Dü könst di ferskeel uunluke. Wan dü muar wed wel, luke iin uun't [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} fersteeg-logbuk].",
'rev-delundel' => 'wis/fersteeg',
'rev-showdeleted' => 'wise',
'revisiondelete' => 'Werjuunen strik of weder iinstel',
-'revdelete-nooldid-title' => 'Niinj färsjoon önjjääwen',
+'revdelete-nooldid-title' => 'Nian werjuun uunden',
'revdelete-nooldid-text' => 'Dü heest nian werjuun för detheer aktjuun uunden, of det werjuun jaft at ei, of dü ferschükst, en aktuel werjuun tu striken.',
'revdelete-nologtype-title' => 'Niinj logtüüp önjjääwen',
'revdelete-nologtype-text' => 'Deer wörd niinj logtüüp for jüheer aksjoon önjjääwen.',
'revdelete-nologid-title' => 'Üngülti logönjdråch',
'revdelete-nologid-text' => 'Deer wör niinj logtüüp ütkiisd unti di kiisde logtüüp bestoont ai.',
-'revdelete-no-file' => 'Jü önjjääwen dootäi bestoont ai.',
-'revdelete-show-file-confirm' => 'Bast dü sääker, dåt de jü sleeked färsjoon foon e dootäi „<nowiki>$1</nowiki>“ foon e $2 am e klook $3 önjsiinj wäät?',
+'revdelete-no-file' => 'Son dateinööm as ei diar.',
+'revdelete-show-file-confirm' => 'Wel dü würelk det stregen werjuun faan det datei „<nowiki>$1</nowiki>“ faan di $2, am a klook $3 uunluke?',
'revdelete-show-file-submit' => 'Jåå',
'revdelete-selected' => "'''{{PLURAL:$2|Ütjsoocht werjuun|Ütjsoocht werjuunen}} faan [[:$1]]:'''",
'logdelete-selected' => "'''{{PLURAL:$1|Ütjsoocht logbukiindrach|Ütjsoocht logbukiindracher}}:'''",
* Persöönelk informatsjuunen, diar näämen wat uungung
*: ''Adresen, Tilefoonnumern, Ferseekerangsnumern an sowat''",
'revdelete-legend' => 'Seeten foon da sachtboorhäids-gränse',
-'revdelete-hide-text' => 'Täkst foon e färsjoon fersteege',
-'revdelete-hide-image' => 'Fersteege wat önj e Dootäi stoont',
+'revdelete-hide-text' => 'Tekst faan det werjuun fersteeg',
+'revdelete-hide-image' => 'Fersteeg, wat uun det datei stäänt',
'revdelete-hide-name' => 'Logbök-aksjoon fersteege',
'revdelete-hide-comment' => 'Beårbingskomäntoor fersteege',
'revdelete-hide-user' => 'Benjüternoome//IP-adräse foon e beårber fersteege',
** Fülk informatsjuunen",
'revdelete-otherreason' => 'Ouderen/tubaikaamenden grün:',
'revdelete-reasonotherlist' => 'Ouderen grün',
-'revdelete-edit-reasonlist' => 'Strikgrüne beårbe',
+'revdelete-edit-reasonlist' => "Grünjer för't striken bewerke",
'revdelete-offender' => 'Autoor foon jüdeer färsjoon:',
# Suppression log
'mergehistory' => 'Fersjoonshistoorie feriine',
'mergehistory-header' => 'Ma jüdeer spetsjåålsid koost dü jü färsjoonshistoori foon en jurtkamstsid ma jü färsjoonshistoori foon en müüljsid feriine.
Stal sääker, dåt jü färsjoonshistoori foon en sid histoorisch koräkt as.',
-'mergehistory-box' => 'Rewisjoone foon tou side feriine:',
+'mergehistory-box' => 'Ferluup faan tau sidjen tuupfeer:',
'mergehistory-from' => 'Jurtkamstsid:',
'mergehistory-into' => 'Müüljsid:',
'mergehistory-list' => 'Werjuunen, diar tuupfeerd wurd kön.',
'searchresults' => 'Fünjen sidjen',
'searchresults-title' => 'Fünjen sidjen för „$1“',
'searchresulttext' => "Wan dü muar auer't schüken uun {{SITENAME}} wed wel, luke efter bi [[{{MediaWiki:Helppage}}|{{int:help}}]].",
-'searchsubtitle' => 'Din säkönjfrååg: „[[:$1|$1]]“ ([[Special:Prefixindex/$1|åle ma „$1“ beganende side]]{{int:pipe-separator}}[[Special:WhatLinksHere/$1|åle side, da eefter „$1“ ferlinke]])',
+'searchsubtitle' => 'Din uunfraag: „[[:$1|$1]]“ ([[Special:Prefixindex/$1|sidjen, diar mä „$1“ began]]{{int:pipe-separator}}[[Special:WhatLinksHere/$1|sidjen, diar efter „$1“ ferwise]])',
'searchsubtitleinvalid' => 'Din säkönjfrååg: "$1".',
'toomanymatches' => 'Diar kaam tuföl resultooten üüb din uunfraag. Fersjük det ööders.',
-'titlematches' => 'Oueriinjstiminge ma sidetiitle',
-'notitlematches' => 'Niinj oueriinjstiming ma sidetiitle',
+'titlematches' => 'Auerianstemangen mä sidjennöömer',
+'notitlematches' => 'Nian auerianstemangen mä sidjennöömer',
'textmatches' => 'Oueriinjstiminge ma inhålte',
'notextmatches' => 'Niinj oueriinjstiming ma inhålte',
'prevn' => '{{PLURAL:$1|leesten|leeste $1}}',
'showingresults' => "Heer {{PLURAL:$1|as '''1''' resultoot|san '''$1''' resultoote}}, beganend ma numer '''$2.'''",
'showingresultsnum' => "Heer {{PLURAL:$3|as '''1''' resultoot|san '''$3''' resultoote}}, beganend ma numer '''$2.'''",
'showingresultsheader' => "{{PLURAL:$5|resultoot '''$1''' foon '''$3'''|resultoote '''$1-$2''' foon '''$3'''}}, for '''$4.'''",
-'nonefound' => "'''Haanewising:''' Deer wårde ståndardmääsi man ainkelde noomerüme döörsoocht. Seet ''all:'' for din Säkbegrip, am åle side (inkl. diskusjoonside, forlååge, äsw.) tu döörsäken unti gesiilt di noome foon di tu döörsäkende noomerüm.",
+'nonefound' => "'''Paase üüb:''' Diar wurd man enkelt nöömrümer trochsoocht. Wan dü ''all:'' föör din wurd skraft, do woort uk uun aal a nöömrümer (datein, kategoriin, föörlaagen asw.) soocht. Dü könst uk en wasen nöömrüm föörwechstel.",
'search-nonefound' => 'For din säkanfrååg würden niinj resultoote fünen.',
'powersearch' => 'Ütjwidjet schüken',
'powersearch-legend' => 'Ütjwidjet schüken',
'prefs-memberingroups' => '{{GENDER:$2|Lasmoot}} faan {{PLURAL:$1|brükerskööl|brükersköölen}}:',
'prefs-registration' => 'Uunmelde-tidj',
'yourrealname' => 'Rocht nööm:',
-'yourlanguage' => 'Spräke:',
+'yourlanguage' => 'Spriak:',
'yourvariant' => 'Spriak:',
'prefs-help-variant' => 'Uun hün skriiwwiis skel a sidjen uunwiset wurd:',
'yournick' => 'Nai signatuur:',
# Associated actions - in the sentence "You do not have permission to X"
'action-read' => 'jüdeer sid leese',
-'action-edit' => 'jüdeer sid beårbe',
-'action-createpage' => 'side mååge',
+'action-edit' => 'detdiar sidj tu bewerkin',
+'action-createpage' => 'nei sidjen tu maagin',
'action-createtalk' => 'diskusjuunssidjen maage',
'action-createaccount' => 'jüdeer brükerkonto mååge',
'action-minoredit' => 'detdiar feranrang üs letj kääntiakne',
'rcshowhidebots' => 'Bots $1',
'rcshowhideliu' => '$1 uunmeldet brükern',
'rcshowhideanons' => 'Anonymen brüker $1',
-'rcshowhidepatr' => '$1 efterluket feranrangen',
+'rcshowhidepatr' => 'Efterluket feranrangen $1',
'rcshowhidemine' => 'Min bidracher $1',
'rclinks' => 'Wis da leeste $1 änringe foon da leeste $2 deege.<br />$3',
'diff' => 'ferskeel',
'recentchangeslinked-noresult' => 'Uun detdiar tidj san jo ferlinket sidjen ei feranert wurden.',
'recentchangeslinked-summary' => "Detdiar spezial-sidj wiset a leetst feranrangen faan ferwiset sidjen (of faan sidjen uun en was kategorii). Sidjen, diar dü [[Special:Watchlist|uun't uug behual]] wel, san '''fäät''' skrewen.",
'recentchangeslinked-page' => 'Sid:',
-'recentchangeslinked-to' => 'Wis änringe aw side, da heerjurt ferlinke',
+'recentchangeslinked-to' => 'Wise feranrangen üüb sidjen, diar heerhen ferwise.',
# Upload
'upload' => 'Datei huuchschüür',
'reuploaddesc' => "Ufbreeg an turag tu't sidj för't huuchsjüüren",
'upload-tryagain' => 'Feranert dateibeskriiwang ufsjüür',
'uploadnologin' => 'Ei uunmeldet',
-'uploadnologintext' => 'Dü möist [[Special:UserLogin|önjmälded weese]], am dat dü dootäie huuchsiinje koost.',
+'uploadnologintext' => 'Dü skel [[Special:UserLogin|uunmeldet]] wees, amdat dü datein huuchschüür könst.',
'upload_directory_missing' => 'Dåt aplees-fertiiknis ($1) breecht än köö ai foon di wäbsärwer mååged wårde.',
'upload_directory_read_only' => 'Dåt aplees-fertiiknis ($1) koon ai foon e wäbsärver beschraawen wårde.',
'uploaderror' => 'Aplees-fäägel',
# File description page
'file-anchor-link' => 'Datei',
'filehist' => 'Dååtäifärsjoone',
-'filehist-help' => 'Klik aw en tidpunkt, am jüdeer färsjoon önjiinjtunaamen.',
+'filehist-help' => 'Klike üüb en tidjponkt, am detdiar werjuun uuntulukin.',
'filehist-deleteall' => 'aal a werjuunen strik',
'filehist-deleteone' => 'jüdeer färsjoon strike',
'filehist-revert' => 'tubääg seete',
'filehist-current' => 'aktuäl',
-'filehist-datetime' => 'Färsjoon foon e',
+'filehist-datetime' => 'Werjuun faan di',
'filehist-thumb' => 'Forlökbil',
-'filehist-thumbtext' => 'Forlökbil for Färsjoon foon $2, am e klook $3',
+'filehist-thumbtext' => 'Föörskau för det werjuun faan $2, klook $3',
'filehist-nothumb' => 'Niinj forlökbil deer',
'filehist-user' => 'brüker',
'filehist-dimensions' => 'Mätje',
'filehist-comment' => 'Komentoor',
'filehist-missing' => 'Datei ei diar',
'imagelinks' => 'Hü det datei brükt woort',
-'linkstoimage' => '{{PLURAL:$1|Jü füliend sid ferwånt|Da füliende $1 side ferwiinje}} jüdeer dååtäi:',
+'linkstoimage' => '{{PLURAL:$1|Detdiar sidj brükt|Jodiar $1 sidjen brük}} detdiar datei:',
'linkstoimage-more' => 'Muar üs $1 {{PLURAL:$1|sidj ferwiset|sidjen ferwise}} üüb detdiar datei.
Det list wiset {{PLURAL:$1|at iarst ferwisang|a iarst $1 ferwisangen}} üüb detdiar datei.
Dü könst uk det [[Special:WhatLinksHere/$2|hial list]] uunluke.',
Ferlicht wel dü det [$2 beskriiwangssidj] feranre.',
'filepage-nofile' => 'En datei mä didiar nööm jaft at ei.',
'filepage-nofile-link' => 'En datei mä didiar nööm jaft at ei, man dü könst det [$1 huuchsjüür].',
-'uploadnewversion-linktext' => 'En nai färsjoon foon jüdeer dåtäi huuchsiinje',
+'uploadnewversion-linktext' => 'En nei werjuun faan detdiar datei huuchschüür',
'shared-repo-from' => 'foon $1',
'shared-repo' => 'en gemiansoom archiif',
'upload-disallowed-here' => 'Dü könst detdiar datei ei auerskriiw.',
'listusers-creationsort' => 'Sortiare efter dootem',
'usereditcount' => '{{PLURAL:$1|feranrang|$1 feranrangen}}',
'usercreated' => '{{GENDER:$3|Maaget}} di $1 am a klook $2',
-'newpages' => 'Naie side',
+'newpages' => 'Nei sidjen',
'newpages-username' => 'Brükernoome:',
'ancientpages' => 'Al loong ei muar bewerket sidjen',
'move' => 'Ferschüwe',
'alphaindexline' => '$1 bit $2',
'nextpage' => 'Näist sid ($1)',
'prevpage' => 'Leest sid ($1)',
-'allpagesfrom' => 'Side wise sunt:',
-'allpagesto' => 'Side wise bit:',
+'allpagesfrom' => 'Sidjen wise sant:',
+'allpagesto' => 'Sidjen wise bit:',
'allarticles' => 'Aal a sidjen',
'allinnamespace' => 'Aal a sidjen (nöömrüm: $1)',
'allnotinnamespace' => 'Aal a sidjen (saner nöömrüm $1)',
'removewatch' => "Ei muar uun't uug behual",
'removedwatchtext' => 'Jü sid „[[:$1]]“ wörd foon din [[Special:Watchlist|eefterkiiklist]] wächhååld.',
'watch' => 'Kiike eefter',
-'watchthispage' => 'Side eefterkiike',
+'watchthispage' => "Detdiar sidj uun't uug behual",
'unwatch' => 'ai mör eefter kiike',
'unwatchthispage' => "Ei muar uun't uug behual",
'notanarticle' => 'Nään artiikel',
'notvisiblerev' => 'Det werjuun faan en öödern brüker as stregen wurden.',
'watchnochange' => "A sidjen, diar dü uun't uug heest, san uun di uunwiset tidjrüm ei bewerket wurden.",
-'watchlist-details' => 'Dü kiikst eefter {{PLURAL:$1|1 sid|$1 side}}.',
+'watchlist-details' => "Dü heest {{PLURAL:$1|1 sidj|$1 sidjen}} uun't uug.",
'wlheader-enotif' => 'Di e-mail siinst as aktiif.',
'wlheader-showupdated' => "Nei feranert sidjen wurd '''fäät''' uunwiset.",
'watchmethod-recent' => "Leetst feranrangen faan sidjen, diar dü uun't uug heest",
'confirmdeletetext' => 'Dü wel en sidj mä aal sin werjuunen strik. Dü skel gudkään, dat dü witjst, wat dü dääst an dat din dun mä a [[{{MediaWiki:Policy-url}}|brükerreegeln]] auerian stemet.',
'actioncomplete' => 'Aksjoon beånd',
'actionfailed' => 'Diar ging wat skiaf',
-'deletedtext' => '„$1“ wörd tunintemååged. In e $2 fanst dü en list foon da tuleest tunintemåågede side.',
+'deletedtext' => "„$1“ as stregen wurden. Uun't $2 fanjst dü a sidjen, diar tuleetst stregen wurden san.",
'dellogpage' => 'Tunintemååg-Logbök',
'dellogpagetext' => 'Diar stun a leetst stregen sidjen an datein.',
'deletionlog' => "logbuk faan't striken",
Gung turag, an began faan föören.",
# Protect
-'protectlogpage' => 'Sideschütse-logbök',
+'protectlogpage' => 'Sidjenseekerangs-logbuk',
'protectlogtext' => 'Detheer as det logbuk mä seekert sidjen.
Üüb [[Special:ProtectedPages|detdiar list]] stun a seekert sidjen.',
'protectedarticle' => 'schütsed „[[$1]]“',
'protect-text' => 'Heer koost dü e schütsstatus for jü sid "$1" önjkiike än änre.',
'protect-locked-blocked' => "Dü könst det sidjenseekerang ei feranre, auer din brükerkonto speret as. So as det sidj '''„$1“:''' seekert wurden.",
'protect-locked-dblock' => "Det dootenbeenk as speret, det sidjenseekerang koon ei feranert wurd. So as det sidj '''„$1“:''' seekert wurden.",
-'protect-locked-access' => "Din brükerkonto ferfäiget ai ouer da nüsie ruchte tu jü änring foon e sideschüts. Heer san da aktuäle sideschütsönjstalinge fon jü sid '''„$1“:'''",
-'protect-cascadeon' => 'Jüdeer sid as nütutids diilj foon e kaskaadenspäre. Jü as önj {{PLURAL:$1|jü füliende sid|da füliende side}} önjbünen, huk döör jü kaskaadenspäropsjoon schütsed {{PLURAL:$1|as|san}}. Di sideschütsstatus koon for jüdeer sid änred wårde, dåtdeer heet ouers nån influs aw jü kaskaadenspäre:',
+'protect-locked-access' => "Dü heest ei det brükerrocht, am det sidjenseekerhaid tu feranrin.
+Det sidj '''„$1“:''' as sodenang seekert wurden:",
+'protect-cascadeon' => 'Detdiar sidj as auer en kaskaadensper seekert wurden. Hat as uun {{PLURAL:$1|detdiar seekert sidj|jodiar seekert sidjen}} iinbünjen.
+Dü könst det seekerhaid feranre, det feranert oober ei det seekerhaid faan jo ööder sidjen.',
'protect-default' => 'Arke brüker',
'protect-fallback' => 'Ferloof bluas för brükern mä "$1"-rochten.',
'protect-level-autoconfirmed' => 'Ferloof bluas för registriaret brükern.',
'tooltip-invert' => 'Saat diar en tiaken, am feranrangen faan sidjen uun didiar nöömrüm ei uuntuwisin.',
'namespace_association' => 'Ferbünjen nöömrüm',
'tooltip-namespace_association' => 'Saat diar en tiaken, am di ferbünjen nöömrüm of diskusjuunsnöömrüm mä iintubetjin.',
-'blanknamespace' => '(Side)',
+'blanknamespace' => '(Sidjen)',
# Contributions
'contributions' => '{{GENDER:$1|Brüker}} bidracher',
# What links here
'whatlinkshere' => 'Ferwisangen üüb detdiar sidj',
-'whatlinkshere-title' => 'Side, da aw "$1" ferlinke',
+'whatlinkshere-title' => 'Sidjen, diar üüb "$1" ferwise',
'whatlinkshere-page' => 'sid:',
-'linkshere' => "Da füliende side ferlinke aw '''„[[:$1]]“''':",
+'linkshere' => "Jodiar sidjen ferwise üüb '''„[[:$1]]“''':",
'nolinkshere' => 'Nian sidj ferwiset üüb [[:$1]]',
'nolinkshere-ns' => "Nian sidj ferwiset üüb '''„[[:$1]]“''' uun di ütjsoocht nöömrüm.",
'isredirect' => 'widerliidjingssid',
'file-exists-sharedrepo' => 'Didiar dateinööm woort al uun en gemiansoom archiif brükt. Wees so gud, an nem en öödern nööm.',
# Export
-'export' => 'Side äksportiire',
+'export' => 'Sidjen eksportiare',
'exporttext' => 'Mä detdiar spezial-sidj könst dü di tekst mä aal sin werjuunen tu en XML-datei eksportiare. Det nei datei koon do faan en ööder MediaWiki-Wiki [[Special:Import|importiaret]] wurd.
Skriiw a sidjennöömer iin uun det tekstfial (man bluas ään noom uun arke rä).
'thumbnail_image-missing' => 'Det datei as wel ei diar: $1',
# Special:Import
-'import' => 'Side importiire',
+'import' => 'Sidjen importiare',
'importinterwiki' => 'Transwiki import',
'import-interwiki-text' => "Schük en Wiki an en sidj tu importiarin ütj. A werjuunen an brükernöömer bliiw erhäälen.
Transwiki-import-aktjuunen wurd uun't [[Special:Log/import|Import-logbuk]] fäästhäälen.",
'tooltip-pt-mytalk' => 'Din diskusjuunssidj',
'tooltip-pt-anontalk' => 'Diskusjuun auer feranrangen faan detdiar IP-adres',
'tooltip-pt-preferences' => 'Äine önjstalinge',
-'tooltip-pt-watchlist' => 'List foon eefterkiikede side',
+'tooltip-pt-watchlist' => "Sidjen, diar dü uun't uug behual wel",
'tooltip-pt-mycontris' => 'List mä aanj bidracher',
'tooltip-pt-login' => 'Wan dü di uunmeldest, heest dü muar mögelkhaiden. Dü säärst det oober ei.',
'tooltip-pt-anonlogin' => 'Wan dü di uunmeldest, heest dü muar mögelkhaiden. Dü säärst det oober ei.',
'tooltip-pt-logout' => 'Ufmelde',
'tooltip-ca-talk' => 'Diskusjuun auer di artiikel',
-'tooltip-ca-edit' => 'Sid beårbe. Hål for dåt spikern jü forlökfunksjoon brüke.',
+'tooltip-ca-edit' => 'Sidj bewerke. Luke di det iarst ans uun, iar dü det seekerst.',
'tooltip-ca-addsection' => 'Nai oufsnaas begane',
'tooltip-ca-viewsource' => 'Jüdeer sid wårt uner ferbading hülen. Di kwältäkst koon önjkiiked wårde.',
'tooltip-ca-history' => 'Ääler werjuunen faan detdiar sidj',
'tooltip-n-recentchanges' => 'Leetst feranrangen faan {{SITENAME}}',
'tooltip-n-randompage' => 'Tufali sid',
'tooltip-n-help' => 'Heelpsid wise',
-'tooltip-t-whatlinkshere' => 'List foon ål da side, da heer jurt wise',
-'tooltip-t-recentchangeslinked' => 'Leest änringen bai side, da foon heer ferlinkd san',
+'tooltip-t-whatlinkshere' => 'Aal a sidjen, diar heerhen ferwise',
+'tooltip-t-recentchangeslinked' => 'Leetst feranrangen faan sidjen, huar faan heer üüb ferwiset woort',
'tooltip-feed-rss' => 'RSS-feed for jüdeer sid',
'tooltip-feed-atom' => 'Atom-feed for jüdeer sid',
'tooltip-t-contributions' => 'List mä bidracher faan didiar brüker uunluke',
'yesterday-at' => 'Jister am a klook $1',
# Bad image list
-'bad_image_list' => 'Formååt:
+'bad_image_list' => 'Formaat:
-Bloot rae, da ma en * begane, wårde ütwjarted. As jarste eefter dåt * mötj en link aw en ai wansched dååtäi stönje.
-Deeraw föliende sidelinke önj dåtseelwi ra definiire ütnååme, önj di kontäkst weerfoon jü dååtäi duch tu schüns kaame mötj.',
+Bluas räen, diar mä en * began, wurd mätääld.
+Det skal bääft di * began mä en ferwisang üüb en ferkiard datei.
+Ferwisangen uun det salew rä wurd üs ütjnoomen uunsen, huar det datei dach uunwiset wurd mut.',
# Metadata
'metadata' => 'Metadååte',
'exif-urgency-other' => 'Faan a brüker fäästlaanj prioriteet ($1)',
# External editor support
-'edit-externally' => 'Jüdeer dåtäi ma en äkstärn prugram beårbe',
+'edit-externally' => 'Detdiar datei mä en ekstern program bewerke',
'edit-externally-help' => '(Sii da [//www.mediawiki.org/wiki/Manual:External_editors Installationsanweisungen] for widere Informasjoon)',
# 'all' in various places, this might be different for inflected languages
# Watchlist editing tools
'watchlisttools-view' => 'Eefterkiiklist: änringe',
-'watchlisttools-edit' => 'normåål beårbe',
-'watchlisttools-raw' => 'Listeformoot beårbe (import/äksport)',
+'watchlisttools-edit' => 'Normool bewerke',
+'watchlisttools-raw' => "Uun't listenformoot bewerke",
# Signatures
'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|Diskusjuun]])',
# Special:PasswordReset
'passwordreset' => 'איפוס סיסמה',
+'passwordreset-text-one' => 'מלאו טופס זה כדי לאפס את הסיסמה.',
+'passwordreset-text-many' => '{{PLURAL:$1||הקלידו אחד מפריטי המידע כדי לאפס את הסיסמה.}}',
'passwordreset-legend' => 'איפוס סיסמה',
'passwordreset-disabled' => 'איפוסי סיסמה בוטלו באתר ויקי זה.',
'passwordreset-emaildisabled' => 'שירותי הדוא"ל בוטלו באתר ויקי זה.',
# Special:PasswordReset
'passwordreset' => 'Reinitialisar contrasigno',
+'passwordreset-text-one' => 'Completa iste formulario pro reinitialisar tu contrasigno.',
'passwordreset-legend' => 'Reinitialisar contrasigno',
'passwordreset-disabled' => 'Le reinitialisation de contrasignos ha essite disactivate in iste wiki.',
'passwordreset-emaildisabled' => 'Le functionalitate de e-mail ha essite disactivate in iste wiki.',
'userlogin-resetpassword-link' => 'Endursetja lykilorð',
'helplogin-url' => 'Help:Innskráning',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Hjálp við innskráningu]]',
+'createacct-join' => 'Sláðu inn þínar upplýsingar fyrir neðan.',
'createacct-emailrequired' => 'Netfang',
'createacct-emailoptional' => 'Netfang (valfrjálst)',
'createacct-email-ph' => 'Skrifaðu niður netfangið þitt',
'createacct-realname' => 'Raunverulegt nafn (valfrjálst)',
'createaccountreason' => 'Ástæða:',
'createacct-reason' => 'Ástæða',
+'createacct-reason-ph' => 'Afhverju ertu að búa til annan aðgang',
'createacct-captcha' => 'Öryggis athugun',
'createacct-imgcaptcha-ph' => 'Sláðu inn textann að ofan',
'createacct-submit' => 'Búa til aðganginn',
Svo virðist sem henni hafi verið eytt.',
'edit-conflict' => 'Breytingaárekstur.',
'edit-no-change' => 'Breyting þín var hunsuð, því engin breyting var á textanum.',
+'postedit-confirmation' => 'Breytingin þín var vistuð.',
'edit-already-exists' => 'Gat ekki skapað nýja síðu.
Hún er nú þegar til.',
'defaultmessagetext' => 'Sjálfgefinn skilaboða texti',
# Special:PasswordReset
'passwordreset' => 'Reimposta password',
+'passwordreset-text-one' => 'Compila questo modulo per reimpostare la tua password.',
'passwordreset-legend' => 'Reimposta password',
'passwordreset-disabled' => 'La reimpostazione delle password è stata disabilitata su questa wiki',
'passwordreset-emaildisabled' => 'Le funzionalità di posta elettronica sono state disabilitate su questa wiki.',
'createacct-yourpasswordagain-ph' => 'パスワードを再入力',
'remembermypassword' => 'このブラウザーにログイン情報を保存 (最長 $1 {{PLURAL:$1|日|日間}})',
'userlogin-remembermypassword' => 'ログイン状態を保持',
-'userlogin-signwithsecure' => 'セキュリティで保護された接続を使用',
+'userlogin-signwithsecure' => 'SSL (https) 接続を使用',
'securelogin-stick-https' => 'ログイン後にHTTPS接続を維持',
'yourdomainname' => 'ドメイン:',
'password-change-forbidden' => 'このウィキではパスワードを変更できません。',
# Special:PasswordReset
'passwordreset' => 'Passwuert zrécksetzen',
+'passwordreset-text-one' => 'Fëllt dëse Formulaire aus fir Äert Passwuert zréckzesetzen.',
+'passwordreset-text-many' => '{{PLURAL:$1|Gitt eng vun dësen Donnéeën a fir Ärt Passwuert zréckzesetzen.}}',
'passwordreset-legend' => 'Passwuert zrécksetzen',
'passwordreset-disabled' => "D'Zerécksetze vum Passwuert ass op dëser Wiki ausgeschalt.",
'passwordreset-emaildisabled' => "D'E-Mail-Fonctioune goufen op dëser Wiki ausgeschalt.",
'postedit-confirmation' => 'Är Ännerung gouf gespäichert.',
'edit-already-exists' => 'Déi nei Säit konnt net ugeluecht ginn, well et se scho gëtt.',
'defaultmessagetext' => 'Standardtext',
+'content-failed-to-parse' => 'Den $2-Inhalt fir den $1-Modell konnt net geparst ginn: $1',
'invalid-content-data' => 'Donnéeë vum Inhalt sinn net valabel',
'content-not-allowed-here' => '"$1"-Inhalt ass op der Säit [[$2]] net erlaabt',
'editwarning-warning' => 'Wann Dir dës Säit verloosst kann dat dozou féieren datt Dir all Ännerungen, déi Dir gemaach hutt, verléiert.
'listusers' => 'Daftar pangguno',
'listusers-editsonly' => 'Tunjuakan hanyo pangguno nan ado jariah',
'listusers-creationsort' => 'Uruikan manuruik tanggal pandaftaran',
-'usereditcount' => '$1 {{PLURAL:$1|suntiangan}}',
+'usereditcount' => '$1 {{PLURAL:$1|}} suntiangan',
'usercreated' => '{{GENDER:$3|Dibuek}} pado $1 pukua $2',
'newpages' => 'Laman baru',
'newpages-username' => 'Namo pangguno:',
** Baulang kali dikosongan
** Pamintaan pangguno',
'protect-edit-reasonlist' => 'Suntiang alasan palinduangan',
-'protect-expiry-options' => '1 jam:1 hour,1 ari:1 day,1 minggu:1 week,2 minggu:2 weeks,1 bulan:1 month,3 bulan:3 months,6 bulan:6 months,1 taun:1 year,salamonyo:infinite',
+'protect-expiry-options' => '1 jam:1 hour,1 ari:1 day,1 pakan:1 week,2 pakan:2 weeks,1 bulan:1 month,3 bulan:3 months,6 bulan:6 months,1 taun:1 year,salamonyo:infinite',
'restriction-type' => 'Palinduangan:',
'restriction-level' => 'Tingkek:',
'minimum-size' => 'Ukuran min',
'ipbenableautoblock' => 'Otomatih sakek alamaik IP tarakhia nan digunoan pangguno ko, jo sado alamaik IP takaik nan mancubo manyuntiang.',
'ipbsubmit' => 'Sakek pangguno ko',
'ipbother' => 'Salamo:',
-'ipboptions' => '2 jam:2 hours,1 ari:1 day,3 ari:3 days,1 minggu:1 week,2 minggu:2 weeks,1 bulan:1 month,3 bulan:3 months,6 bulan:6 months,1 taun:1 year,salamonyo:infinite',
+'ipboptions' => '2 jam:2 hours,1 ari:1 day,3 ari:3 days,1 pakan:1 week,2 pakan:2 weeks,1 bulan:1 month,3 bulan:3 months,6 bulan:6 months,1 taun:1 year,salamonyo:infinite',
'ipbotheroption' => 'lainnyo',
'ipbotherreason' => 'Alasan lain/tambahan:',
'ipbhidename' => 'Suruakan namo pangguno dari daftar jo suntiangan',
'minutes' => '{{PLURAL:$1|1 minik|$1 minik}}',
'hours' => '{{PLURAL:$1|1 jam|$1 jam}}',
'days' => '{{PLURAL:$1|1 ari|$1 ari}}',
-'weeks' => '{{PLURAL:$1|1 minggu|$1 minggu}}',
+'weeks' => '{{PLURAL:$1|1 pakan|$1 pakan}}',
'months' => '{{PLURAL:$1|1 bulan|$1 bulan}}',
'years' => '{{PLURAL:$1|1 taun|$1 taun}}',
'ago' => '$1 nan lalu',
# Special:PasswordReset
'passwordreset' => 'Менување на лозинка',
+'passwordreset-text-one' => 'Пополнете го образецов за да ја измените лозинката.',
+'passwordreset-text-many' => '{{PLURAL:$1|Внесете еден од податоците за да ја смените лозинката.}}',
'passwordreset-legend' => 'Нова лозинка',
'passwordreset-disabled' => 'На ова вики е оневозможено задавање на нова лозинка.',
'passwordreset-emaildisabled' => 'Можностите за е-пошта се исклучени на ова вики',
# Special:PasswordReset
'passwordreset' => 'രഹസ്യവാക്ക് പുനഃക്രമീകരിക്കുക',
+'passwordreset-text-one' => 'രഹസ്യവാക്ക് പുനർസജ്ജീകരിക്കാനായി ഈ ഫോം പൂരിപ്പിക്കുക.',
+'passwordreset-text-many' => '{{PLURAL:$1|താങ്കളുടെ രഹസ്യവാക്ക് പുനസജ്ജീകരിക്കാൻ വിവരശകലം നൽകുക.}}',
'passwordreset-legend' => 'രഹസ്യവാക്ക് പുനഃക്രമീകരിക്കുക',
'passwordreset-disabled' => 'ഈ വിക്കിയിൽ രഹസ്യവാക്ക് പുനഃക്രമീകരിക്കലുകൾ പ്രവർത്തരഹിതമാക്കിയിരിക്കുകയാണ്.',
'passwordreset-emaildisabled' => 'ഈ വിക്കിയിൽ ഇമെയിൽ സൗകര്യങ്ങൾ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു.',
'content-failed-to-parse' => '$2 ഉള്ളടക്കം $1 മാതൃകയിൽ പാഴ്സ് ചെയ്യൽ പരാജയപ്പെട്ടു: $3',
'invalid-content-data' => 'അസാധുവായ ഉള്ളടക്ക ഡേറ്റ',
'content-not-allowed-here' => '"$1" ഉള്ളടക്കം [[$2]] താളിൽ അനുവദിക്കുന്നില്ല',
-'editwarning-warning' => 'ഈ താളിൽ നിന്നു പോകുന്നത് താങ്കൾ വരുത്തിയ മാറ്റങ്ങൾ നഷ്ടപ്പെടാൻ ഇടയാക്കുന്നതാണ്.
-താങ്കൾ ലോഗിൻ ചെയ്തിട്ടുണ്ടെങ്കിൽ, താങ്കളുടെ ക്രമീകരണങ്ങളിൽ "{{int:prefs-editing}}" ഭാഗത്ത് ചെന്ന് ഈ അറിയിപ്പ് ഒഴിവാക്കാവുന്നതാണ്.',
+'editwarning-warning' => 'ഈ താളിൽ നിന്നും പോകുന്നത് താങ്കൾ വരുത്തിയ മാറ്റങ്ങൾ നഷ്ടപ്പെടാൻ ഇടയാക്കും.
+താങ്കൾ ലോഗിൻ ചെയ്തിട്ടുണ്ടെങ്കിൽ, താങ്കളുടെ ക്രമീകരണങ്ങളിൽ "തിരുത്തൽ" എന്ന ഭാഗത്ത് ചെന്ന് ഈ അറിയിപ്പ് പ്രദർശിപ്പിക്കുന്നത് ഒഴിവാക്കാവുന്നതാണ്.',
# Content models
'content-model-wikitext' => 'വിക്കിഎഴുത്ത്',
'oct' => 'okt',
'nov' => 'nov',
'dec' => 'des',
+'january-date' => '$1. januar',
+'february-date' => '$1. februar',
+'march-date' => '$1. mars',
+'april-date' => '$1. april',
+'may-date' => '$1. mai',
+'june-date' => '$1. juni',
+'july-date' => '$1. juli',
+'august-date' => '$1. august',
+'september-date' => '$1. september',
+'october-date' => '$1. oktober',
+'november-date' => '$1. november',
+'december-date' => '$1. desember',
# Categories related messages
'pagecategories' => '{{PLURAL:$1|Kategori|Kategoriar}}',
Det ser ut til at ho er sletta.',
'edit-conflict' => 'Endringskonflikt.',
'edit-no-change' => 'Redigeringa di vart ignorert fordi det ikkje vart gjort endringar i teksten.',
+'postedit-confirmation' => 'Endringa di vart lagra.',
'edit-already-exists' => 'Kunne ikkje opprette ny side fordi ho alt eksisterer.',
'defaultmessagetext' => 'Standard meldingstekst',
'content-failed-to-parse' => 'Klarte ikkje å tolke innhaldet «$2» for innhaldsmodellen «$1»: $3',
'permalink' => 'Anliura fissa',
'print' => 'Stampé',
'view' => 'Vardé',
-'edit' => 'Modìfica',
+'edit' => 'Modifiché',
'create' => 'Creé',
-'editthispage' => "Modìfica st'artìcol-sì",
+'editthispage' => 'Modifiché costa pàgina',
'create-this-page' => 'Creé sta pàgina',
-'delete' => 'Scancela',
+'delete' => 'Scancelé',
'deletethispage' => 'Scancela pàgina',
'undelete_short' => 'Disdëscancela {{PLURAL:$1|na modìfica|$1 modìfiche}}',
'viewdeleted_short' => 'Vardé {{PLURAL:$1|na modìfica scancelà|$1 modìfiche scancelà}}',
'oct' => 'Out.',
'nov' => 'Nov.',
'dec' => 'Dez.',
+'january-date' => '$1 de Janeiro',
+'february-date' => '$1 de Fevereiro',
+'march-date' => '$1 de Março',
+'april-date' => '$1 de Abril',
+'may-date' => '$1 de Maio',
+'june-date' => '$1 de Junho',
+'july-date' => '$1 de Julho',
+'august-date' => '$1 de Agosto',
+'september-date' => '$1 de Setembro',
+'october-date' => '$1 de Outubro',
+'november-date' => '$1 de Novembro',
+'december-date' => '$1 de Dezembro',
# Categories related messages
'pagecategories' => '{{PLURAL:$1|Categoria|Categorias}}',
'createacct-yourpasswordagain' => 'Confirme a palavra-chave',
'createacct-yourpasswordagain-ph' => 'Digite a palavra-chave novamente',
'remembermypassword' => 'Recordar os meus dados neste computador (no máximo, por $1 {{PLURAL:$1|dia|dias}})',
-'userlogin-remembermypassword' => 'Memorizar neste computador',
-'userlogin-signwithsecure' => 'Inicie sessão com servidor seguro',
+'userlogin-remembermypassword' => 'Manter-me autenticado',
+'userlogin-signwithsecure' => 'Use uma ligação segura',
'securelogin-stick-https' => 'Manter a ligação HTTPS após a autenticação',
'yourdomainname' => 'O seu domínio:',
'password-change-forbidden' => 'Não podes alterar senhas nesta wiki.',
'gotaccount' => "Já possui uma conta? '''$1'''.",
'gotaccountlink' => 'Autentique-se',
'userlogin-resetlink' => 'Esqueceu-se do seu nome de utilizador ou da palavra-chave?',
+'userlogin-resetpassword-link' => 'Recuperar palavra-chave',
'helplogin-url' => 'Ajuda:login',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Ajuda a fazer login]]',
'createacct-join' => 'Insira a sua informação abaixo.',
'createacct-realname' => 'Nome verdadeiro (opcional)',
'createaccountreason' => 'Motivo:',
'createacct-reason' => 'Razão',
+'createacct-reason-ph' => 'Porque está a criar outra conta',
'createacct-captcha' => 'Verificar segurança',
+'createacct-imgcaptcha-ph' => 'Digite o texto que vê acima',
+'createacct-submit' => 'Crie a sua conta',
+'createacct-benefit-heading' => '{{SITENAME}} é feito por pessoas como você.',
+'createacct-benefit-body1' => '{{PLURAL:$1|edição|edições}}',
+'createacct-benefit-body2' => '{{PLURAL:$1|página|páginas}}',
+'createacct-benefit-body3' => '{{PLURAL:$1|contribuidor|contribuidores}} recentes',
'badretype' => 'As palavras-chave que introduziu não são iguais.',
'userexists' => 'O nome de utilizador introduzido já existe.
Por favor escolha um nome diferente.',
'loginerror' => 'Erro de autenticação',
+'createacct-error' => 'Erro na criação da conta',
'createaccounterror' => 'Não foi possível criar a conta: $1',
'nocookiesnew' => "A conta de utilizador foi criada, mas neste momento não está autenticado.
A {{SITENAME}} utiliza ''cookies'' para autenticar os utilizadores.
# Email sending
'php-mail-error-unknown' => 'Erro desconhecido na função mail() do PHP',
'user-mail-no-addy' => 'Tentou enviar uma mensagem sem um endereço de correio electrónico',
+'user-mail-no-body' => 'Tentou mandar email sem conteúdo ou com conteúdo demasiado pequeno.',
# Change password dialog
'resetpass' => 'Alterar palavra-chave',
'resetpass-wrong-oldpass' => 'Palavra-chave temporária ou actual inválida.
Pode ter já alterado com sucesso a sua palavra-chave ou solicitado uma nova palavra-chave temporária.',
'resetpass-temp-password' => 'Palavra-chave temporária:',
+'resetpass-abort-generic' => 'Alteração de senha foi cancelada por uma extensão.',
# Special:PasswordReset
'passwordreset' => 'Repor palavra-chave',
+'passwordreset-text-one' => 'Preencha este formulário para repor a sua palavra-passe.',
+'passwordreset-text-many' => '{{PLURAL:$1|Digite uma parte dos dados para redefinir sua senha.}}',
'passwordreset-legend' => 'Reiniciar a palavra-chave',
'passwordreset-disabled' => 'O reinício da palavra-chave foi impossibilitado nesta wiki.',
+'passwordreset-emaildisabled' => 'Recursos de e-mail foram desactivados neste wiki.',
'passwordreset-username' => 'Nome de utilizador:',
'passwordreset-domain' => 'Domínio:',
'passwordreset-capture' => 'Ver o email resultante?',
'changeemail-oldemail' => 'Correio electrónico actual:',
'changeemail-newemail' => 'Correio electrónico novo:',
'changeemail-none' => '(nenhum)',
+'changeemail-password' => 'A sua senha {{SITENAME}}:',
'changeemail-submit' => 'Alterar correio electrónico',
'changeemail-cancel' => 'Cancelar',
'edit-already-exists' => 'Não foi possível criar uma página nova.
Ela já existia.',
'defaultmessagetext' => 'Texto da mensagem padrão',
+'content-failed-to-parse' => 'Falha ao analisar conteúdo $2 para modelo $1:$3',
+'invalid-content-data' => 'Dados de conteúdo inválidos',
'content-not-allowed-here' => 'Conteúdo do tipo "$1" não é permitido na página [[$2]]',
'editwarning-warning' => 'Sair desta página fará com que você perca todas as alterações feitas por você.
Se você fez login, pode desabilitar este aviso na seção "{{int:prefs-editing}}" das suas preferências.',
'searchdisabled' => 'Foi impossibilitada a realização de pesquisas na {{SITENAME}}.
Entretanto, pode realizar pesquisas através do Google.
Note, no entanto, que a indexação da {{SITENAME}} neste motor de busca pode estar desactualizada.',
+'search-error' => 'Um erro ocorreu enquanto se efectuava a busca: $1',
# Preferences page
'preferences' => 'Preferências',
'userrights-notallowed' => 'A sua conta não tem permissão para adicionar ou remover privilégios a utilizadores.',
'userrights-changeable-col' => 'Grupos que pode alterar',
'userrights-unchangeable-col' => 'Grupos que não pode alterar',
+'userrights-conflict' => 'Conflito com os privilégios dos utilizadores! Por favor, aplique as suas mudanças novamente.',
# Groups
'group' => 'Grupo:',
'backend-fail-notsame' => 'Já existe um ficheiro não idêntico em $1 .',
'backend-fail-invalidpath' => '$1 não é um caminho de armazenamento válido.',
'backend-fail-delete' => 'Não foi possível excluir o ficheiro $1.',
+'backend-fail-describe' => 'Não foi possível mudar metadados para o ficheiro "$1".',
'backend-fail-alreadyexists' => 'O ficheiro $1 já existe.',
'backend-fail-store' => 'Não foi possível armazenar o ficheiro $1 em $2.',
'backend-fail-copy' => 'Não foi possível copiar o ficheiro $1 para $2.',
Estes links deviam ser desambiguados, apontando-os para uma página mais apropriada.<br />
Considera-se que uma página é de desambiguação se nela for utilizada uma predefinição que esteja definida em [[MediaWiki:Disambiguationspage]].",
+'pageswithprop' => 'Páginas com uma propriedade',
+'pageswithprop-legend' => 'Páginas com uma propriedade',
+'pageswithprop-text' => 'Esta página lista páginas que usam uma propriedade em particular.',
+'pageswithprop-prop' => 'Nome da propriedade:',
+'pageswithprop-submit' => 'Avançar',
+
'doubleredirects' => 'Redireccionamentos duplos',
'doubleredirectstext' => 'Esta página lista todas as páginas que redireccionam para outras páginas de redireccionamento.
Cada linha contém links para o primeiro e segundo redireccionamentos, bem como o destino do segundo redireccionamento, geralmente contendo a verdadeira página de destino, que devia ser o destino do primeiro redireccionamento.
'mailnologintext' => 'Precisa de estar [[Special:UserLogin|autenticado]] e possuir um endereço de correio válido nas suas [[Special:Preferences|preferências]], para poder enviar correio electrónico a outros utilizadores.',
'emailuser' => 'Enviar correio electrónico a este utilizador',
'emailuser-title-target' => 'Enviar correio eletrónico a {{GENDER:$1|este utilizador|esta utilizadora}}',
+'emailuser-title-notarget' => 'Enviar correio electrónico ao utilizador',
'emailpage' => 'Enviar correio electrónico ao utilizador',
'emailpagetext' => 'Pode usar o formulário abaixo para enviar uma mensagem por correio eletrónico para {{GENDER:$1|este utilizador|esta utilizadora}}.
O endereço de correio que introduziu nas [[Special:Preferences|suas preferências]] irá aparecer no campo do remetente da mensagem "De:", para que o destinatário lhe possa responder diretamente.',
'enotif_mailer' => 'Gerador de Notificações da {{SITENAME}}',
'enotif_reset' => 'Marcar todas as páginas como visitadas',
'enotif_impersonal_salutation' => 'Utilizador da "{{SITENAME}}"',
+'enotif_subject_deleted' => 'A página $1 de {{SITENAME}} foi {{GENDER:$2|eliminada}} por $2',
+'enotif_subject_created' => 'A página $1 de {{SITENAME}} foi {{GENDER:$2|criada}} por $2',
+'enotif_subject_moved' => 'A página $1 de {{SITENAME}} foi {{GENDER:$2|movida}} por $2',
+'enotif_subject_restored' => 'A página $1 de {{SITENAME}} foi {{GENDER:$2|restaurada}} por $2',
+'enotif_subject_changed' => 'A página $1 de {{SITENAME}} foi {{GENDER:$2|alterada}} por $2',
+'enotif_body_intro_deleted' => 'A página $1 de {{SITENAME}} foi {{GENDER:$2|eliminada}} em $PAGEEDITDATE por $2, ver $3.',
+'enotif_body_intro_created' => 'A página $1 em {{SITENAME}} foi {{GENDER:$2| criada}} em $PAGEEDITDATE por $2, ver $3 para a versão actual.',
+'enotif_body_intro_moved' => 'A páginas $1 em {{SITENAME}} foi {{GENDER:$2|movida}} em $PAGEEDITDATE por $2, ver $3 para a versão actual.',
+'enotif_body_intro_restored' => 'A páginas $1 em {{SITENAME}} foi {{GENDER:$2|restaurada}} em $PAGEEDITDATE por $2, ver $3 para a versão actual.',
+'enotif_body_intro_changed' => 'A página $1 em {{SITENAME}} foi {{GENDER:$2|alterada}} em $PAGEEDITDATE por $2, ver $3 para a versão actual.',
'enotif_lastvisited' => 'Consulte $1 para todas as alterações efectuadas desde a sua última visita.',
'enotif_lastdiff' => 'Consulte $1 para ver esta alteração.',
'enotif_anon_editor' => 'utilizador anónimo $1',
'prot_1movedto2' => 'moveu [[$1]] para [[$2]]',
'protect-badnamespace-title' => 'Espaço nominal não passível de protecção',
'protect-badnamespace-text' => 'Páginas neste espaço nominal não podem ser protegidas.',
+'protect-norestrictiontypes-text' => 'Esta página não pode ser protegida porque não há nenhum tipo de restrição disponível.',
+'protect-norestrictiontypes-title' => 'Página não passível de protecção',
'protect-legend' => 'Confirmar protecção',
'protectcomment' => 'Motivo:',
'protectexpiry' => 'Expiração:',
'proxyblocksuccess' => 'Concluído.',
'sorbsreason' => "O seu endereço IP encontra-se listado como ''proxy'' aberto na DNSBL utilizada pela {{SITENAME}}.",
'sorbs_create_account_reason' => "O seu endereço IP encontra-se listado como ''proxy'' aberto na DNSBL utilizada pela {{SITENAME}}. Não pode criar uma conta",
+'xffblockreason' => 'Um endereço IP presente no cabeçalho X-Forwardd-For, seja seu ou de um servidor de proxy que estiver a usar, foi bloqueado. A razão do bloqueio original foi: $1',
'cant-block-while-blocked' => 'Não pode bloquear outros utilizadores enquanto estiver bloqueado.',
'cant-see-hidden-user' => "O utilizador que está tentando bloquear já está bloqueado e oculto.
Como não tem o privilégio para ocultar utilizadores ''(hideuser)'', não pode ver ou editar o bloqueio deste utilizador.",
'thumbnail-more' => 'Ampliar',
'filemissing' => 'Ficheiro não encontrado',
'thumbnail_error' => 'Erro ao criar miniatura: $1',
+'thumbnail_error_remote' => 'Mensagem de erro de $1 :
+$2',
'djvu_page_error' => 'página DjVu inacessível',
'djvu_no_xml' => 'Não foi possível aceder ao XML para o ficheiro DjVU',
'thumbnail-temp-create' => 'Não foi possível criar o ficheiro temporário da miniatura',
'import-interwiki-templates' => 'Incluir todas as predefinições',
'import-interwiki-submit' => 'Importar',
'import-interwiki-namespace' => 'Espaço nominal de destino:',
+'import-interwiki-rootpage' => 'Raiz da página de destino (opcional):',
'import-upload-filename' => 'Nome do ficheiro:',
'import-comment' => 'Comentário:',
'importtext' => 'Exporte o ficheiro da wiki de origem utilizando a página especial [[Special:Export|exportação de páginas]].
'import-error-interwiki' => 'A página "$1" não pode ser importada pois seu nome está reservado para um link externo (interwiki).',
'import-error-special' => 'A página "$1" não pode ser importada porque ela pertence a um espaço nominal especial que não permite páginas.',
'import-error-invalid' => 'A página "$1" não pode ser importada porque seu nome é inválido.',
+'import-error-unserialize' => 'Revisão $2 da página "$1" não pode ser desserializada. Foi relatado que a revisão usava o modelo de conteúdo $3 serializado como $4.',
'import-options-wrong' => '{{PLURAL:$2|Opção errada|Opções erradas}}: <nowiki>$1</nowiki>',
+'import-rootpage-invalid' => 'A raiz da página dada é um título inválido.',
# Import log
'importlogpage' => 'Registo de importações',
* {{msg-mw|Accesskey-ca-delete}}
* {{msg-mw|Tooltip-ca-delete}}
{{Identical|Delete}}',
+'undeletethispage' => 'In the Cologne Blue skin this is the text for link to undelete the page in admin view, at the bottom of the page.',
'deletethispage' => 'In the Cologne Blue skin this is the text for link to delete the page in admin view, at the bottom of the page.
{{Identical|Delete this page}}',
'undelete_short' => "It is tab label. It's really can be named ''nstab-undelete''.",
* {{msg-mw|Tooltip-n-currentevents}}',
'currentevents-url' => "Target page of ''CurrentEvents'' in the sidebar. See also {{msg-mw|currentevents}}.
{{doc-important|Do not translate the \"<tt>Project:</tt>\" part.}}",
-'disclaimers' => 'Used as display name for the link to [[{{MediaWiki:Disclaimerpage}}]] shown at the bottom of every page on the wiki. Example [[{{MediaWiki:Disclaimerpage}}|{{MediaWiki:Disclaimers}}]].',
+'disclaimers' => 'Used as display name for the link to [[{{MediaWiki:Disclaimerpage}}]] shown at the bottom of every page on the wiki. Example [[{{MediaWiki:Disclaimerpage}}|{{MediaWiki:Disclaimers}}]].
+{{Identical|Disclaimer}}',
'disclaimerpage' => 'Used as page for that contains the site disclaimer. Used at the bottom of every page on the wiki. Example: [[{{MediaWiki:Disclaimerpage}}|{{MediaWiki:Disclaimers}}]].
{{doc-important|Do not change the "<tt>Project:</tt>" part.}}',
'edithelp' => 'This is the text that appears on the editing help link that is near the bottom of the editing page',
'exif-pngfilecomment' => 'See also:
* {{msg-mw|Exif-pngfilecomment}}
* {{msg-mw|Exif-giffilecomment}}',
-'exif-disclaimer' => 'Disclaimer for the image.',
+'exif-disclaimer' => 'Disclaimer for the image.
+{{Identical|Disclaimer}}',
'exif-contentwarning' => 'Content warning for the image. For example if the image/media contains violent, sexual or otherwise offensive content.
This comes from the png warning textual chunk. See http://www.w3.org/TR/PNG/#11keywords',
# Special:PasswordReset
'passwordreset' => 'Resetare parolă',
-'passwordreset-text' => 'Completați acest formular pentru a vă reseta parola.',
+'passwordreset-text-one' => 'Completați acest formular pentru a vă reseta parola.',
+'passwordreset-text-many' => '{{PLURAL:$1|Introduceți una din aceste informații pentru a vă reseta parola.}}',
'passwordreset-legend' => 'Resetare parolă',
'passwordreset-disabled' => 'Resetarea parolei a fost dezactivată pe acest wiki.',
'passwordreset-emaildisabled' => 'Funcțiile de e-mail au fost dezactivate de pe acest wiki.',
-'passwordreset-pretext' => '{{PLURAL:$1| | Introduceți mai jos o parte din informații}}',
'passwordreset-username' => 'Nume de utilizator:',
'passwordreset-domain' => 'Domeniu:',
'passwordreset-capture' => 'Vizualizați e-mailul rezultat?',
Se pare că a fost ștearsă.',
'edit-conflict' => 'Conflict de modificare.',
'edit-no-change' => 'Modificarea dvs. a fost ignorată deoarece nu s-a efectuat nicio schimbare.',
+'postedit-confirmation' => 'Modificarea dumneavoastră a fost salvată.',
'edit-already-exists' => 'Pagina nouă nu a putut fi creată.
Ea există deja.',
'defaultmessagetext' => 'Textul implicit',
'oct' => 'окт',
'nov' => 'ноя',
'dec' => 'дек',
+'january-date' => 'Январь $1',
+'february-date' => 'Февраль $1',
+'march-date' => 'Март $1',
+'april-date' => 'Апрель $1',
+'may-date' => 'Май $1',
+'june-date' => 'Июнь $1',
+'july-date' => 'Июль $1',
+'august-date' => 'Август $1',
+'september-date' => 'Сентябрь $1',
+'october-date' => 'Октябрь $1',
+'november-date' => 'Ноябрь $1',
+'december-date' => 'Декабрь $1',
# Categories related messages
'pagecategories' => '{{PLURAL:$1|Категория|Категории}}',
'youhavenewmessages' => 'Вы получили $1 ($2).',
'newmessageslink' => 'новые сообщения',
'newmessagesdifflink' => 'последнее изменение',
-'youhavenewmessagesfromusers' => 'Вы получили $1 от {{PLURAL:$3|$3 участника|$3 участников|$3 участников}} ($2).',
+'youhavenewmessagesfromusers' => 'Вы получили $1 от {{PLURAL:$3|$3 участника|$3 участников}} ($2).',
'youhavenewmessagesmanyusers' => 'Вы получили $1 от множества пользователей ($2).',
'newmessageslinkplural' => '{{PLURAL:$1|новое сообщение|новые сообщения}}',
'newmessagesdifflinkplural' => '{{PLURAL:$1|последнее изменение|последние изменения}}',
'createacct-yourpasswordagain' => 'Подтвердите пароль',
'createacct-yourpasswordagain-ph' => 'Введите пароль еще раз',
'remembermypassword' => 'Помнить мою учётную запись на этом компьютере (не более $1 {{PLURAL:$1|дня|дней|дней}})',
-'userlogin-remembermypassword' => 'Ð\97апомниÑ\82Ñ\8c менÑ\8f',
-'userlogin-signwithsecure' => 'Ð\92Ñ\8bполниÑ\82е вÑ\85од, иÑ\81полÑ\8cзÑ\83Ñ\8f безопаÑ\81нÑ\8bй Ñ\81еÑ\80веÑ\80',
+'userlogin-remembermypassword' => 'Ð\9eÑ\81Ñ\82аваÑ\82Ñ\8cÑ\81Ñ\8f в Ñ\81иÑ\81Ñ\82еме',
+'userlogin-signwithsecure' => 'Ð\97аÑ\89иÑ\89Ñ\91нное Ñ\81оединение',
'securelogin-stick-https' => 'Продолжить подключение по HTTPS после входа',
'yourdomainname' => 'Ваш домен:',
'password-change-forbidden' => 'Вы не можете изменить пароль в этой вики.',
'gotaccountlink' => 'Представьтесь',
'userlogin-resetlink' => 'Забыли данные для входа?',
'userlogin-resetpassword-link' => 'Сброс пароля',
+'helplogin-url' => 'Help:Вход в систему',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Помощь со входом в систему]]',
'createacct-join' => 'Введите свои данные ниже.',
'createacct-emailrequired' => 'Адрес электронной почты',
'createacct-realname' => 'Настоящее имя (необязательно)',
'createaccountreason' => 'Причина:',
'createacct-reason' => 'Причина',
+'createacct-reason-ph' => 'Зачем вы создаёте другую учетную запись',
'createacct-captcha' => 'Проверка безопасности',
'createacct-imgcaptcha-ph' => 'Введите текст, который вы видите выше',
'createacct-submit' => 'Зарегистрировать учётную запись',
'createacct-benefit-heading' => '{{SITENAME}} сделана такими же людьми, как вы.',
'createacct-benefit-body1' => '{{PLURAL:$1|правка|правки|правок}}',
-'createacct-benefit-body2' => '{{PLURAL:$1|страница|страницы|страниц}}',
-'createacct-benefit-body3' => '{{PLURAL:$1|Ñ\83Ñ\87аÑ\81Ñ\82ник|Ñ\83Ñ\87аÑ\81Ñ\82ников|Ñ\83Ñ\87аÑ\81Ñ\82ника}} в последнее время',
+'createacct-benefit-body2' => '{{PLURAL:$1|статья|статьи|статей}}',
+'createacct-benefit-body3' => '{{PLURAL:$1|Ñ\83Ñ\87аÑ\81Ñ\82ник|Ñ\83Ñ\87аÑ\81Ñ\82ника|Ñ\83Ñ\87аÑ\81Ñ\82ников}} за последнее время',
'badretype' => 'Введённые вами пароли не совпадают.',
'userexists' => 'Введённое имя участника уже используется.
Пожалуйста, выберите другое имя.',
'resetpass-wrong-oldpass' => 'Неправильный временный или текущий пароль.
Возможно, вы уже успешно изменили пароль, или запросили новый временный пароль.',
'resetpass-temp-password' => 'Временный пароль:',
+'resetpass-abort-generic' => 'Изменение пароля было прервано расширением.',
# Special:PasswordReset
'passwordreset' => 'Сброс пароля',
+'passwordreset-text-one' => 'Заполните эту форму, чтобы сбросить свой пароль.',
+'passwordreset-text-many' => '{{PLURAL:$1|Введите одну из частей данных для сброса пароля.}}',
'passwordreset-legend' => 'Сбросить пароль',
'passwordreset-disabled' => 'Сбросы пароля отключены на этой вики.',
'passwordreset-emaildisabled' => 'Функции электронной почты отключены в этой вики.',
Временный пароль: $2',
'passwordreset-emailsent' => 'Отправлено электронное письмо с информацией о сбросе пароля.',
'passwordreset-emailsent-capture' => 'Отправлено электронное письмо с информацией о сбросе пароля, текст которого можно увидеть ниже.',
-'passwordreset-emailerror-capture' => 'Было создано электронное письмо с информацией о сбросе пароля, текст которого можно увидеть ниже. Однако его не удалось отправить по следующей причине: $1',
+'passwordreset-emailerror-capture' => 'Было создано электронное письмо с информацией о сбросе пароля, текст которого можно увидеть ниже, однако его не удалось отправить {{GENDER:$2|участнику|участнице}} по следующей причине: $1',
# Special:ChangeEmail
'changeemail' => 'Изменить адрес электронной почты',
Вероятно, она была удалена.',
'edit-conflict' => 'Конфликт редактирования.',
'edit-no-change' => 'Ваша правка была отклонена, так как в тексте не было сделано изменений.',
+'postedit-confirmation' => 'Ваша правка сохранена.',
'edit-already-exists' => 'Невозможно создать новую страницу.
Она уже существует.',
'defaultmessagetext' => 'Текст по умолчанию',
'invalid-content-data' => 'Недопустимые данные',
'content-not-allowed-here' => 'Содержимое "$1" недопустимо на странице [[$2]]',
'editwarning-warning' => 'Переход на другую страницу может привести к потере сделанных вами изменений.
-Если вы зарегистрированы в системе, то вы можете отключить это предупреждение в разделе «{{int:prefs-editing}}» ваших настроек.',
+Если вы зарегистрированы в системе, то вы можете отключить это предупреждение в разделе «Редактирование» ваших настроек.',
# Content models
'content-model-wikitext' => 'вики-текст',
'userrights-notallowed' => 'С вашей учётной записи не разрешено добавлять и удалять права участников.',
'userrights-changeable-col' => 'Группы, которые вы можете изменять',
'userrights-unchangeable-col' => 'Группы, которые вы не можете изменять',
+'userrights-conflict' => 'Конфликт прав участника! Пожалуйста, примените изменения заново.',
# Groups
'group' => 'Группа:',
'notvisiblerev' => 'Версия была удалена',
'watchnochange' => 'За этот период ни одна страница из списка наблюдения не менялась.',
'watchlist-details' => 'В вашем списке наблюдения $1 {{PLURAL:$1|страница|страницы|страниц}}, не считая страниц обсуждения.',
-'wlheader-enotif' => 'Уведомление по эл. почте включено.',
+'wlheader-enotif' => 'Уведомления по эл. почте включены.',
'wlheader-showupdated' => "Страницы, изменившиеся с вашего последнего их посещения, выделены '''жирным''' шрифтом.",
'watchmethod-recent' => 'просмотр последних изменений для наблюдаемых страниц',
'watchmethod-list' => 'просмотр наблюдаемых страниц для последних изменений',
'thumbnail-more' => 'Увеличить',
'filemissing' => 'Файл не найден',
'thumbnail_error' => 'Ошибка создания миниатюры: $1',
+'thumbnail_error_remote' => 'Сообщение об ошибке от $1:
+$2',
'djvu_page_error' => 'Номер страницы DjVu вне досягаемости',
'djvu_no_xml' => 'Невозможно получить XML для DjVu',
'thumbnail-temp-create' => 'Не удаётся создать временный файл эскиза',
'minutes' => '{{PLURAL:$1|$1 минута|$1 минуты|$1 минут}}',
'hours' => '{{PLURAL:$1|$1 час|$1 часа|$1 часов}}',
'days' => '{{PLURAL:$1|$1 день|$1 дня|$1 дней}}',
+'weeks' => '{{PLURAL:$1|$1 неделя|$1 недели|$1 недель}}',
'months' => '{{PLURAL:$1|$1 месяц|$1 месяца|$1 месяцев}}',
'years' => '{{PLURAL:$1|$1 год|$1 года|$1 лет}}',
'ago' => '$1 назад',
'just-now' => 'только что',
+# Human-readable timestamps
+'hours-ago' => '$1 {{PLURAL:$1|час|часа|часов}} назад',
+'minutes-ago' => '$1 {{PLURAL:$1|минуту|минуты|минут}} назад',
+'seconds-ago' => '$1 {{PLURAL:$1|секунду|секунды|секунд}} назад',
+'monday-at' => 'в понедельник в $1',
+'tuesday-at' => 'во вторник в $1',
+'wednesday-at' => 'в среду в $1',
+'thursday-at' => 'в четверг в $1',
+'friday-at' => 'в пятницу в $1',
+'saturday-at' => 'в субботу в $1',
+'sunday-at' => 'в воскресенье в $1',
+'yesterday-at' => 'Вчера в $1',
+
# Bad image list
'bad_image_list' => 'Формат должен быть следующим:
'version-entrypoints-articlepath' => '[https://www.mediawiki.org/wiki/Manual:$wgArticlePath Путь к статье]',
'version-entrypoints-scriptpath' => '[https://www.mediawiki.org/wiki/Manual:$wgScriptPath Путь к скрипту]',
+# Special:Redirect
+'redirect' => 'Перенаправление с файла, участника или идентификатора версии',
+'redirect-legend' => 'Перенаправление на файл или страницу',
+'redirect-summary' => 'Эта специальная страница перенаправляет на файл (с имени файла), страницу (с идентификатора версии) или страницу участника (с числового идентификатора участника).',
+'redirect-submit' => 'Перейти',
+'redirect-lookup' => 'Поиск:',
+'redirect-value' => 'Значение:',
+'redirect-user' => 'Идентификатор участника',
+'redirect-revision' => 'Версия страницы',
+'redirect-file' => 'Название файла',
+'redirect-not-exists' => 'Значение не найдено',
+
# Special:FileDuplicateSearch
'fileduplicatesearch' => 'Поиск одинаковых файлов',
'fileduplicatesearch-summary' => 'Поиск одинаковых файлов по хэш-коду.',
'htmlform-selectorother-other' => 'Иное',
'htmlform-no' => 'Нет',
'htmlform-yes' => 'Да',
+'htmlform-chosen-placeholder' => 'Выберите вариант',
# SQLite database support
'sqlite-has-fts' => '$1 с поддержкой полнотекстового поиска',
'oct' => 'Алт',
'nov' => 'Сэт',
'dec' => 'Ахс',
+'january-date' => 'Тохсунньу $1',
+'february-date' => 'Олунньу $1',
+'march-date' => 'Кулун тутар $1',
+'april-date' => 'Муус устар $1',
+'may-date' => 'Ыам ыйын $1',
+'june-date' => 'Бэс ыйын $1',
+'july-date' => 'От ыйын $1',
+'august-date' => 'Атырдьах ыйын $1',
+'september-date' => 'Балаҕан ыйын $1',
+'october-date' => 'Алтынньы $1',
+'november-date' => 'Сэтинньи $1',
+'december-date' => 'Ахсынньы $1',
# Categories related messages
'pagecategories' => '{{PLURAL:$1|Категория|Категориялар}}',
'yourpassword' => 'Киирии тыла:',
'userlogin-yourpassword' => 'Киирии тыл',
'userlogin-yourpassword-ph' => 'Киирии тылгын суруй',
+'createacct-yourpassword-ph' => 'Киирии тылгын суруй',
'yourpasswordagain' => 'Киирии тылгын хатылаа:',
+'createacct-yourpasswordagain' => 'Киирии тылгын бигэргэт',
+'createacct-yourpasswordagain-ph' => 'Киирии тылгын хатылаа',
'remembermypassword' => 'Миигин бу көмпүүтэргэ сигээ ($1 {{PLURAL:$1|күн|күнтэн ордуга суох}})',
-'userlogin-remembermypassword' => 'Ð\94олоÒ\95ойгоÑ\80 Ñ\85аÑ\82аа',
-'userlogin-signwithsecure' => 'Ð\91игÑ\8d Ñ\81иÑ\8dÑ\80бÑ\8dÑ\80и Ñ\82Ñ\83һанан кииÑ\80ии',
+'userlogin-remembermypassword' => 'ТиһиликÑ\82Ñ\8dн Ñ\82аÑ\85Ñ\81Ñ\8bма',
+'userlogin-signwithsecure' => 'Ð\91игÑ\8d Ñ\85олбонÑ\83Ñ\83',
'securelogin-stick-https' => 'Киирэн баран HTTPS нөҥүө холбонууну салгыырга',
'yourdomainname' => 'Эн дөмүөнүҥ:',
'password-change-forbidden' => 'Бу биикигэ киирии тылы уоарытар табыллыбат.',
'gotaccount' => "Бэлиэтэммитиҥ дуо? '''$1'''.",
'gotaccountlink' => 'Аатыҥ',
'userlogin-resetlink' => 'Киирэр тылгын умнубуккун дуо?',
+'userlogin-resetpassword-link' => 'Киирии тылы уларытыы',
'helplogin-url' => 'Help:Бэлиэ-ааты киллэрии',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Бэлиэтэниигэ көмө]]',
+'createacct-join' => 'Аллара суруй.',
+'createacct-emailrequired' => 'Email аадырыс',
+'createacct-emailoptional' => 'Email аадырыс (булгуччута суох)',
+'createacct-email-ph' => 'Эл аадырыскын суруй',
'createaccountmail' => 'Быстах киирии тылы туһаныы уонна ону email-ынан ыытыы',
+'createacct-realname' => 'Дьиҥнээх аатыҥ (булгуччута суох)',
'createaccountreason' => 'Төрүөтэ:',
+'createacct-reason' => 'Төрүөтэ',
+'createacct-reason-ph' => 'Саҥа аатынан тоҕо киирэҕиний',
+'createacct-captcha' => 'Куттал суох буолуутун тургутуу',
+'createacct-imgcaptcha-ph' => 'Үөһэ көстөрү хатылаа',
+'createacct-submit' => 'Бэлиэтэнии',
+'createacct-benefit-heading' => '{{SITENAME}} ситим-сири эн курдук дьон оҥороллор.',
+'createacct-benefit-body1' => '{{PLURAL:$1|уларытыы|уларытыы}}',
+'createacct-benefit-body2' => '{{PLURAL:$1|сирэй|сирэй}}',
+'createacct-benefit-body3' => 'кэнники {{PLURAL:$1|кыттааччы|кыттааччы}}',
'badretype' => 'Киирии тылларыҥ сөп түбэспэтилэр.',
'userexists' => 'Суруйбут аатыҥ бэлиэр баар.
Бука диэн, атын аатта тал.',
'loginerror' => 'Ааккын система билбэтэ',
+'createacct-error' => 'Бэлиэтэнии кэмигэр алҕас таҕыста',
'createaccounterror' => 'Саҥа аат бэлиэтиир кыах суох: $1',
'nocookiesnew' => 'Маннык ааттаах кыттааччы баар буолла гынан баран, систиэмэҕэ киирэ илик. {{SITENAME}} «cookies» туттар, оттон эн көмпүүтэргэр ону туһанар бобуллубут. Бука диэн «cookies» холбоо, онтон өссө киирэн көр.',
'nocookieslogin' => '{{SITENAME}} дьону билэргэ «cookies» туттар. Эн көмпүүтэргэр «cookies» бобуллубут. Ону холбоон баран өссө киирэн көр.',
'pageinfo-robot-noindex' => 'Ne može da se popiše',
'pageinfo-views' => 'Broj pregleda',
'pageinfo-watchers' => 'Broj nadgledača stranica',
+'pageinfo-few-watchers' => 'Manje od $1 {{PLURAL:$1|pratioca|pratilaca}}',
'pageinfo-redirects-name' => 'Preusmeravanja na stranicu',
'pageinfo-subpages-name' => 'Podstranice ove stranice',
'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|preusmerenje|preusmerenja|preusmerenja}}; $3 {{PLURAL:$3|nepreusmerenje|nepreusmerenja|nepreusmerenja}})',
'ISO 8601 both' => 'xnY-xnm-xnd"T"xnH:xni:xns',
);
-$linkTrail = '/^([a-z]+)(.*)\$/sD';
+$linkTrail = '/^([a-z]+)(.*)$/sD';
$messages = array(
# User preference toggles
# Special:PasswordReset
'passwordreset' => 'Tái tạo mật khẩu',
+'passwordreset-text-one' => 'Hãy điền mẫu đơn này để tái tạo mật khẩu.',
+'passwordreset-text-many' => '{{PLURAL:$1|Nhập một trong những chi tiết sau để táo tạo mật khẩu.}}',
'passwordreset-legend' => 'Tái tạo mật khẩu',
'passwordreset-disabled' => 'Chức năng tái tạo mật khẩu đã bị tắt trên wiki này.',
'passwordreset-emaildisabled' => 'Tính năng gửi thư điện tử không được kích hoạt trên wiki này.',
# Special:PasswordReset
'passwordreset' => '重新設定密碼',
+'passwordreset-text-one' => '完成此表格以重新設定您的密碼。',
+'passwordreset-text-many' => '{{PLURAL:$1|輸入其中一項資料以重新設定您的密碼。}}',
'passwordreset-legend' => '重設密碼',
'passwordreset-disabled' => '此維基上已禁止了重設密碼。',
'passwordreset-emaildisabled' => '電子郵件功能在此 wiki 上已禁用。',
ghostscript
gimpbaseenums
git
+gitblit
gitdir
github
-gitweb
global
globalauth
globalblock
// to $maintenance->mSelf. Keep that here for b/c
$self = $maintenance->getName();
-// Detect compiled mode
+# Load composer's autoloader if present
+if ( is_readable( "$IP/vendor/autoload.php" ) ) {
+ require_once "$IP/vendor/autoload.php";
+}
# Get the MWInit class
require_once "$IP/includes/Init.php";
+# Start the autoloader, so that extensions can derive classes from core files
require_once "$IP/includes/AutoLoader.php";
# Stub the profiler
require_once "$IP/includes/profiler/Profiler.php";
$noRC = $this->hasOption( 'no-rc' );
$wgUser = User::newFromName( $userName );
+ $context = RequestContext::getMain();
+ $context->setUser( $wgUser );
if ( !$wgUser ) {
$this->error( "Invalid username", true );
}
if ( !$wgTitle ) {
$this->error( "Invalid title", true );
}
+ $context->setTitle( $wgTitle );
$page = WikiPage::factory( $wgTitle );
--- /dev/null
+<?php
+/**
+ * Delete archived (non-current) files from storage
+ *
+ * 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 Maintenance
+ * @author Aaron Schulz
+ */
+
+require_once( __DIR__ . '/Maintenance.php' );
+
+/**
+ * Maintenance script to delete archived (non-current) files from storage.
+ *
+ * @TODO: Maybe add some simple logging
+ *
+ * @ingroup Maintenance
+ * @since 1.22
+ */
+class EraseArchivedFile extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Erases traces of deleted files.";
+ $this->addOption( 'delete', 'Perform the deletion' );
+ $this->addOption( 'filename', 'File name', false, true );
+ $this->addOption( 'filekey', 'File storage key (with extension) or "*"', true, true );
+ }
+
+ public function execute() {
+ if ( !$this->hasOption( 'delete' ) ) {
+ $this->output( "Use --delete to actually confirm this script\n" );
+ }
+
+ $filekey = $this->getOption( 'filekey' );
+ $filename = $this->getOption( 'filename' );
+
+ if ( $filekey === '*' ) { // all versions by name
+ if ( !strlen( $filename ) ) {
+ $this->error( "Missing --filename parameter.", 1 );
+ }
+ $afile = false;
+ } else { // specified version
+ $dbw = wfGetDB( DB_MASTER );
+ $row = $dbw->selectRow( 'filearchive', '*',
+ array( 'fa_storage_group' => 'deleted', 'fa_storage_key' => $filekey ),
+ __METHOD__ );
+ if ( !$row ) {
+ $this->error( "No deleted file exists with key '$filekey'.", 1 );
+ }
+ $filename = $row->fa_name;
+ $afile = ArchivedFile::newFromRow( $row );
+ }
+
+ $file = wfLocalFile( $filename );
+ if ( $file->exists() ) {
+ $this->error( "File '$filename' is still a public file, use the delete form.\n", 1 );
+ }
+
+ $this->output( "Purging all thumbnails for file '$filename'..." );
+ $file->purgeCache();
+ $file->purgeHistory();
+ $this->output( "done.\n" );
+
+ if ( $afile instanceof ArchivedFile ) {
+ $this->scrubVersion( $afile );
+ } else {
+ $this->output( "Finding deleted versions of file '$filename'...\n" );
+ $this->scrubAllVersions( $filename );
+ $this->output( "Done\n" );
+ }
+ }
+
+ protected function scrubAllVersions( $name ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $res = $dbw->select( 'filearchive', '*',
+ array( 'fa_name' => $name, 'fa_storage_group' => 'deleted' ),
+ __METHOD__ );
+ foreach ( $res as $row ) {
+ $this->scrubVersion( ArchivedFile::newFromRow( $row ) );
+ }
+ }
+
+ protected function scrubVersion( ArchivedFile $archivedFile ) {
+ $key = $archivedFile->getStorageKey();
+ $name = $archivedFile->getName();
+ $ts = $archivedFile->getTimestamp();
+ $repo = RepoGroup::singleton()->getLocalRepo();
+ $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key;
+ if ( $this->hasOption( 'delete' ) ) {
+ $status = $repo->getBackend()->delete( array( 'src' => $path ) );
+ if ( $status->isOK() ) {
+ $this->output( "Deleted version '$key' ($ts) of file '$name'\n" );
+ } else {
+ $this->output( "Failed to delete version '$key' ($ts) of file '$name'\n" );
+ $this->output( print_r( $status->getErrorsArray(), true ) );
+ }
+ } else {
+ $this->output( "Would delete version '{$key}' ({$ts}) of file '$name'\n" );
+ }
+ }
+}
+
+$maintClass = "EraseArchivedFile";
+require_once( RUN_MAINTENANCE_IF_MAIN );
"classes": [
"mw.Title",
"mw.notification",
+ "mw.user",
"mw.util",
"mw.plugin.*"
]
"../../resources/mediawiki/mediawiki.Title.js",
"../../resources/mediawiki/mediawiki.notify.js",
"../../resources/mediawiki/mediawiki.notification.js",
+ "../../resources/mediawiki/mediawiki.user.js",
"../../resources/mediawiki.api",
"../../resources/jquery/jquery.localize.js"
]
'create-this-page',
'delete',
'deletethispage',
+ 'undeletethispage',
'undelete_short',
'viewdeleted_short',
'protect',
$this->output( "\nDeleting inactive accounts..." );
$dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'user', array( 'user_id' => $del ), __METHOD__ );
+ $dbw->delete( 'user_groups', array( 'ug_user' => $del ), __METHOD__ );
+ $dbw->delete( 'user_former_groups', array( 'ufg_user' => $del ), __METHOD__ );
+ $dbw->delete( 'user_properties', array( 'up_user' => $del ), __METHOD__ );
$dbw->delete( 'logging', array( 'log_user' => $del ), __METHOD__ );
$dbw->delete( 'recentchanges', array( 'rc_user' => $del ), __METHOD__ );
$this->output( "done.\n" );
parent::__construct();
$this->mDescription = "Send SQL queries to a MediaWiki database";
$this->addOption( 'cluster', 'Use an external cluster by name', false, true );
+ $this->addOption( 'slave', 'Use a slave server (either "any" or by name)', false, true );
}
public function execute() {
- // Get a DB handle (with this wiki's DB select) from the appropriate load balancer
+ // Get the appropriate load balancer (for this wiki)
if ( $this->hasOption( 'cluster' ) ) {
$lb = wfGetLBFactory()->getExternalLB( $this->getOption( 'cluster' ) );
- $dbw = $lb->getConnection( DB_MASTER ); // master for external LB
} else {
- $dbw = wfGetDB( DB_MASTER ); // master for primary LB for this wiki
+ $lb = wfGetLB();
}
+ // Figure out which server to use
+ if ( $this->hasOption( 'slave' ) ) {
+ $server = $this->getOption( 'slave' );
+ if ( $server === 'any' ) {
+ $index = DB_SLAVE;
+ } else {
+ $index = null;
+ for ( $i = 0; $i < $lb->getServerCount(); ++$i ) {
+ if ( $lb->getServerName( $i ) === $server ) {
+ $index = $i;
+ break;
+ }
+ }
+ if ( $index === null ) {
+ $this->error( "No slave server configured with the name '$server'.", 1 );
+ }
+ }
+ } else {
+ $index = DB_MASTER;
+ }
+ // Get a DB handle (with this wiki's DB selected) from the appropriate load balancer
+ $dbw = $lb->getConnection( $index );
+ if ( $this->hasOption( 'slave' ) && $dbw->getLBInfo( 'master' ) !== null ) {
+ $this->error( "The server selected ({$dbw->getServer()}) is not a slave.", 1 );
+ }
+
if ( $this->hasArg( 0 ) ) {
$file = fopen( $this->getArg( 0 ), 'r' );
if ( !$file ) {
'user.tokens',
),
),
+ 'mediawiki.icon' => array(
+ 'styles' => 'resources/mediawiki/mediawiki.icon.css',
+ ),
'mediawiki.debug' => array(
'scripts' => 'resources/mediawiki/mediawiki.debug.js',
'styles' => 'resources/mediawiki/mediawiki.debug.css',
),
'mediawiki.special.changeslist' => array(
'styles' => 'resources/mediawiki.special/mediawiki.special.changeslist.css',
- 'dependencies' => array( 'jquery.makeCollapsible' ),
+ 'dependencies' => array( 'jquery.makeCollapsible', 'mediawiki.icon' ),
),
'mediawiki.special.movePage' => array(
'scripts' => 'resources/mediawiki.special/mediawiki.special.movePage.js',
/* Private Members */
/**
- * @var profileCache {Object} Keyed by userAgent string,
+ * @var {Object} profileCache Keyed by userAgent string,
* value is the parsed $.client.profile object for that user agent.
*/
var profileCache = {};
/**
* Get an object containing information about the client.
*
- * @param nav {Object} An object with atleast a 'userAgent' and 'platform' key.
+ * @param {Object} nav An object with atleast a 'userAgent' and 'platform' key.
* Defaults to the global Navigator object.
- * @return {Object} The resulting client object will be in the following format:
+ * @returns {Object} The resulting client object will be in the following format:
* {
* 'name': 'firefox',
* 'layout': 'gecko',
// Generic version digit
x = 'x',
// Strings found in user agent strings that need to be conformed
- wildUserAgents = ['Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3'],
+ wildUserAgents = ['Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3', 'Iceweasel'],
// Translations for conforming user agent strings
userAgentTranslations = [
// Tons of browsers lie about being something they are not
- [/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/, ''],
+ [/(Firefox|MSIE|KHTML,?\slike\sGecko|Konqueror)/, ''],
// Chrome lives in the shadow of Safari still
['Chrome Safari', 'Chrome'],
// KHTML is the layout engine not the browser - LIES!
// version detectection
versionPrefixes = [
'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'netscape6', 'opera', 'version', 'konqueror',
- 'lynx', 'msie', 'safari', 'ps3'
+ 'lynx', 'msie', 'safari', 'ps3', 'android'
],
// Used as matches 2, 3 and 4 in version extraction - 3 is used as actual version number
versionSuffix = '(\\/|\\;?\\s|)([a-z0-9\\.\\+]*?)(\\;|dev|rel|\\)|\\s|$)',
// Names of known browsers
names = [
'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'konqueror', 'lynx', 'msie', 'opera',
- 'safari', 'ipod', 'iphone', 'blackberry', 'ps3', 'rekonq'
+ 'safari', 'ipod', 'iphone', 'blackberry', 'ps3', 'rekonq', 'android'
],
// Tanslations for conforming browser names
nameTranslations = [],
},
/**
- * Checks the current browser against a support map object to determine if the browser has been black-listed or
- * not. If the browser was not configured specifically it is assumed to work. It is assumed that the body
- * element is classified as either "ltr" or "rtl". If neither is set, "ltr" is assumed.
+ * Checks the current browser against a support map object.
*
* A browser map is in the following format:
* {
+ * // Multiple rules with configurable operators
+ * 'msie': [['>=', 7], ['!=', 9]],
+ * // Match no versions
+ * 'iphone': false,
+ * // Match any version
+ * 'android': null
+ * }
+ *
+ * It can optionally be split into ltr/rtl sections:
+ * {
* 'ltr': {
- * // Multiple rules with configurable operators
- * 'msie': [['>=', 7], ['!=', 9]],
- * // Blocked entirely
+ * 'android': null,
* 'iphone': false
* },
* 'rtl': {
- * // Test against a string
- * 'msie': [['!==', '8.1.2.3']],
- * // RTL rules do not fall through to LTR rules, you must explicity set each of them
+ * 'android': false,
+ * // rules are not inherited from ltr
* 'iphone': false
* }
* }
*
- * @param map {Object} Browser support map
- * @param profile {Object} (optional) a client-profile object.
+ * @param {Object} map Browser support map
+ * @param {Object} [profile] A client-profile object
+ * @param {boolean} [exactMatchOnly=false] Only return true if the browser is matched, otherwise
+ * returns true if the browser is not found.
*
- * @return Boolean true if browser known or assumed to be supported, false if blacklisted
+ * @returns {boolean} The current browser is in the support map
*/
- test: function ( map, profile ) {
+ test: function ( map, profile, exactMatchOnly ) {
/*jshint evil: true */
var conditions, dir, i, op, val;
profile = $.isPlainObject( profile ) ? profile : $.client.profile();
- dir = $( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr';
+ if ( map.ltr && map.rtl ) {
+ dir = $( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr';
+ map = map[dir];
+ }
// Check over each browser condition to determine if we are running in a compatible client
- if ( typeof map[dir] !== 'object' || map[dir][profile.name] === undefined ) {
- // Unknown, so we assume it's working
- return true;
+ if ( typeof map !== 'object' || map[profile.name] === undefined ) {
+ // Not found, return true if exactMatchOnly not set, false otherwise
+ return !exactMatchOnly;
}
- conditions = map[dir][profile.name];
+ conditions = map[profile.name];
if ( conditions === false ) {
+ // Match no versions
return false;
}
+ if ( conditions === null ) {
+ // Match all versions
+ return true;
+ }
for ( i = 0; i < conditions.length; i++ ) {
op = conditions[i][0];
val = conditions[i][1];
}
+ /**
+ * Sets the sort count of the columns that are not affected by the sorting to have them sorted
+ * in default (ascending) order when their header cell is clicked the next time.
+ *
+ * @param {jQuery} $headers
+ * @param {Number[][]} sortList
+ * @param {Number[][]} headerToColumns
+ */
+ function setHeadersOrder( $headers, sortList, headerToColumns ) {
+ // Loop through all headers to retrieve the indices of the columns the header spans across:
+ $.each( headerToColumns, function( headerIndex, columns ) {
+
+ $.each( columns, function( i, columnIndex ) {
+ var header = $headers[headerIndex];
+
+ if ( !isValueInArray( columnIndex, sortList ) ) {
+ // Column shall not be sorted: Reset header count and order.
+ header.order = 0;
+ header.count = 0;
+ } else {
+ // Column shall be sorted: Apply designated count and order.
+ $.each( sortList, function( j, sortColumn ) {
+ if ( sortColumn[0] === i ) {
+ header.order = sortColumn[1];
+ header.count = sortColumn[1] + 1;
+ return false;
+ }
+ } );
+ }
+ } );
+
+ } );
+ }
+
function isValueInArray( v, a ) {
var l = a.length;
for ( var i = 0; i < l; i++ ) {
$.each( sortObjects, function( i, sortObject ) {
$.each ( sortObject, function( columnIndex, order ) {
var orderIndex = ( order === 'desc' ) ? 1 : 0;
- sortList.push( [columnIndex, orderIndex] );
+ sortList.push( [parseInt( columnIndex, 10 ), orderIndex] );
} );
} );
return sortList;
}
}
+ // Reset order/counts of cells not affected by sorting
+ setHeadersOrder( $headers, config.sortList, headerToColumns );
+
// Set CSS for headers
setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, columnToHeader );
appendToTable(
sortList = convertSortList( sortList );
}
+ // Set each column's sort count to be able to determine the correct sort
+ // order when clicking on a header cell the next time
+ setHeadersOrder( $headers, sortList, headerToColumns );
+
// re-build the cache for the tbody cells
cache = buildCache( table );
cursor: pointer;
}
-.mw-enhancedchanges-arrow.mw-collapsible-toggle-collapsed {
- /* @embed */
- background: url(images/arrow-collapsed-ltr.png) no-repeat left bottom;
-}
-
-.mw-enhancedchanges-arrow.mw-collapsible-toggle-expanded {
- /* @embed */
- background: url(images/arrow-expanded.png) no-repeat left bottom;
-}
-
.mw-changeslist-line-watched .mw-title,
.mw-enhanced-watched .mw-enhanced-rc-time {
font-weight: bold;
--- /dev/null
+/* General-purpose icons via CSS. Classes here should be named "mw-icon-*". */
+
+/* For the collapsed and expanded arrows, we also provide selectors to make it
+ * easy to use them with jquery.makeCollapsible. */
+.mw-icon-arrow-collapsed,
+.mw-collapsible-arrow.mw-collapsible-toggle-collapsed {
+ /* @embed */
+ background: url(images/arrow-collapsed-ltr.png) no-repeat left bottom;
+}
+
+.mw-icon-arrow-expanded,
+.mw-collapsible-arrow.mw-collapsible-toggle-expanded {
+ /* @embed */
+ background: url(images/arrow-expanded.png) no-repeat left bottom;
+}
-/*
- * Implementation for mediaWiki.user
+/**
+ * @class mw.user
+ * @singleton
*/
-
( function ( mw, $ ) {
+ var callbacks, options, tokens, user;
/**
- * User object
+ * Gets the current user's groups or rights.
+ *
+ * @private
+ * @param {string} info One of 'groups' or 'rights'
+ * @param {Function} callback
*/
- function User( options, tokens ) {
- var user, callbacks;
-
- /* Private Members */
-
- user = this;
- callbacks = {};
-
- /**
- * Gets the current user's groups or rights.
- * @param {String} info: One of 'groups' or 'rights'.
- * @param {Function} callback
- */
- function getUserInfo( info, callback ) {
- var api;
- if ( callbacks[info] ) {
- callbacks[info].add( callback );
- return;
- }
- callbacks.rights = $.Callbacks('once memory');
- callbacks.groups = $.Callbacks('once memory');
+ function getUserInfo( info, callback ) {
+ var api;
+ if ( callbacks[info] ) {
callbacks[info].add( callback );
- api = new mw.Api();
- api.get( {
- action: 'query',
- meta: 'userinfo',
- uiprop: 'rights|groups'
- } ).always( function ( data ) {
- var rights, groups;
- if ( data.query && data.query.userinfo ) {
- rights = data.query.userinfo.rights;
- groups = data.query.userinfo.groups;
- }
- callbacks.rights.fire( rights || [] );
- callbacks.groups.fire( groups || [] );
- } );
+ return;
}
+ callbacks.rights = $.Callbacks('once memory');
+ callbacks.groups = $.Callbacks('once memory');
+ callbacks[info].add( callback );
+ api = new mw.Api();
+ api.get( {
+ action: 'query',
+ meta: 'userinfo',
+ uiprop: 'rights|groups'
+ } ).always( function ( data ) {
+ var rights, groups;
+ if ( data.query && data.query.userinfo ) {
+ rights = data.query.userinfo.rights;
+ groups = data.query.userinfo.groups;
+ }
+ callbacks.rights.fire( rights || [] );
+ callbacks.groups.fire( groups || [] );
+ } );
+ }
- /* Public Members */
-
- this.options = options || new mw.Map();
+ callbacks = {};
- this.tokens = tokens || new mw.Map();
+ // Extend the skeleton mw.user from mediawiki.js
+ // This is kind of ugly but we're stuck with this for b/c reasons
+ options = mw.user.options || new mw.Map();
+ tokens = mw.user.tokens || new mw.Map();
- /* Public Methods */
+ mw.user = user = {
+ options: options,
+ tokens: tokens,
/**
* Generates a random user session ID (32 alpha-numeric characters).
* This information would potentially be stored in a cookie to identify a user during a
* session or series of sessions. Its uniqueness should not be depended on.
*
- * @return String: Random set of 32 alpha-numeric characters
+ * @return {string} Random set of 32 alpha-numeric characters
*/
- this.generateRandomSessionId = function () {
+ generateRandomSessionId: function () {
var i, r,
id = '',
seed = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
id += seed.substring( r, r + 1 );
}
return id;
- };
+ },
/**
* Gets the current user's name.
*
- * @return Mixed: User name string or null if users is anonymous
+ * @return {string|null} User name string or null if users is anonymous
*/
- this.getName = function () {
+ getName: function () {
return mw.config.get( 'wgUserName' );
- };
+ },
/**
- * @deprecated since 1.20 use mw.user.getName() instead
+ * @inheritdoc #getName
+ * @deprecated since 1.20 use #getName instead
*/
- this.name = function () {
- return this.getName();
- };
+ name: function () {
+ return user.getName();
+ },
/**
* Get date user registered, if available.
*
- * @return {Date|false|null} date user registered, or false for anonymous users, or
+ * @return {Date|boolean|null} Date user registered, or false for anonymous users, or
* null when data is not available
*/
- this.getRegistration = function () {
+ getRegistration: function () {
var registration = mw.config.get( 'wgUserRegistration' );
- if ( this.isAnon() ) {
+ if ( user.isAnon() ) {
return false;
} else if ( registration === null ) {
// Information may not be available if they signed up before
} else {
return new Date( registration );
}
- };
+ },
/**
* Checks if the current user is anonymous.
*
- * @return Boolean
+ * @return {boolean}
*/
- this.isAnon = function () {
+ isAnon: function () {
return user.getName() === null;
- };
+ },
/**
- * @deprecated since 1.20 use mw.user.isAnon() instead
+ * @inheritdoc #isAnon
+ * @deprecated since 1.20 use #isAnon instead
*/
- this.anonymous = function () {
+ anonymous: function () {
return user.isAnon();
- };
+ },
/**
- * Gets a random session ID automatically generated and kept in a cookie.
+ * Gets a random ID automatically generated and stored in a session cookie.
*
* This ID is ephemeral for everyone, staying in their browser only until they close
* their browser.
*
- * @return String: User name or random session ID
+ * @return {string} Random session ID
*/
- this.sessionId = function () {
+ sessionId: function () {
var sessionId = $.cookie( 'mediaWiki.user.sessionId' );
if ( typeof sessionId === 'undefined' || sessionId === null ) {
sessionId = user.generateRandomSessionId();
$.cookie( 'mediaWiki.user.sessionId', sessionId, { 'expires': null, 'path': '/' } );
}
return sessionId;
- };
+ },
/**
* Gets the current user's name or the session ID
*
* @return {string} User name or random session ID
*/
- this.id = function () {
+ id: function () {
var name = user.getName();
if ( name ) {
return name;
}
return user.sessionId();
- };
+ },
/**
* Gets the user's bucket, placing them in one at random based on set odds if needed.
*
- * @param key String: Name of bucket
- * @param options Object: Bucket configuration options
- * @param options.buckets Object: List of bucket-name/relative-probability pairs (required,
- * must have at least one pair)
- * @param options.version Number: Version of bucket test, changing this forces rebucketing
- * (optional, default: 0)
- * @param options.expires Number: Length of time (in days) until the user gets rebucketed
- * (optional, default: 30)
- * @return String: Bucket name - the randomly chosen key of the options.buckets object
- *
- * @example
* mw.user.bucket( 'test', {
* 'buckets': { 'ignored': 50, 'control': 25, 'test': 25 },
* 'version': 1,
* 'expires': 7
* } );
+ *
+ * @param {string} key Name of bucket
+ * @param {Object} options Bucket configuration options
+ * @param {Object} options.buckets List of bucket-name/relative-probability pairs (required,
+ * must have at least one pair)
+ * @param {number} options.version Version of bucket test, changing this forces rebucketing
+ * (optional, default: 0)
+ * @param {number} options.expires Length of time (in days) until the user gets rebucketed
+ * (optional, default: 30)
+ * @return {string} Bucket name - the randomly chosen key of the options.buckets object
*/
- this.bucket = function ( key, options ) {
+ bucket: function ( key, options ) {
var cookie, parts, version, bucket,
range, k, rand, total;
);
}
return bucket;
- };
+ },
/**
* Gets the current user's groups.
+ *
+ * @param {Function} callback
*/
- this.getGroups = function ( callback ) {
+ getGroups: function ( callback ) {
getUserInfo( 'groups', callback );
- };
+ },
/**
* Gets the current user's rights.
+ *
+ * @param {Function} callback
*/
- this.getRights = function ( callback ) {
+ getRights: function ( callback ) {
getUserInfo( 'rights', callback );
- };
- }
-
- // Extend the skeleton mw.user from mediawiki.js
- // This is kind of ugly but we're stuck with this for b/c reasons
- mw.user = new User( mw.user.options, mw.user.tokens );
+ }
+ };
}( mediaWiki, jQuery ) );
}
/**
- * Load skin and user CSS files in the correct order
- * fixes bug 22916
+ * Loads skin and user CSS files.
* @param $out OutputPage object
*/
function setupSkinUserCss( OutputPage $out ) {
'MockFSFile' => "$testDir/phpunit/mocks/filebackend/MockFSFile.php",
'MockFileBackend' => "$testDir/phpunit/mocks/filebackend/MockFileBackend.php",
'MockBitmapHandler' => "$testDir/phpunit/mocks/media/MockBitmapHandler.php",
+ 'MockImageHandler' => "$testDir/phpunit/mocks/media/MockBitmapHandler.php",
+ 'MockSvgHandler' => "$testDir/phpunit/mocks/media/MockBitmapHandler.php",
# tests/phpunit/languages
'LanguageClassesTestCase' => "$testDir/phpunit/languages/LanguageClassesTestCase.php",
<?php
/**
- * Helper code for the MediaWiki parser test suite.
+ * Helper code for the MediaWiki parser test suite. Some code is duplicated
+ * in PHPUnit's NewParserTests.php, so you'll probably want to update both
+ * at the same time.
*
* Copyright © 2004, 2010 Brion Vibber <brion@pobox.com>
* http://www.mediawiki.org/
'wgNoFollowDomainExceptions' => array(),
'wgThumbnailScriptPath' => false,
'wgUseImageResize' => true,
+ 'wgSVGConverter' => 'null',
+ 'wgSVGConverters' => array( 'null' => 'echo "1">$output' ),
'wgLocaltimezone' => 'UTC',
'wgAllowExternalImages' => true,
'wgUseTidy' => false,
# Clear the message cache
MessageCache::singleton()->clear();
+ // Remember to update newParserTests.php after changing the below
+ // (and it uses a slightly different syntax just for teh lulz)
$this->uploadDir = $this->setupUploadDir();
$user = User::createNew( 'WikiSysop' );
$image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
'fileExists' => true
), $this->db->timestamp( '20130225203040' ), $user );
+ $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
+ $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', array(
+ 'size' => 12345,
+ 'width' => 240,
+ 'height' => 180,
+ 'bits' => 24,
+ 'media_type' => MEDIATYPE_DRAWING,
+ 'mime' => 'image/svg+xml',
+ 'metadata' => serialize( array() ),
+ 'sha1' => wfBaseConvert( '', 16, 36, 31 ),
+ 'fileExists' => true
+ ), $this->db->timestamp( '20010115123500' ), $user );
+
# This image will be blacklisted in [[MediaWiki:Bad image list]]
$image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
$image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', array(
copy( "$IP/skins/monobook/wiki.png", "$dir/e/ea/Thumb.png" );
wfMkdirParents( $dir . '/0/09', null, __METHOD__ );
copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" );
-
+ wfMkdirParents( $dir . '/f/ff', null, __METHOD__ );
+ copy( "$IP/skins/monobook/headbg.jpg", "$dir/f/ff/Foobar.svg" );
+ file_put_contents( "$dir/f/ff/Foobar.svg",
+ '<?xml version="1.0" encoding="utf-8"?>' .
+ '<svg xmlns="http://www.w3.org/2000/svg" />' );
return $dir;
}
"$dir/0/09/Bad.jpg",
+ "$dir/f/ff/Foobar.svg",
+ "$dir/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png",
+ "$dir/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png",
+ "$dir/thumb/f/ff/Foobar.svg/langde-270px-Foobar.svg.png",
+ "$dir/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png",
+ "$dir/thumb/f/ff/Foobar.svg/langde-180px-Foobar.svg.png",
+ "$dir/thumb/f/ff/Foobar.svg/langde-360px-Foobar.svg.png",
+
"$dir/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
)
);
"$dir/thumb/3/3a/Foobar.jpg",
"$dir/thumb/3/3a",
"$dir/thumb/3",
-
"$dir/e/ea",
"$dir/e",
-
+ "$dir/f/ff/",
+ "$dir/f/",
+ "$dir/thumb/f/ff/Foobar.svg",
+ "$dir/thumb/f/ff/",
+ "$dir/thumb/f/",
"$dir/0/09/",
"$dir/0/",
"$dir/thumb",
!! input
{{NUMBEROFFILES}}
!! result
-<p>3
+<p>4
</p>
!! end
!! end
+
+!! test
+SVG thumbnails with no language set
+!! options
+!! input
+[[File:Foobar.svg|thumb|width=200]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.svg" class="image"><img alt="" src="http://example.com/images/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png" width="180" height="180" class="thumbimage" srcset="http://example.com/images/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png 1.5x, http://example.com/images/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png 2x" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.svg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>width=200</div></div></div>
+
+!! end
+
+!! test
+SVG thumbnails with language de
+!! options
+!! input
+[[File:Foobar.svg|thumb|width=200|lang=de]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/index.php?title=File:Foobar.svg&lang=de" class="image"><img alt="" src="http://example.com/images/thumb/f/ff/Foobar.svg/langde-180px-Foobar.svg.png" width="180" height="180" class="thumbimage" srcset="http://example.com/images/thumb/f/ff/Foobar.svg/langde-270px-Foobar.svg.png 1.5x, http://example.com/images/thumb/f/ff/Foobar.svg/langde-360px-Foobar.svg.png 2x" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.svg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>width=200</div></div></div>
+
+!! end
+
+!! test
+SVG thumbnails with invalid language code
+!! options
+!! input
+[[File:Foobar.svg|thumb|width=200|lang=invalid.language.code]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.svg" class="image"><img alt="" src="http://example.com/images/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png" width="180" height="180" class="thumbimage" srcset="http://example.com/images/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png 1.5x, http://example.com/images/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png 2x" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.svg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>lang=invalid.language.code</div></div></div>
+
+!! end
+
!! test
BUG 1887: A ISBN with a thumbnail
!! input
</p>
!! end
+
###
### Parsoids-specific tests
### Parsoid-PHP parser incompatibilities
*
* options['printableQuery'] - value of query string for printable, or omitted for none
* options['handheldQuery'] - value of query string for handheld, or omitted for none
- * options['handheldForIPhone'] - value of the $wgHandheldForIPhone global
* options['media'] - passed into the method under the same name
* options['expectedReturn'] - expected return value
* options['message'] - PHPUnit message for assertion
$fauxRequest = new FauxRequest( $queryData, false );
$this->setMWGlobals( array(
'wgRequest' => $fauxRequest,
- 'wgHandheldForIPhone' => $args['handheldForIPhone']
) );
$actualReturn = OutputPage::transformCssMedia( $args['media'] );
$this->assertSame( $args['expectedReturn'], $actualReturn, $args['message'] );
}
- /**
- * Tests a case of transformCssMedia with both values of wgHandheldForIPhone.
- * Used to verify that behavior is orthogonal to that option.
- *
- * If the value of wgHandheldForIPhone should matter, use assertTransformCssMediaCase.
- *
- * @param array $args key-value array of arguments as shown in assertTransformCssMediaCase.
- * Will be mutated.
- */
- protected function assertTransformCssMediaCaseWithBothHandheldForIPhone( $args ) {
- $message = $args['message'];
- foreach ( array( true, false ) as $handheldForIPhone ) {
- $args['handheldForIPhone'] = $handheldForIPhone;
- $stringHandheldForIPhone = var_export( $handheldForIPhone, true );
- $args['message'] = "$message. \$wgHandheldForIPhone was $stringHandheldForIPhone";
- $this->assertTransformCssMediaCase( $args );
- }
- }
-
/**
* Tests print requests
*/
public function testPrintRequests() {
- $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+ $this->assertTransformCssMediaCase( array(
'printableQuery' => '1',
'media' => 'screen',
'expectedReturn' => null,
'message' => 'On printable request, screen returns null'
) );
- $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+ $this->assertTransformCssMediaCase( array(
'printableQuery' => '1',
'media' => self::SCREEN_MEDIA_QUERY,
'expectedReturn' => null,
'message' => 'On printable request, screen media query returns null'
) );
- $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+ $this->assertTransformCssMediaCase( array(
'printableQuery' => '1',
'media' => self::SCREEN_ONLY_MEDIA_QUERY,
'expectedReturn' => null,
'message' => 'On printable request, screen media query with only returns null'
) );
- $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+ $this->assertTransformCssMediaCase( array(
'printableQuery' => '1',
'media' => 'print',
'expectedReturn' => '',
*/
public function testScreenRequests() {
$this->assertTransformCssMediaCase( array(
- 'handheldForIPhone' => false,
'media' => 'screen',
'expectedReturn' => 'screen',
- 'message' => 'On screen request, with handheldForIPhone false, screen media type is preserved'
+ 'message' => 'On screen request, screen media type is preserved'
+ ) );
+
+ $this->assertTransformCssMediaCase( array(
+ 'media' => 'handheld',
+ 'expectedReturn' => 'handheld',
+ 'message' => 'On screen request, handheld media type is preserved'
) );
- $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+ $this->assertTransformCssMediaCase( array(
'media' => self::SCREEN_MEDIA_QUERY,
'expectedReturn' => self::SCREEN_MEDIA_QUERY,
'message' => 'On screen request, screen media query is preserved.'
) );
- $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+ $this->assertTransformCssMediaCase( array(
'media' => self::SCREEN_ONLY_MEDIA_QUERY,
'expectedReturn' => self::SCREEN_ONLY_MEDIA_QUERY,
'message' => 'On screen request, screen media query with only is preserved.'
) );
- $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+ $this->assertTransformCssMediaCase( array(
'media' => 'print',
'expectedReturn' => 'print',
'message' => 'On screen request, print media type is preserved'
}
/**
- * Tests handheld and wgHandheldForIPhone behavior
+ * Tests handheld behavior
*/
public function testHandheld() {
- $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+ $this->assertTransformCssMediaCase( array(
'handheldQuery' => '1',
'media' => 'handheld',
'expectedReturn' => '',
'message' => 'On request with handheld querystring and media is handheld, returns empty string'
) );
- $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+ $this->assertTransformCssMediaCase( array(
'handheldQuery' => '1',
'media' => 'screen',
'expectedReturn' => null,
'message' => 'On request with handheld querystring and media is screen, returns null'
) );
-
- // A bit counter-intuitively, $wgHandheldForIPhone should only matter if the query handheld is false or omitted
- $this->assertTransformCssMediaCase( array(
- 'handheldQuery' => '0',
- 'media' => 'screen',
- 'handheldForIPhone' => true,
- 'expectedReturn' => 'screen and (min-device-width: 481px)',
- 'message' => 'With $wgHandheldForIPhone true, screen media type is transformed'
- ) );
-
- $this->assertTransformCssMediaCase( array(
- 'media' => 'handheld',
- 'handheldForIPhone' => true,
- 'expectedReturn' => 'handheld, only screen and (max-device-width: 480px)',
- 'message' => 'With $wgHandheldForIPhone true, handheld media type is transformed'
- ) );
-
- $this->assertTransformCssMediaCase( array(
- 'media' => 'handheld',
- 'handheldForIPhone' => false,
- 'expectedReturn' => 'handheld',
- 'message' => 'With $wgHandheldForIPhone false, handheld media type is preserved'
- ) );
}
}
--- /dev/null
+<?php
+/**
+ * PHPUnit tests for XMLTypeCheck.
+ * @author physikerwelt
+ * @group ?
+ * @covers XMLTypeCheck
+ */
+class XmlTypeCheckTest extends MediaWikiTestCase {
+ const WELL_FORMED_XML = "<root><child /></root>";
+ const MAL_FORMED_XML = "<root><child /></error>";
+
+ /**
+ * @covers XMLTypeCheck::newFromString
+ * @covers XMLTypeCheck::getRootElement
+ */
+ public function testWellFormedXML() {
+ $testXML = XmlTypeCheck::newFromString( self::WELL_FORMED_XML );
+ $this->assertTrue( $testXML->wellFormed );
+ $this->assertEquals( 'root', $testXML->getRootElement() );
+ }
+
+ /**
+ * @covers XMLTypeCheck::newFromString
+ */
+ public function testMalFormedXML() {
+ $testXML = XmlTypeCheck::newFromString( self::MAL_FORMED_XML );
+ $this->assertFalse( $testXML->wellFormed );
+ }
+
+}
\ No newline at end of file
public function getParserOutput( Title $title, $revId = null, ParserOptions $options = null, $generateHtml = true ) {
return new ParserOutput( $this->getNativeData() );
}
+
+ /**
+ * @see AbstractContent::fillParserOutput()
+ *
+ * @param $title Title Context title for parsing
+ * @param $revId int|null Revision ID (for {{REVISIONID}})
+ * @param $options ParserOptions|null Parser options
+ * @param $generateHtml bool Whether or not to generate HTML
+ * @param $output ParserOutput The output object to fill (reference).
+ */
+ protected function fillParserOutput( Title $title, $revId,
+ ParserOptions $options, $generateHtml, ParserOutput &$output ) {
+ $output = new ParserOutput( $this->getNativeData() );
+ }
}
* @group Database
* ^--- needed, because we do need the database to test link updates
*/
-class CssContentTest extends MediaWikiTestCase {
+class CssContentTest extends JavaScriptContentTest {
protected function setUp() {
parent::setUp();
*/
class TextContentTest extends MediaWikiLangTestCase {
protected $context;
+ protected $savedContentGetParserOutput;
protected function setUp() {
+ global $wgHooks;
+
parent::setUp();
// Anon user
$user = new User();
$user->setName( '127.0.0.1' );
+ $this->context = new RequestContext( new FauxRequest() );
+ $this->context->setTitle( Title::newFromText( 'Test' ) );
+ $this->context->setUser( $user );
+
$this->setMwGlobals( array(
'wgUser' => $user,
'wgTextModelsToParse' => array(
'wgAlwaysUseTidy' => false,
) );
- $this->context = new RequestContext( new FauxRequest() );
- $this->context->setTitle( Title::newFromText( 'Test' ) );
- $this->context->setUser( $user );
+ // bypass hooks that force custom rendering
+ if ( isset( $wgHooks['ContentGetParserOutput'] ) ) {
+ $this->savedContentGetParserOutput = $wgHooks['ContentGetParserOutput'];
+ unset( $wgHooks['ContentGetParserOutput'] );
+ }
+ }
+
+ public function teardown() {
+ global $wgHooks;
+
+ // restore hooks that force custom rendering
+ if ( $this->savedContentGetParserOutput !== null ) {
+ $wgHooks['ContentGetParserOutput'] = $this->savedContentGetParserOutput;
+ }
+
+ parent::teardown();
}
public function newContent( $text ) {
);
}
+ /**
+ * @dataProvider provideUpsert
+ */
+ function testUpsert( $sql, $sqlText ) {
+ $this->database->upsert(
+ $sql['table'],
+ $sql['rows'],
+ $sql['uniqueIndexes'],
+ $sql['set'],
+ __METHOD__
+ );
+ $this->assertLastSql( $sqlText );
+ }
+
+ public static function provideUpsert() {
+ return array(
+ array(
+ array(
+ 'table' => 'upsert_table',
+ 'rows' => array( 'field' => 'text', 'field2' => 'text2' ),
+ 'uniqueIndexes' => array( 'field' ),
+ 'set' => array( 'field' => 'set' ),
+ ),
+ "BEGIN; " .
+ "UPDATE upsert_table " .
+ "SET field = 'set' " .
+ "WHERE ((field = 'text')); " .
+ "INSERT IGNORE INTO upsert_table " .
+ "(field,field2) " .
+ "VALUES ('text','text2'); " .
+ "COMMIT"
+ ),
+ );
+ }
+
/**
* @dataProvider provideDeleteJoin
*/
'#settings td p strong'
),
array(
- # Not sure how 4+ values should behave,
- # testing to make sure changes are detected
- '.foo { x-unknown: 1 2 3 4 5; }',
- '.foo { x-unknown: 1 4 3 2 5; }',
+ // Do not mangle 5 or more values
+ '.foo { -x-unknown: 1 2 3 4 5; }'
),
array(
- '.foo { x-unknown: 1 2 3 4 5 6; }',
- '.foo { x-unknown: 1 4 3 2 5 6; }',
+ '.foo { -x-unknown: 1 2 3 4 5 6; }'
),
// Shorthand / Three notation
$tmpGlobals['wgParser'] = new StubObject( 'wgParser', $GLOBALS['wgParserConf']['class'], array( $GLOBALS['wgParserConf'] ) );
+ $tmpGlobals['wgFileExtensions'][] = 'svg';
+ $tmpGlobals['wgSVGConverter'] = 'rsvg';
+ $tmpGlobals['wgSVGConverters']['rsvg'] = '$path/rsvg-convert -w $width -h $height $input -o $output';
+
if ( $GLOBALS['wgStyleDirectory'] === false ) {
$tmpGlobals['wgStyleDirectory'] = "$IP/skins";
}
foreach ( $wgMediaHandlers as $type => $handler ) {
$tmpGlobals['wgMediaHandlers'][$type] = 'MockBitmapHandler';
}
+ // Vector images have to be handled slightly differently
+ $tmpGlobals['wgMediaHandlers']['image/svg+xml'] = 'MockSvgHandler';
$tmpHooks = $wgHooks;
$tmpHooks['ParserTestParser'][] = 'ParserTestParserHook::setup';
$this->db->timestamp( '20010115123500' ), $user
);
}
+ $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
+ if ( !$this->db->selectField( 'image', '1', array( 'img_name' => $image->getName() ) ) ) {
+ $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', array(
+ 'size' => 12345,
+ 'width' => 200,
+ 'height' => 200,
+ 'bits' => 24,
+ 'media_type' => MEDIATYPE_DRAWING,
+ 'mime' => 'image/svg+xml',
+ 'metadata' => serialize( array() ),
+ 'sha1' => wfBaseConvert( '', 16, 36, 31 ),
+ 'fileExists' => true
+ ), $this->db->timestamp( '20010115123500' ), $user );
+ }
}
//ParserTest setup/teardown functions
$backend->store( array(
'src' => "$IP/skins/monobook/headbg.jpg", 'dst' => "$base/local-public/0/09/Bad.jpg"
) );
+
+ // No helpful SVG file to copy, so make one ourselves
+ $tmpDir = wfTempDir();
+ $tempFsFile = new TempFSFile( "$tmpDir/Foobar.svg" );
+ $tempFsFile->autocollect(); // destroy file when $tempFsFile leaves scope
+ file_put_contents( "$tmpDir/Foobar.svg",
+ '<?xml version="1.0" encoding="utf-8"?>' .
+ '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"><text>Foo</text></svg>' );
+
+ $backend->prepare( array( 'dir' => "$base/local-public/f/ff" ) );
+ $backend->quickStore( array(
+ 'src' => "$tmpDir/Foobar.svg", 'dst' => "$base/local-public/f/ff/Foobar.svg"
+ ) );
}
/**
"$base/local-public/0/09/Bad.jpg",
+ "$base/local-public/f/ff/Foobar.svg",
+ "$base/local-thumb/f/ff/Foobar.svg/180px-Foobar.svg.jpg",
+ "$base/local-thumb/f/ff/Foobar.svg/270px-Foobar.svg.jpg",
+ "$base/local-thumb/f/ff/Foobar.svg/360px-Foobar.svg.jpg",
+ "$base/local-thumb/f/ff/Foobar.svg/langde-180px-Foobar.svg.jpg",
+ "$base/local-thumb/f/ff/Foobar.svg/langde-270px-Foobar.svg.jpg",
+ "$base/local-thumb/f/ff/Foobar.svg/langde-360px-Foobar.svg.jpg",
+
"$base/local-public/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
)
);
* @ingroup Media
*/
+class MockBitmapHandler extends BitmapHandler {
+ function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+ return MockImageHandler::doFakeTransform( $this, $image, $dstPath, $dstUrl, $params, $flags );
+ }
+ function doClientImage( $image, $scalerParams ) {
+ return $this->getClientScalingThumbnailImage( $image, $scalerParams );
+ }
+}
+class MockSvgHandler extends SvgHandler {
+ function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+ return MockImageHandler::doFakeTransform( $this, $image, $dstPath, $dstUrl, $params, $flags );
+ }
+}
/**
* Mock handler for images.
*
*
* @ingroup Media
*/
-class MockBitmapHandler extends BitmapHandler {
+class MockImageHandler {
/**
* Override BitmapHandler::doTransform() making sure we do not generate
* will be consumed by the unit test. There is no need to create a real
* thumbnail on the filesystem.
*/
- function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+ static function doFakeTransform( $that, $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
# Example of what we receive:
# $image: LocalFile
# $dstPath: /tmp/transform_7d0a7a2f1a09-1.jpg
# $dstUrl : http://example.com/images/thumb/0/09/Bad.jpg/320px-Bad.jpg
# $params: width: 320, descriptionUrl http://trunk.dev/wiki/File:Bad.jpg
- $this->normaliseParams( $image, $params );
+ $that->normaliseParams( $image, $params );
$scalerParams = array(
# The size to which the image will be resized
# In some cases, we do not bother generating a thumbnail.
if ( !$image->mustRender() &&
$scalerParams['physicalWidth'] == $scalerParams['srcWidth']
- && $scalerParams['physicalHeight'] == $scalerParams['srcHeight'] ) {
+ && $scalerParams['physicalHeight'] == $scalerParams['srcHeight']
+ ) {
wfDebug( __METHOD__ . ": returning unscaled image\n" );
- return $this->getClientScalingThumbnailImage( $image, $scalerParams );
+ // getClientScalingThumbnailImage is protected
+ return $that->doClientImage( $image, $scalerParams );
}
return new ThumbnailImage( $image, $dstUrl, false, $params );
$maintClass = 'PHPUnitMaintClass';
require RUN_MAINTENANCE_IF_MAIN;
-require_once 'PHPUnit/Runner/Version.php';
+if ( !class_exists( 'PHPUnit_Runner_Version') ) {
+ require_once 'PHPUnit/Runner/Version.php';
+}
if ( PHPUnit_Runner_Version::id() !== '@package_version@'
&& version_compare( PHPUnit_Runner_Version::id(), '3.6.7', '<' )
) {
die( 'PHPUnit 3.6.7 or later required, you have ' . PHPUnit_Runner_Version::id() . ".\n" );
}
-require_once 'PHPUnit/Autoload.php';
+
+if ( !class_exists( 'PHPUnit_TextUI_Command' ) ) {
+ require_once 'PHPUnit/Autoload.php';
+}
MediaWikiPHPUnitCommand::main();
( function ( $ ) {
- var uacount, uas, testMap;
QUnit.module( 'jquery.client', QUnit.newMwEnvironment() );
- /** Number of user-agent defined */
- uacount = 0;
-
- uas = ( function () {
-
+ var uacount = 0,
// Object keyed by userAgent. Value is an array (human-readable name, client-profile object, navigator.platform value)
// Info based on results from http://toolserver.org/~krinkle/testswarm/job/174/
- var uas = {
+ uas = {
// Internet Explorer 6
// Internet Explorer 7
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)': {
rtl: true
}
},
+ // Iceweasel 15.0.1
+ 'Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1 Iceweasel/15.0.1': {
+ title: 'Iceweasel 15.0.1',
+ platform: 'Linux',
+ profile: {
+ name: 'iceweasel',
+ layout: 'gecko',
+ layoutVersion: 20100101,
+ platform: 'linux',
+ version: '15.0.1',
+ versionBase: '15',
+ versionNumber: 15
+ },
+ wikiEditor: {
+ ltr: true,
+ rtl: true
+ }
+ },
// Firefox 5
// Safari 3
// Safari 4
}
},
// Safari 5
+ // Safari 6
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/536.29.13 (KHTML, like Gecko) Version/6.0.4 Safari/536.29.13': {
+ title: 'Safari 6',
+ platform: 'MacIntel',
+ profile: {
+ name: 'safari',
+ layout: 'webkit',
+ layoutVersion: 536,
+ platform: 'mac',
+ version: '6.0.4',
+ versionBase: '6',
+ versionNumber: 6
+ },
+ wikiEditor: {
+ ltr: true,
+ rtl: true
+ }
+ },
+ // Safari 6.0.5+ (doesn't have the comma in "KHTML, like Gecko")
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1': {
+ title: 'Safari 6',
+ platform: 'MacIntel',
+ profile: {
+ name: 'safari',
+ layout: 'webkit',
+ layoutVersion: 536,
+ platform: 'mac',
+ version: '6.0.5',
+ versionBase: '6',
+ versionNumber: 6
+ },
+ wikiEditor: {
+ ltr: true,
+ rtl: true
+ }
+ },
// Opera 10+
'Opera/9.80 (Windows NT 5.1)': {
title: 'Opera 10+ (exact version unspecified)',
rtl: true
}
},
+ // Android WebKit Browser 2.3
+ 'Mozilla/5.0 (Linux; U; Android 2.3.5; en-us; HTC Vision Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1': {
+ title: 'Android WebKit Browser 2.3',
+ platform: 'Linux armv7l',
+ profile: {
+ name: 'android',
+ layout: 'webkit',
+ layoutVersion: 533,
+ platform: 'linux',
+ version: '2.3.5',
+ versionBase: '2',
+ versionNumber: 2.3
+ },
+ wikiEditor: {
+ ltr: true,
+ rtl: true
+ }
+ },
// Bug #34924
'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.34 (KHTML, like Gecko) rekonq Safari/534.34': {
title: 'Rekonq',
rtl: true
}
}
- };
- $.each( uas, function () {
- uacount++;
- } );
- return uas;
- }() );
-
- QUnit.test( 'profile userAgent support', uacount, function ( assert ) {
- // Generate a client profile object and compare recursively
- var uaTest = function ( rawUserAgent, data ) {
- var ret = $.client.profile( {
- userAgent: rawUserAgent,
- platform: data.platform
- } );
- assert.deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent );
- };
+ },
+ testMap = {
+ // Example from WikiEditor
+ // Make sure to use raw numbers, a string like "7.0" would fail on a
+ // version 10 browser since in string comparaison "10" is before "7.0" :)
+ 'ltr': {
+ 'msie': [['>=', 7.0]],
+ 'firefox': [['>=', 2]],
+ 'opera': [['>=', 9.6]],
+ 'safari': [['>=', 3]],
+ 'chrome': [['>=', 3]],
+ 'netscape': [['>=', 9]],
+ 'blackberry': false,
+ 'ipod': false,
+ 'iphone': false
+ },
+ 'rtl': {
+ 'msie': [['>=', 8]],
+ 'firefox': [['>=', 2]],
+ 'opera': [['>=', 9.6]],
+ 'safari': [['>=', 3]],
+ 'chrome': [['>=', 3]],
+ 'netscape': [['>=', 9]],
+ 'blackberry': false,
+ 'ipod': false,
+ 'iphone': false
+ }
+ }
+ ;
- // Loop through and run tests
- $.each( uas, uaTest );
+ // Count test cases
+ $.each( uas, function () {
+ uacount++;
} );
- QUnit.test( 'profile return validation for current user agent', 7, function ( assert ) {
+ QUnit.test( 'profile( navObject )', 7, function ( assert ) {
var p = $.client.profile();
function unknownOrType( val, type, summary ) {
assert.equal( typeof p.versionNumber, 'number', 'p.versionNumber is a number' );
} );
- // Example from WikiEditor
- // Make sure to use raw numbers, a string like "7.0" would fail on a
- // version 10 browser since in string comparaison "10" is before "7.0" :)
- testMap = {
- 'ltr': {
- 'msie': [['>=', 7.0]],
- 'firefox': [['>=', 2]],
- 'opera': [['>=', 9.6]],
- 'safari': [['>=', 3]],
- 'chrome': [['>=', 3]],
- 'netscape': [['>=', 9]],
- 'blackberry': false,
- 'ipod': false,
- 'iphone': false
- },
- 'rtl': {
- 'msie': [['>=', 8]],
- 'firefox': [['>=', 2]],
- 'opera': [['>=', 9.6]],
- 'safari': [['>=', 3]],
- 'chrome': [['>=', 3]],
- 'netscape': [['>=', 9]],
- 'blackberry': false,
- 'ipod': false,
- 'iphone': false
- }
- };
+ QUnit.test( 'profile( navObject ) - samples', uacount, function ( assert ) {
+ // Loop through and run tests
+ $.each( uas, function ( rawUserAgent, data ) {
+ // Generate a client profile object and compare recursively
+ var ret = $.client.profile( {
+ userAgent: rawUserAgent,
+ platform: data.platform
+ } );
+ assert.deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent );
+ } );
+ } );
- QUnit.test( 'test', 1, function ( assert ) {
+ QUnit.test( 'test( testMap )', 4, function ( assert ) {
// .test() uses eval, make sure no exceptions are thrown
// then do a basic return value type check
- var testMatch = $.client.test( testMap );
+ var testMatch = $.client.test( testMap ),
+ ie7Profile = $.client.profile( {
+ 'userAgent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)',
+ 'platform': ''
+ } );
+
+ assert.equal( typeof testMatch, 'boolean', 'map with ltr/rtl split returns a boolean value' );
+
+ testMatch = $.client.test( testMap.ltr );
+
+ assert.equal( typeof testMatch, 'boolean', 'simple map (without ltr/rtl split) returns a boolean value' );
+
+ assert.equal( $.client.test( {
+ 'msie': null
+ }, ie7Profile ), true, 'returns true if any version of a browser are allowed (null)' );
+
+ assert.equal( $.client.test( {
+ 'msie': false
+ }, ie7Profile ), false, 'returns false if all versions of a browser are not allowed (false)' );
+ } );
+
+ QUnit.test( 'test( testMap, exactMatchOnly )', 2, function ( assert ) {
+ var ie7Profile = $.client.profile( {
+ 'userAgent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)',
+ 'platform': ''
+ } );
- assert.equal( typeof testMatch, 'boolean', 'test returns a boolean value' );
+ assert.equal( $.client.test( {
+ 'firefox': [['>=', 2]]
+ }, ie7Profile, false ), true, 'returns true if browser not found and exactMatchOnly not set' );
+ assert.equal( $.client.test( {
+ 'firefox': [['>=', 2]]
+ }, ie7Profile, true ), false, 'returns false if browser not found and exactMatchOnly is set' );
} );
- QUnit.test( 'User-agent matches against WikiEditor\'s compatibility map', uacount * 2, function ( assert ) {
+ QUnit.test( 'test( testMap) - WikiEditor sample', uacount * 2, function ( assert ) {
var $body = $( 'body' ),
bodyClasses = $body.attr( 'class' );
$table.find( '.headerSort:eq(0)' ).click();
}
);
+ tableTest(
+ 'Basic planet table: ascending by name (multiple clicks)',
+ header,
+ planets,
+ ascendingName,
+ function ( $table ) {
+ $table.tablesorter();
+ $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(1)' ).click();
+ $table.find( '.headerSort:eq(0)' ).click();
+ }
+ );
tableTest(
'Basic planet table: descending by name',
header,
$table.data( 'tablesorter' ).sort();
}
);
+ tableTest(
+ 'Sort via click event after having initialized the tablesorter with initial sorting',
+ header,
+ initial,
+ descasc,
+ function ( $table ) {
+ $table.tablesorter(
+ { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
+ );
+ $table.find( '.headerSort:eq(0)' ).click();
+ }
+ );
+ tableTest(
+ 'Multi-sort via click event after having initialized the tablesorter with initial sorting',
+ header,
+ initial,
+ asc,
+ function ( $table ) {
+ $table.tablesorter(
+ { sortList: [ { 0: 'desc' }, { 1: 'desc' } ] }
+ );
+ $table.find( '.headerSort:eq(0)' ).click();
+
+ // Pretend to click while pressing the multi-sort key
+ var event = $.Event( 'click' );
+ event[$table.data( 'tablesorter' ).config.sortMultiSortKey] = true;
+ $table.find( '.headerSort:eq(1)' ).trigger( event );
+ }
+ );
QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
var $table = tableCreate( header, initial );
$table.tablesorter(
$table.find( '.headerSort:eq(0)' ).click();
}
);
+ tableTest( 'Sorting with colspanned headers: sort spanned column twice',
+ header,
+ initial,
+ [ caa4, bbc2, abc3, aab5, aaa1 ],
+ function ( $table ) {
+ // Make colspanned header for test
+ $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
+ $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
+
+ $table.tablesorter();
+ $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).click();
+ }
+ );
tableTest( 'Sorting with colspanned headers: subsequent column',
header,
initial,
$table.find( '.headerSort:eq(1)' ).click();
}
);
+ tableTest( 'Sorting with colspanned headers: sort subsequent column twice',
+ header,
+ initial,
+ [ aab5, caa4, abc3, bbc2, aaa1 ],
+ function ( $table ) {
+ // Make colspanned header for test
+ $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
+ $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
+
+ $table.tablesorter();
+ $table.find( '.headerSort:eq(1)' ).click();
+ $table.find( '.headerSort:eq(1)' ).click();
+ }
+ );
+
// Regression tests!
tableTest(
== Details==
-Automated Selenium test scripts written for MediaWiki Installer is available at https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/core.git;a=tree;f=tests/selenium/installer;hb=HEAD.
+Automated Selenium test scripts written for MediaWiki Installer is available at https://git.wikimedia.org/tree/mediawiki%2Fcore.git/HEAD/tests%2Fselenium%2Finstaller.
Detailed test cases available at http://www.mediawiki.org/wiki/New_installer/Test_plan.
Version : MediaWiki 1.18alpha