Merge "resourceloader: Improve test cases for MessageBlobStore"
authorKrinkle <krinklemail@gmail.com>
Thu, 28 Mar 2019 20:37:42 +0000 (20:37 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 28 Mar 2019 20:37:42 +0000 (20:37 +0000)
112 files changed:
.phpcs.xml
HISTORY
RELEASE-NOTES-1.33
autoload.php
includes/Block.php
includes/DefaultSettings.php
includes/GlobalFunctions.php
includes/HistoryBlob.php [deleted file]
includes/Linker.php
includes/MovePage.php
includes/SiteConfiguration.php
includes/Title.php
includes/actions/HistoryAction.php
includes/actions/RollbackAction.php
includes/api/ApiQueryRevisions.php
includes/api/ApiQuerySiteinfo.php
includes/api/ApiStashEdit.php
includes/content/WikiTextStructure.php
includes/deferred/LinksDeletionUpdate.php
includes/deferred/LinksUpdate.php
includes/historyblob/ConcatenatedGzipHistoryBlob.php [new file with mode: 0644]
includes/historyblob/DiffHistoryBlob.php [new file with mode: 0644]
includes/historyblob/HistoryBlob.php [new file with mode: 0644]
includes/historyblob/HistoryBlobCurStub.php [new file with mode: 0644]
includes/historyblob/HistoryBlobStub.php [new file with mode: 0644]
includes/htmlform/fields/HTMLDateTimeField.php
includes/http/PhpHttpRequest.php
includes/installer/Installer.php
includes/installer/i18n/en.json
includes/installer/i18n/qqq.json
includes/jobqueue/jobs/ThumbnailRenderJob.php
includes/libs/objectcache/APCBagOStuff.php
includes/libs/objectcache/APCUBagOStuff.php
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/CachedBagOStuff.php
includes/libs/objectcache/EmptyBagOStuff.php
includes/libs/objectcache/HashBagOStuff.php
includes/libs/objectcache/MemcachedBagOStuff.php
includes/libs/objectcache/MemcachedPeclBagOStuff.php
includes/libs/objectcache/MultiWriteBagOStuff.php
includes/libs/objectcache/RESTBagOStuff.php
includes/libs/objectcache/RedisBagOStuff.php
includes/libs/objectcache/ReplicatedBagOStuff.php
includes/libs/objectcache/WANObjectCache.php
includes/libs/objectcache/WinCacheBagOStuff.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/DatabaseDomain.php
includes/libs/rdbms/database/DatabaseMssql.php
includes/libs/rdbms/database/DatabasePostgres.php
includes/libs/rdbms/database/IDatabase.php
includes/objectcache/SqlBagOStuff.php
includes/preferences/DefaultPreferencesFactory.php
includes/specialpage/ChangesListSpecialPage.php
includes/specialpage/FormSpecialPage.php
includes/specialpage/QueryPage.php
includes/specials/SpecialActiveusers.php
includes/specials/SpecialAllMessages.php
includes/specials/SpecialAutoblockList.php
includes/specials/SpecialBlockList.php
includes/specials/SpecialBooksources.php
includes/specials/SpecialComparePages.php
includes/specials/SpecialContributions.php
includes/specials/SpecialListusers.php
includes/specials/SpecialNewpages.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialSearch.php
includes/specials/SpecialUpload.php
includes/specials/SpecialUploadStash.php
includes/specials/forms/PreferencesFormOOUI.php
includes/specials/pagers/ContribsPager.php
languages/Language.php
languages/i18n/be-tarask.json
languages/i18n/da.json
languages/i18n/en.json
languages/i18n/exif/ja.json
languages/i18n/fr.json
languages/i18n/fy.json
languages/i18n/hr.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/ml.json
languages/i18n/pl.json
languages/i18n/qqq.json
languages/i18n/ru.json
languages/i18n/sah.json
languages/i18n/sc.json
languages/i18n/sk.json
languages/i18n/so.json
languages/i18n/sr-ec.json
languages/i18n/sv.json
languages/i18n/tr.json
languages/i18n/uk.json
languages/i18n/zh-hant.json
languages/messages/MessagesNqo.php
maintenance/findOrphanedFiles.php
maintenance/install.php
maintenance/mediawiki.Title/generateJsToUpperCaseList.js [new file with mode: 0644]
maintenance/mediawiki.Title/generatePhpCharToUpperMappings.php [new file with mode: 0755]
profileinfo.php
resources/Resources.php
resources/lib/jquery.ui/jquery.ui.spinner.js [deleted file]
resources/lib/jquery.ui/themes/smoothness/jquery.ui.spinner.css [deleted file]
resources/src/mediawiki.Title/Title.js
resources/src/mediawiki.Title/phpCharToUpper.js [deleted file]
resources/src/mediawiki.Title/phpCharToUpper.json [new file with mode: 0644]
resources/src/mediawiki.special.preferences.ooui/tabs.js
resources/src/mediawiki.special.preferences.styles.ooui.less
resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
tests/phpunit/includes/db/DatabasePostgresTest.php
tests/phpunit/includes/libs/objectcache/CachedBagOStuffTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseMssqlTest.php

index d1e54a7..ce0eac4 100644 (file)
                <exclude-pattern>*/includes/Feed\.php</exclude-pattern>
                <exclude-pattern>*/includes/filerepo/file/LocalFile\.php</exclude-pattern>
                <exclude-pattern>*/includes/gallery/PackedOverlayImageGallery\.php</exclude-pattern>
-               <exclude-pattern>*/includes/HistoryBlob\.php</exclude-pattern>
                <exclude-pattern>*/includes/htmlform/HTMLFormElement\.php</exclude-pattern>
                <exclude-pattern>*/includes/libs/filebackend/FileBackendStore\.php</exclude-pattern>
                <exclude-pattern>*/includes/libs/filebackend/FSFileBackend\.php</exclude-pattern>
diff --git a/HISTORY b/HISTORY
index e409813..921b881 100644 (file)
--- a/HISTORY
+++ b/HISTORY
@@ -14927,6 +14927,154 @@ MediaWiki's parser test suite can now be expanded with additional test
 files. Custom extensions can add their test files to this array, and
 they will be run along with the main tests by maintenance/parserTests.php
 
+= MediaWiki 1.8=
+
+== MediaWiki 1.8.5 ==
+
+September 10, 2007
+
+This is a security fix update to the Fall 2006 quarterly release snapshot. A
+possible HTML/XSS injection vector in the API pretty-printing mode has been
+found and fixed.
+
+The vulnerability may be worked around in an unfixed version by simply
+disabling the API interface if it is not in use, by adding this to
+LocalSettings.php:
+
+:[[Manual:$wgEnableAPI|$wgEnableAPI]] = false;
+
+(This is the default setting in 1.8.x.)
+
+Not vulnerable versions:
+* 1.11 >= 1.11.0
+* 1.10 >= 1.10.2
+* 1.9 >= 1.9.4
+* 1.8 >= 1.8.5
+
+Vulnerable versions:
+* 1.11 <= 1.11.0rc1
+* 1.10 <= 1.10.1
+* 1.9 <= 1.9.3
+* 1.8 <= 1.8.4 (if $wgEnableAPI has been switched on)
+
+MediaWiki 1.7 and below are not affected as they do not include the faulty
+function, however the BotQuery extension is similarly vulnerable unless updated
+to the latest SVN version.
+
+== MediaWiki 1.8.4 ==
+
+February 20, 2007
+
+This is a security and bug-fix update to the Fall 2006 quarterly release.
+
+An XSS injection vulnerability based on Microsoft Internet Explorer's UTF-7
+charset autodetection was located in the AJAX support module, affecting MSIE
+users on MediaWiki 1.6.x and up when the optional setting
+[[Manual:$wgUseAjax|$wgUseAjax]] is enabled.
+
+If you are using an extension based on the optional Ajax module, either disable
+it or upgrade to a version containing the fix:
+* 1.9: fixed in 1.9.3
+* 1.8: fixed in 1.8.4
+* 1.7: fixed in 1.7.3
+* 1.6: fixed in 1.6.10
+
+There is no known danger in the default configuration, with $wgUseAjax off.
+
+* (bug [[bugzilla:8819|8819]]) Fix full path disclosure with skins dependencies
+* Add 'charset' to Content-Type headers on various HTTP error responses to
+forestall additional UTF-7-autodetect XSS issues. PHP sends only 'text/html' by
+default when the script didn't specify more details, which some inconsiderate
+browsers consider a license to autodetect the deadly, hard-to-escape UTF-7.
+This fixes an issue with the Ajax interface error message on MSIE when
+[[Manual:$wgUseAjax|$wgUseAjax]] is enabled (not default configuration); this
+UTF-7 variant on a previously fixed attack vector was discovered by Moshe BA
+from BugSec: http://www.bugsec.com/articles.php?Security=24
+* Trackback responses now specify XML content type
+
+== MediaWiki 1.8.3 ==
+
+January 9, 2007
+
+MediaWiki 1.8.3 fixes several issues in the Fall 2006 snapshot release:
+
+* ([[mediazilla:7831|7831]]) Regression in AutoAuthenticate hook
+* Run PHP install version checks on update.php so command-line updaters see new
+version requirements
+* Do a check for the PHP 5.0.x 64-bit bug, since this is much more disruptive
+as of MW 1.8 than it used to be. Install or upgrade now aborts with a warning
+and a request to upgrade.
+* XSS fix in AJAX module
+
+An XSS injection vulnerability was located in the AJAX support module,
+affecting MediaWiki 1.6.x and up when the optional setting $wgUseAjax is
+enabled.
+
+There is no danger in the default configuration, with $wgUseAjax off.
+
+If you are using an extension based on the optional AJAX module, either disable
+it or upgrade to a version containing the fix:
+
+== MediaWiki 1.8.2 ==
+
+October 13, 2006
+
+MediaWiki 1.8.2 fixes several issues in the Fall 2006 snapshot release:
+
+* ([[mediazilla:7565|7565]]) Fixed typos in German localisation
+* ([[mediazilla:7562|7562]]) Fix non-ASCII namespaces on Windows/XAMPP servers
+
+== MediaWiki 1.8.1 ==
+
+October 11, 2006
+
+MediaWiki 1.8.1 fixes several issues in the Fall 2006 snapshot release:
+
+* Fix PHP notice and estimates for dumpBackup.php and friends
+* Improved register_globals paranoia checks
+* ([[mediazilla:7545|7545]]) Fix PHP version check on install
+* Experimental web API disabled by default
+* Disable PHP exception backtrace printing unless $wgShowExceptionDetails is
+set. Backtraces may contain sensitive information in function call parameters.
+
+== MediaWiki 1.8.0 ==
+
+October 10, 2006
+
+This is the quarterly release snapshot for Fall 2006. While the code has been
+running on Wikipedia for some time, installation and upgrade bits may be less
+well tested. Bug fix releases may follow in the coming days or weeks.
+
+MediaWiki is now using a "continuous integration" development model with
+quarterly snapshot releases. The latest development code is always kept "ready
+to run", and in fact runs our own sites on Wikipedia.
+
+Release branches will continue to receive security updates for about a year
+from first release, but nonessential bugfixes and feature development happen
+will be made on the development trunk and appear in the next quarterly release.
+
+Those wishing to use the latest code instead of a branch release can obtain it
+from source control: [[Download from SVN]]
+
+== Configuration changes ==
+* $wgUseETag, to enable/disable sending of HTTP ETag headers (default: disabled)
+* $wgLegalTitleChars now includes '+' by default for better compatibility with
+importing data dumps from Wikipedia
+* $wgDefaultUserOptions now includes all default option settings instead of
+only overrides.
+
+== Major new features ==
+* ([[mediazilla:7098|7098]]) Add an option to disable/enable sending of HTTP
+ETag headers, as it seems to result in broken behaviour in combination with
+Squid 2.6 (disabled by default).
+* ([[mediazilla:550|550]]) Allow blocks on anonymous users only.
+* ([[mediazilla:6420|6420]]) Render thumbnails for DJVU images, support
+multipage DJVU display on image pages. Added new 'page=' thumbnail option to
+select a page from a multipage djvu for thumbnail generation.
+* Full Postgres support is now enabled. It requires version 8.1 or better, and
+needs to have both plpgsql and tsearch2 already installed.
+* ([[mediazilla:6386|6386]]) fix grammatical errors in danish naming of talk
+namespaces.
 
 == Changes since 1.7 ==
 
@@ -15177,6 +15325,70 @@ they will be run along with the main tests by maintenance/parserTests.php
 * (bug 7537) Add php5 to $wgFileBlacklist
 * (bug 6929) Restore AutoAuthenticate hook
 
+== Languages updated ==
+* Albanian (sq)
+* Bashkir (ba)
+* Bavarian (bar) stub file
+* Belarusian (be)
+* Bishnupriya (bpy) stub file
+* Brazilian Portuguese (pt-br)
+* Cantonese (zh-yue)
+* Catalan (ca)
+* Czech (cs)
+* Dutch (nl)
+* English (en)
+* Finnish (fi)
+* French (fr)
+* Georgian (ka)
+* German (de)
+* Hebrew (he)
+* Hungarian (hu)
+* Indonesian (id)
+* Japanese (ja)
+* Korean (ko)
+* Latin (la)
+* Lojban (jbo)
+* Macedonian (mk)
+* Mazandarani (mzn)
+* Polish (pl)
+* Portuguese (pt)
+* Ripuarian (ksh)
+* Romani (rmy)
+* Russian (ru)
+* Slovak (sk)
+* Spanish (es)
+* Tajic (tg)
+* Tatar (tt)
+* Telugu (te)
+* Uzbek (uz)
+* Yiddish (yi)
+
+== Compatibility ==
+MediaWiki 1.8 requires PHP 5 (5.1 recommended). PHP 4 is no longer supported.
+
+MySQL 3.23.x is no longer supported; some older hosts may need to upgrade. At
+this time we still recommend 4.0, but 4.1/5.0 will work fine in most cases.
+
+== Upgrading ==
+Some minor database changes have been made since 1.7:
+* new fields and indexes on ipblocks
+* index change on recentchanges
+
+Several changes from 1.5 and 1.6 do require updates to be run on upgrade. To
+ensure that these tables are filled with data, run refreshLinks.php after the
+upgrade.
+
+If you are upgrading from MediaWiki 1.4.x or earlier, some major database
+changes are made, and there is a slightly higher chance that things could
+break. Don't forget to always back up your database before upgrading!
+
+=== Caveats ===
+Some output, particularly involving user-supplied inline HTML, may not produce
+100% valid or well-formed XHTML output. Testers are welcome to set $wgMimeType
+= "application/xhtml+xml"; to test for remaining problem cases, but this is not
+recommended on live sites. (This must be set for MathML to display properly in
+Mozilla.)
+
 = MediaWiki 1.7=
 
 == MediaWiki 1.7.3 ==
@@ -15952,6 +16164,362 @@ set $wgMimeType = "application/xhtml+xml"; to test for remaining problem
 cases, but this is not recommended on live sites. (This must be set for
 MathML to display properly in Mozilla.)
 
+= MediaWiki 1.6 =
+
+== MediaWiki 1.6.12 ==
+
+February 7, 2009
+
+This is a security update to the Spring 2006 quarterly release.
+
+A number of cross-site scripting (XSS) security vulnerabilities were discovered
+in the web-based installer (config/index.php). These vulnerabilities all
+require a live installer -- once the installer has been used to install a
+wiki,  it is deactivated.
+
+Note that cross-site scripting vulnerabilities can be used to attack any
+website in the same cookie domain. So if you have an uninstalled copy of
+MediaWiki on the same site as an active web service, MediaWiki could be used to
+attack the active service.
+
+If you are hosting an old copy of MediaWiki that you have never installed, you
+are advised to remove it from the web.
+
+== MediaWiki 1.6.11 ==
+
+December 15, 2008
+
+This is a security update to the Spring 2006 quarterly release.
+
+David Remahl of Apple's Product Security team has identified a number of
+security issues in previous releases of MediaWiki. Subsequent analysis by the
+MediaWiki development team expanded the scope of these vulnerabilities. The
+issues with a significant impact are as follows:
+
+* An XSS vulnerability affecting Internet Explorer clients for all MediaWiki
+installations with uploads enabled. [CVE-2008-5250]
+* An XSS vulnerability affecting clients with SVG scripting capability (such as
+Firefox 1.5+), for all MediaWiki installations with SVG uploads enabled.
+[CVE-2008-5250]
+* A CSRF vulnerability affecting the Special:Import feature, for all MediaWiki
+installations since the feature was introduced in 1.3.0. [CVE-2008-5252]
+
+XSS (cross-site scripting) vulnerabilities allow an attacker to steal an
+authorised user's login session, and to act as that user on the wiki. The
+authorised user must visit a web page controlled by the attacker in order to
+activate the attack. Intranet wikis are vulnerable if the attacker can
+determine the intranet URL, even if the attacker cannot access it.
+
+CSRF vulnerabilities allow an attacker to act as an authorised user on the
+wiki, but unlike an XSS vulnerability, the attacker can only act as the user in
+a specific and restricted way. The present CSRF vulnerability allows pages to
+be edited, with forged revision histories. Like an XSS vulnerability, the
+authorised user must visit the malicious web page to activate the attack.
+
+Rather than backport our SVG validation code to this ancient branch, we have
+instead disabled SVG uploads. To enable SVG uploads, please upgrade to
+MediaWiki 1.13.3 or later.
+
+The other two issues have been fixed.
+
+== MediaWiki 1.6.10 ==
+
+February 20, 2007
+
+This is a security and bug-fix update to the Spring 2006 quarterly release.
+
+An XSS injection vulnerability based on Microsoft Internet Explorer's UTF-7
+charset autodetection was located in the AJAX support module, affecting MSIE
+users on MediaWiki 1.6.x and up when the optional setting $wgUseAjax is enabled.
+
+If you are using an extension based on the optional Ajax module, either disable
+it or upgrade to a version containing the fix:
+
+* 1.9: fixed in 1.9.3
+* 1.8: fixed in 1.8.4
+* 1.7: fixed in 1.7.3
+* 1.6: fixed in 1.6.10
+
+There is no known danger in the default configuration, with $wgUseAjax off.
+
+* ([[mediazilla:8819|bug 8819]]) Fix full path disclosure with skins
+dependencies
+* Add 'charset' to Content-Type headers on various HTTP error responses to
+forestall additional UTF-7-autodetect XSS issues. PHP sends only 'text/html' by
+default when the script didn't specify more details, which some inconsiderate
+browsers consider a license to autodetect the deadly, hard-to-escape UTF-7.
+This fixes an issue with the Ajax interface error message on MSIE when
+$wgUseAjax is enabled (not default configuration); this UTF-7 variant on a
+previously fixed attack vector was discovered by Moshe BA from BugSec:
+http://www.bugsec.com/articles.php?Security=24
+* Trackback responses now specify XML content type
+
+== MediaWiki 1.6.9 ==
+
+January 9, 2007
+
+* ([[mediazilla:6621|bug 6621]]) Backported German translation for
+'eauthentsent'
+
+* ([[mediazilla:6680|bug 6680]]) Added localisation for Dutch bookstore list
+(nl)
+* ([[mediazilla:6730|bug 6730]]) Clearer usage of message 'titlematch' in
+German translation (de)
+* XSS fix in AJAX module
+
+An XSS injection vulnerability was located in the AJAX support module,
+affecting MediaWiki 1.6.x and up when the optional setting $wgUseAjax is
+enabled.
+
+There is no danger in the default configuration, with $wgUseAjax off.
+
+If you are using an extension based on the optional AJAX module, either disable
+it or upgrade to a version containing the fix:
+
+* 1.9: fixed in 1.9.0rc2
+* 1.8: fixed in 1.8.3
+* 1.7: fixed in 1.7.2
+* 1.6: fixed in 1.6.9
+
+== MediaWiki 1.6.8 ==
+
+July 8, 2006
+
+MediaWiki 1.6.8 is a security and bugfix maintenance release of the Spring 2006
+snapshot:
+
+A potential HTML/JavaScript-injection vulnerability in a debugging script has
+been fixed. Only versions and configurations of PHP vulnerable to the $GLOBALS
+overwrite vulnerability are affected.
+
+As a workaround for existing installs, profileinfo.php may simply be deleted if
+it's not being used.
+
+* ([[mediazilla:5957|bug 5957]]) Updates to Hebrew translation (he)
+* Respect language directionality when displaying arrow in
+Special:Brokenredirects
+* ([[mediazilla:6415|bug 6415]]) Typo in Parser.php
+* Fixed potential XSS in profileinfo.php
+
+== MediaWiki 1.6.7 ==
+
+June 6, 2006
+
+MediaWiki 1.6.7 is a security and bugfix maintenance release of the Spring 2006
+snapshot:
+
+An HTML/JavaScript-injection vulnerability in the edit form has been closed.
+This vulnerability was new in 1.6.0; MediaWiki versions 1.5.x or earlier are
+not affected.
+
+Extensions, comments, and <nowiki><nowiki></nowiki> sections are now handled in
+a one-pass way which is more reliable and safer. Under earlier versions of
+MediaWiki, certain extensions could be abused to inject HTML/JavaScript into
+the page.
+
+Additional precautions are made against offsite form submissions when the
+restricted raw HTML mode is enabled.
+
+Some small localization and user interface updates are also included.
+
+*([[MediaZilla:6051|bug 6051]]) Improvement to German localisation (de)
+*([[MediaZilla:6017|bug 6017]]) Update bookstore list for German language (de)
+*([[MediaZilla:6138|bug 6138]]) Minor grammar tweak in "loginreqlink"
+*([[MediaZilla:5957|bug 5957]]) Update for Hebrew language (he)
+*Increase robustness of parser placeholders; fixes some glitches when adjacent
+to identifier-ish constructs such as URLs.
+*([[MediaZilla:5384|bug 5384]]) Fix <nowiki><!-- comments --> in <ref></nowiki>
+extension
+*Nesting of different tag extensions and comments should now work more
+consistently and more safely. A cleaner, one-pass tag strip lets the 'outer'
+tag either take source (<nowiki><nowiki></nowiki>-style) or pass it down to
+further parsing (<nowiki><ref></nowiki>-style). There should no longer be
+surprise expansion of foreign extensions inside HTML output, or differences in
+behavior based on the order tags are loaded.
+*([[MediaZilla:885|bug 885]]) Pre-save transform no longer silently appends
+close tags
+*Pre-save transform no longer changes the case of close tags
+*Edit security precautions in raw HTML mode, etc
+
+== MediaWiki 1.6.6 ==
+
+May 23, 2006
+
+MediaWiki 1.6.6 is a security and bugfix maintenance release.
+
+An XSS injection vector in brace replacement has been fixed, as have some
+potential problems with table parsing. Upgrading is strongly recommended for
+all users of 1.6. MediaWiki versions 1.5 and earlier are not affected.
+
+Additionally some localization and user interface updates are included.
+
+* Correct "revertpage" message in English
+* ([[MediaZilla:5507|bug 5507]]) Logouttext now uses wiki markup
+* (bugs [[MediaZilla:5857|5857]], [[MediaZilla:5957|5957]]) Update for German
+localisation (de)
+* ([[MediaZilla:5586|bug 5586]]) <nowiki><gallery></nowiki> treated text as
+links
+* ([[MediaZilla:5957|bug 5957]]) Update for Hebrew language (he)
+* ([[MediaZilla:6025|bug 6025]]) SpecialImport: wrong message when no file
+selected
+* ([[MediaZilla:6015|bug 6015]]) EditPage: add spacing in the boxes "edit is
+minor" and "watch this"
+* ([[MediaZilla:6018|bug 6018]]) Userrights: new message when no user specified
+('nouserspecified')
+* ([[MediaZilla:6055|bug 6055]]) Fix for HTML/JS injection bug in variable
+handler (found by Nick Jenkins)
+* Reordered wiki table handling and <nowiki>__TOC__</nowiki> extraction in the
+parser to better handle some overlapping tag cases.
+* Only the first <nowiki>__TOC__</nowiki> is now turned into a TOC.
+* ([[MediaZilla:361|bug 361]]) URL in URL, they were almost fixed. Now they are.
+
+== MediaWiki 1.6.5 ==
+
+May 2, 2006
+
+* Rolled back the buggy patch for [[MediaZilla:5497|bug 5497]].
+
+== MediaWiki 1.6.4 ==
+
+May 2, 2006
+
+* Further improvements to Hebrew localisation
+* ([[MediaZilla:5544|bug 5544]]) Fix redirect arrow in Special:Listredirects
+for right-to-left languages
+* Replace "doubleredirectsarrow" with a content language check that picks the
+appropriate arrow
+* Remove live debugging hack which caused errors with certain database names
+* ([[MediaZilla:5510|bug 5510]]) Warning produced when using
+<nowiki>{{SUBPAGENAME}}</nowiki> in some namespaces
+* ([[MediaZilla:5548|bug 5548]]) Improvements to Indonesian localisation
+[patch: Ivan Lanin]
+* ([[MediaZilla:5403|bug 5403]]) Fix Special:Newpages RSS/Atom feeds
+* ([[MediaZilla:3359|bug 3359]]) Add hooks on completion of file upload
+* ([[MediaZilla:5184|bug 5184]]) CSS misapplied to elements in
+Special:Allmessages due to conflicting anchor identifiers
+* ([[MediaZilla:5519|bug 5519]]) Allow sidebar cache to be disabled; disable it
+by default.
+* Add $wgReservedUsernames configuration directive to block account creation/use
+* ([[MediaZilla:5576|bug 5576]]) Remove debugging hack in session check
+* ([[MediaZilla:5181|bug 5181]]) Update "nogomatch" for Slovak
+* ([[MediaZilla:5594|bug 5594]]) Id translation up to '# Login and logout
+pages' section
+* ([[MediaZilla:5536|bug 5536]]) Use content language for editing help link
+* Minor improvements to English language files
+* Improvements to German localisation files
+* ([[MediaZilla:5628|bug 5628]]) Translations for MessagesHr.php
+* (bugs [[MediaZilla:5595|5595]], [[MediaZilla:5644|5644]]) Localisation for
+Bosnian language (bs)
+* ([[MediaZilla:5592|bug 5592]]) Actions are logged with the default language
+for the wiki, not the language of the user performing the operation.
+* ([[MediaZilla:5646|bug 5646]]) Compare for identical types in wfElement()
+* Fix for concurrency problem in job queue (image description page invalidation)
+* ([[MediaZilla:5497|bug 5497]]) regeression in HTML normalization in 1.6
+(unclosed <nowiki><li>,<dd>,<dt></nowiki>)
+* ([[MediaZilla:5709|bug 5709]]) Allow customisation of separator for categories
+* ([[MediaZilla:4834|bug 4834]]) Fix XHTML output when using $wgMaxTocLevel
+* Improvements to update scripts; print out the version, check for superuser
+credentials before attempting a connection, and produce a friendlier error if
+the connection fails
+* ([[MediaZilla:5005|bug 5005]]): Fix XHTML <nowiki><gallery></nowiki> output.
+* ([[MediaZilla:5315|bug 5315]]) "Expires: -1" HTTP header made strictly valid
+(using 1970 date).
+* ([[MediaZilla:4825|bug 4825]]): note in DefaultSettings.php about 'profiling'
+table creation
+* Remove unneeded extra whitespace at top of Special:Categories
+* Rewrite reassignEdits script to be more efficient; support optional updates
+to recent changes table; add reporting and silent modes
+* Updated initStats maintenance script
+* ([[MediaZilla:5723|bug 5723]]) Don't count pages linked to from the MediaWiki
+namespace as "wanted"
+* ([[MediaZilla:5789|bug 5789]]) Treat "loginreqpagetext" as wikitext
+* ([[MediaZilla:5796|bug 5796]]) We require MySQL >=4.0.14
+
+== MediaWiki 1.6.3 ==
+
+April 10, 2006
+
+* Fix disappearing red-linked items in the watchlist editing view
+* ([[MediaZilla:5512|bug 5512]]) Spacing in "page has a history" deletion
+warning
+* ([[MediaZilla:5508|bug 5508]]) Switch ENGINE in table statements back to
+TYPE; fixes regression where some versions of MySQL 4.0.x wouldn't work
+* Added note about [[Manual:$wgUrlProtocols|$wgUrlProtocols]] format change
+
+== MediaWiki 1.6.2 ==
+
+April 8, 2006
+
+* Further improvements to Hebrew localisation
+* Fix 'copyright' message for Romanian
+* ([[MediaZilla:5476|bug 5476]]) Invalid xhtml in German localization
+* ([[MediaZilla:5479|bug 5479]]) Id translation for preferences tabs caption
+* ([[MediaZilla:5493|bug 5493]]) Id translation for special pages
+* Additional path fixes in the updater
+* ([[MediaZilla:5344|bug 5344]]) Fix regression that broke slashes in extension
+tag parameters
+
+== MediaWiki 1.6.1 ==
+
+April 5, 2006
+
+Some minor issues in the 1.6.0 release have been corrected:
+* ([[MediaZilla:5458|bug 5458]]) Fix double-URL encoding in block log link in
+contribs and contribs link in block log
+* ([[MediaZilla:5462|bug 5462]]) Bogus missing patch warning in updater
+* ([[MediaZilla:5461|bug 5461]]) Use of deprecated "showhideminor" in
+Special:Recentchangeslinked
+* PHP warning when allow_call_time_pass_reference is off
+* Update to Finnish localization
+
+== MediaWiki 1.6.0 ==
+
+April 5, 2006
+
+MediaWiki is now using a "continuous integration" development model with
+quarterly snapshot releases. The latest development code is always kept "ready
+to run", and in fact runs our own sites on Wikipedia.
+
+Release branches will continue to receive security updates for about a year
+from first release, but nonessential bugfixes and feature development will take
+place on the development trunk and will appear in the next quarterly release.
+
+Those wishing to use the latest code instead of a branch release can [[Download
+from SVN|obtain it from source control]].
+
+=== What's new in 1.6 ===
+
+'''User interface:'''
+* The account creation form has been separated from the user login form.
+* Page protection/unprotection uses a new, expanded form
+
+'''Templates:'''
+* Categories and "what links here" now update as expected when adding or
+removing links in a template.
+* Template parameters can now have default values, as <nowiki>{{{name|default
+value}}}</nowiki>
+
+'''Uploads:'''
+* Optional support for rasterizing SVG images to PNG for inline display
+
+'''Feeds:'''
+* Feed generation upgraded to Atom 1.0
+* Diffs in RSS and Atom feeds are now colored for improved readability.
+
+'''Database:'''
+* MySQL 3.23.x support dropped; 4.0 or later required
+* Experimental support for Unicode mode of MySQL 4.1/5.0 (moderately tested)
+* Experimental Oracle support (not well tested!)
+
+'''Anti-spam extension support:'''
+* [[meta:SpamBlacklist extension|SpamBlacklist extension]] now has support for
+automated cleanup.
+* Support for a [[meta:ConfirmEdit extension|captcha extension]] to restrict
+automated spam edits.
+
+Numerous bug fixes and other behind-the-scenes changes have been made; see the
+file HISTORY for a complete change list.
+
 == Changes since 1.5 ==
 
 * (bug 2885) More PHP 5.1 fixes: skin, search, log, undelete
@@ -16696,9 +17264,49 @@ fully support the editing toolbar, but was found to be too confusing.
 * (bug 2139) Show page title in subtitle when viewing "read only" page
 * (bug 5452) Update language name for Cree
 
+== Compatibility ==
+
+Older PHP 4.2 and 4.1 releases are no longer supported; PHP 4 users must
+upgrade to 4.3 or later.
+
+MediaWiki 1.6 is the last major version to support PHP 4; future versions will
+require PHP 5.
+
+MySQL 3.23.x is no longer supported; some older hosts may need to upgrade.
+At this time we still recommend 4.0, but 4.1/5.0 will work fine in most cases.
+
+== Upgrading ==
+
+Several changes to the database have been made from 1.5; these are relatively
+minor but do require that the update process be run before the new code will
+work properly:
+
+* A new "templatelinks" table tracks template inclusions.
+* A new "externallinks" table tracks URL links; this can be used by a mass
+spam-cleanup tool in the SpamBlacklist extension.
+* A new "jobs" table stores a queue of pages to update in the background; this
+is used to update links in including pages when templates are edited.
+
+To ensure that these tables are filled with data, run refreshLinks.php after
+the upgrade.
+
+If you are upgrading from MediaWiki 1.4.x or earlier, some major database
+changes are made, and there is a slightly higher chance that things could
+break. Don't forget to always back up your database before upgrading!
+
+=== Caveats ===
+
+Some output, particularly involving user-supplied inline HTML, may not produce
+100% valid or well-formed XHTML output. Testers are welcome to set $wgMimeType
+= "application/xhtml+xml"; to test for remaining problem cases, but this is not
+recommended on live sites. (This must be set for MathML to display properly in
+Mozilla.)
 
 
-----
+= MediaWiki 1.5 =
+
+== MediaWiki 1.5.9 ==
+* (bug 3359) Add hooks on completion of file upload
 
 == MediaWiki 1.5.8 ==
 
@@ -17824,7 +18432,141 @@ set $wgMimeType = "application/xhtml+xml"; to test for remaining problem
 cases, but this is not recommended on live sites. (This must be set for
 MathML to display properly in Mozilla.)
 
-----
+= MediaWiki 1.4 =
+
+== MediaWiki 1.4.15 ==
+
+(released March 26, 2006) MediaWiki 1.4.15 is a security maintenance release. A
+bug in decoding of certain encoded links could allow injection of raw HTML into
+page output; this could potentially lead to XSS attacks. Additionally, this
+release may display more correctly in IE7 betas.
+
+== MediaWiki 1.4.14 ==
+(released January 19, 2006) MediaWiki 1.4.14 is a security and bugfix
+maintenance release. A bug in edit comment formatting could send PHP into an
+infinite loop if certain malformed links were included. In most installations,
+this would cause the script to fail after PHP's 30-second failsafe timeout. For
+several other minor fixes, see the complete changelog at the end of this file.
+
+== MediaWiki 1.4.13 ==
+(released January 5, 2006) MediaWiki 1.4.13 is a security maintenance
+release.Detection for uploads of Windows Metafile (.wmf) images has been added
+to help protect against a client-side vulnerability in unpatched Microsoft
+Windows operating systems. Sites which have enabled uploads and added
+non-standard file types (such as .ogg, .doc, or .pdf) should upgrade to this
+release to ensure that malicious .wmf files can't be uploaded with a fake
+extension; such files could put visitors to the site at risk. For more details
+on this, see: http://en.wikipedia.org/wiki/Windows_Metafile_vulnerability
+
+== MediaWiki 1.4.12 ==
+(released 2005-11-02) MediaWiki 1.4.12 is a bugfix and security maintenance
+release. A change in PHP 4.4.1 broke handling of extension and
+<nowiki><pre></nowiki> sections, causing garbage data to be inserted in output
+and saved edits. This version works around the change. This release includes
+further corrections to the inline CSS style sanitation which works around a
+JavaScript "feature" on Microsoft Internet Explorer. Users of Microsoft
+Internet Explorer for Windows may be vulnerable to XSS injections on prior 1.4
+releases; users of standards-compliant browsers are not vulnerable.
+
+== MediaWiki 1.4.11 ==
+(released 2005-10-05) MediaWiki 1.4.11 is a security maintenance release.
+Unsafe handling of CSS by Microsoft Internet Explorer could be exploited to
+produce cross-site scripting attacks by JavaScript injection to clients running
+that browser. This release blacklists several additional variants from use in
+HTML inline style attributes. All publicly accessible wikis are recommended to
+upgrade to reduce the risk to visitors using Microsoft web browsers. Note: the
+MediaWiki 1.4.x series is not compatible with PHP 5.0.5 or higher. Upgrade to
+the 1.5.0 release if you require this version of PHP 5.
+
+== MediaWiki 1.4.10 ==
+(released 2005-09-21) MediaWiki 1.4.10 is a security maintenance release. A bug
+in edit submission handling could cause corruption of the previous revision in
+the database if an abnormal URL was used, such as those used by some spambots.
+Affected releases:
+* 1.4.x <= 1.4.9; fixed in 1.4.10
+* 1.3.x <= 1.3.15; fixed in 1.3.16
+1.5 release candidates are not affected by this problem. All publicly editable
+wikis are strongly recommended to upgrade immediately.
+1.4 releases can be manually patched by changing this bit in EditPage.php:
+
+<syntaxhighlight lang="php">
+function importFormData( &$request ) {
+        if( $request->wasPosted() ) {
+</syntaxhighlight>
+to:
+<syntaxhighlight lang="php">
+    function importFormData( &$request ) {
+        if( $request->getVal( 'action' ) == 'submit' && $request->wasPosted() )
+        {
+</syntaxhighlight>
+== MediaWiki 1.4.9 ==
+(released 2005-08-29) MediaWiki 1.4.9 is a security maintenance release. It
+corrects two cross-site scripting security bugs:
+* <nowiki><math></nowiki> tags were handled incorrectly when TeX rendering
+support is off, as in the default configuration.
+* Extension or <nowiki><nowiki></nowiki> sections in Wiki table syntax could
+bypass HTML style attribute restrictions for cross-site scripting attacks
+against Microsoft Internet Explorer Wikis where the optional math support has
+been *enabled* are not vulnerable to the first, but are vulnerable to the
+second.
+
+== MediaWiki 1.4.8 ==
+(released 2005-08-23) MediaWiki 1.4.8 is a bug fix and security maintenance
+release. A flaw in the interaction between extensions and HTML attribute
+sanitization was discovered which could allow unauthorized use of offsite
+resources in style sheets, and possible exploitation of a JavaScript injection
+feature on Microsoft Internet Explorer. This version expands the returned text
+and properly checks it before output. Additionally, an update to
+skins/MonoBook.php ensures that sites using the default MonoBook skin will
+display correctly in the Internet Explorer 7 beta. (1.3 and 1.5 are not
+affected by this bug.)
+
+== MediaWiki 1.4.7 ==
+(released 2005-07-16)
+MediaWiki 1.4.7 is a bug fix release. Those affected by the following problems
+in 1.4.6 should upgrade:
+* Watchlist breakage on MySQL 3.23.x and with table prefix enabled
+* Possible breakage in watchlist, some image resizing modes on PHP 4.1.2 1.4.6
+included a fix for a cross-site scripting vulnerability, so anyone running
+older 1.4 releases is very strongly encouraged to upgrade as well. Note to
+upgraders: this version of MediaWiki is known to produce a large number of
+notice-level warnings under the newly released PHP 4.4.0. These appear however
+to be harmless; if you encounter them add this to your LocalSettings.php to
+suppress the notices: error_reporting( E_ALL & ~E_NOTICE ); PHP 5.1.0beta3 is
+known to be incompatible at this time.
+
+== MediaWiki 1.4.6 ==
+(released 2005-07-07) MediaWiki 1.4.6 is a bug fix and security update release.
+Incorrect escaping of a parameter in the page move template could
+be used to inject JavaScript code by getting a victim to visit a maliciously
+constructed URL. Users of vulnerable releases are recommended to upgrade to
+this release. Vulnerable versions:
+* 1.5 preview series: n <= 1.5beta2 vulnerable, fixed in 1.5beta3
+* 1.4 stable series: 1.4beta6 <= n <= 1.4.5 vulnerable, fixed in 1.4.6
+* 1.3 legacy series: not vulnerable This release also includes fixes for some
+rare bug annoying HTTP errors, a PHP 4.1.2 breakage bug, and works around some
+template limitations introduced in 1.4.5. See the changelog at the end of this
+file for a detailed list of bugs fixed.
+
+== MediaWiki 1.4.5 ==
+(released 2005-06-03) MediaWiki 1.4.5 is a security update and bugfix release.
+Incorrect handling of page template inclusions made it possible to inject
+JavaScript code into HTML attributes, which could lead to cross-site scripting
+attacks on a publicly editable wiki. Vulnerable releases and fix:
+* 1.5 prerelease: fixed in 1.5alpha2
+* 1.4 stable series: fixed in 1.4.5
+* 1.3 legacy series: fixed in 1.3.13
+* 1.2 series no longer supported; upgrade to 1.4.5 strongly recommended This
+release also includes a number of bug fixes (see changelog below) and merges
+some large-server load balancing patches from Wikipedia. An experimental rate
+limiter for page edits and moves can be enabled with global, per-IP,
+per-subnet, or per-user bases. See configuration options in
+includes/DefaultSettings.php
+
+== MediaWiki 1.4.4 ==
+(released 2005-05-04) MediaWiki 1.4.4 is a bugfix release for the 1.4 stable
+release series. Some bugs in the installer/updater and refreshLinks maintenance
+script were introduced in the last release and have been corrected.
 
 == MediaWiki 1.4.3 ==
 
@@ -18417,3 +19159,384 @@ going to run a public MediaWiki, so you can be notified of security fixes.
 === IRC help ===
 
 There's usually someone online in #mediawiki on irc.freenode.net
+
+=MediaWiki 1.3=
+
+== MediaWiki 1.3.18 ==
+(released 2005-11-02)
+MediaWiki 1.3.18 is a bugfix and security maintenance release. A change in PHP
+4.4.1 broke handling of extension and <nowiki><pre></nowiki> sections, causing
+garbage data to be inserted in output and saved edits. This version works
+around the change. This release includes further corrections to the inline CSS
+style sanitation which works around a JavaScript "feature" on Microsoft
+Internet Explorer. Users of Microsoft Internet Explorer for Windows may be
+vulnerable to XSS injections on prior 1.3 releases; users of
+standards-compliant browsers are not vulnerable.
+
+== MediaWiki 1.3.17 ==
+(released 2005-10-05)
+MediaWiki 1.3.17 is a security maintenance release. Unsafe handling of CSS by
+Microsoft Internet Explorer could be exploited to produce cross-site scripting
+attacks by JavaScript injection to clients running that browser. This release
+blacklists several additional variants from use in HTML inline style
+attributes. All publicly accessible wikis are recommended to upgrade to reduce
+the risk to visitors using Microsoft web browsers.Note: the MediaWiki 1.3.x
+series is not compatible with PHP 5.0.5 or higher. Upgrade to the 1.5.0 release
+if you require this version of PHP 5.
+
+== MediaWiki 1.3.16 ==
+(released 2005-09-21)
+MediaWiki 1.3.16 is a security maintenance release. A bug in edit submission
+handling could cause corruption of the previous revision in the database if an
+abnormal URL was used, such as those used by some spambots. Affected releases:
+* 1.4.x <= 1.4.9; fixed in 1.4.10
+* 1.3.x <= 1.3.15; fixed in 1.3.16
+1.5 release candidates are not affected by this problem. All publicly editable
+wikis are strongly recommended to upgrade immediately.
+1.3 releases can be manually patched by changing this bit in
+{{manual|EditPage.php}}:
+<syntaxhighlight lang="php">
+    if( $this->tokenOk( $request ) ) {
+        $this->save    = $request->wasPosted() && !$this->preview;
+    } else {
+</syntaxhighlight>
+to:
+<syntaxhighlight lang="php">
+    if( $this->tokenOk( $request ) ) {
+        $this->save    = $request->getVal( 'action' ) == 'submit' &&
+                         $request->wasPosted() && !$this->preview;
+    } else {
+</syntaxhighlight>
+
+== MediaWiki 1.3.15, 2005-08-29 ==
+MediaWiki 1.3.15 is a security maintenance release. It corrects across-site
+scripting security bug:
+* <nowiki><math></nowiki> tags were handled incorrectly when TeX rendering
+support is off, as in the default configuration. Wikis where the optional math
+support has been *enabled* are not vulnerable. The 1.3.x series is no longer
+maintained except for security fixes; new users and those seeking bug fixes
+should upgrade to 1.4.9 or 1.5.0.
+
+== MediaWiki 1.3.14, 2005-08-23 ==
+MediaWiki 1.3.14 is a security maintenance release. A flaw in the interaction
+between extensions and HTML attribute sanitization was discovered which could
+allow unauthorized use of offsite resources in style sheets, and possible
+exploitation of a JavaScript injection feature on Microsoft Internet Explorer.
+The 1.3.x series is no longer maintained except for security fixes; new users
+and those seeking bug fixes should upgrade to 1.4.8 or 1.5.0. Existing 1.3.x
+installations not willing to upgrade to the current stable release should apply
+the change manually:
+In includes/Parser.php, function {{code|inline=y|lang=php|fixTagAttributes()}}
+add:
+<syntaxhighlight lang="php">
+       # Any placeholder items should have been unstripped already before
+       # we got to this point. Raw text inserted later could be dangerous.
+       if( strpos( $t, UNIQ_PREFIX ) !== false ) {
+           wfDebug( "Parser::fixTagAttributes found stripped data placeholder;
+           dropping attributes\n" );
+           $t = '';
+       }
+</syntaxhighlight>
+If you are actively using extensions to generate HTML attribute values, upgrade
+to 1.4 or 1.5 for a more thorough fix.
+
+== MediaWiki 1.3.13, 2005-06-03 ==
+MediaWiki 1.3.13 is a security maintenance release. Incorrect handling of page
+template inclusions made it possible to inject JavaScript code into HTML
+attributes, which could lead to cross-site scripting attacks on a publicly
+editable wiki. Vulnerable releases and fix:
+* 1.5 prerelease: fixed in 1.5alpha2
+* 1.4 stable series: fixed in 1.4.5
+* 1.3 legacy series: fixed in 1.3.13
+* 1.2 series no longer supported; upgrade to 1.4.5 strongly recommended The
+1.3.x series is no longer maintained except for security fixes; new users and
+those seeking general bug fixes should install 1.4.5. Existing 1.3.x
+installations not willing or able to upgrade to the current stable relase
+should update the installation to 1.3.13; only includes/Parser.php has changed
+from 1.3.12.
+
+== MediaWiki 1.3.12, 2005-02-20 ==
+MediaWiki 1.3.12 is a security maintenance release. A cross-site scripting
+injection vulnerability was discovered, which affects only MSIE clients and is
+only open if MediaWiki has been manually configured to run output through HTML
+Tidy ($wgUseTidy). The 1.3.x series is no longer maintained except for security
+fixes; new users and those seeking bug fixes should upgrade to 1.4.2. Existing
+1.3.x installations using Tidy not willing to upgrade to the current stable
+relase should either turn off Tidy or update the installation to 1.3.12.
+
+== MediaWiki 1.3.11, 2005-02-20 ==
+MediaWiki 1.3.11 is a security release.
+A security audit found and fixed a number of problems. Users of MediaWiki
+1.3.10 and earlier should upgrade to 1.3.11; users of 1.4 beta releases should
+upgrade to 1.4rc1.
+
+=== Cross-site scripting vulnerability ===
+XSS injection points can be used to hijack session and authentication cookies
+as well as more serious attacks.
+* Media: links output raw text into an attribute value, potentially abusable
+for JavaScript injection. This has been corrected.
+* Additional checks added to file upload to protect against MSIE and Safari
+MIME-type autodetection bugs.
+As of <code>1.3.10/1.4beta6</code>, per-user customized CSS and JavaScript is
+disabled by default as a general precaution. Sites which want this ability may
+set {{wg|AllowUserCss}} and {{wg|AllowUserJs}} in LocalSettings.php.
+
+=== Cross-site request forgery ===
+An attacker could use JavaScript-submitted forms to perform various restricted
+actions by tricking an authenticated user into visiting a malicious web page. A
+fix for page editing in 1.3.10/1.4beta6 has been expanded in this release to
+other forms and functions. Authors of bot tools may need to update their code
+to include the additional fields.
+
+=== Directory traversal ===
+An unchecked parameter in image deletion could allow an authenticated
+administrator to delete arbitary files in directories writable by the web
+server, and confirm existence of files not deletable.
+
+== MediaWiki 1.3.10, 2005-02-03 ==
+MediaWiki 1.3.10 is a security release.
+An attacker could craft a URL which, when visited by a particular logged-in
+user, would execute arbitrary JavaScript code on the user's browser in the
+wiki's site context. This attack has been blocked, and as an extra precaution
+the user CSS and JavaScript subpage support is now disabled by default. Sites
+which want this ability may set {{wg|AllowUserCss}} and {{wg|AllowUserJs}} in
+{{manual|LocalSettings.php}}. Additional protections have been added against
+off-site form submissions
+hijacking user credentials. Authors of bot tools may need to update their code
+to include additional fields. All wikis running 1.3.x are strongly urged to
+upgrade to 1.3.10.
+Changes from 1.3.9:
+* Logged-in edits and preview of user CSS/JS are now locked to a session token.
+* Per-user CSS and JavaScript subpage customizations now disabled by default.
+They can be re-enabled via {{wg|AllowUserJs}} and {{wg|AllowUserCss}}.
+* Removed .ogg from the default uploads whitelist as an extra precaution. If
+your web server is configured to serve Ogg files with the correct Content-Type
+header, you can re-add it in LocalSettings.php: {{wg|FileExtensions}}<code>[] =
+'ogg'</code>
+
+== MediaWiki 1.3.9, 2004-12-12 ==
+MediaWiki 1.3.9 is a security and bug fix release.
+A flaw in upload handling has been found which may allow upload and  execution
+of arbitrary scripts with the permissions of the web server. Only wikis that
+have enabled uploads and have a vulnerable Apache  configuration will be
+affected, but to be safe all wikis should upgrade. Wikis with uploads available
+should either disable uploads or upgrade to 1.3.9 immediately; if other files
+are customized and require merging changes,
+includes/{{manual|SpecialUpload.php}} may be replaced individually to add the
+fix. (It is also recommended to configure your web server to disable script
+execution in the 'images' subdirectory where uploads are placed, which prevents
+most attacks even if the wiki fails.)
+Changes from 1.3.8:
+* Backported "Templates used in this page"-feature of EditPage
+* Allow "MySkin" as a default skin.
+* ({{bugzilla|938}}) Parse namespaces correctly on self-interwiki links
+* ({{bugzilla|1010}}) fix broken Commons image link on [[Skin:Classic|Classic]]
+& [[Skin:Cologne Blue|Cologne Blue]]
+* ({{bugzilla|1004}}) Norsk language names for interwiki links changed, Nauruan
+language name changed
+* Enhance upload extension blacklist to protect against vulnerable Apache
+configurations
+
+== MediaWiki 1.3.8, 2004-11-15 ==
+MediaWiki 1.3.8 is a bugfix release. Those running wikis with uploads enabled
+are strongly recommended to upgrade as this fixes several problems with
+overwriting previously-uploaded files.
+Changes from 1.3.7:
+* ({{bugzilla|506}}) fix {{code|inline=y|lang=html|array_key_exists()}} warning
+for IIS servers using ISAPI mode
+* ({{bugzilla|718}}) fix bad charset in (file) cached pages
+* use local numerals in category page (for Hindi et al)
+* alias month abbreviations to month names in Hindi
+* add localized numerals for Gujarati and Kannada
+* fix Category and project namespaces for Hindi
+* Don't output bogus timestamp on [[Special:RecentChanges]] if no entries
+* Correct template include path which broke some but not all Windows installs
+* Fix edit form submission problem with some PHP versions
+* Disallow unreachable titles with %XX hex codes
+* Allow page [[0]] to be renamed
+* ({{bugzilla|774}}) when saving with <code>section=new</code>, return to the
+anchor as with existing numbered section edits
+* Experimental shared upload overlay area (disabled by default)
+* ({{bugzilla|806}}) Removed some "Wikipedia" hardcoding in German localization
+* User option localization fix for some extensions
+* ({{bugzilla|809}}) now try to load the mysql php extension if it isn't loaded
+* ({{bugzilla|848}}) fix error message in [[Special:Newpages]] RSS and Atom
+feeds
+* ({{bugzilla|26}}) fix cache headers on anon talk page notification
+* ({{bugzilla|874}}) added 'cgi' to {{wg|FileBlacklist}}
+* ({{bugzilla|862}}) localize date and time format for Finnish
+* ({{bugzilla|548}}) Don't overwrite images until the user confirms it
+
+== MediaWiki 1.3.7, 2004-10-18 ==
+Changes from 1.3.6:
+* Fix protected-page related security issue.
+
+== MediaWiki 1.3.6, 2004-10-14 ==
+Changes from 1.3.5:
+* ({{bugzilla|296}}) Variables in user interface messages are no longer
+substituted at install time, so changes to the site name etc should be easier
+to make
+* ({{bugzilla|149}}) [[Special:RecentChanges]] "changes from" link preserves
+limit
+* ({{bugzilla|433}}) tooltip for "Undelete" tab now labeled correctly
+* ({{bugzilla|439}}) unclickable "Move" tab no longer displays on protected
+pages
+* ({{bugzilla|484}}) graceful deletion of images where the actual file is
+missing
+* ({{bugzilla|686}}) fixed [[plural]]s in Catalan localization
+* Fixed potential HTML/JavaScript injection attack in the
+[[Extension:UnicodeConverter|UnicodeConverter]] extension. (This extension is
+not enabled by default.)
+* Fixed potential HTML/JavaScript injection attack via raw page views to a
+maliciously crafted wiki page.
+* ({{bugzilla|187}}, {{bugzilla|669}}) Fixed centered thumbnails, using
+{{code|inline=y|lang=html|<div>}} instead of {{code|inline=y|lang=html|<span>}}.
+* catch MySQL error 2000 during installation.
+* ({{bugzilla|704}}) Removed misleading LocalSettings.sample
+* Fix cross site scripting bugs in [[Special:Ipblocklist]],
+[[Special:EmailUser]]
+* Fix SQL injection and cross site scripting bugs in Special:Maintenance
+* Fix cross site scripting bugs and possible filename validation vulnerability
+in ImagePage.
+* and more of that sort
+
+== MediaWiki 1.3.5, 2004-09-30 ==
+Changes from 1.3.4:
+* Clean up input validation in 'raw' page output mode which was a potential
+cross-site scripting opportunity.
+
+== MediaWiki 1.3.4, 2004-09-28 ==
+=== SECURITY NOTE ===
+As of 1.3.4, MediaWiki performs some screening of newly uploaded files for
+validity. (Some)  corrupt image files, and HTML files mistakenly or maliciously
+masquerading as images, should now be rejected. These checks protect against
+Internet Explorer security holes relating to type autodetection which are a
+potential cross-site scripting attack vector, and also rejects at least one
+known version of the "JPEG virus" which might attack unpatched clients. If you
+already have invalid files uploaded this will not protect against them. If you
+have expanded the <code>filetype</code> whitelist or disabled the strict type
+checking, other dangerous file types may still get through. You should always
+be careful when allowing uploads!
+Changes from 1.3.3:
+* Fixed lots of template-related bugs, esp. for cases where template variables
+are used for links, images, etc.
+* Fixed transformation of page messages when viewing [[Special:Allmessages]]
+* Handle "ISBN ISBN 1234" correctly
+* Fixed warning on Category pages
+* Fixed some bad error messages on login page
+* Fixed history entry for initial main page on install
+* Removed problematic <code>{</code> and <code>}</code> from legal title
+characters
+* Strip leading blank from output in preformatted text.
+* Fixed problem when moving pages to titles with '#' in
+* Optional {{wg|RawHtml}} for raw {{code|inline=y|lang=html|<html>}} sections.
+Use only on limited- participation 'trusted' wikis, as it does not protect
+against cross-site scripting attacks. For security, this option can only be
+enabled if in {{wg|WhitelistEdit}} mode.
+* Fixed problem where pages which were created as a redirect following a move
+never showed on [[Special:Randompage]].
+* Fixed line spacing on printed table of contents
+* Allow links to pages with names of the form [[RFC 1234]]
+* Fixed broken edit links being shown for sections from included templates
+* Verify that uploaded image files are of the claimed type.
+
+== MediaWiki 1.3.3, 2004-09-09 ==
+Changes from 1.3.2:
+* Fix for long numeric page titles
+* Fix Go search for "0", numeric almost-self-links
+* Avoid caching of pages with "You have new messages" headers
+* Fix for upgrades as non-root users from 1.2 command-line installs.
+* Fix for {{wg|DebugDumpSql}} debug mode.
+* {{wg|ExtraNamespaces}} setting for configuring additional namespaces (see
+note in {{manual|DefaultSettings.php}})
+* 'recache' on query pages now disabled when miser mode is on; special case the
+global settings in your {{manual|LocalSettings.php}} to do automatic updates.
+* Don't block UTF-8 titles containing byte 0xA0 (bug added in 1.3.2)
+* Watch/unwatch tabs now shown on edit pages in MonoBook.
+* Fix default skin in Irish localization (ga)
+* Add Traditional Chinese localization (zh-tw)
+* Changed default sortkey of subcategories. Don't include "Category:"-prefix
+any longer
+* More helpful info on spam catcher.
+* Allow larger offsets for queries such as [[Special:Listusers]]
+* Semicolon (;) added to French non-break space rules
+* Possible fix for some install errors with path names permission problems.
+* Removed [[Project:All system messages]], which has been superseded by the
+much faster [[Special:Allmessages]]. This speeds up installation considerably.
+
+== MediaWiki 1.3.2, 2004-08-30 ==
+Changes from 1.3.1:
+* Fix namespaced page creation links when no go match
+* When cookies are disabled, don't show login screen twice
+* Install should no longer die when PHP is pre-configured to compress output
+* Fixed bug that caused long Japanese pages to time out with Tidy active
+* When session.handler is set incorrectly, try automatic override to 'files'
+* Watch/Unwatch links back to the affected page instead of Main Page
+* Upload link no longer displayed on Monobook if uploading is disabled
+* Special:Allmessages faster, shows correct original text, works in safe mode
+
+== MediaWiki 1.3.1, 2004-08-14 ==
+Changes from 1.3.0:
+* Watchlist parameters now work with register_globals off
+* Fixed parsing of ''italics'' and '''bold''' mark-up (again)
+* Special:Allpages display is more sensible on smaller wikis
+* Fixed XHTML parsing error in classic skins
+* Moved pages update watchlist correctly
+* Fixed rebuildall.php on case-sensitive Unix filesystems
+* Disabled file cache compression by default due to incompatibility with output
+buffer compression (ob_gzhandler)
+* New magic word {{code|inline=y|PAGENAMEE}} (URL-escaped version of
+{{code|inline=y|PAGENAME}})
+* Installation avoids blank username; better message on missing XML module
+* {{wg|WhitelistAccount}} no longer breaks all logins.
+
+== MediaWiki 1.3.0, 2004-08-11 ==
+Look & layout:
+* New default layout '[[Skin:MonoBook|MonoBook]]' (available on PHP4 only
+currently)
+* Print stylesheet now built-in to every page
+* More or less correct XHTML 1.0 (served as text/html by default)
+Wiki features:
+* Image captions can now include links and other basic formatting
+* Image bounding box can be specified instead of width, e.g. as 100x100px,
+making the image not wider than 100px and not higher than 100px, keeping aspect
+ratio.
+* Templates have been expanded with parameters, and separated from the
+MediaWiki: localization scheme.
+* Categories more or less work
+* added a special page for listing users with sysop rights.
+Editing:
+* Automatic merging of edit conflicts that don't directly interfere
+* Edit summaries can now include basic formatting and links
+Metadata and output:
+* Linked Creative Commons copyright metadata (optional)
+* RSS 2.0 & Atom 0.3 feeds for Recent Changes, New Pages
+Optional modules:
+* WikiHiero hieroglyphic module can be added (separate download)
+* Timeline module can be added (separate download). Requires ploticus.
+* TeX now has an experimental MathML output mode (incomplete!)
+Installation and upgrading:
+* The old install.php and update.php have been removed. In-place installation
+introduced in 1.2 is now the standard installation and upgrade method, see
+INSTALL and UPGRADE for directions.
+Database:
+* The links table has been changed to use a cur_id for l_from. The link tables
+must be converted on upgrade, which may entail some downtime.
+Code and compatibility:
+* Should now run clean with error reporting set to E_ALL.
+* register_globals hack from 1.2 has been replaced with safer code
+* Bundled PHPTAL 0.7.0 from http://phptal.sourceforge.net/ (with some patches)
+* Most image-related code moved to Image.php
+* More fixes for PHP 4.1.2 (thanks to Asheesh Laroia)
+* URL encoding fix for anchors
+* All languages now available in UTF-8 mode
+* Various other fixes
+
+=== Caveats ===
+Some output, particularly involving user-supplied inline HTML, may not produce
+100% valid or well-formed XHTML output. Testers are welcome to set $wgMimeType
+= "application/xhtml+xml"; to test for remaining problem cases, but this is not
+recommended on live sites. (This must be set for MathML to display properly in
+Mozilla.) The new 'MonoBook' skin is not compatible with PHP 5 due to bugs in
+the underlying PHPTAL library. It will be automatically disabled when running
+on PHP5; the older look and feel will be used instead.
index cfaf82d..ddd6da9 100644 (file)
@@ -134,7 +134,7 @@ For notes on 1.32.x and older releases, see HISTORY.
 * Updated wikimedia/php-session-serializer from 1.0.6 to 1.0.7.
 
 ==== Removed external libraries ====
-* 
+* (T219403) jquery.ui.spinner, deprecated since 1.31, was removed.
 
 === Bug fixes in 1.33 ===
 * (T164211) Special:UserRights could sometimes fail with a
@@ -341,6 +341,10 @@ because of Phabricator reports.
   Use CdnCacheUpdate::newFromTitles() instead.
 * Handling of multiple arguments by the Block constructor, deprecated in 1.26,
   has been removed.
+* The translation of main page in Sardinian (sc) was changed from "Pàgina Base"
+  to "Pàgina printzipale". Existing wikis using this content language need to
+  move the main page or change the name through MediaWiki:Mainpage page.
+* wfSplitWikiID(), deprecated in 1.32, has been removed.
 
 === Deprecations in 1.33 ===
 * The configuration option $wgUseESI has been deprecated, and is expected
@@ -404,6 +408,7 @@ because of Phabricator reports.
 * ManualLogEntry::setTags() is deprecated, use ManualLogEntry::addTags()
   instead. The setTags() method was overriding the tags, addTags() doesn't
   override, only adds new tags.
+* Block::isValid is deprecated, since it is no longer needed in core.
 
 === Other changes in 1.33 ===
 * (T201747) Html::openElement() warns if given an element name with a space
@@ -412,6 +417,11 @@ because of Phabricator reports.
   changed to explicitly cast. Subclasses relying on the base-class
   implementation should check whether they need to override it now.
 * BagOStuff::add is now abstract and must explicitly be defined in subclasses.
+* LinksDeletionUpdate is now a subclass of LinksUpdate. As a consequence,
+  the following hooks will now be triggered upon page deletion in addition
+  to page updates: LinksUpdateConstructed, LinksUpdate, LinksUpdateComplete.
+  LinksUpdateAfterInsert is not triggered since deletions do not cause
+  insertions into links tables.
 
 == Compatibility ==
 MediaWiki 1.33 requires PHP 7.0.13 or later. Although HHVM 3.18.5 or later is
index 16236ed..e9495b3 100644 (file)
@@ -298,7 +298,7 @@ $wgAutoloadLocalClasses = [
        'ComposerVendorHtaccessCreator' => __DIR__ . '/includes/composer/ComposerVendorHtaccessCreator.php',
        'ComposerVersionNormalizer' => __DIR__ . '/includes/composer/ComposerVersionNormalizer.php',
        'CompressOld' => __DIR__ . '/maintenance/storage/compressOld.php',
-       'ConcatenatedGzipHistoryBlob' => __DIR__ . '/includes/HistoryBlob.php',
+       'ConcatenatedGzipHistoryBlob' => __DIR__ . '/includes/historyblob/ConcatenatedGzipHistoryBlob.php',
        'Config' => __DIR__ . '/includes/config/Config.php',
        'ConfigException' => __DIR__ . '/includes/config/ConfigException.php',
        'ConfigFactory' => __DIR__ . '/includes/config/ConfigFactory.php',
@@ -398,7 +398,7 @@ $wgAutoloadLocalClasses = [
        'Diff' => __DIR__ . '/includes/diff/DairikiDiff.php',
        'DiffEngine' => __DIR__ . '/includes/diff/DiffEngine.php',
        'DiffFormatter' => __DIR__ . '/includes/diff/DiffFormatter.php',
-       'DiffHistoryBlob' => __DIR__ . '/includes/HistoryBlob.php',
+       'DiffHistoryBlob' => __DIR__ . '/includes/historyblob/DiffHistoryBlob.php',
        'DiffOp' => __DIR__ . '/includes/diff/DairikiDiff.php',
        'DiffOpAdd' => __DIR__ . '/includes/diff/DairikiDiff.php',
        'DiffOpChange' => __DIR__ . '/includes/diff/DairikiDiff.php',
@@ -628,9 +628,9 @@ $wgAutoloadLocalClasses = [
        'HashSiteStore' => __DIR__ . '/includes/site/HashSiteStore.php',
        'HashtableReplacer' => __DIR__ . '/includes/libs/replacers/HashtableReplacer.php',
        'HistoryAction' => __DIR__ . '/includes/actions/HistoryAction.php',
-       'HistoryBlob' => __DIR__ . '/includes/HistoryBlob.php',
-       'HistoryBlobCurStub' => __DIR__ . '/includes/HistoryBlob.php',
-       'HistoryBlobStub' => __DIR__ . '/includes/HistoryBlob.php',
+       'HistoryBlob' => __DIR__ . '/includes/historyblob/HistoryBlob.php',
+       'HistoryBlobCurStub' => __DIR__ . '/includes/historyblob/HistoryBlobCurStub.php',
+       'HistoryBlobStub' => __DIR__ . '/includes/historyblob/HistoryBlobStub.php',
        'HistoryPager' => __DIR__ . '/includes/actions/pagers/HistoryPager.php',
        'Hooks' => __DIR__ . '/includes/Hooks.php',
        'Html' => __DIR__ . '/includes/Html.php',
@@ -1707,8 +1707,8 @@ $wgAutoloadLocalClasses = [
        'ZhConverter' => __DIR__ . '/languages/classes/LanguageZh.php',
        'ZipDirectoryReader' => __DIR__ . '/includes/utils/ZipDirectoryReader.php',
        'ZipDirectoryReaderError' => __DIR__ . '/includes/utils/ZipDirectoryReaderError.php',
-       'concatenatedgziphistoryblob' => __DIR__ . '/includes/HistoryBlob.php',
-       'historyblobcurstub' => __DIR__ . '/includes/HistoryBlob.php',
-       'historyblobstub' => __DIR__ . '/includes/HistoryBlob.php',
+       'concatenatedgziphistoryblob' => __DIR__ . '/includes/historyblob/ConcatenatedGzipHistoryBlob.php',
+       'historyblobcurstub' => __DIR__ . '/includes/historyblob/HistoryBlobCurStub.php',
+       'historyblobstub' => __DIR__ . '/includes/historyblob/HistoryBlobStub.php',
        'profile_point' => __DIR__ . '/profileinfo.php',
 ];
index 700e551..b17ec86 100644 (file)
@@ -964,9 +964,12 @@ class Block {
 
        /**
         * Is the block address valid (i.e. not a null string?)
+        *
+        * @deprecated since 1.33 No longer needed in core.
         * @return bool
         */
        public function isValid() {
+               wfDeprecated( __METHOD__, '1.33' );
                return $this->getTarget() != null;
        }
 
index d173d35..3a040c8 100644 (file)
@@ -9030,16 +9030,6 @@ $wgPriorityHints = false;
  */
 $wgElementTiming = false;
 
-/**
- * Temporary option to show rollback confirmation user settings
- * without activating the feature itself
- * @see T217039
- * @since 1.33
- * @deprecated 1.33
- * @var bool
- */
-$wgDisableRollbackConfirmationFeature = false;
-
 /**
  * For really cool vim folding this needs to be at the end:
  * vim: foldmarker=@{,@} foldmethod=marker
index 55b78ac..20f3fd0 100644 (file)
@@ -2610,22 +2610,6 @@ function wfWikiID() {
        }
 }
 
-/**
- * Split a wiki ID into DB name and table prefix
- *
- * @param string $wiki
- *
- * @return array
- * @deprecated 1.32
- */
-function wfSplitWikiID( $wiki ) {
-       $bits = explode( '-', $wiki, 2 );
-       if ( count( $bits ) < 2 ) {
-               $bits[] = '';
-       }
-       return $bits;
-}
-
 /**
  * Get a Database object.
  *
diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php
deleted file mode 100644 (file)
index bca6c7e..0000000
+++ /dev/null
@@ -1,711 +0,0 @@
-<?php
-/**
- * Efficient concatenated text 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
- */
-
-/**
- * Base class for general text storage via the "object" flag in old_flags, or
- * two-part external storage URLs. Used for represent efficient concatenated
- * storage, and migration-related pointer objects.
- */
-interface HistoryBlob {
-       /**
-        * Adds an item of text, returns a stub object which points to the item.
-        * You must call setLocation() on the stub object before storing it to the
-        * database
-        *
-        * @param string $text
-        *
-        * @return string The key for getItem()
-        */
-       function addItem( $text );
-
-       /**
-        * Get item by key, or false if the key is not present
-        *
-        * @param string $key
-        *
-        * @return string|bool
-        */
-       function getItem( $key );
-
-       /**
-        * Set the "default text"
-        * This concept is an odd property of the current DB schema, whereby each text item has a revision
-        * associated with it. The default text is the text of the associated revision. There may, however,
-        * be other revisions in the same object.
-        *
-        * Default text is not required for two-part external storage URLs.
-        *
-        * @param string $text
-        */
-       function setText( $text );
-
-       /**
-        * Get default text. This is called from Revision::getRevisionText()
-        *
-        * @return string
-        */
-       function getText();
-}
-
-/**
- * Concatenated gzip (CGZ) storage
- * Improves compression ratio by concatenating like objects before gzipping
- */
-class ConcatenatedGzipHistoryBlob implements HistoryBlob {
-       public $mVersion = 0, $mCompressed = false, $mItems = [], $mDefaultHash = '';
-       public $mSize = 0;
-       public $mMaxSize = 10000000;
-       public $mMaxCount = 100;
-
-       public function __construct() {
-               if ( !function_exists( 'gzdeflate' ) ) {
-                       throw new MWException( "Need zlib support to read or write this "
-                               . "kind of history object (ConcatenatedGzipHistoryBlob)\n" );
-               }
-       }
-
-       /**
-        * @param string $text
-        * @return string
-        */
-       public function addItem( $text ) {
-               $this->uncompress();
-               $hash = md5( $text );
-               if ( !isset( $this->mItems[$hash] ) ) {
-                       $this->mItems[$hash] = $text;
-                       $this->mSize += strlen( $text );
-               }
-               return $hash;
-       }
-
-       /**
-        * @param string $hash
-        * @return array|bool
-        */
-       public function getItem( $hash ) {
-               $this->uncompress();
-               if ( array_key_exists( $hash, $this->mItems ) ) {
-                       return $this->mItems[$hash];
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * @param string $text
-        * @return void
-        */
-       public function setText( $text ) {
-               $this->uncompress();
-               $this->mDefaultHash = $this->addItem( $text );
-       }
-
-       /**
-        * @return array|bool
-        */
-       public function getText() {
-               $this->uncompress();
-               return $this->getItem( $this->mDefaultHash );
-       }
-
-       /**
-        * Remove an item
-        *
-        * @param string $hash
-        */
-       public function removeItem( $hash ) {
-               $this->mSize -= strlen( $this->mItems[$hash] );
-               unset( $this->mItems[$hash] );
-       }
-
-       /**
-        * Compress the bulk data in the object
-        */
-       public function compress() {
-               if ( !$this->mCompressed ) {
-                       $this->mItems = gzdeflate( serialize( $this->mItems ) );
-                       $this->mCompressed = true;
-               }
-       }
-
-       /**
-        * Uncompress bulk data
-        */
-       public function uncompress() {
-               if ( $this->mCompressed ) {
-                       $this->mItems = unserialize( gzinflate( $this->mItems ) );
-                       $this->mCompressed = false;
-               }
-       }
-
-       /**
-        * @return array
-        */
-       function __sleep() {
-               $this->compress();
-               return [ 'mVersion', 'mCompressed', 'mItems', 'mDefaultHash' ];
-       }
-
-       function __wakeup() {
-               $this->uncompress();
-       }
-
-       /**
-        * Helper function for compression jobs
-        * Returns true until the object is "full" and ready to be committed
-        *
-        * @return bool
-        */
-       public function isHappy() {
-               return $this->mSize < $this->mMaxSize
-                       && count( $this->mItems ) < $this->mMaxCount;
-       }
-}
-
-/**
- * Pointer object for an item within a CGZ blob stored in the text table.
- */
-class HistoryBlobStub {
-       /**
-        * @var array One-step cache variable to hold base blobs; operations that
-        * pull multiple revisions may often pull multiple times from the same
-        * blob. By keeping the last-used one open, we avoid redundant
-        * unserialization and decompression overhead.
-        */
-       protected static $blobCache = [];
-
-       /** @var int */
-       public $mOldId;
-
-       /** @var string */
-       public $mHash;
-
-       /** @var string */
-       public $mRef;
-
-       /**
-        * @param string $hash The content hash of the text
-        * @param int $oldid The old_id for the CGZ object
-        */
-       function __construct( $hash = '', $oldid = 0 ) {
-               $this->mHash = $hash;
-       }
-
-       /**
-        * Sets the location (old_id) of the main object to which this object
-        * points
-        * @param int $id
-        */
-       function setLocation( $id ) {
-               $this->mOldId = $id;
-       }
-
-       /**
-        * Sets the location (old_id) of the referring object
-        * @param string $id
-        */
-       function setReferrer( $id ) {
-               $this->mRef = $id;
-       }
-
-       /**
-        * Gets the location of the referring object
-        * @return string
-        */
-       function getReferrer() {
-               return $this->mRef;
-       }
-
-       /**
-        * @return string|false
-        */
-       function getText() {
-               if ( isset( self::$blobCache[$this->mOldId] ) ) {
-                       $obj = self::$blobCache[$this->mOldId];
-               } else {
-                       $dbr = wfGetDB( DB_REPLICA );
-                       $row = $dbr->selectRow(
-                               'text',
-                               [ 'old_flags', 'old_text' ],
-                               [ 'old_id' => $this->mOldId ]
-                       );
-
-                       if ( !$row ) {
-                               return false;
-                       }
-
-                       $flags = explode( ',', $row->old_flags );
-                       if ( in_array( 'external', $flags ) ) {
-                               $url = $row->old_text;
-                               $parts = explode( '://', $url, 2 );
-                               if ( !isset( $parts[1] ) || $parts[1] == '' ) {
-                                       return false;
-                               }
-                               $row->old_text = ExternalStore::fetchFromURL( $url );
-
-                       }
-
-                       if ( !in_array( 'object', $flags ) ) {
-                               return false;
-                       }
-
-                       if ( in_array( 'gzip', $flags ) ) {
-                               // This shouldn't happen, but a bug in the compress script
-                               // may at times gzip-compress a HistoryBlob object row.
-                               $obj = unserialize( gzinflate( $row->old_text ) );
-                       } else {
-                               $obj = unserialize( $row->old_text );
-                       }
-
-                       if ( !is_object( $obj ) ) {
-                               // Correct for old double-serialization bug.
-                               $obj = unserialize( $obj );
-                       }
-
-                       // Save this item for reference; if pulling many
-                       // items in a row we'll likely use it again.
-                       $obj->uncompress();
-                       self::$blobCache = [ $this->mOldId => $obj ];
-               }
-
-               return $obj->getItem( $this->mHash );
-       }
-
-       /**
-        * Get the content hash
-        *
-        * @return string
-        */
-       function getHash() {
-               return $this->mHash;
-       }
-}
-
-/**
- * To speed up conversion from 1.4 to 1.5 schema, text rows can refer to the
- * leftover cur table as the backend. This avoids expensively copying hundreds
- * of megabytes of data during the conversion downtime.
- *
- * Serialized HistoryBlobCurStub objects will be inserted into the text table
- * on conversion if $wgLegacySchemaConversion is set to true.
- */
-class HistoryBlobCurStub {
-       /** @var int */
-       public $mCurId;
-
-       /**
-        * @param int $curid The cur_id pointed to
-        */
-       function __construct( $curid = 0 ) {
-               $this->mCurId = $curid;
-       }
-
-       /**
-        * Sets the location (cur_id) of the main object to which this object
-        * points
-        *
-        * @param int $id
-        */
-       function setLocation( $id ) {
-               $this->mCurId = $id;
-       }
-
-       /**
-        * @return string|bool
-        */
-       function getText() {
-               $dbr = wfGetDB( DB_REPLICA );
-               $row = $dbr->selectRow( 'cur', [ 'cur_text' ], [ 'cur_id' => $this->mCurId ] );
-               if ( !$row ) {
-                       return false;
-               }
-               return $row->cur_text;
-       }
-}
-
-/**
- * Diff-based history compression
- * Requires xdiff 1.5+ and zlib
- */
-class DiffHistoryBlob implements HistoryBlob {
-       /** @var array Uncompressed item cache */
-       public $mItems = [];
-
-       /** @var int Total uncompressed size */
-       public $mSize = 0;
-
-       /**
-        * @var array Array of diffs. If a diff D from A to B is notated D = B - A,
-        * and Z is an empty string:
-        *
-        *              { item[map[i]] - item[map[i-1]]   where i > 0
-        *    diff[i] = {
-        *              { item[map[i]] - Z                where i = 0
-        */
-       public $mDiffs;
-
-       /** @var array The diff map, see above */
-       public $mDiffMap;
-
-       /** @var int The key for getText()
-        */
-       public $mDefaultKey;
-
-       /** @var string Compressed storage */
-       public $mCompressed;
-
-       /** @var bool True if the object is locked against further writes */
-       public $mFrozen = false;
-
-       /**
-        * @var int The maximum uncompressed size before the object becomes sad
-        * Should be less than max_allowed_packet
-        */
-       public $mMaxSize = 10000000;
-
-       /** @var int The maximum number of text items before the object becomes sad */
-       public $mMaxCount = 100;
-
-       /** Constants from xdiff.h */
-       const XDL_BDOP_INS = 1;
-       const XDL_BDOP_CPY = 2;
-       const XDL_BDOP_INSB = 3;
-
-       function __construct() {
-               if ( !function_exists( 'gzdeflate' ) ) {
-                       throw new MWException( "Need zlib support to read or write DiffHistoryBlob\n" );
-               }
-       }
-
-       /**
-        * @throws MWException
-        * @param string $text
-        * @return int
-        */
-       function addItem( $text ) {
-               if ( $this->mFrozen ) {
-                       throw new MWException( __METHOD__ . ": Cannot add more items after sleep/wakeup" );
-               }
-
-               $this->mItems[] = $text;
-               $this->mSize += strlen( $text );
-               $this->mDiffs = null; // later
-               return count( $this->mItems ) - 1;
-       }
-
-       /**
-        * @param string $key
-        * @return string
-        */
-       function getItem( $key ) {
-               return $this->mItems[$key];
-       }
-
-       /**
-        * @param string $text
-        */
-       function setText( $text ) {
-               $this->mDefaultKey = $this->addItem( $text );
-       }
-
-       /**
-        * @return string
-        */
-       function getText() {
-               return $this->getItem( $this->mDefaultKey );
-       }
-
-       /**
-        * @throws MWException
-        */
-       function compress() {
-               if ( !function_exists( 'xdiff_string_rabdiff' ) ) {
-                       throw new MWException( "Need xdiff 1.5+ support to write DiffHistoryBlob\n" );
-               }
-               if ( isset( $this->mDiffs ) ) {
-                       // Already compressed
-                       return;
-               }
-               if ( $this->mItems === [] ) {
-                       return;
-               }
-
-               // Create two diff sequences: one for main text and one for small text
-               $sequences = [
-                       'small' => [
-                               'tail' => '',
-                               'diffs' => [],
-                               'map' => [],
-                       ],
-                       'main' => [
-                               'tail' => '',
-                               'diffs' => [],
-                               'map' => [],
-                       ],
-               ];
-               $smallFactor = 0.5;
-
-               $mItemsCount = count( $this->mItems );
-               for ( $i = 0; $i < $mItemsCount; $i++ ) {
-                       $text = $this->mItems[$i];
-                       if ( $i == 0 ) {
-                               $seqName = 'main';
-                       } else {
-                               $mainTail = $sequences['main']['tail'];
-                               if ( strlen( $text ) < strlen( $mainTail ) * $smallFactor ) {
-                                       $seqName = 'small';
-                               } else {
-                                       $seqName = 'main';
-                               }
-                       }
-                       $seq =& $sequences[$seqName];
-                       $tail = $seq['tail'];
-                       $diff = $this->diff( $tail, $text );
-                       $seq['diffs'][] = $diff;
-                       $seq['map'][] = $i;
-                       $seq['tail'] = $text;
-               }
-               unset( $seq ); // unlink dangerous alias
-
-               // Knit the sequences together
-               $tail = '';
-               $this->mDiffs = [];
-               $this->mDiffMap = [];
-               foreach ( $sequences as $seq ) {
-                       if ( $seq['diffs'] === [] ) {
-                               continue;
-                       }
-                       if ( $tail === '' ) {
-                               $this->mDiffs[] = $seq['diffs'][0];
-                       } else {
-                               $head = $this->patch( '', $seq['diffs'][0] );
-                               $this->mDiffs[] = $this->diff( $tail, $head );
-                       }
-                       $this->mDiffMap[] = $seq['map'][0];
-                       $diffsCount = count( $seq['diffs'] );
-                       for ( $i = 1; $i < $diffsCount; $i++ ) {
-                               $this->mDiffs[] = $seq['diffs'][$i];
-                               $this->mDiffMap[] = $seq['map'][$i];
-                       }
-                       $tail = $seq['tail'];
-               }
-       }
-
-       /**
-        * @param string $t1
-        * @param string $t2
-        * @return string
-        */
-       function diff( $t1, $t2 ) {
-               # Need to do a null concatenation with warnings off, due to bugs in the current version of xdiff
-               # "String is not zero-terminated"
-               Wikimedia\suppressWarnings();
-               $diff = xdiff_string_rabdiff( $t1, $t2 ) . '';
-               Wikimedia\restoreWarnings();
-               return $diff;
-       }
-
-       /**
-        * @param string $base
-        * @param string $diff
-        * @return bool|string
-        */
-       function patch( $base, $diff ) {
-               if ( function_exists( 'xdiff_string_bpatch' ) ) {
-                       Wikimedia\suppressWarnings();
-                       $text = xdiff_string_bpatch( $base, $diff ) . '';
-                       Wikimedia\restoreWarnings();
-                       return $text;
-               }
-
-               # Pure PHP implementation
-
-               $header = unpack( 'Vofp/Vcsize', substr( $diff, 0, 8 ) );
-
-               # Check the checksum if hash extension is available
-               $ofp = $this->xdiffAdler32( $base );
-               if ( $ofp !== false && $ofp !== substr( $diff, 0, 4 ) ) {
-                       wfDebug( __METHOD__ . ": incorrect base checksum\n" );
-                       return false;
-               }
-               if ( $header['csize'] != strlen( $base ) ) {
-                       wfDebug( __METHOD__ . ": incorrect base length\n" );
-                       return false;
-               }
-
-               $p = 8;
-               $out = '';
-               while ( $p < strlen( $diff ) ) {
-                       $x = unpack( 'Cop', substr( $diff, $p, 1 ) );
-                       $op = $x['op'];
-                       ++$p;
-                       switch ( $op ) {
-                               case self::XDL_BDOP_INS:
-                                       $x = unpack( 'Csize', substr( $diff, $p, 1 ) );
-                                       $p++;
-                                       $out .= substr( $diff, $p, $x['size'] );
-                                       $p += $x['size'];
-                                       break;
-                               case self::XDL_BDOP_INSB:
-                                       $x = unpack( 'Vcsize', substr( $diff, $p, 4 ) );
-                                       $p += 4;
-                                       $out .= substr( $diff, $p, $x['csize'] );
-                                       $p += $x['csize'];
-                                       break;
-                               case self::XDL_BDOP_CPY:
-                                       $x = unpack( 'Voff/Vcsize', substr( $diff, $p, 8 ) );
-                                       $p += 8;
-                                       $out .= substr( $base, $x['off'], $x['csize'] );
-                                       break;
-                               default:
-                                       wfDebug( __METHOD__ . ": invalid op\n" );
-                                       return false;
-                       }
-               }
-               return $out;
-       }
-
-       /**
-        * Compute a binary "Adler-32" checksum as defined by LibXDiff, i.e. with
-        * the bytes backwards and initialised with 0 instead of 1. See T36428.
-        *
-        * @param string $s
-        * @return string|bool False if the hash extension is not available
-        */
-       function xdiffAdler32( $s ) {
-               if ( !function_exists( 'hash' ) ) {
-                       return false;
-               }
-
-               static $init;
-               if ( $init === null ) {
-                       $init = str_repeat( "\xf0", 205 ) . "\xee" . str_repeat( "\xf0", 67 ) . "\x02";
-               }
-
-               // The real Adler-32 checksum of $init is zero, so it initialises the
-               // state to zero, as it is at the start of LibXDiff's checksum
-               // algorithm. Appending the subject string then simulates LibXDiff.
-               return strrev( hash( 'adler32', $init . $s, true ) );
-       }
-
-       function uncompress() {
-               if ( !$this->mDiffs ) {
-                       return;
-               }
-               $tail = '';
-               $mDiffsCount = count( $this->mDiffs );
-               for ( $diffKey = 0; $diffKey < $mDiffsCount; $diffKey++ ) {
-                       $textKey = $this->mDiffMap[$diffKey];
-                       $text = $this->patch( $tail, $this->mDiffs[$diffKey] );
-                       $this->mItems[$textKey] = $text;
-                       $tail = $text;
-               }
-       }
-
-       /**
-        * @return array
-        */
-       function __sleep() {
-               $this->compress();
-               if ( $this->mItems === [] ) {
-                       $info = false;
-               } else {
-                       // Take forward differences to improve the compression ratio for sequences
-                       $map = '';
-                       $prev = 0;
-                       foreach ( $this->mDiffMap as $i ) {
-                               if ( $map !== '' ) {
-                                       $map .= ',';
-                               }
-                               $map .= $i - $prev;
-                               $prev = $i;
-                       }
-                       $info = [
-                               'diffs' => $this->mDiffs,
-                               'map' => $map
-                       ];
-               }
-               if ( isset( $this->mDefaultKey ) ) {
-                       $info['default'] = $this->mDefaultKey;
-               }
-               $this->mCompressed = gzdeflate( serialize( $info ) );
-               return [ 'mCompressed' ];
-       }
-
-       function __wakeup() {
-               // addItem() doesn't work if mItems is partially filled from mDiffs
-               $this->mFrozen = true;
-               $info = unserialize( gzinflate( $this->mCompressed ) );
-               unset( $this->mCompressed );
-
-               if ( !$info ) {
-                       // Empty object
-                       return;
-               }
-
-               if ( isset( $info['default'] ) ) {
-                       $this->mDefaultKey = $info['default'];
-               }
-               $this->mDiffs = $info['diffs'];
-               if ( isset( $info['base'] ) ) {
-                       // Old format
-                       $this->mDiffMap = range( 0, count( $this->mDiffs ) - 1 );
-                       array_unshift( $this->mDiffs,
-                               pack( 'VVCV', 0, 0, self::XDL_BDOP_INSB, strlen( $info['base'] ) ) .
-                               $info['base'] );
-               } else {
-                       // New format
-                       $map = explode( ',', $info['map'] );
-                       $cur = 0;
-                       $this->mDiffMap = [];
-                       foreach ( $map as $i ) {
-                               $cur += $i;
-                               $this->mDiffMap[] = $cur;
-                       }
-               }
-               $this->uncompress();
-       }
-
-       /**
-        * Helper function for compression jobs
-        * Returns true until the object is "full" and ready to be committed
-        *
-        * @return bool
-        */
-       function isHappy() {
-               return $this->mSize < $this->mMaxSize
-                       && count( $this->mItems ) < $this->mMaxCount;
-       }
-
-}
-
-// phpcs:ignore Generic.CodeAnalysis.UnconditionalIfStatement.Found
-if ( false ) {
-       // Blobs generated by MediaWiki < 1.5 on PHP 4 were serialized with the
-       // class name coerced to lowercase. We can improve efficiency by adding
-       // autoload entries for the lowercase variants of these classes (T166759).
-       // The code below is never executed, but it is picked up by the AutoloadGenerator
-       // parser, which scans for class_alias() calls.
-       class_alias( ConcatenatedGzipHistoryBlob::class, 'concatenatedgziphistoryblob' );
-       class_alias( HistoryBlobCurStub::class, 'historyblobcurstub' );
-       class_alias( HistoryBlobStub::class, 'historyblobstub' );
-}
index df99556..17dc037 100644 (file)
@@ -1765,15 +1765,7 @@ class Linker {
                        $inner = $context->msg( 'brackets' )->rawParams( $inner )->escaped();
                }
 
-               /**
-                * FIXME
-                * Remove all references to DisableRollbackConfirmationFeature
-                * after release of rollback feature. See T199534
-                */
-               if ( !MediaWikiServices::getInstance()
-                               ->getMainConfig()->get( 'DisableRollbackConfirmationFeature' ) &&
-                        $context->getUser()->getBoolOption( 'showrollbackconfirmation' )
-               ) {
+               if ( $context->getUser()->getBoolOption( 'showrollbackconfirmation' ) ) {
                        $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
                        $stats->increment( 'rollbackconfirmation.event.load' );
                        $context->getOutput()->addModules( 'mediawiki.page.rollback.confirmation' );
index bcec0a1..db5750a 100644 (file)
@@ -106,7 +106,7 @@ class MovePage {
 
                $oldid = $this->oldTitle->getArticleID();
 
-               if ( strlen( $this->newTitle->getDBkey() ) < 1 ) {
+               if ( $this->newTitle->getDBkey() === '' ) {
                        $status->fatal( 'articleexists' );
                }
                if (
index 7af80dc..b400797 100644 (file)
@@ -562,7 +562,7 @@ class SiteConfiguration {
                                ->execute();
 
                        $data = trim( $result->getStdout() );
-                       if ( $result->getExitCode() != 0 || !strlen( $data ) ) {
+                       if ( $result->getExitCode() || $data === '' ) {
                                throw new MWException( "Failed to run getConfiguration.php: {$result->getStdout()}" );
                        }
                        $res = unserialize( $data );
index 0f45839..ce0b959 100644 (file)
@@ -1702,16 +1702,18 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return string Base name
         */
        public function getBaseText() {
+               $text = $this->getText();
                if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
-                       return $this->getText();
+                       return $text;
                }
 
-               $parts = explode( '/', $this->getText() );
-               # Don't discard the real title if there's no subpage involved
-               if ( count( $parts ) > 1 ) {
-                       unset( $parts[count( $parts ) - 1] );
+               $lastSlashPos = strrpos( $text, '/' );
+               // Don't discard the real title if there's no subpage involved
+               if ( $lastSlashPos === false ) {
+                       return $text;
                }
-               return implode( '/', $parts );
+
+               return substr( $text, 0, $lastSlashPos );
        }
 
        /**
index fbf43e0..cc11233 100644 (file)
@@ -78,7 +78,7 @@ class HistoryAction extends FormlessAction {
                                        ->rawParams( $this->getLanguage()->pipeList( $links ) )
                                        ->escaped();
                }
-               return $subtitle;
+               return Html::rawElement( 'div', [ 'class' => 'mw-history-subtitle' ], $subtitle );
        }
 
        /**
index 0e86fda..e2fc265 100644 (file)
@@ -73,20 +73,12 @@ class RollbackAction extends FormAction {
        }
 
        /**
-        * @throws ConfigException
         * @throws ErrorPageError
         * @throws ReadOnlyError
         * @throws ThrottledError
         */
        public function show() {
-               /**
-                * FIXME
-                * Remove temporary check of DisableRollbackConfirmationFeature
-                * after release of rollback feature. See T199534
-                */
-               $config = \MediaWiki\MediaWikiServices::getInstance()->getMainConfig();
-               if ( $config->get( 'DisableRollbackConfirmationFeature' ) == true ||
-                        $this->getUser()->getOption( 'showrollbackconfirmation' ) == false ||
+               if ( $this->getUser()->getOption( 'showrollbackconfirmation' ) == false ||
                         $this->getRequest()->wasPosted() ) {
                        $this->handleRollbackRequest();
                } else {
index 3781ab0..7e46c1a 100644 (file)
@@ -268,7 +268,7 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                                                [ 'ar_rev_id' => $revids ],
                                                __METHOD__
                                        ),
-                               ], false );
+                               ], $db::UNION_DISTINCT );
                                $res = $db->query( $sql, __METHOD__ );
                                foreach ( $res as $row ) {
                                        if ( (int)$row->id === (int)$params['startid'] ) {
index 96fa8a1..82a52b4 100644 (file)
@@ -664,8 +664,8 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                }
 
                $data = [
-                       'url' => strlen( $url ) ? $url : '',
-                       'text' => strlen( $text ) ? $text : '',
+                       'url' => (string)$url,
+                       'text' => (string)$text,
                ];
 
                return $this->getResult()->addValue( 'query', $property, $data );
index fe5f6c4..5184562 100644 (file)
@@ -477,7 +477,7 @@ class ApiStashEdit extends ApiBase {
         * @param string $newKey
         */
        private static function pruneExcessStashedEntries( BagOStuff $cache, User $user, $newKey ) {
-               $key = $cache->makeKey( 'stash-edit-recent', $user->getId() );
+               $key = $cache->makeKey( 'stash-edit-recent', sha1( $user->getName() ) );
 
                $keyList = $cache->get( $key ) ?: [];
                if ( count( $keyList ) >= self::MAX_CACHE_RECENT ) {
index 0e03e72..2f3a6f6 100644 (file)
@@ -154,7 +154,7 @@ class WikiTextStructure {
                        'enableSectionEditTokens' => false,
                        'allowTOC' => false,
                ] );
-               if ( strlen( $text ) == 0 ) {
+               if ( $text === '' ) {
                        $this->allText = "";
                        // empty text - nothing to seek here
                        return;
index 4444bac..0e24134 100644 (file)
  */
 use MediaWiki\MediaWikiServices;
 use Wikimedia\ScopedCallback;
-use Wikimedia\Rdbms\IDatabase;
 
 /**
  * Update object handling the cleanup of links tables after a page was deleted.
  */
-class LinksDeletionUpdate extends DataUpdate implements EnqueueableDataUpdate {
+class LinksDeletionUpdate extends LinksUpdate implements EnqueueableDataUpdate {
        /** @var WikiPage */
        protected $page;
-       /** @var int */
-       protected $pageId;
        /** @var string */
        protected $timestamp;
 
-       /** @var IDatabase */
-       private $db;
-
        /**
         * @param WikiPage $page Page we are updating
         * @param int|null $pageId ID of the page we are updating [optional]
@@ -44,63 +38,37 @@ class LinksDeletionUpdate extends DataUpdate implements EnqueueableDataUpdate {
         * @throws MWException
         */
        function __construct( WikiPage $page, $pageId = null, $timestamp = null ) {
-               parent::__construct();
-
                $this->page = $page;
                if ( $pageId ) {
-                       $this->pageId = $pageId; // page ID at time of deletion
+                       $this->mId = $pageId; // page ID at time of deletion
                } elseif ( $page->exists() ) {
-                       $this->pageId = $page->getId();
+                       $this->mId = $page->getId();
                } else {
                        throw new InvalidArgumentException( "Page ID not known. Page doesn't exist?" );
                }
 
                $this->timestamp = $timestamp ?: wfTimestampNow();
+
+               $fakePO = new ParserOutput();
+               $fakePO->setCacheTime( $timestamp );
+               parent::__construct( $page->getTitle(), $fakePO, false );
        }
 
-       public function doUpdate() {
+       protected function doIncrementalUpdate() {
                $services = MediaWikiServices::getInstance();
                $config = $services->getMainConfig();
                $lbFactory = $services->getDBLoadBalancerFactory();
                $batchSize = $config->get( 'UpdateRowsPerQuery' );
 
-               // Page may already be deleted, so don't just getId()
-               $id = $this->pageId;
-
-               if ( $this->ticket ) {
-                       // Make sure all links update threads see the changes of each other.
-                       // This handles the case when updates have to batched into several COMMITs.
-                       $scopedLock = LinksUpdate::acquirePageLock( $this->getDB(), $id );
-                       if ( !$scopedLock ) {
-                               throw new RuntimeException( "Could not acquire lock for page ID '{$id}'." );
-                       }
-               }
+               $id = $this->mId;
+               $title = $this->mTitle;
 
-               $title = $this->page->getTitle();
                $dbw = $this->getDB(); // convenience
 
-               // Delete restrictions for it
-               $dbw->delete( 'page_restrictions', [ 'pr_page' => $id ], __METHOD__ );
+               parent::doIncrementalUpdate();
 
-               // Fix category table counts
-               $cats = $dbw->selectFieldValues(
-                       'categorylinks',
-                       'cl_to',
-                       [ 'cl_from' => $id ],
-                       __METHOD__
-               );
-               $catBatches = array_chunk( $cats, $batchSize );
-               foreach ( $catBatches as $catBatch ) {
-                       $this->page->updateCategoryCounts( [], $catBatch, $id );
-                       if ( count( $catBatches ) > 1 ) {
-                               // Only sacrifice atomicity if necessary due to size
-                               $lbFactory->commitAndWaitForReplication(
-                                       __METHOD__, $this->ticket, [ 'domain' => $dbw->getDomainID() ]
-                               );
-                       }
-               }
-
-               // Refresh counts on categories that should be empty now
+               // Typically, a category is empty when deleted, so check that we don't leave
+               // spurious row in the category table.
                if ( $title->getNamespace() === NS_CATEGORY ) {
                        // T166757: do the update after the main job DB commit
                        DeferredUpdates::addCallableUpdate( function () use ( $title ) {
@@ -109,52 +77,11 @@ class LinksDeletionUpdate extends DataUpdate implements EnqueueableDataUpdate {
                        } );
                }
 
-               $this->batchDeleteByPK(
-                       'pagelinks',
-                       [ 'pl_from' => $id ],
-                       [ 'pl_from', 'pl_namespace', 'pl_title' ],
-                       $batchSize
-               );
-               $this->batchDeleteByPK(
-                       'imagelinks',
-                       [ 'il_from' => $id ],
-                       [ 'il_from', 'il_to' ],
-                       $batchSize
-               );
-               $this->batchDeleteByPK(
-                       'categorylinks',
-                       [ 'cl_from' => $id ],
-                       [ 'cl_from', 'cl_to' ],
-                       $batchSize
-               );
-               $this->batchDeleteByPK(
-                       'templatelinks',
-                       [ 'tl_from' => $id ],
-                       [ 'tl_from', 'tl_namespace', 'tl_title' ],
-                       $batchSize
-               );
-               $this->batchDeleteByPK(
-                       'externallinks',
-                       [ 'el_from' => $id ],
-                       [ 'el_id' ],
-                       $batchSize
-               );
-               $this->batchDeleteByPK(
-                       'langlinks',
-                       [ 'll_from' => $id ],
-                       [ 'll_from', 'll_lang' ],
-                       $batchSize
-               );
-               $this->batchDeleteByPK(
-                       'iwlinks',
-                       [ 'iwl_from' => $id ],
-                       [ 'iwl_from', 'iwl_prefix', 'iwl_title' ],
-                       $batchSize
-               );
+               // Delete restrictions for the deleted page
+               $dbw->delete( 'page_restrictions', [ 'pr_page' => $id ], __METHOD__ );
 
-               // Delete any redirect entry or page props entries
+               // Delete any redirect entry
                $dbw->delete( 'redirect', [ 'rd_from' => $id ], __METHOD__ );
-               $dbw->delete( 'page_props', [ 'pp_page' => $id ], __METHOD__ );
 
                // Find recentchanges entries to clean up...
                $rcIdsForTitle = $dbw->selectFieldValues(
@@ -191,46 +118,14 @@ class LinksDeletionUpdate extends DataUpdate implements EnqueueableDataUpdate {
                ScopedCallback::consume( $scopedLock );
        }
 
-       private function batchDeleteByPK( $table, array $conds, array $pk, $bSize ) {
-               $services = MediaWikiServices::getInstance();
-               $lbFactory = $services->getDBLoadBalancerFactory();
-               $dbw = $this->getDB(); // convenience
-
-               $res = $dbw->select( $table, $pk, $conds, __METHOD__ );
-
-               $pkDeleteConds = [];
-               foreach ( $res as $row ) {
-                       $pkDeleteConds[] = $dbw->makeList( (array)$row, LIST_AND );
-                       if ( count( $pkDeleteConds ) >= $bSize ) {
-                               $dbw->delete( $table, $dbw->makeList( $pkDeleteConds, LIST_OR ), __METHOD__ );
-                               $lbFactory->commitAndWaitForReplication(
-                                       __METHOD__, $this->ticket, [ 'domain' => $dbw->getDomainID() ]
-                               );
-                               $pkDeleteConds = [];
-                       }
-               }
-
-               if ( $pkDeleteConds ) {
-                       $dbw->delete( $table, $dbw->makeList( $pkDeleteConds, LIST_OR ), __METHOD__ );
-               }
-       }
-
-       protected function getDB() {
-               if ( !$this->db ) {
-                       $this->db = wfGetDB( DB_MASTER );
-               }
-
-               return $this->db;
-       }
-
        public function getAsJobSpecification() {
                return [
                        'domain' => $this->getDB()->getDomainID(),
                        'job' => new JobSpecification(
                                'deleteLinks',
-                               [ 'pageId' => $this->pageId, 'timestamp' => $this->timestamp ],
+                               [ 'pageId' => $this->mId, 'timestamp' => $this->timestamp ],
                                [ 'removeDuplicates' => true ],
-                               $this->page->getTitle()
+                               $this->mTitle
                        )
                ];
        }
index 045795e..14f86b7 100644 (file)
@@ -122,7 +122,11 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate {
                parent::__construct();
 
                $this->mTitle = $title;
-               $this->mId = $title->getArticleID( Title::GAID_FOR_UPDATE );
+
+               if ( !$this->mId ) {
+                       // NOTE: subclasses may initialize mId before calling this constructor!
+                       $this->mId = $title->getArticleID( Title::GAID_FOR_UPDATE );
+               }
 
                if ( !$this->mId ) {
                        throw new InvalidArgumentException(
@@ -1180,7 +1184,7 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate {
        /**
         * @return IDatabase
         */
-       private function getDB() {
+       protected function getDB() {
                if ( !$this->db ) {
                        $this->db = wfGetDB( DB_MASTER );
                }
diff --git a/includes/historyblob/ConcatenatedGzipHistoryBlob.php b/includes/historyblob/ConcatenatedGzipHistoryBlob.php
new file mode 100644 (file)
index 0000000..f6ca2f5
--- /dev/null
@@ -0,0 +1,146 @@
+<?php
+/**
+ * Efficient concatenated text 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
+ */
+
+/**
+ * Concatenated gzip (CGZ) storage
+ * Improves compression ratio by concatenating like objects before gzipping
+ */
+class ConcatenatedGzipHistoryBlob implements HistoryBlob {
+       public $mVersion = 0, $mCompressed = false, $mItems = [], $mDefaultHash = '';
+       public $mSize = 0;
+       public $mMaxSize = 10000000;
+       public $mMaxCount = 100;
+
+       public function __construct() {
+               if ( !function_exists( 'gzdeflate' ) ) {
+                       throw new MWException( "Need zlib support to read or write this "
+                               . "kind of history object (ConcatenatedGzipHistoryBlob)\n" );
+               }
+       }
+
+       /**
+        * @param string $text
+        * @return string
+        */
+       public function addItem( $text ) {
+               $this->uncompress();
+               $hash = md5( $text );
+               if ( !isset( $this->mItems[$hash] ) ) {
+                       $this->mItems[$hash] = $text;
+                       $this->mSize += strlen( $text );
+               }
+               return $hash;
+       }
+
+       /**
+        * @param string $hash
+        * @return array|bool
+        */
+       public function getItem( $hash ) {
+               $this->uncompress();
+               if ( array_key_exists( $hash, $this->mItems ) ) {
+                       return $this->mItems[$hash];
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * @param string $text
+        * @return void
+        */
+       public function setText( $text ) {
+               $this->uncompress();
+               $this->mDefaultHash = $this->addItem( $text );
+       }
+
+       /**
+        * @return array|bool
+        */
+       public function getText() {
+               $this->uncompress();
+               return $this->getItem( $this->mDefaultHash );
+       }
+
+       /**
+        * Remove an item
+        *
+        * @param string $hash
+        */
+       public function removeItem( $hash ) {
+               $this->mSize -= strlen( $this->mItems[$hash] );
+               unset( $this->mItems[$hash] );
+       }
+
+       /**
+        * Compress the bulk data in the object
+        */
+       public function compress() {
+               if ( !$this->mCompressed ) {
+                       $this->mItems = gzdeflate( serialize( $this->mItems ) );
+                       $this->mCompressed = true;
+               }
+       }
+
+       /**
+        * Uncompress bulk data
+        */
+       public function uncompress() {
+               if ( $this->mCompressed ) {
+                       $this->mItems = unserialize( gzinflate( $this->mItems ) );
+                       $this->mCompressed = false;
+               }
+       }
+
+       /**
+        * @return array
+        */
+       function __sleep() {
+               $this->compress();
+               return [ 'mVersion', 'mCompressed', 'mItems', 'mDefaultHash' ];
+       }
+
+       function __wakeup() {
+               $this->uncompress();
+       }
+
+       /**
+        * Helper function for compression jobs
+        * Returns true until the object is "full" and ready to be committed
+        *
+        * @return bool
+        */
+       public function isHappy() {
+               return $this->mSize < $this->mMaxSize
+                       && count( $this->mItems ) < $this->mMaxCount;
+       }
+}
+
+// phpcs:ignore Generic.CodeAnalysis.UnconditionalIfStatement.Found
+if ( false ) {
+       // Blobs generated by MediaWiki < 1.5 on PHP 4 were serialized with the
+       // class name coerced to lowercase. We can improve efficiency by adding
+       // autoload entries for the lowercase variants of these classes (T166759).
+       // The code below is never executed, but it is picked up by the AutoloadGenerator
+       // parser, which scans for class_alias() calls.
+       class_alias( ConcatenatedGzipHistoryBlob::class, 'concatenatedgziphistoryblob' );
+}
diff --git a/includes/historyblob/DiffHistoryBlob.php b/includes/historyblob/DiffHistoryBlob.php
new file mode 100644 (file)
index 0000000..8d92fe5
--- /dev/null
@@ -0,0 +1,377 @@
+<?php
+/**
+ * Efficient concatenated text 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
+ */
+
+/**
+ * Diff-based history compression
+ * Requires xdiff 1.5+ and zlib
+ */
+class DiffHistoryBlob implements HistoryBlob {
+       /** @var array Uncompressed item cache */
+       public $mItems = [];
+
+       /** @var int Total uncompressed size */
+       public $mSize = 0;
+
+       /**
+        * @var array Array of diffs. If a diff D from A to B is notated D = B - A,
+        * and Z is an empty string:
+        *
+        *              { item[map[i]] - item[map[i-1]]   where i > 0
+        *    diff[i] = {
+        *              { item[map[i]] - Z                where i = 0
+        */
+       public $mDiffs;
+
+       /** @var array The diff map, see above */
+       public $mDiffMap;
+
+       /** @var int The key for getText()
+        */
+       public $mDefaultKey;
+
+       /** @var string Compressed storage */
+       public $mCompressed;
+
+       /** @var bool True if the object is locked against further writes */
+       public $mFrozen = false;
+
+       /**
+        * @var int The maximum uncompressed size before the object becomes sad
+        * Should be less than max_allowed_packet
+        */
+       public $mMaxSize = 10000000;
+
+       /** @var int The maximum number of text items before the object becomes sad */
+       public $mMaxCount = 100;
+
+       /** Constants from xdiff.h */
+       const XDL_BDOP_INS = 1;
+       const XDL_BDOP_CPY = 2;
+       const XDL_BDOP_INSB = 3;
+
+       function __construct() {
+               if ( !function_exists( 'gzdeflate' ) ) {
+                       throw new MWException( "Need zlib support to read or write DiffHistoryBlob\n" );
+               }
+       }
+
+       /**
+        * @throws MWException
+        * @param string $text
+        * @return int
+        */
+       function addItem( $text ) {
+               if ( $this->mFrozen ) {
+                       throw new MWException( __METHOD__ . ": Cannot add more items after sleep/wakeup" );
+               }
+
+               $this->mItems[] = $text;
+               $this->mSize += strlen( $text );
+               $this->mDiffs = null; // later
+               return count( $this->mItems ) - 1;
+       }
+
+       /**
+        * @param string $key
+        * @return string
+        */
+       function getItem( $key ) {
+               return $this->mItems[$key];
+       }
+
+       /**
+        * @param string $text
+        */
+       function setText( $text ) {
+               $this->mDefaultKey = $this->addItem( $text );
+       }
+
+       /**
+        * @return string
+        */
+       function getText() {
+               return $this->getItem( $this->mDefaultKey );
+       }
+
+       /**
+        * @throws MWException
+        */
+       function compress() {
+               if ( !function_exists( 'xdiff_string_rabdiff' ) ) {
+                       throw new MWException( "Need xdiff 1.5+ support to write DiffHistoryBlob\n" );
+               }
+               if ( isset( $this->mDiffs ) ) {
+                       // Already compressed
+                       return;
+               }
+               if ( $this->mItems === [] ) {
+                       return;
+               }
+
+               // Create two diff sequences: one for main text and one for small text
+               $sequences = [
+                       'small' => [
+                               'tail' => '',
+                               'diffs' => [],
+                               'map' => [],
+                       ],
+                       'main' => [
+                               'tail' => '',
+                               'diffs' => [],
+                               'map' => [],
+                       ],
+               ];
+               $smallFactor = 0.5;
+
+               $mItemsCount = count( $this->mItems );
+               for ( $i = 0; $i < $mItemsCount; $i++ ) {
+                       $text = $this->mItems[$i];
+                       if ( $i == 0 ) {
+                               $seqName = 'main';
+                       } else {
+                               $mainTail = $sequences['main']['tail'];
+                               if ( strlen( $text ) < strlen( $mainTail ) * $smallFactor ) {
+                                       $seqName = 'small';
+                               } else {
+                                       $seqName = 'main';
+                               }
+                       }
+                       $seq =& $sequences[$seqName];
+                       $tail = $seq['tail'];
+                       $diff = $this->diff( $tail, $text );
+                       $seq['diffs'][] = $diff;
+                       $seq['map'][] = $i;
+                       $seq['tail'] = $text;
+               }
+               unset( $seq ); // unlink dangerous alias
+
+               // Knit the sequences together
+               $tail = '';
+               $this->mDiffs = [];
+               $this->mDiffMap = [];
+               foreach ( $sequences as $seq ) {
+                       if ( $seq['diffs'] === [] ) {
+                               continue;
+                       }
+                       if ( $tail === '' ) {
+                               $this->mDiffs[] = $seq['diffs'][0];
+                       } else {
+                               $head = $this->patch( '', $seq['diffs'][0] );
+                               $this->mDiffs[] = $this->diff( $tail, $head );
+                       }
+                       $this->mDiffMap[] = $seq['map'][0];
+                       $diffsCount = count( $seq['diffs'] );
+                       for ( $i = 1; $i < $diffsCount; $i++ ) {
+                               $this->mDiffs[] = $seq['diffs'][$i];
+                               $this->mDiffMap[] = $seq['map'][$i];
+                       }
+                       $tail = $seq['tail'];
+               }
+       }
+
+       /**
+        * @param string $t1
+        * @param string $t2
+        * @return string
+        */
+       function diff( $t1, $t2 ) {
+               # Need to do a null concatenation with warnings off, due to bugs in the current version of xdiff
+               # "String is not zero-terminated"
+               Wikimedia\suppressWarnings();
+               $diff = xdiff_string_rabdiff( $t1, $t2 ) . '';
+               Wikimedia\restoreWarnings();
+               return $diff;
+       }
+
+       /**
+        * @param string $base
+        * @param string $diff
+        * @return bool|string
+        */
+       function patch( $base, $diff ) {
+               if ( function_exists( 'xdiff_string_bpatch' ) ) {
+                       Wikimedia\suppressWarnings();
+                       $text = xdiff_string_bpatch( $base, $diff ) . '';
+                       Wikimedia\restoreWarnings();
+                       return $text;
+               }
+
+               # Pure PHP implementation
+
+               $header = unpack( 'Vofp/Vcsize', substr( $diff, 0, 8 ) );
+
+               # Check the checksum if hash extension is available
+               $ofp = $this->xdiffAdler32( $base );
+               if ( $ofp !== false && $ofp !== substr( $diff, 0, 4 ) ) {
+                       wfDebug( __METHOD__ . ": incorrect base checksum\n" );
+                       return false;
+               }
+               if ( $header['csize'] != strlen( $base ) ) {
+                       wfDebug( __METHOD__ . ": incorrect base length\n" );
+                       return false;
+               }
+
+               $p = 8;
+               $out = '';
+               while ( $p < strlen( $diff ) ) {
+                       $x = unpack( 'Cop', substr( $diff, $p, 1 ) );
+                       $op = $x['op'];
+                       ++$p;
+                       switch ( $op ) {
+                               case self::XDL_BDOP_INS:
+                                       $x = unpack( 'Csize', substr( $diff, $p, 1 ) );
+                                       $p++;
+                                       $out .= substr( $diff, $p, $x['size'] );
+                                       $p += $x['size'];
+                                       break;
+                               case self::XDL_BDOP_INSB:
+                                       $x = unpack( 'Vcsize', substr( $diff, $p, 4 ) );
+                                       $p += 4;
+                                       $out .= substr( $diff, $p, $x['csize'] );
+                                       $p += $x['csize'];
+                                       break;
+                               case self::XDL_BDOP_CPY:
+                                       $x = unpack( 'Voff/Vcsize', substr( $diff, $p, 8 ) );
+                                       $p += 8;
+                                       $out .= substr( $base, $x['off'], $x['csize'] );
+                                       break;
+                               default:
+                                       wfDebug( __METHOD__ . ": invalid op\n" );
+                                       return false;
+                       }
+               }
+               return $out;
+       }
+
+       /**
+        * Compute a binary "Adler-32" checksum as defined by LibXDiff, i.e. with
+        * the bytes backwards and initialised with 0 instead of 1. See T36428.
+        *
+        * @param string $s
+        * @return string|bool False if the hash extension is not available
+        */
+       function xdiffAdler32( $s ) {
+               if ( !function_exists( 'hash' ) ) {
+                       return false;
+               }
+
+               static $init;
+               if ( $init === null ) {
+                       $init = str_repeat( "\xf0", 205 ) . "\xee" . str_repeat( "\xf0", 67 ) . "\x02";
+               }
+
+               // The real Adler-32 checksum of $init is zero, so it initialises the
+               // state to zero, as it is at the start of LibXDiff's checksum
+               // algorithm. Appending the subject string then simulates LibXDiff.
+               return strrev( hash( 'adler32', $init . $s, true ) );
+       }
+
+       function uncompress() {
+               if ( !$this->mDiffs ) {
+                       return;
+               }
+               $tail = '';
+               $mDiffsCount = count( $this->mDiffs );
+               for ( $diffKey = 0; $diffKey < $mDiffsCount; $diffKey++ ) {
+                       $textKey = $this->mDiffMap[$diffKey];
+                       $text = $this->patch( $tail, $this->mDiffs[$diffKey] );
+                       $this->mItems[$textKey] = $text;
+                       $tail = $text;
+               }
+       }
+
+       /**
+        * @return array
+        */
+       function __sleep() {
+               $this->compress();
+               if ( $this->mItems === [] ) {
+                       $info = false;
+               } else {
+                       // Take forward differences to improve the compression ratio for sequences
+                       $map = '';
+                       $prev = 0;
+                       foreach ( $this->mDiffMap as $i ) {
+                               if ( $map !== '' ) {
+                                       $map .= ',';
+                               }
+                               $map .= $i - $prev;
+                               $prev = $i;
+                       }
+                       $info = [
+                               'diffs' => $this->mDiffs,
+                               'map' => $map
+                       ];
+               }
+               if ( isset( $this->mDefaultKey ) ) {
+                       $info['default'] = $this->mDefaultKey;
+               }
+               $this->mCompressed = gzdeflate( serialize( $info ) );
+               return [ 'mCompressed' ];
+       }
+
+       function __wakeup() {
+               // addItem() doesn't work if mItems is partially filled from mDiffs
+               $this->mFrozen = true;
+               $info = unserialize( gzinflate( $this->mCompressed ) );
+               unset( $this->mCompressed );
+
+               if ( !$info ) {
+                       // Empty object
+                       return;
+               }
+
+               if ( isset( $info['default'] ) ) {
+                       $this->mDefaultKey = $info['default'];
+               }
+               $this->mDiffs = $info['diffs'];
+               if ( isset( $info['base'] ) ) {
+                       // Old format
+                       $this->mDiffMap = range( 0, count( $this->mDiffs ) - 1 );
+                       array_unshift( $this->mDiffs,
+                               pack( 'VVCV', 0, 0, self::XDL_BDOP_INSB, strlen( $info['base'] ) ) .
+                               $info['base'] );
+               } else {
+                       // New format
+                       $map = explode( ',', $info['map'] );
+                       $cur = 0;
+                       $this->mDiffMap = [];
+                       foreach ( $map as $i ) {
+                               $cur += $i;
+                               $this->mDiffMap[] = $cur;
+                       }
+               }
+               $this->uncompress();
+       }
+
+       /**
+        * Helper function for compression jobs
+        * Returns true until the object is "full" and ready to be committed
+        *
+        * @return bool
+        */
+       function isHappy() {
+               return $this->mSize < $this->mMaxSize
+                       && count( $this->mItems ) < $this->mMaxCount;
+       }
+
+}
diff --git a/includes/historyblob/HistoryBlob.php b/includes/historyblob/HistoryBlob.php
new file mode 100644 (file)
index 0000000..36c7c8f
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Efficient concatenated text 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
+ */
+
+/**
+ * Base class for general text storage via the "object" flag in old_flags, or
+ * two-part external storage URLs. Used for represent efficient concatenated
+ * storage, and migration-related pointer objects.
+ */
+interface HistoryBlob {
+       /**
+        * Adds an item of text, returns a stub object which points to the item.
+        * You must call setLocation() on the stub object before storing it to the
+        * database
+        *
+        * @param string $text
+        *
+        * @return string The key for getItem()
+        */
+       function addItem( $text );
+
+       /**
+        * Get item by key, or false if the key is not present
+        *
+        * @param string $key
+        *
+        * @return string|bool
+        */
+       function getItem( $key );
+
+       /**
+        * Set the "default text"
+        * This concept is an odd property of the current DB schema, whereby each text item has a revision
+        * associated with it. The default text is the text of the associated revision. There may, however,
+        * be other revisions in the same object.
+        *
+        * Default text is not required for two-part external storage URLs.
+        *
+        * @param string $text
+        */
+       function setText( $text );
+
+       /**
+        * Get default text. This is called from Revision::getRevisionText()
+        *
+        * @return string
+        */
+       function getText();
+}
diff --git a/includes/historyblob/HistoryBlobCurStub.php b/includes/historyblob/HistoryBlobCurStub.php
new file mode 100644 (file)
index 0000000..8858c8d
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Efficient concatenated text 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
+ */
+
+/**
+ * To speed up conversion from 1.4 to 1.5 schema, text rows can refer to the
+ * leftover cur table as the backend. This avoids expensively copying hundreds
+ * of megabytes of data during the conversion downtime.
+ *
+ * Serialized HistoryBlobCurStub objects will be inserted into the text table
+ * on conversion if $wgLegacySchemaConversion is set to true.
+ */
+class HistoryBlobCurStub {
+       /** @var int */
+       public $mCurId;
+
+       /**
+        * @param int $curid The cur_id pointed to
+        */
+       function __construct( $curid = 0 ) {
+               $this->mCurId = $curid;
+       }
+
+       /**
+        * Sets the location (cur_id) of the main object to which this object
+        * points
+        *
+        * @param int $id
+        */
+       function setLocation( $id ) {
+               $this->mCurId = $id;
+       }
+
+       /**
+        * @return string|bool
+        */
+       function getText() {
+               $dbr = wfGetDB( DB_REPLICA );
+               $row = $dbr->selectRow( 'cur', [ 'cur_text' ], [ 'cur_id' => $this->mCurId ] );
+               if ( !$row ) {
+                       return false;
+               }
+               return $row->cur_text;
+       }
+}
+
+// phpcs:ignore Generic.CodeAnalysis.UnconditionalIfStatement.Found
+if ( false ) {
+       // Blobs generated by MediaWiki < 1.5 on PHP 4 were serialized with the
+       // class name coerced to lowercase. We can improve efficiency by adding
+       // autoload entries for the lowercase variants of these classes (T166759).
+       // The code below is never executed, but it is picked up by the AutoloadGenerator
+       // parser, which scans for class_alias() calls.
+       class_alias( HistoryBlobCurStub::class, 'historyblobcurstub' );
+}
diff --git a/includes/historyblob/HistoryBlobStub.php b/includes/historyblob/HistoryBlobStub.php
new file mode 100644 (file)
index 0000000..4995d3b
--- /dev/null
@@ -0,0 +1,150 @@
+<?php
+/**
+ * Efficient concatenated text 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
+ */
+
+/**
+ * Pointer object for an item within a CGZ blob stored in the text table.
+ */
+class HistoryBlobStub {
+       /**
+        * @var array One-step cache variable to hold base blobs; operations that
+        * pull multiple revisions may often pull multiple times from the same
+        * blob. By keeping the last-used one open, we avoid redundant
+        * unserialization and decompression overhead.
+        */
+       protected static $blobCache = [];
+
+       /** @var int */
+       public $mOldId;
+
+       /** @var string */
+       public $mHash;
+
+       /** @var string */
+       public $mRef;
+
+       /**
+        * @param string $hash The content hash of the text
+        * @param int $oldid The old_id for the CGZ object
+        */
+       function __construct( $hash = '', $oldid = 0 ) {
+               $this->mHash = $hash;
+       }
+
+       /**
+        * Sets the location (old_id) of the main object to which this object
+        * points
+        * @param int $id
+        */
+       function setLocation( $id ) {
+               $this->mOldId = $id;
+       }
+
+       /**
+        * Sets the location (old_id) of the referring object
+        * @param string $id
+        */
+       function setReferrer( $id ) {
+               $this->mRef = $id;
+       }
+
+       /**
+        * Gets the location of the referring object
+        * @return string
+        */
+       function getReferrer() {
+               return $this->mRef;
+       }
+
+       /**
+        * @return string|false
+        */
+       function getText() {
+               if ( isset( self::$blobCache[$this->mOldId] ) ) {
+                       $obj = self::$blobCache[$this->mOldId];
+               } else {
+                       $dbr = wfGetDB( DB_REPLICA );
+                       $row = $dbr->selectRow(
+                               'text',
+                               [ 'old_flags', 'old_text' ],
+                               [ 'old_id' => $this->mOldId ]
+                       );
+
+                       if ( !$row ) {
+                               return false;
+                       }
+
+                       $flags = explode( ',', $row->old_flags );
+                       if ( in_array( 'external', $flags ) ) {
+                               $url = $row->old_text;
+                               $parts = explode( '://', $url, 2 );
+                               if ( !isset( $parts[1] ) || $parts[1] == '' ) {
+                                       return false;
+                               }
+                               $row->old_text = ExternalStore::fetchFromURL( $url );
+
+                       }
+
+                       if ( !in_array( 'object', $flags ) ) {
+                               return false;
+                       }
+
+                       if ( in_array( 'gzip', $flags ) ) {
+                               // This shouldn't happen, but a bug in the compress script
+                               // may at times gzip-compress a HistoryBlob object row.
+                               $obj = unserialize( gzinflate( $row->old_text ) );
+                       } else {
+                               $obj = unserialize( $row->old_text );
+                       }
+
+                       if ( !is_object( $obj ) ) {
+                               // Correct for old double-serialization bug.
+                               $obj = unserialize( $obj );
+                       }
+
+                       // Save this item for reference; if pulling many
+                       // items in a row we'll likely use it again.
+                       $obj->uncompress();
+                       self::$blobCache = [ $this->mOldId => $obj ];
+               }
+
+               return $obj->getItem( $this->mHash );
+       }
+
+       /**
+        * Get the content hash
+        *
+        * @return string
+        */
+       function getHash() {
+               return $this->mHash;
+       }
+}
+
+// phpcs:ignore Generic.CodeAnalysis.UnconditionalIfStatement.Found
+if ( false ) {
+       // Blobs generated by MediaWiki < 1.5 on PHP 4 were serialized with the
+       // class name coerced to lowercase. We can improve efficiency by adding
+       // autoload entries for the lowercase variants of these classes (T166759).
+       // The code below is never executed, but it is picked up by the AutoloadGenerator
+       // parser, which scans for class_alias() calls.
+       class_alias( HistoryBlobStub::class, 'historyblobstub' );
+}
index 7b59a1d..ffdf5f8 100644 (file)
@@ -171,11 +171,19 @@ class HTMLDateTimeField extends HTMLTextField {
                        }
                }
 
-               return new MediaWiki\Widget\DateTimeInputWidget( $params );
+               if ( $this->mType === 'date' ) {
+                       return new MediaWiki\Widget\DateInputWidget( $params );
+               } else {
+                       return new MediaWiki\Widget\DateTimeInputWidget( $params );
+               }
        }
 
        protected function getOOUIModules() {
-               return [ 'mediawiki.widgets.datetime' ];
+               if ( $this->mType === 'date' ) {
+                       return [ 'mediawiki.widgets.DateInputWidget' ];
+               } else {
+                       return [ 'mediawiki.widgets.datetime' ];
+               }
        }
 
        protected function shouldInfuseOOUI() {
index 30ab181..d2af8c8 100644 (file)
@@ -217,7 +217,7 @@ class PhpHttpRequest extends MWHttpRequest {
                                        break;
                                }
 
-                               if ( strlen( $buf ) ) {
+                               if ( $buf !== '' ) {
                                        call_user_func( $this->callback, $fh, $buf );
                                }
                        }
index a954008..ea022bb 100644 (file)
@@ -1607,9 +1607,7 @@ abstract class Installer {
                }
                if ( $status->isOk() ) {
                        $this->showMessage(
-                               'config-install-success',
-                               $this->getVar( 'wgServer' ),
-                               $this->getVar( 'wgScriptPath' )
+                               'config-install-db-success'
                        );
                        $this->setVar( '_InstallDone', true );
                }
index c248468..66b657b 100644 (file)
        "config-install-done": "<strong>Congratulations!</strong>\nYou have installed MediaWiki.\n\nThe installer has generated a <code>LocalSettings.php</code> file.\nIt contains all your configuration.\n\nYou will need to download it and put it in the base of your wiki installation (the same directory as index.php). The download should have started automatically.\n\nIf the download was not offered, or if you cancelled it, you can restart the download by clicking the link below:\n\n$3\n\n<strong>Note:</strong> If you do not do this now, this generated configuration file will not be available to you later if you exit the installation without downloading it.\n\nWhen that has been done, you can <strong>[$2 enter your wiki]</strong>.",
        "config-install-done-path": "<strong>Congratulations!</strong>\nYou have installed MediaWiki.\n\nThe installer has generated a <code>LocalSettings.php</code> file.\nIt contains all your configuration.\n\nYou will need to download it and put it at <code>$4</code>. The download should have started automatically.\n\nIf the download was not offered, or if you cancelled it, you can restart the download by clicking the link below:\n\n$3\n\n<strong>Note:</strong> If you do not do this now, this generated configuration file will not be available to you later if you exit the installation without downloading it.\n\nWhen that has been done, you can <strong>[$2 enter your wiki]</strong>.",
        "config-install-success": "MediaWiki has been successfully installed. You can now visit <$1$2> to view your wiki.\nIf you have questions, check out our frequently asked questions list:\n<https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ> or use one of the\nsupport forums linked on that page.",
+       "config-install-db-success": "Database was successfully set up",
        "config-download-localsettings": "Download <code>LocalSettings.php</code>",
        "config-help": "help",
        "config-help-tooltip": "click to expand",
index bf19769..7c86a5a 100644 (file)
        "config-install-mainpage-failed": "Used as error message. Parameters:\n* $1 - detailed error message",
        "config-install-done": "Parameters:\n* $1 is the URL to LocalSettings download\n* $2 is a link to the wiki.\n* $3 is a download link with attached download icon. The config-download-localsettings message will be used as the link text.",
        "config-install-done-path": "Parameters:\n* $1 is the URL to LocalSettings download\n* $2 is a link to the wiki.\n* $3 is a download link with attached download icon. The config-download-localsettings message will be used as the link text.\n* $4 is the filesystem location of where the LocalSettings.php file should be saved to.",
-       "config-install-success": "Gives user information that installation was successful. Parameters:\n* $1 - server name\n* $2 - script path",
+       "config-install-db-success": "Shown after DB is set up. In web installer this is step prior to downloading LocalSettings.php",
+       "config-install-success": "Gives user information that installation was successful. Only shown in command line installer. Parameters:\n* $1 - server name\n* $2 - script path",
        "config-download-localsettings": "The link text used in the download link in config-install-done.",
        "config-help": "This is used in help boxes.\n{{Identical|Help}}",
        "config-help-tooltip": "Tooltip for the 'help' links ({{msg-mw|config-help}}), to make it clear they'll expand in place rather than open a new page",
index 63575eb..eb8b1a2 100644 (file)
@@ -93,7 +93,7 @@ class ThumbnailRenderJob extends Job {
                if ( $wgUploadThumbnailRenderHttpCustomDomain ) {
                        $parsedUrl = wfParseUrl( $thumbUrl );
 
-                       if ( !$parsedUrl || !isset( $parsedUrl['path'] ) || !strlen( $parsedUrl['path'] ) ) {
+                       if ( !isset( $parsedUrl['path'] ) || $parsedUrl['path'] === '' ) {
                                $this->setLastError( __METHOD__ . ": invalid thumb URL: $thumbUrl" );
                                return false;
                        }
index 0d4ca11..902cd6a 100644 (file)
@@ -72,11 +72,7 @@ class APCBagOStuff extends BagOStuff {
                }
        }
 
-       protected function doGet( $key, $flags = 0 ) {
-               return $this->unserialize( apc_fetch( $key . self::KEY_SUFFIX ) );
-       }
-
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
                $casToken = null;
 
                $blob = apc_fetch( $key . self::KEY_SUFFIX );
index c36a1dc..da6544b 100644 (file)
@@ -39,11 +39,7 @@ class APCUBagOStuff extends APCBagOStuff {
                parent::__construct( $params );
        }
 
-       protected function doGet( $key, $flags = 0 ) {
-               return $this->unserialize( apcu_fetch( $key . self::KEY_SUFFIX ) );
-       }
-
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
                $casToken = null;
 
                $blob = apcu_fetch( $key . self::KEY_SUFFIX );
index bdfed82..0dd7b57 100644 (file)
@@ -175,13 +175,9 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         *
         * @param string $key
         * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
-        * @param int|null $oldFlags [unused]
         * @return mixed Returns false on failure or if the item does not exist
         */
-       public function get( $key, $flags = 0, $oldFlags = null ) {
-               // B/C for ( $key, &$casToken = null, $flags = 0 )
-               $flags = is_int( $oldFlags ) ? $oldFlags : $flags;
-
+       public function get( $key, $flags = 0 ) {
                $this->trackDuplicateKeys( $key );
 
                return $this->doGet( $key, $flags );
@@ -223,22 +219,10 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
        /**
         * @param string $key
         * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
+        * @param mixed|null &$casToken Token to use for check-and-set comparisons
         * @return mixed Returns false on failure or if the item does not exist
         */
-       abstract protected function doGet( $key, $flags = 0 );
-
-       /**
-        * @note This method is only needed if merge() uses mergeViaCas()
-        *
-        * @param string $key
-        * @param mixed &$casToken
-        * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
-        * @return mixed Returns false on failure or if the item does not exist
-        * @throws Exception
-        */
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
-               throw new Exception( __METHOD__ . ' not implemented.' );
-       }
+       abstract protected function doGet( $key, $flags = 0, &$casToken = null );
 
        /**
         * Set an item
@@ -304,13 +288,10 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         */
        protected function mergeViaCas( $key, $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
                do {
-                       $this->clearLastError();
-                       $reportDupes = $this->reportDupes;
-                       $this->reportDupes = false;
                        $casToken = null; // passed by reference
-                       $currentValue = $this->getWithToken( $key, $casToken, self::READ_LATEST );
-                       $this->reportDupes = $reportDupes;
-
+                       // Get the old value and CAS token from cache
+                       $this->clearLastError();
+                       $currentValue = $this->doGet( $key, self::READ_LATEST, $casToken );
                        if ( $this->getLastError() ) {
                                $this->logger->warning(
                                        __METHOD__ . ' failed due to I/O error on get() for {key}.',
@@ -365,7 +346,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                }
 
                $curCasToken = null; // passed by reference
-               $this->getWithToken( $key, $curCasToken, self::READ_LATEST );
+               $this->doGet( $key, self::READ_LATEST, $curCasToken );
                if ( $casToken === $curCasToken ) {
                        $success = $this->set( $key, $value, $exptime, $flags );
                } else {
index 95dda71..8892f73 100644 (file)
@@ -32,6 +32,7 @@
  *   up going to the HashBagOStuff used for the in-memory cache).
  *
  * @ingroup Cache
+ * @TODO: Make this class use composition instead of calling super
  */
 class CachedBagOStuff extends HashBagOStuff {
        /** @var BagOStuff */
@@ -50,10 +51,10 @@ class CachedBagOStuff extends HashBagOStuff {
                $this->attrMap = $backend->attrMap;
        }
 
-       protected function doGet( $key, $flags = 0 ) {
-               $ret = parent::doGet( $key, $flags );
+       public function get( $key, $flags = 0 ) {
+               $ret = parent::get( $key, $flags );
                if ( $ret === false && !$this->hasKey( $key ) ) {
-                       $ret = $this->backend->doGet( $key, $flags );
+                       $ret = $this->backend->get( $key, $flags );
                        $this->set( $key, $ret, 0, self::WRITE_CACHE_ONLY );
                }
                return $ret;
index e0f4364..ffe3a4c 100644 (file)
@@ -27,7 +27,9 @@
  * @ingroup Cache
  */
 class EmptyBagOStuff extends BagOStuff {
-       protected function doGet( $key, $flags = 0 ) {
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
+               $casToken = null;
+
                return false;
        }
 
index d1f312a..d24f408 100644 (file)
@@ -58,12 +58,10 @@ class HashBagOStuff extends BagOStuff {
                }
        }
 
-       protected function doGet( $key, $flags = 0 ) {
-               if ( !$this->hasKey( $key ) ) {
-                       return false;
-               }
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
+               $casToken = null;
 
-               if ( $this->expire( $key ) ) {
+               if ( !$this->hasKey( $key ) || $this->expire( $key ) ) {
                        return false;
                }
 
@@ -72,18 +70,9 @@ class HashBagOStuff extends BagOStuff {
                unset( $this->bag[$key] );
                $this->bag[$key] = $temp;
 
-               return $this->bag[$key][self::KEY_VAL];
-       }
-
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
-               $casToken = null;
+               $casToken = $this->bag[$key][self::KEY_CAS];
 
-               $value = $this->doGet( $key );
-               if ( $value !== false ) {
-                       $casToken = $this->bag[$key][self::KEY_CAS];
-               }
-
-               return $value;
+               return $this->bag[$key][self::KEY_VAL];
        }
 
        public function set( $key, $value, $exptime = 0, $flags = 0 ) {
index b0fbece..71e3331 100644 (file)
@@ -50,13 +50,7 @@ class MemcachedBagOStuff extends BagOStuff {
                ];
        }
 
-       protected function doGet( $key, $flags = 0 ) {
-               $casToken = null;
-
-               return $this->getWithToken( $key, $casToken, $flags );
-       }
-
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
                return $this->client->get( $this->validateKeyEncoding( $key ), $casToken );
        }
 
index 62a57b6..489f001 100644 (file)
@@ -138,7 +138,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                return $params;
        }
 
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
                $this->debugLog( "get($key)" );
                if ( defined( Memcached::class . '::GET_EXTENDED' ) ) { // v3.0.0
                        $flags = Memcached::GET_EXTENDED;
index fb99717..adb9bb8 100644 (file)
@@ -103,7 +103,7 @@ class MultiWriteBagOStuff extends BagOStuff {
                }
        }
 
-       protected function doGet( $key, $flags = 0 ) {
+       public function get( $key, $flags = 0 ) {
                if ( ( $flags & self::READ_LATEST ) == self::READ_LATEST ) {
                        // If the latest write was a delete(), we do NOT want to fallback
                        // to the other tiers and possibly see the old value. Also, this
@@ -349,4 +349,8 @@ class MultiWriteBagOStuff extends BagOStuff {
        public function makeGlobalKey( $class, $component = null ) {
                return $this->caches[0]->makeGlobalKey( ...func_get_args() );
        }
+
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
+               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
+       }
 }
index 3303692..4d8ae59 100644 (file)
@@ -84,13 +84,7 @@ class RESTBagOStuff extends BagOStuff {
                $this->client->setLogger( $logger );
        }
 
-       protected function doGet( $key, $flags = 0 ) {
-               $casToken = null;
-
-               return $this->getWithToken( $key, $casToken, $flags );
-       }
-
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
                $casToken = null;
 
                $req = [
index af90c5c..2c74d45 100644 (file)
@@ -86,13 +86,9 @@ class RedisBagOStuff extends BagOStuff {
                $this->attrMap[self::ATTR_SYNCWRITES] = self::QOS_SYNCWRITES_NONE;
        }
 
-       protected function doGet( $key, $flags = 0 ) {
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
                $casToken = null;
 
-               return $this->getWithToken( $key, $casToken, $flags );
-       }
-
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
                list( $server, $conn ) = $this->getConnection( $key );
                if ( !$conn ) {
                        return false;
@@ -329,21 +325,29 @@ class RedisBagOStuff extends BagOStuff {
                return $result;
        }
 
-       public function changeTTL( $key, $expiry = 0, $flags = 0 ) {
+       public function changeTTL( $key, $exptime = 0, $flags = 0 ) {
                list( $server, $conn ) = $this->getConnection( $key );
                if ( !$conn ) {
                        return false;
                }
 
-               $expiry = $this->convertToRelative( $expiry );
+               $relative = $this->expiryIsRelative( $exptime );
                try {
-                       $result = $conn->expire( $key, $expiry );
+                       if ( $exptime == 0 ) {
+                               $result = $conn->persist( $key );
+                               $this->logRequest( 'persist', $key, $server, $result );
+                       } elseif ( $relative ) {
+                               $result = $conn->expire( $key, $this->convertToRelative( $exptime ) );
+                               $this->logRequest( 'expire', $key, $server, $result );
+                       } else {
+                               $result = $conn->expireAt( $key, $this->convertToExpiry( $exptime ) );
+                               $this->logRequest( 'expireAt', $key, $server, $result );
+                       }
                } catch ( RedisException $e ) {
                        $result = false;
                        $this->handleException( $conn, $e );
                }
 
-               $this->logRequest( 'expire', $key, $server, $result );
                return $result;
        }
 
index eb3f6f8..70f9096 100644 (file)
@@ -74,7 +74,7 @@ class ReplicatedBagOStuff extends BagOStuff {
                $this->readStore->setDebug( $debug );
        }
 
-       protected function doGet( $key, $flags = 0 ) {
+       public function get( $key, $flags = 0 ) {
                return ( $flags & self::READ_LATEST )
                        ? $this->writeStore->get( $key, $flags )
                        : $this->readStore->get( $key, $flags );
@@ -160,4 +160,8 @@ class ReplicatedBagOStuff extends BagOStuff {
        public function makeGlobalKey( $class, $component = null ) {
                return $this->writeStore->makeGlobalKey( ...func_get_args() );
        }
+
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
+               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
+       }
 }
index ba21156..9f9cc3c 100644 (file)
@@ -194,8 +194,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        /** Tiny positive float to use when using "minTime" to assert an inequality */
        const TINY_POSTIVE = 0.000001;
 
-       /** Seconds of delay after get() where set() storms are a consideration with 'lockTSE' */
-       const SET_DELAY_HIGH_SEC = 0.1;
+       /** Milliseconds of delay after get() where set() storms are a consideration with 'lockTSE' */
+       const SET_DELAY_HIGH_MS = 50;
        /** Min millisecond set() backoff for keys in hold-off (far less than INTERIM_KEY_TTL) */
        const RECENT_SET_LOW_MS = 50;
        /** Max millisecond set() backoff for keys in hold-off (far less than INTERIM_KEY_TTL) */
@@ -521,31 +521,34 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @param int $ttl Seconds to live. Special values are:
         *   - WANObjectCache::TTL_INDEFINITE: Cache forever (default)
         * @param array $opts Options map:
-        *   - lag : Seconds of replica DB lag. Typically, this is either the replica DB lag
+        *   - lag: seconds of replica DB lag. Typically, this is either the replica DB lag
         *      before the data was read or, if applicable, the replica DB lag before
         *      the snapshot-isolated transaction the data was read from started.
         *      Use false to indicate that replication is not running.
         *      Default: 0 seconds
-        *   - since : UNIX timestamp of the data in $value. Typically, this is either
+        *   - since: UNIX timestamp of the data in $value. Typically, this is either
         *      the current time the data was read or (if applicable) the time when
         *      the snapshot-isolated transaction the data was read from started.
         *      Default: 0 seconds
-        *   - pending : Whether this data is possibly from an uncommitted write transaction.
+        *   - pending: whether this data is possibly from an uncommitted write transaction.
         *      Generally, other threads should not see values from the future and
         *      they certainly should not see ones that ended up getting rolled back.
         *      Default: false
-        *   - lockTSE : if excessive replication/snapshot lag is detected, then store the value
+        *   - lockTSE: if excessive replication/snapshot lag is detected, then store the value
         *      with this TTL and flag it as stale. This is only useful if the reads for this key
         *      use getWithSetCallback() with "lockTSE" set. Note that if "staleTTL" is set
         *      then it will still add on to this TTL in the excessive lag scenario.
         *      Default: WANObjectCache::TSE_NONE
-        *   - staleTTL : Seconds to keep the key around if it is stale. The get()/getMulti()
+        *   - staleTTL: seconds to keep the key around if it is stale. The get()/getMulti()
         *      methods return such stale values with a $curTTL of 0, and getWithSetCallback()
         *      will call the regeneration callback in such cases, passing in the old value
         *      and its as-of time to the callback. This is useful if adaptiveTTL() is used
         *      on the old value's as-of time when it is verified as still being correct.
         *      Default: WANObjectCache::STALE_TTL_NONE.
+        *   - creating: optimize for the case where the key does not already exist.
+        *      Default: false
         * @note Options added in 1.28: staleTTL
+        * @note Options added in 1.33: creating
         * @return bool Success
         */
        final public function set( $key, $value, $ttl = self::TTL_INDEFINITE, array $opts = [] ) {
@@ -553,6 +556,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                $lockTSE = $opts['lockTSE'] ?? self::TSE_NONE;
                $staleTTL = $opts['staleTTL'] ?? self::STALE_TTL_NONE;
                $age = isset( $opts['since'] ) ? max( 0, $now - $opts['since'] ) : 0;
+               $creating = $opts['creating'] ?? false;
                $lag = $opts['lag'] ?? 0;
 
                // Do not cache potentially uncommitted data as it might get rolled back
@@ -618,14 +622,23 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
 
                // Wrap that value with time/TTL/version metadata
                $wrapped = $this->wrap( $value, $logicalTTL ?: $ttl, $now );
+               $storeTTL = $ttl + $staleTTL;
 
-               $func = function ( $cache, $key, $cWrapped ) use ( $wrapped ) {
-                       return ( is_string( $cWrapped ) )
-                               ? false // key is tombstoned; do nothing
-                               : $wrapped;
-               };
+               if ( $creating ) {
+                       $ok = $this->cache->add( self::VALUE_KEY_PREFIX . $key, $wrapped, $storeTTL );
+               } else {
+                       $ok = $this->cache->merge(
+                               self::VALUE_KEY_PREFIX . $key,
+                               function ( $cache, $key, $cWrapped ) use ( $wrapped ) {
+                                       // A string value means that it is a tombstone; do nothing in that case
+                                       return ( is_string( $cWrapped ) ) ? false : $wrapped;
+                               },
+                               $storeTTL,
+                               1 // 1 attempt
+                       );
+               }
 
-               return $this->cache->merge( self::VALUE_KEY_PREFIX . $key, $func, $ttl + $staleTTL, 1 );
+               return $ok;
        }
 
        /**
@@ -1124,7 +1137,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *      stampede is worth avoiding. Note that if the key falls out of cache then concurrent
         *      threads will all run the callback on cache miss until the value is saved in cache.
         *      The only stampede protection in that case is from duplicate cache sets when the
-        *      callback takes longer than WANObjectCache::SET_DELAY_HIGH_SEC seconds; consider
+        *      callback takes longer than WANObjectCache::SET_DELAY_HIGH_MS milliseconds; consider
         *      using "busyValue" if such stampedes are a problem. Note that the higher "lockTSE" is
         *      set, the higher the worst-case staleness of returned values can be. Also note that
         *      this option does not by itself handle the case of the key simply expiring on account
@@ -1150,10 +1163,17 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *      It is generally preferable to use a class constant when setting this value.
         *      This has no effect unless pcTTL is used.
         *      Default: WANObjectCache::PC_PRIMARY.
-        *   - version: Integer version number. This allows for callers to make breaking changes to
-        *      how values are stored while maintaining compatability and correct cache purges. New
-        *      versions are stored alongside older versions concurrently. Avoid storing class objects
-        *      however, as this reduces compatibility (due to serialization).
+        *   - version: Integer version number. This lets callers make breaking changes to the format
+        *      of cached values without causing problems for sites that use non-instantaneous code
+        *      deployments. Old and new code will recognize incompatible versions and purges from
+        *      both old and new code will been seen by each other. When this method encounters an
+        *      incompatibly versioned value at the provided key, a "variant key" will be used for
+        *      reading from and saving to cache. The variant key is specific to the key and version
+        *      number provided to this method. If the variant key value is older than that of the
+        *      provided key, or the provided key is non-existant, then the variant key will be seen
+        *      as non-existant. Therefore, delete() calls invalidate the provided key's variant keys.
+        *      The "checkKeys" and "touchedCallback" options still apply to variant keys as usual.
+        *      Avoid storing class objects, as this reduces compatibility (due to serialization).
         *      Default: null.
         *   - minAsOf: Reject values if they were generated before this UNIX timestamp.
         *      This is useful if the source of a key is suspected of having possibly changed
@@ -1411,6 +1431,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                                }
                        } elseif ( !$useMutex || $hasLock ) {
                                if ( $this->checkAndSetCooloff( $key, $kClass, $ago, $lockTSE, $hasLock ) ) {
+                                       $setOpts['creating'] = ( $curValue === false );
                                        // Save the value unless a lock-winning thread is already expected to do that
                                        $setOpts['lockTSE'] = $lockTSE;
                                        $setOpts['staleTTL'] = $staleTTL;
@@ -1457,7 +1478,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                // consistent hashing).
                if ( $lockTSE < 0 || $hasLock ) {
                        return true; // either not a priori hot or thread has the lock
-               } elseif ( $elapsed <= self::SET_DELAY_HIGH_SEC ) {
+               } elseif ( $elapsed <= self::SET_DELAY_HIGH_MS * 1e3 ) {
                        return true; // not enough time for threads to pile up
                }
 
index fb64d77..818f6f1 100644 (file)
  * @ingroup Cache
  */
 class WinCacheBagOStuff extends BagOStuff {
-       protected function doGet( $key, $flags = 0 ) {
-               $blob = wincache_ucache_get( $key );
-
-               return is_string( $blob ) ? unserialize( $blob ) : false;
-       }
-
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
                $casToken = null;
 
                $blob = wincache_ucache_get( $key );
@@ -43,12 +37,10 @@ class WinCacheBagOStuff extends BagOStuff {
                }
 
                $value = unserialize( $blob );
-               if ( $value === false ) {
-                       return false;
+               if ( $value !== false ) {
+                       $casToken = (string)$blob; // don't bother hashing this
                }
 
-               $casToken = $blob; // don't bother hashing this
-
                return $value;
        }
 
@@ -58,7 +50,7 @@ class WinCacheBagOStuff extends BagOStuff {
                }
 
                $curCasToken = null; // passed by reference
-               $this->getWithToken( $key, $curCasToken, self::READ_LATEST );
+               $this->doGet( $key, self::READ_LATEST, $curCasToken );
                if ( $casToken === $curCasToken ) {
                        $success = $this->set( $key, $value, $exptime, $flags );
                } else {
index fcd15f1..ae4b71a 100644 (file)
@@ -61,8 +61,10 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        const SLOW_WRITE_SEC = 0.500;
        const SMALL_WRITE_ROWS = 100;
 
-       /** @var string Whether lock granularity is on the level of the entire database */
+       /** @var string Lock granularity is on the level of the entire database */
        const ATTR_DB_LEVEL_LOCKING = 'db-level-locking';
+       /** @var string The SCHEMA keyword refers to a grouping of tables in a database */
+       const ATTR_SCHEMAS_AS_TABLE_GROUPS = 'supports-schemas';
 
        /** @var int New Database instance will not be connected yet when returned */
        const NEW_UNCONNECTED = 0;
@@ -480,12 +482,15 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        /**
         * @param string $dbType A possible DB type (sqlite, mysql, postgres,...)
         * @param string|null $driver Optional name of a specific DB client driver
-        * @return array Map of (Database::ATTRIBUTE_* constant => value) for all such constants
+        * @return array Map of (Database::ATTR_* constant => value) for all such constants
         * @throws InvalidArgumentException
         * @since 1.31
         */
        final public static function attributesFromType( $dbType, $driver = null ) {
-               static $defaults = [ self::ATTR_DB_LEVEL_LOCKING => false ];
+               static $defaults = [
+                       self::ATTR_DB_LEVEL_LOCKING => false,
+                       self::ATTR_SCHEMAS_AS_TABLE_GROUPS => false
+               ];
 
                $class = self::getClass( $dbType, $driver );
 
@@ -549,7 +554,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        /**
-        * @return array Map of (Database::ATTRIBUTE_* constant => value
+        * @return array Map of (Database::ATTR_* constant => value
         * @since 1.31
         */
        protected static function getAttributes() {
index 8d82854..ca57938 100644 (file)
@@ -42,11 +42,11 @@ class DatabaseDomain {
         * @param string $prefix Table prefix
         */
        public function __construct( $database, $schema, $prefix ) {
-               if ( $database !== null && ( !is_string( $database ) || !strlen( $database ) ) ) {
+               if ( $database !== null && ( !is_string( $database ) || $database === '' ) ) {
                        throw new InvalidArgumentException( 'Database must be null or a non-empty string.' );
                }
                $this->database = $database;
-               if ( $schema !== null && ( !is_string( $schema ) || !strlen( $schema ) ) ) {
+               if ( $schema !== null && ( !is_string( $schema ) || $schema === '' ) ) {
                        throw new InvalidArgumentException( 'Schema must be null or a non-empty string.' );
                }
                $this->schema = $schema;
index 57412c2..2aefd5f 100644 (file)
@@ -1412,6 +1412,9 @@ class DatabaseMssql extends Database {
                return "CAST( $field AS NVARCHAR )";
        }
 
+       public static function getAttributes() {
+               return [ self::ATTR_SCHEMAS_AS_TABLE_GROUPS => true ];
+       }
 }
 
 /**
index a5dc171..3041a33 100644 (file)
@@ -1465,6 +1465,10 @@ SQL;
                return $row ? ( strtolower( $row->default_transaction_read_only ) === 'on' ) : false;
        }
 
+       public static function getAttributes() {
+               return [ self::ATTR_SCHEMAS_AS_TABLE_GROUPS => true ];
+       }
+
        /**
         * @param string $lockName
         * @return string Integer
index eac9bae..b4440d6 100644 (file)
@@ -114,6 +114,11 @@ interface IDatabase {
         */
        const QUERY_PSEUDO_PERMANENT = 2;
 
+       /** @var bool Parameter to unionQueries() for UNION ALL */
+       const UNION_ALL = true;
+       /** @var bool Parameter to unionQueries() for UNION DISTINCT */
+       const UNION_DISTINCT = false;
+
        /**
         * A string describing the current software version, and possibly
         * other details in a user-friendly way. Will be listed on Special:Version, etc.
@@ -1384,7 +1389,7 @@ interface IDatabase {
         * This is used for providing overload point for other DB abstractions
         * not compatible with the MySQL syntax.
         * @param array $sqls SQL statements to combine
-        * @param bool $all Use UNION ALL
+        * @param bool $all Either IDatabase::UNION_ALL or IDatabase::UNION_DISTINCT
         * @return string SQL fragment
         */
        public function unionQueries( $sqls, $all );
index 4fa9999..088f94e 100644 (file)
@@ -234,22 +234,34 @@ class SqlBagOStuff extends BagOStuff {
                }
        }
 
-       protected function doGet( $key, $flags = 0 ) {
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
                $casToken = null;
 
-               return $this->getWithToken( $key, $casToken, $flags );
-       }
+               $blobs = $this->fetchBlobMulti( [ $key ] );
+               if ( array_key_exists( $key, $blobs ) ) {
+                       $blob = $blobs[$key];
+                       $value = $this->unserialize( $blob );
+
+                       $casToken = ( $value !== false ) ? $blob : null;
 
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
-               $values = $this->getMulti( [ $key ] );
-               if ( array_key_exists( $key, $values ) ) {
-                       $casToken = $values[$key];
-                       return $values[$key];
+                       return $value;
                }
+
                return false;
        }
 
        public function getMulti( array $keys, $flags = 0 ) {
+               $values = [];
+
+               $blobs = $this->fetchBlobMulti( $keys );
+               foreach ( $blobs as $key => $blob ) {
+                       $values[$key] = $this->unserialize( $blob );
+               }
+
+               return $values;
+       }
+
+       public function fetchBlobMulti( array $keys, $flags = 0 ) {
                $values = []; // array of (key => value)
 
                $keysByTable = [];
@@ -298,7 +310,7 @@ class SqlBagOStuff extends BagOStuff {
                                        if ( $this->isExpired( $db, $row->exptime ) ) { // MISS
                                                $this->debug( "get: key has expired" );
                                        } else { // HIT
-                                               $values[$key] = $this->unserialize( $db->decodeBlob( $row->value ) );
+                                               $values[$key] = $db->decodeBlob( $row->value );
                                        }
                                } catch ( DBQueryError $e ) {
                                        $this->handleWriteError( $e, $db, $row->serverIndex );
@@ -420,7 +432,7 @@ class SqlBagOStuff extends BagOStuff {
                                ],
                                [
                                        'keyname' => $key,
-                                       'value' => $db->encodeBlob( $this->serialize( $casToken ) )
+                                       'value' => $db->encodeBlob( $casToken )
                                ],
                                __METHOD__
                        );
index b2f5342..a42726f 100644 (file)
@@ -870,17 +870,6 @@ class DefaultPreferencesFactory implements PreferencesFactory {
                                'section' => 'rendering/advancedrendering',
                                'label-message' => 'tog-showrollbackconfirmation',
                        ];
-
-                       /**
-                        * FIXME
-                        * Remove temporary help text and references to DisableRollbackConfirmationFeature
-                        * after release of rollback feature. See T199534
-                        */
-                       if ( MediaWikiServices::getInstance()
-                               ->getMainConfig()->get( 'DisableRollbackConfirmationFeature' ) ) {
-                               $defaultPreferences['showrollbackconfirmation']
-                               ['help-message'] = 'tog-showrollbackconfirmation-prerelease-warning';
-                       }
                }
        }
 
index a8271ac..82bc84d 100644 (file)
@@ -616,9 +616,7 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        }
 
        /**
-        * Main execution point
-        *
-        * @param string $subpage
+        * @param string|null $subpage
         */
        public function execute( $subpage ) {
                $this->rcSubpage = $subpage;
index d1c6aea..939460f 100644 (file)
@@ -31,7 +31,7 @@
 abstract class FormSpecialPage extends SpecialPage {
        /**
         * The sub-page of the special page.
-        * @var string
+        * @var string|null
         */
        protected $par = null;
 
@@ -166,7 +166,7 @@ abstract class FormSpecialPage extends SpecialPage {
        /**
         * Basic SpecialPage workflow: get a form, send it to the user; get some data back,
         *
-        * @param string $par Subpage string if one was specified
+        * @param string|null $par Subpage string if one was specified
         */
        public function execute( $par ) {
                $this->setParameter( $par );
@@ -188,7 +188,7 @@ abstract class FormSpecialPage extends SpecialPage {
 
        /**
         * Maybe do something interesting with the subpage parameter
-        * @param string $par
+        * @param string|null $par
         */
        protected function setParameter( $par ) {
                $this->par = $par;
index b88479a..f0cb7e5 100644 (file)
@@ -578,7 +578,7 @@ abstract class QueryPage extends SpecialPage {
        /**
         * This is the actual workhorse. It does everything needed to make a
         * real, honest-to-gosh query page.
-        * @param string $par
+        * @param string|null $par
         */
        public function execute( $par ) {
                $user = $this->getUser();
index 0c709af..f52a6f3 100644 (file)
@@ -33,9 +33,7 @@ class SpecialActiveUsers extends SpecialPage {
        }
 
        /**
-        * Show the special page
-        *
-        * @param string $par Parameter passed to the page or null
+        * @param string|null $par Parameter passed to the page or null
         */
        public function execute( $par ) {
                $out = $this->getOutput();
index 2482d74..878440d 100644 (file)
@@ -35,9 +35,7 @@ class SpecialAllMessages extends SpecialPage {
        }
 
        /**
-        * Show the special page
-        *
-        * @param string $par Parameter passed to the page or null
+        * @param string|null $par Parameter passed to the page or null
         */
        public function execute( $par ) {
                $out = $this->getOutput();
index cab5a2e..34c3371 100644 (file)
@@ -34,9 +34,7 @@ class SpecialAutoblockList extends SpecialPage {
        }
 
        /**
-        * Main execution point
-        *
-        * @param string $par Title fragment
+        * @param string|null $par Title fragment
         */
        public function execute( $par ) {
                $this->setHeaders();
index 186e5ad..fd27fdc 100644 (file)
@@ -36,9 +36,7 @@ class SpecialBlockList extends SpecialPage {
        }
 
        /**
-        * Main execution point
-        *
-        * @param string $par Title fragment
+        * @param string|null $par Title fragment
         */
        public function execute( $par ) {
                $this->setHeaders();
index 2fe38ed..ea9ddaf 100644 (file)
@@ -36,9 +36,7 @@ class SpecialBookSources extends SpecialPage {
        }
 
        /**
-        * Show the special page
-        *
-        * @param string $isbn ISBN passed as a subpage parameter
+        * @param string|null $isbn ISBN passed as a subpage parameter
         */
        public function execute( $isbn ) {
                $out = $this->getOutput();
index d6fb10f..9d1b79e 100644 (file)
@@ -43,7 +43,7 @@ class SpecialComparePages extends SpecialPage {
        /**
         * Show a form for filtering namespace and username
         *
-        * @param string $par
+        * @param string|null $par
         * @return string
         */
        public function execute( $par ) {
index c0303b2..055a6e2 100644 (file)
@@ -244,9 +244,9 @@ class SpecialContributions extends IncludableSpecialPage {
 
                                $output = $pager->getBody();
                                if ( !$this->including() ) {
-                                       $output = '<p>' . $pager->getNavigationBar() . '</p>' .
+                                       $output = $pager->getNavigationBar() .
                                                $output .
-                                               '<p>' . $pager->getNavigationBar() . '</p>';
+                                               $pager->getNavigationBar();
                                }
                                $out->addHTML( $output );
                        }
@@ -313,7 +313,11 @@ class SpecialContributions extends IncludableSpecialPage {
                $links = '';
                if ( $talk ) {
                        $tools = self::getUserLinks( $this, $userObj );
-                       $links = $this->getLanguage()->pipeList( $tools );
+                       $links = Html::openElement( 'span', [ 'class' => 'mw-changeslist-links' ] );
+                       foreach ( $tools as $tool ) {
+                               $links .= Html::rawElement( 'span', [], $tool ) . ' ';
+                       }
+                       $links = trim( $links ) . Html::closeElement( 'span' );
 
                        // Show a note if the user is blocked and display the last block log entry.
                        // Do not expose the autoblocks, since that may lead to a leak of accounts' IPs,
@@ -353,7 +357,10 @@ class SpecialContributions extends IncludableSpecialPage {
                        }
                }
 
-               return $this->msg( 'contribsub2' )->rawParams( $user, $links )->params( $userObj->getName() );
+               return Html::rawElement( 'div', [ 'class' => 'mw-contributions-user-tools' ],
+                       $this->msg( 'contributions-subtitle' )->rawParams( $user )->params( $userObj->getName() )
+                       . ' ' . $links
+               );
        }
 
        /**
index 2c35815..7aef4ae 100644 (file)
@@ -35,9 +35,7 @@ class SpecialListUsers extends IncludableSpecialPage {
        }
 
        /**
-        * Show the special page
-        *
-        * @param string $par (optional) A group to list users from
+        * @param string|null $par (optional) A group to list users from
         */
        public function execute( $par ) {
                $this->setHeaders();
index 1f81cf0..1b8ba85 100644 (file)
@@ -39,6 +39,9 @@ class SpecialNewpages extends IncludableSpecialPage {
                parent::__construct( 'Newpages' );
        }
 
+       /**
+        * @param string|null $par
+        */
        protected function setup( $par ) {
                $opts = new FormOptions();
                $this->opts = $opts; // bind
@@ -70,6 +73,9 @@ class SpecialNewpages extends IncludableSpecialPage {
                $opts->validateIntBounds( 'limit', 0, 5000 );
        }
 
+       /**
+        * @param string $par
+        */
        protected function parseParams( $par ) {
                $bits = preg_split( '/\s*,\s*/', trim( $par ) );
                foreach ( $bits as $bit ) {
@@ -115,7 +121,7 @@ class SpecialNewpages extends IncludableSpecialPage {
        /**
         * Show a form for filtering namespace and username
         *
-        * @param string $par
+        * @param string|null $par
         */
        public function execute( $par ) {
                $out = $this->getOutput();
index 46b5520..c8f65c1 100644 (file)
@@ -136,9 +136,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
        }
 
        /**
-        * Main execution point
-        *
-        * @param string $subpage
+        * @param string|null $subpage
         */
        public function execute( $subpage ) {
                // Backwards-compatibility: redirect to new feed URLs
index 62c867b..8865654 100644 (file)
@@ -224,7 +224,8 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                        $sql = $subsql[0];
                } else {
                        // need to resort and relimit after union
-                       $sql = $dbr->unionQueries( $subsql, false ) . ' ORDER BY rc_timestamp DESC';
+                       $sql = $dbr->unionQueries( $subsql, $dbr::UNION_DISTINCT ) .
+                               ' ORDER BY rc_timestamp DESC';
                        $sql = $dbr->limitResult( $sql, $limit, false );
                }
 
index e6d0632..171566b 100644 (file)
@@ -102,7 +102,7 @@ class SpecialSearch extends SpecialPage {
        /**
         * Entry point
         *
-        * @param string $par
+        * @param string|null $par
         */
        public function execute( $par ) {
                $request = $this->getRequest();
@@ -115,7 +115,7 @@ class SpecialSearch extends SpecialPage {
                // parameter, but also as part of the primary url. This can have PII implications
                // in releasing page view data. As such issue a 301 redirect to the correct
                // URL.
-               if ( strlen( $par ) && !strlen( $term ) ) {
+               if ( $par !== null && $par !== '' && $term === '' ) {
                        $query = $request->getValues();
                        unset( $query['title'] );
                        // Strip underscores from title parameter; most of the time we'll want
index dbb1481..5a1b8fb 100644 (file)
@@ -146,8 +146,7 @@ class SpecialUpload extends SpecialPage {
        }
 
        /**
-        * Special page entry point
-        * @param string $par
+        * @param string|null $par
         * @throws ErrorPageError
         * @throws Exception
         * @throws FatalError
index 4d0c20c..fe55d94 100644 (file)
@@ -60,7 +60,7 @@ class SpecialUploadStash extends UnlistedSpecialPage {
        /**
         * Execute page -- can output a file directly or show a listing of them.
         *
-        * @param string $subPage Subpage, e.g. in
+        * @param string|null $subPage Subpage, e.g. in
         *   https://example.com/wiki/Special:UploadStash/foo.jpg, the "foo.jpg" part
         * @return bool Success
         */
index 81abf1c..fd98dcb 100644 (file)
@@ -117,45 +117,12 @@ class PreferencesFormOOUI extends OOUIHTMLForm {
        }
 
        protected function wrapFieldSetSection( $legend, $section, $attributes, $isRoot ) {
-               // to get a user visible effect, wrap the fieldset into a framed panel layout
-               if ( $isRoot ) {
-                       // Mimic TabPanelLayout
-                       $wrapper = new OOUI\PanelLayout( [
-                               'expanded' => false,
-                               'scrollable' => true,
-                               // Framed and padded for no-JS, frame hidden with CSS
-                               'framed' => true,
-                               'infusable' => false,
-                               'classes' => [ 'oo-ui-stackLayout oo-ui-indexLayout-stackLayout' ]
-                       ] );
-                       $layout = new OOUI\PanelLayout( [
-                               'expanded' => false,
-                               'scrollable' => true,
-                               'infusable' => false,
-                               'classes' => [ 'oo-ui-tabPanelLayout' ]
-                       ] );
-                       $wrapper->appendContent( $layout );
-               } else {
-                       $wrapper = $layout = new OOUI\PanelLayout( [
-                               'expanded' => false,
-                               'padded' => true,
-                               'framed' => true,
-                               'infusable' => false,
-                       ] );
-               }
+               $layout = parent::wrapFieldSetSection( $legend, $section, $attributes, $isRoot );
 
-               $layout->appendContent(
-                       new OOUI\FieldsetLayout( [
-                               'label' => $legend,
-                               'infusable' => false,
-                               'items' => [
-                                       new OOUI\Widget( [
-                                               'content' => new OOUI\HtmlSnippet( $section )
-                                       ] ),
-                               ],
-                       ] + $attributes )
-               );
-               return $wrapper;
+               $layout->addClasses( [ 'mw-prefs-fieldset-wrapper' ] );
+               $layout->removeClasses( [ 'oo-ui-panelLayout-framed' ] );
+
+               return $layout;
        }
 
        /**
@@ -163,61 +130,50 @@ class PreferencesFormOOUI extends OOUIHTMLForm {
         * @return string
         */
        function getBody() {
-               // Construct fake tabs to avoid FOUC. The structure mimics OOUI's tabPanelLayout.
-               // TODO: Consider creating an infusable TabPanelLayout in OOUI-PHP.
-               $fakeTabs = [];
-               foreach ( $this->getPreferenceSections() as $i => $key ) {
-                       $fakeTabs[] =
-                               Html::rawElement(
-                                       'div',
-                                       [
-                                               'class' =>
-                                                       'oo-ui-widget oo-ui-widget-enabled oo-ui-optionWidget ' .
-                                                       'oo-ui-tabOptionWidget oo-ui-labelElement' .
-                                                       ( $i === 0 ? ' oo-ui-optionWidget-selected' : '' )
+               $tabPanels = [];
+               foreach ( $this->mFieldTree as $key => $val ) {
+                       if ( !is_array( $val ) ) {
+                               wfDebug( __METHOD__ . " encountered a field not attached to a section: '$key'" );
+                               continue;
+                       }
+                       $label = $this->getLegend( $key );
+                       $content =
+                               $this->getHeaderText( $key ) .
+                               $this->displaySection( $this->mFieldTree[$key] ) .
+                               $this->getFooterText( $key );
+
+                       $tabPanels[] = new OOUI\TabPanelLayout( [
+                               'classes' => [ 'mw-htmlform-autoinfuse-lazy' ],
+                               'name' => 'mw-prefsection-' . $key,
+                               'label' => $label,
+                               'content' => new OOUI\FieldsetLayout( [
+                                       'classes' => [ 'mw-prefs-section-fieldset' ],
+                                       'label' => $label,
+                                       'items' => [
+                                               new OOUI\Widget( [
+                                                       'content' => new OOUI\HtmlSnippet( $content )
+                                               ] ),
                                        ],
-                                       Html::element(
-                                               'a',
-                                               [
-                                                       'class' => 'oo-ui-labelElement-label',
-                                                       // Make this a usable link instead of a span so the tabs
-                                                       // can be used before JS runs
-                                                       'href' => '#mw-prefsection-' . $key
-                                               ],
-                                               $this->getLegend( $key )
-                                       )
-                               );
+                               ] ),
+                               'expanded' => false,
+                               'framed' => true,
+                       ] );
                }
-               $fakeTabsHtml = Html::rawElement(
-                       'div',
-                       [ 'class' => 'oo-ui-layout oo-ui-panelLayout oo-ui-indexLayout-tabPanel' ],
-                       Html::rawElement(
-                               'div',
-                               [ 'class' => 'oo-ui-widget oo-ui-widget-enabled oo-ui-selectWidget ' .
-                                       'oo-ui-selectWidget-depressed oo-ui-tabSelectWidget' ],
-                               implode( $fakeTabs )
-                       )
-               );
-
-               return Html::rawElement(
-                       'div',
-                       [ 'class' => 'oo-ui-layout oo-ui-panelLayout oo-ui-panelLayout-framed mw-prefs-faketabs' ],
-                       Html::rawElement(
-                               'div',
-                               [ 'class' => 'oo-ui-layout oo-ui-menuLayout oo-ui-menuLayout-static ' .
-                                       'oo-ui-menuLayout-top oo-ui-menuLayout-showMenu oo-ui-indexLayout' ],
-                               Html::rawElement(
-                                       'div',
-                                       [ 'class' => 'oo-ui-menuLayout-menu' ],
-                                       $fakeTabsHtml
-                               ) .
-                               Html::rawElement(
-                                       'div',
-                                       [ 'class' => 'oo-ui-menuLayout-content mw-htmlform-autoinfuse-lazy' ],
-                                       $this->displaySection( $this->mFieldTree, '', 'mw-prefsection-' )
-                               )
-                       )
-               );
+
+               $indexLayout = new OOUI\IndexLayout( [
+                       'infusable' => true,
+                       'expanded' => false,
+                       'autoFocus' => false,
+                       'classes' => [ 'mw-prefs-tabs' ],
+               ] );
+               $indexLayout->addTabPanels( $tabPanels );
+
+               return new OOUI\PanelLayout( [
+                       'framed' => true,
+                       'expanded' => false,
+                       'classes' => [ 'mw-prefs-tabs-wrapper' ],
+                       'content' => $indexLayout
+               ] );
        }
 
        /**
index 382ba2f..626fc48 100644 (file)
@@ -154,6 +154,19 @@ class ContribsPager extends RangeChronologicalPager {
                return $query;
        }
 
+       /**
+        * Wrap the navigation bar in a p element with identifying class.
+        * In future we may want to change the `p` tag to a `div` and upstream
+        * this to the parent class.
+        *
+        * @return string HTML
+        */
+       function getNavigationBar() {
+               return Html::rawElement( 'p', [ 'class' => 'mw-pager-navigation-bar' ],
+                       parent::getNavigationBar()
+               );
+       }
+
        /**
         * This method basically executes the exact same code as the parent class, though with
         * a hook added, to allow extensions to add additional queries.
index 9eea7ab..71d350f 100644 (file)
@@ -3515,8 +3515,8 @@ class Language {
         * Truncate a string to a specified number of characters, appending an optional
         * string (e.g. for ellipsis).
         *
-        * This provides multibyte version of truncate() method of this class, suitable for truncation
-        * based on number of characters, instead of number of bytes.
+        * This provides multibyte version of truncateForDatabase() method of this class,
+        * suitable for truncation based on number of characters, instead of number of bytes.
         *
         * If $length is negative, the string will be truncated from the beginning.
         *
index 6a17cc9..cfe315c 100644 (file)
        "apihelp-no-such-module": "Модуль «$1» ня знойдзены.",
        "apisandbox": "Пясочніца API",
        "apisandbox-jsonly": "Для выкарыстаньня API-пясочніцы патрэбны JavaScript.",
-       "apisandbox-api-disabled": "API Ð·Ð°Ð±Ð°Ñ\80онены на гэтым сайце.",
+       "apisandbox-api-disabled": "API Ð°Ð´ÐºÐ»Ñ\8eÑ\87аны на гэтым сайце.",
        "apisandbox-intro": "Выкарыстоўвайце гэтую старонку для экспэрымэнтаў з <strong>API вэб-сэрвісу MediaWiki</strong>.\nЗьвяртайцеся да [[mw:API:Main page|дакумэнтацыі API]] для дадатковай інфармацыі па выкарыстаньні API. Напрыклад, [https://www.mediawiki.org/wiki/API#A_simple_example як атрымаць зьмест галоўнай старонкі]. Абярыце дзеяньне, каб пабачыць болей узораў.\n\nЗьвярніце ўвагу, што нягледзячы на тое, што гэта пясочніца, вашыя дзеяньні могуць унесьці зьмены ў вікі.",
        "apisandbox-submit": "Зрабіць запыт",
        "apisandbox-reset": "Ачысьціць",
        "rollback": "Адкаціць рэдагаваньні",
        "rollback-confirmation-confirm": "Калі ласка, пацьвердзіце:",
        "rollback-confirmation-yes": "Адкаціць",
+       "rollback-confirmation-no": "Адмяніць",
        "rollbacklink": "адкат",
        "rollbacklinkcount": "адкаціць $1 {{PLURAL:$1|рэдагаваньне|рэдагаваньні|рэдагаваньняў}}",
        "rollbacklinkcount-morethan": "адкаціць больш за $1 {{PLURAL:$1|рэдагаваньне|рэдагаваньні|рэдагаваньняў}}",
        "confirm-unwatch-top": "Выдаліць гэтую старонку з Вашага сьпісу назіраньня?",
        "confirm-rollback-button": "Так",
        "confirm-rollback-top": "Адкаціць праўкі на гэтай старонцы?",
+       "confirm-rollback-bottom": "Гэтае дзеяньне імгненна адкоціць абраныя зьмены гэтай старонкі.",
        "confirm-mcrrestore-title": "Аднавіць вэрсію",
        "confirm-mcrundo-title": "Адмяніць зьмену",
        "mcrundofailed": "Адмена не атрымалася",
index cd90f56..48eef87 100644 (file)
        "rcfilters-watchlist-edit-watchlist-button": "Rediger din liste med overvågede sider",
        "rcfilters-watchlist-showupdated": "Ændringer til sider du ikke har besøgt siden ændringerne blev gjort vises med <strong>fed</strong>.",
        "rcfilters-preference-label": "Brug grænsefladesnittet uden JavaScript",
+       "rcfilters-preference-help": "Indlæser Seneste ændringer uden mulighed for søgning med filter eller for fremhævelse.",
        "rcfilters-watchlist-preference-label": "Brug grænsefladesnittet uden JavaScript",
        "rcfilters-watchlist-preference-help": "Indlæser overvågningslisten uden mulighed for søgning med filter eller fremhævelse.",
        "rcfilters-filter-showlinkedfrom-label": "Vis ændringer på sider hvortil der linkes fra",
        "tag-list-wrapper": "[[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]: $2",
        "tag-mw-new-redirect": "Ny omdirigering",
        "tag-mw-removed-redirect": "Fjernede omdirigering",
-       "tag-mw-changed-redirect-target": "Omdigeringsmål ændret",
+       "tag-mw-changed-redirect-target": "Omdirigeringsmål ændret",
        "tag-mw-blank": "Sidetømning",
        "tag-mw-blank-description": "Redigeringer som tømmer en side",
        "tag-mw-replace": "Erstattet",
        "authpage-cannot-login": "Kunne ikke starte login.",
        "cannotauth-not-allowed-title": "Adgang nægtet",
        "cannotauth-not-allowed": "Du har ikke tilladelse til at bruge denne side",
+       "changecredentials": "Skift adgangskode",
+       "changecredentials-submit": "Skift adgangskode",
        "removecredentials": "Fjern akkreditiver",
        "removecredentials-submit": "Fjern akkreditiver",
        "removecredentials-invalidsubpage": "$1 er ikke en gyldig type for akkreditiver.",
index 05bbf3c..65b956e 100644 (file)
@@ -47,7 +47,6 @@
        "tog-useeditwarning": "Warn me when I leave an edit page with unsaved changes",
        "tog-prefershttps": "Always use a secure connection while logged in",
        "tog-showrollbackconfirmation": "Show a confirmation prompt when clicking on a rollback link",
-       "tog-showrollbackconfirmation-prerelease-warning": "Please note: This feature is not available yet. If you set this preference now, your choice will be remembered [https://meta.wikimedia.org/wiki/WMDE_Technical_Wishes/Rollback#Status when the feature is released].",
        "underline-always": "Always",
        "underline-never": "Never",
        "underline-default": "Skin or browser default",
        "mycontris": "Contributions",
        "anoncontribs": "Contributions",
        "contribsub2": "For {{GENDER:$3|$1}} ($2)",
+       "contributions-subtitle": "For {{GENDER:$3|$1}}",
        "contributions-userdoesnotexist": "User account \"$1\" is not registered.",
        "negative-namespace-not-supported": "Namespaces with negative values are not supported.",
        "nocontribs": "No changes were found matching these criteria.",
index 0c5ed54..353ad9c 100644 (file)
        "exif-compression-34712": "JPEG2000",
        "exif-copyrighted-true": "著作権あり",
        "exif-copyrighted-false": "著作権情報未設定",
-       "exif-photometricinterpretation-0": "黒と白(白が0です)",
+       "exif-photometricinterpretation-0": "黒と白(白が0)",
        "exif-photometricinterpretation-1": "黒と白(黒が0)",
        "exif-photometricinterpretation-2": "RGB",
-       "exif-photometricinterpretation-3": "Palette",
-       "exif-photometricinterpretation-5": "Separated (Probably CMYK)",
+       "exif-photometricinterpretation-3": "パレット",
+       "exif-photometricinterpretation-4": "透明マスク",
+       "exif-photometricinterpretation-5": "分版 (おそらく CMYK)",
        "exif-photometricinterpretation-6": "YCbCr",
        "exif-photometricinterpretation-8": "CIE L*a*b*",
        "exif-photometricinterpretation-9": "CIE L*a*b* (ICC エンコード)",
index cd60511..fb989a5 100644 (file)
        "ip_range_exceeded": "L'intervalle des adresses IP est plus grand l'intervalle maximum. Intervalle autorisé : /$1 .",
        "ip_range_toolow": "Les intervalles d'adresses IP ne sont effectivement pas autorisés.",
        "proxyblocker": "Bloqueur de mandataires",
-       "proxyblockreason": "Votre adresse IP a été bloquée car il s’agit d’un mandataire ouvert.\nVeuillez contacter votre fournisseur d’accès Internet ou votre soutien technique et l’informer de ce sérieux problème de sécurité.",
+       "proxyblockreason": "Votre adresse IP a été bloquée car il s’agit d’un mandataire ouvert.\nVeuillez contacter votre fournisseur d’accès à Internet ou votre service d’assistance technique et l’informer de ce sérieux problème de sécurité.",
        "sorbsreason": "Votre adresse IP est listée comme mandataire ouvert dans le DNSBL utilisé par {{SITENAME}}.",
        "sorbs_create_account_reason": "Votre adresse IP est listée comme mandataire ouvert dans le DNSBL utilisé par {{SITENAME}}.\nVous ne pouvez pas créer un compte.",
        "softblockrangesreason": "Les contributions anonymes ne sont pas autorisées à partir de votre adresse IP ($1). Veuillez vous connecter.",
index a3903bb..b13c282 100644 (file)
@@ -58,7 +58,7 @@
        "tog-ccmeonemails": "Stjoer my in kopy fan e-mails dy't ik nei oare meidoggers stjoer",
        "tog-diffonly": "Side-ynhâld dy't feroare wurdt net sjen litte",
        "tog-showhiddencats": "Ferburgen kategoryen werjaan",
-       "tog-norollbackdiff": "Feroarings weilitte nei tebekdraaien",
+       "tog-norollbackdiff": "Gjin ferskillen sjen litte nei it útfieren fan weromdraaien",
        "underline-always": "Altyd",
        "underline-never": "Nea",
        "underline-default": "Webblêder-standert",
        "newarticle": "(Nij)",
        "newarticletext": "Jo hawwe in keppeling folge nei in side dêr't noch gjin tekst op stiet.\nOm sels tekst te meistjsen kinne jo dy gewoan yntype in dit bewurkingsfjild\n([$1 Mear ynformaasje oer bewurkjen].)\nOars kinne jo tebek mei de tebek-knop fan jo blêder.",
        "anontalkpagetext": "----\n<em>Dit is de oerlisside fan in ûnbekende meidogger; in meidogger dy't him/har net oanmeld hat.</em>\nOm't der gjin namme bekend is, wurdt it ynternet-adres brûkt om oan te jaan om wa't it giet.\nMar faak is it sa dat sa'n adres net altyd troch deselde persoan brûkt wurdt.\nAs jo it idee hawwe dat jo as ûnbekende meidogger opmerkings foar in oar krije, dan kinne jo jo [[Special:CreateAccount|registrearje]], of jo [[Special:UserLogin|oanmelde]]. Fan in oanmelde meidogger is it ynternet-adres net sichtber, en as oanmelde meidogger krije jo allinnich opmerkings dy't foar josels bedoeld binne.",
-       "noarticletext": "Der stjit noch gjin tekst op dizze side. Jo kinne\n[[Special:Search/{{PAGENAME}}|hjirboppe nei dy tekst sykje]], of [{{fullurl:{{FULLPAGENAME}}|action=edit}} de side skriuwe].",
+       "noarticletext": "Der stiet noch gjin tekst op dizze side.\nJo kinne [[Special:Search/{{PAGENAME}}|nei dizze sidenamme sykje]] yn oare siden,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} de besibbe lochs trochsykje]\nof [{{fullurl:{{FULLPAGENAME}}|action=edit}} dizze side oanmeitsje]</span>.",
        "userpage-userdoesnotexist": "Jo bewurkje in meidoggerside fan in meidogger dy't net bestiet (meidogger \"<nowiki>$1</nowiki>\").\nKontrolearje oft jo dizze side wol oanmeitsje/bewurkje wolle.",
        "userpage-userdoesnotexist-view": "It meidochakkount \"$1\" bestiet net.",
        "clearyourcache": "<strong>Opmerking:</strong> Nei it fêstlizzen kin it nedich wêze de oerslach fan dyn blêder te leegjen foardat de wizigings te sjen binne.\n* <strong>Firefox / Safari:</strong> Hâld <em>Shift</em> yntreaun wylst jo op <em>Dizze side fernije</em> klikke, of typ <em>Ctrl-F5</em> of <em>Ctrl-R</em> (<em>⌘-R</em> op in Mac)\n* <strong>Google Chrome:</strong> Typ <em>CTRL-Shift-R</em> (<em>⌘-Shift-R</em> op in Mac)\n* <strong>Internet Explorer:</strong> Hâld <em>Ctrl</em> yntreaun wylst jo <em>Vernieuwen'' klikke of typ <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Leegje jo cache yn <em>Extra → Voorkeuren</em>",
        "updated": "(Bewurke)",
        "note": "<strong>Opmerking:</strong>",
        "previewnote": "<strong>Tink der om dat dit allinnich in foarfertoaning is!</strong>\nJo feroarings binne noch net fêstlein!",
+       "continue-editing": "Nei it bewurkingsfjild gean",
        "previewconflict": "Dizze side belanget allinich it earste bewurkingsfjild oan.",
        "session_fail_preview": "<strong>Jo bewurking is net ferwurke, om't de sessygegevens ferlern gien binne.</strong>\nBesykje it nochris. As it dan noch net slagget, [[Special:UserLogout|meld jo dan ôf]] en wer oan.",
        "session_fail_preview_html": "<strong>Jo bewurking is net ferwurke, om't sesjegegevens ferlern gien binne.</strong>\n\n<em>Om't yn {{SITENAME}} rûge HTML ynskeakele is, is in foarfertoaning net mûglik as beskerming tsjin oanfallen mei JavaScript.</em>\n\n<strong>As dit in legitime bewurking is, besykje it dan fannijs.</strong>\nAs it dan  noch net slagget, [[Special:UserLogout|meld jo dan ôf]] en wer oan.",
        "right-edituserjson": "De JSON-bestannen fan oare meidoggers bewurkje",
        "right-edituserjs": "De JS-bestannen fan oare meidoggers bewurkje",
        "right-rollback": "Gau de bewurkings weromdraaie fan 'e lêste meidogger dy't in beskate side bewurke",
-       "right-markbotedits": "Tebekdraaide bewurkings markearje as botbewurkings",
+       "right-markbotedits": "Weromdraaide bewurkings markearje as botbewurkings",
        "right-noratelimit": "Hat gjin tiidsôfhinklike beheinings",
        "right-import": "Siden út oare wiki's ymportearje",
        "right-importupload": "Siden ymportearje út in opladen bestân",
        "action-suppressionlog": "dit beskerme logboek besjen",
        "action-block": "dizze meidogger in bewurkingsblokkade oplizze",
        "action-protect": "it befeiligingsnivo fan dizze side oanpasse",
-       "action-rollback": "gau de bewurkings omdraaien fan 'e lêste meidogger dy't in beskate side bewurke",
+       "action-rollback": "gau de bewurkings weromdraaien fan 'e lêste meidogger dy't in beskate side bewurke",
        "action-import": "dizze side fan in oare wiki ymportearje",
        "action-importupload": "siden ymportearjen út in opladen bestân",
        "action-patrol": "bewurkings fan oaren as kontrolearre beskôgje",
        "rollbacklinkcount-morethan": "mear as $1 {{PLURAL:$1|bewurking|bewurkings}} weromdraaie",
        "rollbackfailed": "Weromdraaien fan wizigings net slagge.",
        "cantrollback": "Dizze feroaring kin net werom setten wurde, om't der mar ien skriuwer is.",
-       "alreadyrolled": "Kin de feroaring fan [[:$1]] troch [[User:$2|$2]] ([[User talk:$2|oerlis]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) net werom sette;\nin oar hat de feroaring werom set, of oars wat oan de side feroare.\n\nDe lêste feroaring wie fan [[User:$3|$3]] ([[User talk:$3|oerlis]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
+       "alreadyrolled": "Kin de wiziging fan [[:$1]] troch [[User:$2|$2]] ([[User talk:$2|oerlis]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) net weromdraaie;\nin oar hat de wiziging weromdraaid, of oars wat oan de side feroare.\n\nDe lêste wiziging wie fan [[User:$3|$3]] ([[User talk:$3|oerlis]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "De gearfetting wie: <em>$1</em>.",
        "revertpage": "Bewurkings fan [[Special:Contributions/$2|$2]] ([[User talk:$2|oerlis]]) weromset ta de lêste ferzje fan [[User:$1|$1]]",
-       "rollback-success": "Bewurkings fan {{GENDER:$3|$1}} omdraaid;\nde lêste ferzje fan {{GENDER:$4|$2}} weromset.",
+       "rollback-success": "Wizigings fan {{GENDER:$3|$1}} weromdraaid;\nde lêste ferzje fan {{GENDER:$4|$2}} weromset.",
        "protectlogpage": "Skoattelloch",
        "protectlogtext": "Hjirûnder wurdt it skoateljen en frijjaan fan siden oanjûn.\nSjoch [[Special:ProtectedPages|Skoattele side]] foar mear ynformaasje.",
        "protectedarticle": "\"[[$1]]\" skoattele",
        "movepagebtn": "Side omneame",
        "pagemovedsub": "Werneamen slagge",
        "movepage-moved": "<strong>\"$1\" hjit no \"$2\"</strong>",
+       "movepage-moved-redirect": "In trochferwizing is oanmakke.",
        "articleexists": "Der is al in side mei dy namme, of oars is de namme dy't jo oanjûn hawwe net tastien. Besykje it op 'e nij.",
        "movetalk": "Titel fan oerlisside ek feroarje, as dy der is.",
        "movepage-page-moved": "De side $1 is werneamd nei $2.",
        "revertmove": "werom sette",
        "delete_and_move_text": "== Wiskjen nedich ==\nDe doelside \"[[:$1]]\" is der al.\nMoat dy wiske wurde om plak te meitsjen foar it werneamen?",
        "delete_and_move_confirm": "Ja, wiskje de side",
-       "delete_and_move_reason": "Wiske om plak te meitsjen foar in werneamde side",
+       "delete_and_move_reason": "Wiske en meitsje plak foar it werneamen fan \"[[$1]]\"",
        "export": "Eksportearje",
        "exportall": "Alle siden eksportearje",
        "export-submit": "Eksportearje",
        "autosumm-blank": "Alle ynhâld fan de side weismiten",
        "autosumm-replace": "Side ferfong mei '$1'",
        "autoredircomment": "Ferwiist troch nei [[$1]]",
-       "autosumm-new": "Nije Side: $1",
+       "autosumm-new": "Side makke mei \"$1\"",
        "size-bytes": "$1 B",
        "size-kilobytes": "$1 KB",
        "size-megabytes": "$1 MB",
        "intentionallyblankpage": "Dizze side is bewust leech lizzen en wurdt brûkt foar benchmarks, ensfh.",
        "tag-list-wrapper": "[[Special:Tags|{{PLURAL:$1|Lebel|Lebels}}]]: $2",
        "tag-mw-new-redirect": "Nije trochferwizing",
+       "tag-mw-changed-redirect-target": "Trochferwizingsdoel feroare",
+       "tag-mw-changed-redirect-target-description": "Bewurkings dy't it einpunt fan in trochferwizing feroarje",
        "tag-mw-undo": "Ungedien meitsjen",
        "tags-source-header": "Boarne",
        "tags-active-header": "Aktyf?",
        "htmlform-cloner-create": "Mear tafoegje",
        "htmlform-cloner-delete": "Fuortsmite",
        "logentry-delete-delete": "$1 {{GENDER:$2|hat}} de side $3 wiske",
+       "logentry-delete-delete_redir": "$1 {{GENDER:$2|hat}} de trochferwizing $3 by it oerskriuwen wiske",
        "revdelete-restricted": "hat beheinings oplein oan behearders",
        "revdelete-unrestricted": "hat beheinings foar behearders goedmakke",
        "logentry-move-move": "$1 {{GENDER:$2|hat}} de side $3 omneamd ta $4",
index 9aeb97d..2383108 100644 (file)
                        "Zeljko.filipin"
                ]
        },
-       "tog-underline": "Podcrtavanje veza:",
-       "tog-hideminor": "Sakrivaj manje izmjene sa popisa nedavnih promjena",
-       "tog-hidepatrolled": "Sakrivaj patrolirane izmjene sa popisa nedavnih promjena",
-       "tog-newpageshidepatrolled": "Sakrivaj patrolirane stranice sa popisa novih stranica",
-       "tog-hidecategorization": "Sakrivaj kategorizaciju stranica",
+       "tog-underline": "Podcrtavanje poveznica",
+       "tog-hideminor": "Sakrij manje izmjene u nedavnim promjenama",
+       "tog-hidepatrolled": "Sakrij pregledane izmjene u nedavnim promjenama",
+       "tog-newpageshidepatrolled": "Sakrij pregledane stranice iz popisa novih stranica",
+       "tog-hidecategorization": "Sakrij kategorizaciju stranica",
        "tog-extendwatchlist": "Proširi popis praćenih stranica tako da prikaže sve promjene, ne samo najnovije",
        "tog-usenewrc": "Grupne promjene po stranici u popisu nedavnih izmjena i popisu praćenih stranica (zahtijeva JavaScript)",
        "tog-numberheadings": "Automatski označi naslove brojevima",
        "youhavenewmessagesmulti": "Imate nove poruke na $1",
        "editsection": "uredi",
        "editold": "uredi",
-       "viewsourceold": "prikaži izvor",
+       "viewsourceold": "vidi izvor",
        "editlink": "uredi",
-       "viewsourcelink": "prikaži izvor",
+       "viewsourcelink": "vidi izvornik",
        "editsectionhint": "Uredi odlomak: $1",
        "toc": "Sadržaj",
        "showtoc": "prikaži",
        "perfcached": "Sljedeći podaci su iz međuspremnika i možda nisu najsvježiji. Međuspremnik sadrži $1 {{PLURAL:$1|rezultat|rezultata}} pretraživanja.",
        "perfcachedts": "Sljedeći podaci su iz međuspremnika i posljednji puta su ažurirani u $1. Međuspremnik sadrži $4 {{PLURAL:$4|rezultat|rezultata}} pretraživanja.",
        "querypage-no-updates": "Osvježavanje ove stranice je trenutačno onemogućeno. Nove promjene neće biti vidljive.",
-       "viewsource": "Prikaži izvor",
+       "viewsource": "Vidi izvornik",
        "viewsource-title": "Vidi kôd stranice $1",
        "actionthrottled": "Uređivanje je usporeno",
        "actionthrottledtext": "Kao mjera protiv spama, ograničeni vam je broj ovih radnji u određenom vremenu, i trenutačno ste dostigli to ograničenje. Pokušajte opet za par minuta.",
index 67aaaad..5d810af 100644 (file)
        "invalidemailaddress": "L'indirizzo e-mail indicato ha un formato non valido. Inserire un indirizzo valido o svuotare la casella.",
        "cannotchangeemail": "Gli indirizzi e-mail non possono essere modificati in questo wiki.",
        "emaildisabled": "Questo sito non può inviare messaggi di posta elettronica.",
-       "accountcreated": "Accesso creato",
+       "accountcreated": "Utenza creata",
        "accountcreatedtext": "È stata creata un'utenza per [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|msg]]).",
        "createaccount-title": "Creazione di un accesso a {{SITENAME}}",
        "createaccount-text": "Qualcuno ha creato un accesso a {{SITENAME}} ($4) a nome di $2, associato a questo indirizzo di posta elettronica. La password per l'utente \"$2\" è impostata a \"$3\".\nÈ opportuno eseguire un accesso quanto prima e cambiare la password immediatamente.\n\nSe l'accesso è stato creato per errore, si può ignorare questo messaggio.",
index f1ec639..7f170ad 100644 (file)
        "botpasswords-existing": "既存のボット用パスワード",
        "botpasswords-createnew": "ボット用パスワードの新規作成",
        "botpasswords-editexisting": "既存のボットのパスワードを編集",
-       "botpasswords-label-needsreset": "(パスワードをリセットする必要があります)",
+       "botpasswords-label-needsreset": "(パスワードのリセットが必要です)",
        "botpasswords-label-appid": "ボット名:",
        "botpasswords-label-create": "作成",
        "botpasswords-label-update": "更新",
        "botpasswords-invalid-name": "指定された利用者名には、ボット用パスワードの区切りである「$1」 が含まれていません。",
        "botpasswords-not-exist": "利用者「$1」はボット「$2」のパスワードを所持していません。",
        "botpasswords-needs-reset": "{{GENDER:$1|利用者}}「$1」のボット名「$2」のためのパスワードはリセットする必要があります。",
-       "botpasswords-locked": "アカウントがロックされているため、bot用パスワードではログインできません。",
+       "botpasswords-locked": "アカウントがロックされているため、ボット用パスワードでログインできません。",
        "resetpass_forbidden": "パスワードは変更できません",
        "resetpass_forbidden-reason": "パスワードは変更できません: $1",
        "resetpass-no-info": "このページに直接アクセスするためにはログインしている必要があります。",
index 75b69a0..9120560 100644 (file)
        "deleting-backlinks-warning": "<strong>മുന്നറിയിപ്പ്:</strong> മറ്റു താളുകളിൽ നിന്നും [[Special:WhatLinksHere/{{FULLPAGENAME}}|താളിലേയ്ക്കുള്ള കണ്ണികൾ]] അല്ലെങ്കിൽ ഉൾപ്പെടുത്തിയിട്ടുള്ള താളുകൾ താങ്കൾ മായ്ക്കാൻ പോവുകയാണ്.",
        "deleting-subpages-warning": "<strong>മുന്നറിയിപ്പ്:</strong> താങ്കൾ മായ്ക്കാൻ പോകുന്ന താളിന് [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|ഒരു ഉപതാൾ|$1 ഉപതാളുകൾ|51=അമ്പതിലധികം ഉപതാളുകൾ}}]] ഉണ്ട്.",
        "rollback": "തിരുത്തുകൾ റോൾബാക്ക് ചെയ്യുക",
+       "rollback-confirmation-confirm": "ദയവായി സ്ഥിരീകരിക്കുക:",
+       "rollback-confirmation-yes": "റോൾബാക്ക്",
+       "rollback-confirmation-no": "റദ്ദാക്കുക",
        "rollbacklink": "റോൾബാക്ക്",
        "rollbacklinkcount": "{{PLURAL:$1|ഒരു തിരുത്ത്|$1 തിരുത്തുകൾ}} മുൻപ്രാപനം ചെയ്യുക",
        "rollbacklinkcount-morethan": "{{PLURAL:$1|ഒന്നിലധികം തിരുത്തുകൾ|$1 എണ്ണത്തിലധികം തിരുത്തുകൾ}} മുൻപ്രാപനം ചെയ്യുക",
        "imgmultipagenext": "അടുത്ത താൾ →",
        "imgmultigo": "പോകൂ!",
        "imgmultigoto": "$1 താളിലേക്ക് പോകുക",
-       "img-lang-default": "(സ്വതേ വേണ്ട ഭാഷ)",
+       "img-lang-default": "(സ്വതേയുള്ള ഭാഷ)",
        "img-lang-info": "ഈ ചിത്രം ഈ ഭാഷയിൽ കാണിക്കുക: $1. $2",
        "img-lang-go": "പോകൂ",
        "ascending_abbrev": "ആരോഹണം",
index 1749737..b8e6e83 100644 (file)
        "tog-useeditwarning": "Ostrzegaj mnie, gdy opuszczam stronę edycji bez zapisania zmian",
        "tog-prefershttps": "Zawsze używaj bezpiecznego połączenia po zalogowaniu",
        "tog-showrollbackconfirmation": "Wyświetl komunikat potwierdzający kliknięcie linku wycofującego edycję",
+       "tog-showrollbackconfirmation-prerelease-warning": "Zauważ, że ta funkcja nie jest jeszcze dostępna. Jeżeli teraz ustawisz tą preferencję, twój wybór zostanie zapamiętany [https://meta.wikimedia.org/wiki/WMDE_Technical_Wishes/Rollback#Status gdy funkcja zostanie wydana].",
        "underline-always": "Zawsze",
        "underline-never": "Nigdy",
        "underline-default": "według ustawień skórki lub przeglądarki",
        "deleting-backlinks-warning": "<strong>Uwaga:</strong> Do strony, którą masz zamiar usunąć, odwołują się [[Special:WhatLinksHere/{{FULLPAGENAME}}|inne strony]].",
        "deleting-subpages-warning": "<strong>Ostrzeżenie:</strong> Strona którą chcesz usunąć ma [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|jedną podstronę|$1 podstrony|$1 podstron|51=ponad 50 podstron}}]].",
        "rollback": "Cofnij edycję",
+       "rollback-confirmation-confirm": "Potwierdź:",
+       "rollback-confirmation-yes": "Wycofaj",
+       "rollback-confirmation-no": "Anuluj",
        "rollbacklink": "cofnij",
        "rollbacklinkcount": "cofnij $1 {{PLURAL:$1|edycję|edycje|edycji}}",
        "rollbacklinkcount-morethan": "cofnij więcej niż $1 {{PLURAL:$1|edycję|edycje|edycji}}",
        "confirm-unwatch-top": "Usunąć tę stronę z listy obserwowanych?",
        "confirm-rollback-button": "OK",
        "confirm-rollback-top": "Wycofać edycje tej strony?",
+       "confirm-rollback-bottom": "Ta akcja natychmiastowo wycofa wybrane zmiany wykonane na tej stronie.",
        "confirm-mcrrestore-title": "Odtwórz wersję",
        "confirm-mcrundo-title": "Cofnij zmianę",
        "mcrundofailed": "Cofnięcie nie powiodło się",
index 7b0533a..f37b5c7 100644 (file)
        "tog-useeditwarning": "Used as label for the checkbox in [[Special:Preferences#mw-prefsection-editing|Special:Preferences]].",
        "tog-prefershttps": "Toggle option used in [[Special:Preferences]] that indicates if the user wants to use a secure connection when logged in",
        "tog-showrollbackconfirmation": "Toggle option used in [[Special:Preferences]] to enable/disable rollback confirmation prompt. Should be visible only to users with rollback rights.",
-       "tog-showrollbackconfirmation-prerelease-warning": "Notice for wikis where the option can be set before the feature is enabled.\n\nNote: This notice is temporary and will only appear before the rollback confirmation feature is released.",
        "underline-always": "Used in [[Special:Preferences#mw-prefsection-rendering|Preferences]].\n\nThis option means \"always underline links\", there are also options {{msg-mw|Underline-never}} and {{msg-mw|Underline-default}}.\n\n{{Gender}}\n{{Identical|Always}}",
        "underline-never": "Used in [[Special:Preferences#mw-prefsection-rendering|Preferences]].\n\nThis option means \"never underline links\", there are also options {{msg-mw|Underline-always}} and {{msg-mw|Underline-default}}.\n\n{{Gender}}\n{{Identical|Never}}",
        "underline-default": "Used in [[Special:Preferences#mw-prefsection-rendering|Preferences]].\n\nThis option means \"underline links as in your user skin or your browser\", there are also options {{msg-mw|Underline-never}} and {{msg-mw|Underline-always}}.\n\n{{Gender}}\n{{Identical|Browser default}}",
        "mycontris": "In the personal urls page section - right upper corner.\n\nSee also:\n* {{msg-mw|Mycontris}}\n* {{msg-mw|Accesskey-pt-mycontris}}\n* {{msg-mw|Tooltip-pt-mycontris}}\n{{Identical|Contribution}}",
        "anoncontribs": "Same as {{msg-mw|mycontris}} but used for non-logged-in users.\n\nSee also:\n* {{msg-mw|Accesskey-pt-anoncontribs}}\n* {{msg-mw|Tooltip-pt-anoncontribs}}\n{{Identical|Contribution}}",
        "contribsub2": "Contributions for \"user\" (links). Parameters:\n* $1 is an IP address or a username, with a link which points to the user page (if registered user).\n* $2 is list of tool links. The list contains a link which has text {{msg-mw|Sp-contributions-talk}}.\n* $3 is a plain text username used for GENDER.\n{{Identical|For $1}}",
+       "contributions-subtitle": "Successor to {{msg-mw|contribssub2}}. Contributions for \"user\". Parameters:\n* $1 is an IP address or a username, with a link which points to the user page (if registered user).\n",
        "contributions-userdoesnotexist": "This message is used in [[Special:Contributions]]. It is used to tell the user that the name he searched for doesn't exist.\n\nParameters:\n* $1 - a username\n{{Identical|Userdoesnotexist}}",
        "negative-namespace-not-supported": "This message is used in [[Special:Contributions]] to tell users that use namespaces with negative value. It not supported as associated namespace(s) doesn't exist.",
        "nocontribs": "Used in [[Special:Contributions]] and [[Special:DeletedContributions]].\n\nSee examples: [[Special:Contributions/x]] and [[Special:DeletedContributions/x]].\n\nParameters:\n* $1 - (Unused) the user name",
index 8fa684a..6fcd0a3 100644 (file)
        "deleting-backlinks-warning": "<strong>Предупреждение:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Другие страницы]] ссылаются на страницу, которую вы собираетесь удалить, или содержат её.",
        "deleting-subpages-warning": "<strong>Предупреждение:</strong> У страницы, которую вы собираетесь удалить, имеется [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|$1 подстраница|$1 подстраницы|$1 подстраниц|51=более 50 подстраниц}}]].",
        "rollback": "Откатить изменения",
+       "rollback-confirmation-confirm": "Пожалуйста, подтвердите:",
+       "rollback-confirmation-yes": "Откатить",
+       "rollback-confirmation-no": "Отмена",
        "rollbacklink": "откатить",
        "rollbacklinkcount": "откатить $1 {{PLURAL:$1|правку|правки|правок}}",
        "rollbacklinkcount-morethan": "откатить более $1 {{PLURAL:$1|правки|правок}}",
index 03b66bb..fbbfed5 100644 (file)
        "userlogout": "Тахсыы",
        "notloggedin": "Ааккын эппэтиҥ",
        "userlogin-noaccount": "Бэлиэтэнэ иликкин дуо?",
-       "userlogin-joinproject": "{{SITENAME}} Ñ\81иÑ\82им-Ñ\81иÑ\80гÑ\8d ÐºÐ¸Ð¸Ñ\80ии",
+       "userlogin-joinproject": "{{SITENAME}} Ñ\81иÑ\82им-Ñ\81иÑ\80гÑ\8d Ð±Ñ\8dлиÑ\8dÑ\82Ñ\8dнии",
        "createaccount": "Бэлиэтэнии",
        "userlogin-resetpassword-link": "Аһарык тылгын санаттараҕын дуо?",
        "userlogin-helplink2": "Киирэргэ көмө",
        "subject-preview": "Аата маннык көстүөҕэ:",
        "previewerrortext": "Уларытыыгын бигэргэтиэх иннинэ көрдөрөргө алҕас таҕыста.",
        "blockedtitle": "Кыттааччы уларытар кыаҕа быһылынна",
-       "blockedtext": "'''Эн Ð°Ð°Ñ\82Ñ\8bÒ¥ Ñ\8dбÑ\8dÑ\82Ñ\8dÑ\80 IP-аадÑ\8bÑ\80Ñ\8bÒ»Ñ\8bÒ¥ Ð±Ð¾Ð±Ñ\83лÑ\83ннÑ\83лаÑ\80.'''\n\nÐ\91оппÑ\83Ñ\82 ÐºÐ¸Ò»Ð¸ $1.\nТөÑ\80Ò¯Ó©Ñ\82Ñ\8d: ''«$2»''.\n\n*Ð\91обÑ\83ллÑ\83бÑ\83Ñ\82: $8\n*Ð\91обÑ\83Ñ\83 Ð±Ð¾Ð»Ð´Ñ\8cоÒ\95о: $6\n*Ð\91обÑ\83лÑ\83нна: $7\n\nЭн $1 Ð´Ð¸Ñ\8dн ÐºÐ¸Ò»Ð¸Ñ\8dÑ\85Ñ\8d Ñ\8dбÑ\8dÑ\82Ñ\8dÑ\80 Ð°Ñ\82Ñ\8bн [[{{MediaWiki:Grouppage-sysop}}|админиÑ\81Ñ\82Ñ\80ааÑ\82аÑ\80га]] Ñ\81Ñ\83Ñ\80Ñ\83йан Ð±Ñ\8bһааÑ\80Ñ\81Ñ\8bаÑ\85Ñ\85Ñ\8bн Ñ\81өп.\nÐ\91олÒ\95ой, Ó©Ñ\81кө Ñ\80егиÑ\81Ñ\82Ñ\80аÑ\86иÑ\8fламмаÑ\82аÑ\85 Ð±Ñ\83оллаÑ\85Ñ\85Ñ\8bна, Ñ\8dбÑ\8dÑ\82Ñ\8dÑ\80 Ñ\8dл. Ð°Ð°Ð´Ñ\8bÑ\80Ñ\8bÑ\81кÑ\8bн [[Special:Preferences|бигÑ\8dÑ\80гÑ\8dппÑ\8dÑ\82Ñ\8dÑ\85]] Ð±Ñ\83оллаÑ\85Ñ\85Ñ\8bна, Ñ\8dбÑ\8dÑ\82Ñ\8dÑ\80 Ñ\81Ñ\83Ñ\80Ñ\83к Ñ\81Ñ\83Ñ\80Ñ\83йаÑ\80Ñ\8bÒ¥ Ð±Ð¾Ð±Ñ\83ллÑ\83бÑ\83Ñ\82 Ð±Ñ\83оллаÒ\95Ñ\8bна Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñ\81Ñ\82Ñ\80ааÑ\82аÑ\80га Ñ\81Ñ\83Ñ\80Ñ\83йаÑ\80 ÐºÑ\8bаÒ\95Ñ\8bÒ¥ Ñ\81Ñ\83оÑ\85.\nЭн IP-аадÑ\8bÑ\80Ñ\8bÒ»Ñ\8bÒ¥ â\80\94 $3, Ð±Ð¾Ð±Ñ\83Ñ\83 Ð½Ò¯Ó©Ð¼Ñ\8dÑ\80Ñ\8d â\80\94 #$5.\nÐ\9eнÑ\83 Ñ\81Ñ\83Ñ\80Ñ\83ккаÑ\80 ÐºÐ¸Ð»Ð»Ñ\8dÑ\80Ñ\8dÑ\8dр.",
+       "blockedtext": "'''Эн Ð±Ñ\8dлиÑ\8d-ааÑ\82Ñ\8bÒ¥ Ñ\8dбÑ\8dÑ\82Ñ\8dÑ\80 IP-аадÑ\8bÑ\80Ñ\8bÒ»Ñ\8bÒ¥ Ð±Ð¾Ð±Ñ\83ллÑ\83бÑ\83Ñ\82Ñ\82аÑ\80.'''\n\nÐ\91оппÑ\83Ñ\82 Ð´Ñ\8cаһабÑ\8bл - $1.\nТөÑ\80Ò¯Ó©Ñ\82үн Ð¼Ð°Ð½Ñ\8b Ñ\8bйÑ\8bбÑ\8bÑ\82: <em>$2</em>.\n\n*Ð\91обÑ\83ллÑ\83бÑ\83Ñ\82 ÐºÒ¯Ð½Ñ\8d\8bйа: $8\n*Ð\91обÑ\83Ñ\83 Ð±Ð¾Ð»Ð´Ñ\8cоÒ\95о: $6\n*Ð\91обÑ\83Ñ\83 Ñ\81оÑ\80Ñ\83га: $7\n\nЭн $1 Ð´Ð¸Ñ\8dн ÐºÐ¸Ò»Ð¸Ñ\8dÑ\85Ñ\8d Ñ\8dбÑ\8dÑ\82Ñ\8dÑ\80 Ð°Ñ\82Ñ\8bн [[{{MediaWiki:Grouppage-sysop}}|дÑ\8cаһабÑ\8bлга]] Ñ\81Ñ\83Ñ\80Ñ\83йан, Ð±Ñ\8bһааÑ\80Ñ\81Ñ\8bаÑ\85Ñ\85Ñ\8bн Ñ\81өп.\nÐ\91олÒ\95ой, Ó©Ñ\81көÑ\82Ó© Ñ\8dл. Ð°Ð°Ð´Ñ\8bÑ\80Ñ\8bÑ\81кÑ\8bн [[Special:Preferences|Ñ\82Ñ\83Ñ\80Ñ\83оÑ\80Ñ\83Ñ\83лаÑ\80гаÑ\80]] Ñ\8dппÑ\8dÑ\82Ñ\8dÑ\85 Ñ\8dбÑ\8dÑ\82Ñ\8dÑ\80 Ð±Ð¸Ð³Ñ\8dÑ\80гÑ\8dппÑ\8dÑ\82Ñ\8dÑ\85 Ð±Ñ\83оллаÑ\85Ñ\85Ñ\8bна, Ñ\8dбÑ\8dÑ\82Ñ\8dÑ\80 Ð±Ð¾Ð¿Ð¿Ñ\83Ñ\82 ÐºÐ¸Ò»Ð¸ Ñ\81Ñ\83Ñ\80Ñ\83к Ñ\81Ñ\83Ñ\80Ñ\83йаÑ\80гÑ\8bн Ð±Ð¾Ð¿Ð¿Ñ\83Ñ\82 Ð±Ñ\83оллаÒ\95Ñ\8bна, \"{{int:emailuser}}\" Ñ\84Ñ\83нкÑ\86иÑ\8fнÑ\8b Ñ\82Ñ\83һанаÑ\80 ÐºÑ\8bаÒ\95Ñ\8bÒ¥ Ñ\81Ñ\83оÑ\85.\nЭн IP-аадÑ\8bÑ\80Ñ\8bÒ»Ñ\8bÒ¥ â\80\94 $3, Ð±Ð¾Ð±Ñ\83Ñ\83 Ð½Ò¯Ó©Ð¼Ñ\8dÑ\80Ñ\8d â\80\94 #$5.\nÐ\9cанÑ\8b Ñ\81Ñ\83Ñ\80Ñ\83ккаÑ\80 Ñ\8bйаар.",
        "autoblockedtext": "Эн IP-аадырыскын ханнык эрэ бу бырайыакка кыттара бобуллубут киһи туһана сылдьыбыт, онон бу IP-аадырыс бобуулаах. Боппут администраатар ($1) ол төрүөтүн маннык суруйбут:\n\n:''$2''\n\n*Бобуллубут: $8\n*Бобуу болдьоҕо: $6\n*Бобулунна: $7\n\nЭн $1 диэн киһиэхэ эбэтэр атын [[{{MediaWiki:Grouppage-sysop}}|администраатарга]] сурук суруйан быһаарсыаххын сөп.\n\nБолҕой, өскө регистрацияламматах буоллаххына, эбэтэр эл. аадырыскын [[Special:Preferences|бигэргэппэтэх]]  буоллаххына, эбэтэр сурук суруйарыҥ бобуллубут буоллаҕына администраатарга суруйар кыаҕыҥ суох.\n\nIP-аадырыһыҥ $3, бобуу нүөмэрэ — #$5.\nОну суруккар киллэрээр.",
        "systemblockedtext": "Бэлиэ-ааккын эбэтэр IP-аадырыскын MediaWiki хааччахтаабыт.\nЫйыллыбыт төрүөтэ:\n\n:<em>$2</em>\n\n* Хааччах саҕаланыыта: $8\n* Хааччах уһуллуута: $6\n* Бүөлээһин соруга: $7\n\nЭн билиҥҥи IP-аадырыһыҥ $3.\nБыһаарсар буоллаххына, бу сибидиэнньэлэри этээр дуу.",
        "blockednoreason": "биир да биричиинэ сөп түбэспэт",
        "accmailtext": "[[User talk:$1|$1]] кыттааччыга түбэспиччэ бэлиэлэртэн оҥоһуллубут аһарык тыл бу аадырыска $2 ыытылынна.\nТиһиккэ бэлиэтэнэн баран аһарыккын ''[[Special:ChangePassword|уларытыаххын]]'' сөп.",
        "newarticle": "(Саҥа ыстатыйа)",
        "newarticletext": "Эн суох сирэйгэ киирэ сатаатыҥ.\nМаннык ааттаах саҥа ыстатыйаны оҥорор буоллаххына, аллара баар түннүккэ суруй\n(сиһ. [$1 көмөнү] көрүөххүн сөп).\nӨскө манна сыыһа киирбит буоллаххына интэриниэтиҥ бырагыраамматын \"төнүн\" диэххин сөп.",
-       "anontalkpagetext": "----\n<em>Бу аатын эппэтэх, бэлиэтэнэ илик эбэтэр бэлиэтэниэн баҕарбат кыттааччы сирэйэ.</em>\nОл иһин кинини чопчулаары IP-аадырыһын туттабыт.\nБу аадырыһы атын кыттааччылар эмиэ туһаныахтарын сөп.\nӨскөтө Эн ааккын эппэтэх кыттааччы Эйиэхэ туһуламматах суругу туттум дии саныыр буоллаххына, бука диэн, [[Special:CreateAccount|бэлиэтэн]] биитэр [[Special:UserLogin|урут бэлиэтэммит ааккынан киир]]. Оччоҕо булкуур тахсыа суоҕа.",
+       "anontalkpagetext": "----\n<em>Бу аатын эппэтэх, бэлиэтэнэ илик эбэтэр бэлиэтэниэн баҕарбат кыттааччы сирэйэ.</em>\nОл иһин кинини чопчулаары IP-аадырыһын туттабыт.\nБу аадырыһы атын кыттааччылар эмиэ туһаныахтарын сөп.\nӨскөтө Эн, ааккын эппэтэх кыттааччы буоллаххына, Эйиэхэ туһуламматах суругу туттум дии саныыр буоллаххына, бука диэн, [[Special:CreateAccount|бэлиэтэн]] биитэр [[Special:UserLogin|урут бэлиэтэммит ааккынан киир]]. Оччоҕо булкуур тахсыа суоҕа.",
        "noarticletext": "Билигин бу сирэй кураанах.\nБу аат атын ыстатыйаларга туттулларын [[Special:Search/{{PAGENAME}}|булуоххун сөп]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} сурунаалларга көрдүөххүн сөп],\nэбэтэр [{{fullurl:{{FULLPAGENAME}}|action=edit}} маннык ааттаах саҥа ыстатыйаны суруйуоххун сөп]</span>.",
        "noarticletext-nopermission": "Билигин бу сирэй кураанах.\nБу [[Special:Search/{{PAGENAME}}|ааты атын сирэйдэргэ көрдөөн көрүөххүн]] сөп,\nэбэтэр <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} сурунаалларга манна сыһыаннаах суруктары булуоххун сөп].</span> Бу сирэйи айар кыаҕыҥ суох.",
        "missing-revision": "«{{FULLPAGENAME}}» сирэй $1 барыла суох.\n\nМаннык үксүн хайыы-үйэ сотуллубут билэҕэ эргэрбит сигэнэн бардахха буолааччы.\nСиһилии баҕар [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} сотуу сурунаалыгар] баара буолуо.",
        "diff-paragraph-moved-toold": "Параграф көһөрүллүбүт. Баттаан урукку сиригэр көс.",
        "difference-missing-revision": "$2 барыл бу тэҥнээһиҥҥэ ($1) көстүбэтэ.\n\nБу үксүн хайыы-үйэ сотуллубут сирэйи кытта тэҥнээри эргэрбит сигэнэн кэллэххэ баар буолааччы.\nСиһилии баҕар [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} сотуу сурунаалыгар] баара буолуо.",
        "searchresults": "Булулунна",
+       "search-filter-title-prefix-reset": "Сирэйдэри барытын көрдөөһүн",
        "searchresults-title": "Көрдөөһүн түмүгэ \"$1\"",
        "titlematches": "Ыстатыйалар ааттара хоһулаһар",
        "textmatches": "Ыстатыйалар истэрэ хатыланар",
        "prefs-watchlist-edits": "Кэтээбил тиһигэр уларытыы көстүүтүн муҥутуур ахсаана:",
        "prefs-watchlist-edits-max": "Улааппыта: 1000",
        "prefs-watchlist-token": "Кэтэбил тиһигин бэлиэтэ (токен):",
+       "prefs-watchlist-managetokens": "Токеннары салайыы",
        "prefs-misc": "Атын туруоруулар",
        "prefs-resetpass": "Кирии тылы уларытыы",
        "prefs-changeemail": "Аадырыһы уларытыы уонна сотуу",
        "prefs-dateformat": "Күн-дьыл көрүҥэ (формаата)",
        "prefs-timeoffset": "Кэм уратыта",
        "prefs-advancedediting": "Сүрүн туруоруулар",
+       "prefs-developertools": "Оҥорооччу тэриллэрэ",
        "prefs-editor": "Эрэдээктэр",
        "prefs-preview": "Инники көрүү",
        "prefs-advancedrc": "Дириҥэтиллибит туруоруулар",
        "prefs-advancedwatchlist": "Дириҥэтиллибит туруоруулар",
        "prefs-displayrc": "Туруоруулары көрдөр",
        "prefs-displaywatchlist": "Көстүүтүн туруоруулара",
+       "prefs-changesrc": "Көстүбүт уларытыылар",
+       "prefs-changeswatchlist": "Көрдөр;ллэр уларытыылар",
        "prefs-tokenwatchlist": "Токен",
        "prefs-diffs": "Уратылара",
        "prefs-help-prefershttps": "Аныгыскы киириигэр үлэлиир буолуо.",
        "recentchangeslinked-feed": "Сигэнэр уларытыылар",
        "recentchangeslinked-toolbox": "Сигэнэр уларытыылар",
        "recentchangeslinked-title": "\"$1\" кытта сибээстээх уларытыылар",
-       "recentchangeslinked-summary": "Сирэй аатын суруйдаххына киниэхэ эбэтэр кини сигэнэр сирэйдэрин уларытыы көстүөҕэ. (Категорияҕа киирэрэ сирэйдэри көрөргө маннык суруй: Category:Категория аата). [[Special:Watchlist|Кэтиир сирэйдэриҥ]] уларыйыыта <strong>модьу бичигинэн</strong> бэлиэтэммит.",
+       "recentchangeslinked-summary": "Сирэй аатын суруйдаххына киниэхэ эбэтэр кини сигэнэр сирэйдэрин уларытыы көстүөҕэ. (Категорияҕа киирэр сирэйдэри көрөргө маннык суруй: {{ns:category}}:Категория аата). [[Special:Watchlist|Кэтиир сирэйдэриҥ]] уларыйыыта <strong>модьу бичигинэн</strong> бэлиэтэнэр.",
        "recentchangeslinked-page": "Сирэй аата:",
        "recentchangeslinked-to": "Төттөрүтүн, ыйыллыбыт сирэйгэ сигэнэр сирэйдэри көрдөр",
        "recentchanges-page-added-to-category": "[[:$1]] категорияҕа эбилиннэ",
        "filehist-filesize": "Билэ кээмэйэ",
        "filehist-comment": "Хос быһаарыы",
        "imagelinks": "Билэни туттуу",
-       "linkstoimage": "Бу билэҕэ маннык атын {{PLURAL:$1|сирэй сигэнэр|$1 сирэйдэр сигэнэллэр}}:",
-       "linkstoimage-more": "$1 {{PLURAL:$1|сирэйтэн|сирэйтэн}} элбэх сирэй бу билэҕэ сигэнэр.\nОнтон бу тиһиккэ {{PLURAL:$1|$1 эрэ сигэ көһүннэ|$1 сигэ эрэ көһүннэ}}.\nӨссө [[Special:WhatLinksHere/$2|толору тиһиги]] көрүөххүн сөп.",
-       "nolinkstoimage": "Атын сирэйдэр бу билэҕэ сигэммэттэр.",
+       "linkstoimage": "Бу билэни бу {{PLURAL:$1|сирэй туһанар|$1 сирэйдэр туһаналлар}}:",
+       "linkstoimage-more": "$1 {{PLURAL:$1|сирэйтэн|сирэйтэн}} элбэх сирэй бу билэни туһанар.\nБу испииһэккэ {{PLURAL:$1|$1 эрэ туһанар сирэй көрдөрүлүннэ|$1 эрэ туһанар сирэй көрдөрүлүннэ}}.\nЭбии [[Special:WhatLinksHere/$2|толору испииһэги]] көрүөххүн сөп.",
+       "nolinkstoimage": "Атын сирэйдэр бу билэни туһамматтар.",
        "morelinkstoimage": "Бу билэҕэ [[Special:WhatLinksHere/$1|атын сигэлэри]] көрөргө.",
        "linkstoimage-redirect": "$1 (утаарыы-билэ) $2",
        "duplicatesoffile": "Бу билэ {{PLURAL:$1|дубликаата манна көстөр|$1 дубликаата манна көстөллөр}} ([[Special:FileDuplicateSearch/$2|сиһилии]]):",
index dee3859..ae331b9 100644 (file)
@@ -10,7 +10,8 @@
                        "Via maxima",
                        "Uharteko",
                        "Taxandru",
-                       "Macofe"
+                       "Macofe",
+                       "Zoranzoki21"
                ]
        },
        "tog-underline": "Sutalìnia sos ligòngios",
        "disclaimerpage": "Project:Avertèntzias generales",
        "edithelp": "Agiudu pro su càmbiu o s'iscritura",
        "helppage-top-gethelp": "Agiudu",
-       "mainpage": "Pàgina Base",
+       "mainpage": "Pàgina printzipale",
        "mainpage-description": "Pàgina printzipale",
        "policy-url": "Project:Polìticas",
        "portal": "Portale comunidade",
index ae38bab..546fa8b 100644 (file)
@@ -39,7 +39,8 @@
                        "Pmikolas44",
                        "Fitoschido",
                        "Matěj Suchánek",
-                       "Vlad5250"
+                       "Vlad5250",
+                       "Robert Važan"
                ]
        },
        "tog-underline": "Podčiarkovať odkazy:",
@@ -88,7 +89,7 @@
        "underline-always": "Vždy",
        "underline-never": "Nikdy",
        "underline-default": "Podľa nastavení prehliadača alebo témy vzhľadu",
-       "editfont-style": "Štýl písma oblasti na úpravy:",
+       "editfont-style": "Štýl písma v editore:",
        "editfont-monospace": "S pevnou šírkou znaku",
        "editfont-sansserif": "Bezpätkové písmo",
        "editfont-serif": "Pätkové písmo",
        "resetpass-expired": "Platnosť vášho hesla vypršala. Pre prihlásenie si nastavte nové heslo.",
        "resetpass-expired-soft": "Platnosť vášho hesla vypršala, musíte si nastaviť nové. Zvoľte si nové heslo nebo kliknite na „{{int:authprovider-resetpass-skip-label}}“ a nastavte si ho neskôr.",
        "resetpass-validity-soft": "Vaše heslo je neplatné: $1\n\nVyberte si nové heslo, alebo kliknite na „{{int:authprovider-resetpass-skip-label}}“ a nastavte si ho neskôr.",
-       "passwordreset": "Reset hesla",
+       "passwordreset": "Obnova hesla",
        "passwordreset-text-one": "Pre získanie nového hesla vyplňte tento formulár.",
        "passwordreset-text-many": "{{PLURAL:$1|Pre získanie nového hesla e-mailom, zadajte jeden z údajov.}}",
        "passwordreset-disabled": "Obnovenie hesla bolo na tejto wiki zakázané.",
        "changeemail-submit": "Zmeniť e-mail",
        "changeemail-throttled": "Uskutočnili ste príliš mnoho neúspešných pokusov o prihlásenie. Prosím, počkajte $1 predtým, než to skúsite znova.",
        "changeemail-nochange": "Zadajte prosím odlišnú e-mailovú adresu.",
-       "resettokens": "Obnoviť tokeny",
+       "resettokens": "Reinicializovať kľúče",
        "resettokens-text": "Tu môžete obnoviť tokeny, ktoré umožňujú prístup k určitým súkromným údajom spojeným s vaším účtom.\n\nMali by ste to urobiť, ak ste ich omylom niekomu poskytli alebo ak bolo vaše konto zneužité.",
        "resettokens-no-tokens": "Neexistujú žiadne tokeny, ktoré by bolo možné obnoviť.",
        "resettokens-tokens": "Tokeny:",
        "prefs-watchlist-days-max": "Najviac $1 {{PLURAL:$1|deň|dni|dní}}",
        "prefs-watchlist-edits": "Počet úprav, ktorý sa zobrazí v rozšírenom zozname sledovaných:",
        "prefs-watchlist-edits-max": "Maximum: 1000",
-       "prefs-watchlist-token": "Token zoznamu sledovaných?",
+       "prefs-watchlist-token": "Kľúč pre sledovanie stránok",
+       "prefs-watchlist-managetokens": "Správa kľúčov",
        "prefs-misc": "Rôzne",
        "prefs-resetpass": "Zmeniť heslo",
        "prefs-changeemail": "Zmeniť alebo odstrániť e-mailovú adresu",
        "recentchangescount": "Štandardne zobrazovaný počet úprav:",
        "prefs-help-recentchangescount": "Toto sa týka posledných úprav, histórií stránok a záznamov.",
        "prefs-help-watchlist-token2": "Toto je tajný kľúč k webovému kanálu vášho zoznamu sledovaných stránok.\nkaždý, kto ho pozná si bude môcť prečítať váš zoznam sledovaných stránok, preto ho nezverejňujte.\n[[Special:ResetTokens|Kliknite sem, ak potrebujete vytvoriť nový]].",
+       "prefs-help-tokenmanagement": "Môžete vidieť a reinicializovať tajný kľúč k vašemu účtu, ktorý poskytuje prístup k webovému kanálu vášho zoznamu sledovaných stránok. Ktokoľvek, kto pozná tento kľúč, môže čítať váš zoznam sledovaných stránok, tak kľúč nezdieľajte.",
        "savedprefs": "Vaše nastavenia boli uložené.",
        "savedrights": "Práva {{GENDER:$1|používateľa|používateľky}} $1 boli uložené.",
        "timezonelegend": "Časové pásmo:",
        "prefs-common-config": "Spoločné CSS/JS pre všetky témy:",
        "prefs-reset-intro": "Túto stránku môžete použiť na vrátenie predvolených hodnôt vašich nastavení.\nTúto operáciu nemožno vrátiť.",
        "prefs-emailconfirm-label": "Overenie e-mailu:",
-       "youremail": "Váš e-mail²",
+       "youremail": "Email:",
        "username": "{{GENDER:$1|Používateľské meno}}:",
        "prefs-memberingroups": "{{GENDER:$2|Člen|Členovia}} {{PLURAL:$1|skupiny|skupín}}:",
        "group-membership-link-with-expiry": "$1 (do $2)",
        "prefs-timeoffset": "Časový posun",
        "prefs-advancedediting": "Všeobecné možnosti",
        "prefs-developertools": "Vývojárske nástroje",
-       "prefs-editor": "Používateľ",
+       "prefs-editor": "Editor",
        "prefs-preview": "Náhľad",
        "prefs-advancedrc": "Rozšírené možnosti",
        "prefs-advancedrendering": "Rozšírené možnosti",
        "prefs-advancedwatchlist": "Rozšírené možnosti",
        "prefs-displayrc": "Možnosti zobrazenia",
        "prefs-displaywatchlist": "Možnosti zobrazenia",
-       "prefs-tokenwatchlist": "Token",
+       "prefs-changeswatchlist": "Zobrazené zmeny",
+       "prefs-pageswatchlist": "Sledované stránky",
+       "prefs-tokenwatchlist": "Kľúč",
        "prefs-diffs": "Rozdiely",
        "prefs-help-prefershttps": "Táto voľba sa prejaví pri vašom ďalšom prihlásení.",
        "prefswarning-warning": "Vykonali ste zmeny v nastaveniach, ktoré zatiaľ nie sú uložené. Ak túto stránku opustíte bez kliknutia na „$1“, vaše nastavenia sa neaktualizujú.",
        "rcfilters-other-review-tools": "Ďalšie kontrolné nástroje",
        "rcfilters-group-results-by-page": "Zoskupiť výsledky podľa stránky",
        "rcfilters-activefilters": "Aktívne filtre",
+       "rcfilters-activefilters-hide": "Skryť",
        "rcfilters-advancedfilters": "Pokročilé filtre",
        "rcfilters-limit-title": "Zobraziť zmeny",
        "rcfilters-limit-and-date-label": "{{PLURAL:$1|Jedna úprava|$1 úpravy|$1 úprav}}, $2",
        "rcfilters-savedqueries-apply-and-setdefault-label": "Vytvoriť predvolený filter",
        "rcfilters-savedqueries-cancel-label": "Zrušiť",
        "rcfilters-savedqueries-add-new-title": "Uložiť súčasné nastavenie filtrov",
+       "rcfilters-savedqueries-already-saved": "Tieto filtre sú už uložené. Zmeňte niektoré nastavenia, ak chcete vytvoriť nový uložený filter.",
        "rcfilters-restore-default-filters": "Obnoviť predvolené filtre",
        "rcfilters-clear-all-filters": "Zrušiť všetky filtre",
        "rcfilters-show-new-changes": "Zobraziť najnovšie zmeny",
        "recentchangeslinked-feed": "Súvisiace úpravy",
        "recentchangeslinked-toolbox": "Súvisiace úpravy",
        "recentchangeslinked-title": "Zmeny týkajúce sa „$1”",
-       "recentchangeslinked-summary": "Vložte názov článku, aby ste videli zmeny stránok, na ktoré odkazuje alebo ktoré odkazujú na danú stránku (aby ste videli články v kategórii, zadajte Kategória:Názov kategórie).\nStránky, ktoré sa nachádzajú vo vašom [[Special:Watchlist|zozname sledovaných]] sú vyznačené <strong>hrubo</strong>.",
+       "recentchangeslinked-summary": "Vložte názov stránky, aby ste videli zmeny iných stránok, na ktoré odkazuje alebo ktoré odkazujú na danú stránku. (Ak chcete vidieť stránky v kategórii, zadajte {{ns:category}}:Názov kategórie).\nStránky, ktoré sa nachádzajú vo vašom [[Special:Watchlist|zozname sledovaných]] sú vyznačené <strong>hrubo</strong>.",
        "recentchangeslinked-page": "Názov stránky:",
        "recentchangeslinked-to": "Zobraziť zmeny na stránkach, ''ktoré odkazujú na'' zadanú stránku",
        "recentchanges-page-added-to-category": "[[:$1]] zaradená do kategórie",
        "filehist-filesize": "veľkosť súboru",
        "filehist-comment": "komentár",
        "imagelinks": "Použitie súboru",
-       "linkstoimage": "Na tento obrázok {{PLURAL:$1|odkazuje nasledujúca stránka|odkazujú nasledujúce $1 stránky|odkazuje nasledujúcich $1 stránok}}:",
+       "linkstoimage": "Na tento súbor {{PLURAL:$1|odkazuje nasledujúca stránka|odkazujú nasledujúce $1 stránky|odkazuje nasledujúcich $1 stránok}}:",
        "linkstoimage-more": "Viac ako $1 {{PLURAL:$1|stránka odkazuje|stránky odkazujú|stránok odkazuje}} na tento súbor.\nNasledovný zoznam zobrazuje {{PLURAL:$1|prvú stránku odkazujúcu|prvé $1 stránky odkazujúce|prvých $1 stránok odkazujúcich}} iba na tento súbor.\nMôžete si pozrieť [[Special:WhatLinksHere/$2|úplný zoznam]].",
-       "nolinkstoimage": "Žiadne stránky neobsahujú odkazy na tento obrázok.",
+       "nolinkstoimage": "Žiadne stránky neobsahujú odkazy na tento súbor.",
        "morelinkstoimage": "Zobraziť [[Special:WhatLinksHere/$1|ďalšie odkazy]] na tento súbor.",
        "linkstoimage-redirect": "$1 (presmerovanie súboru) $2",
        "duplicatesoffile": "{{PLURAL:$1|Nasledujúci súbor je duplikát|Nasledujúce $1 súbory sú duplikáty||Nasledujúcich $1 súborov sú duplikáty}} tohto súboru ([[Special:FileDuplicateSearch/$2|podrobnosti]]):",
        "speciallogtitlelabel": "Cieľ (názov alebo {{ns:user}}:Používateľské meno):",
        "log": "Záznamy",
        "logeventslist-submit": "Zobraziť",
+       "logeventslist-patrol-log": "Kniha preverených úprav",
+       "logeventslist-tag-log": "Kniha značiek",
        "all-logs-page": "Všetky verejné záznamy",
        "alllogstext": "Kombinované zobrazenie všetkých dostupných záznamov {{GRAMMAR:genitív|{{SITENAME}}}}.\nMôžete zúžiť rozsah, ak zvolíte typ záznamu, používateľské meno alebo dotyčnú stránku (záleží na veľkosti písmen).",
        "logempty": "V zázname neboli nájdené zodpovedajúce položky.",
        "listgrouprights-namespaceprotection-header": "Obmedzenia menných priestorov",
        "listgrouprights-namespaceprotection-namespace": "Menný priestor",
        "listgrouprights-namespaceprotection-restrictedto": "Práva umožňujúce redaktorovi upravovať",
+       "listgrants": "Skupiny oprávnení",
        "trackingcategories": "Sledovacie kategórie",
        "trackingcategories-summary": "Táto stránka obsahuje zoznam sledovacích kategórií, ktoré automaticky pridáva softvér MediaWiki. Ich mená je možné zmeniť úpravou príslušných systémových hlásení v mennom priestore {{ns:8}}.",
        "trackingcategories-msg": "Sledovacia kategória",
        "deletionlog": "záznam zmazaní",
        "log-name-create": "Záznam vytvorení stránok",
        "log-description-create": "Toto je zoznam posledných vytvorených stránok.",
+       "logentry-create-create": "$1 {{GENDER:$2|vytvoril|vytvorila|vytvoril(a)}} stránku $3",
        "reverted": "Obnovené na skoršiu verziu",
        "deletecomment": "Dôvod:",
        "deleteotherreason": "Iný/ďalší dôvod:",
        "pageinfo-display-title": "Zobrazovaný názov",
        "pageinfo-default-sort": "Predvolený kľúč zoraďovania:",
        "pageinfo-length": "Dĺžka stránky (v bajtoch)",
+       "pageinfo-namespace": "Menný priestor",
        "pageinfo-article-id": "ID stránky",
        "pageinfo-language": "Jazyk obsahu stránky",
        "pageinfo-language-change": "zmeniť",
        "pageinfo-category-subcats": "Počet podkategórií",
        "pageinfo-category-files": "Počet súborov",
        "pageinfo-user-id": "ID používateľa",
+       "pageinfo-view-protect-log": "Zobraziť knihu zamknutí tejto stránky.",
        "markaspatrolleddiff": "Označiť ako preverené",
        "markaspatrolledtext": "Označiť túto stránku ako preverenú",
        "markaspatrolledtext-file": "Označiť túto revíziu súboru ako preverenú",
        "specialpages": "Špeciálne stránky",
        "specialpages-note-top": "Legenda",
        "specialpages-note-restricted": "* Bežné špeciálne stránky.\n* <strong class=\"mw-specialpagerestricted\">Špeciálne stránky s obmedzeným prístupom.</strong>",
-       "specialpages-group-maintenance": "Údržbové správy",
+       "specialpages-group-maintenance": "Údržba",
        "specialpages-group-other": "Iné špeciálne stránky",
        "specialpages-group-login": "Prihlásenie / registrácia",
        "specialpages-group-changes": "Posledné úpravy a záznamy",
        "compare-invalid-title": "Názov, ktorý ste zadali nie je platný.",
        "compare-title-not-exists": "Názov, ktorý ste zadali neexistuje.",
        "compare-revision-not-exists": "Revízia, ktorú ste zadali, neexistuje.",
-       "diff-form": "'''formulár'''",
+       "diff-form": "Rozdiely",
        "dberr-problems": "Prepáčte! Táto stránka má práve technické problémy.",
        "dberr-again": "Skúste niekoľko minút počkať a potom opäť načítať stránku.",
        "dberr-info": "(Spojenie s databázovým serverom neúspešné: $1)",
        "json-error-unsupported-type": "Odovzdaná hodnota nekódovateľného typu",
        "headline-anchor-title": "Odkaz na túto sekciu",
        "special-characters-group-latin": "Latinka",
-       "special-characters-group-latinextended": "Latina rozšírené",
+       "special-characters-group-latinextended": "Rozšírenie latinky",
        "special-characters-group-ipa": "IPA",
        "special-characters-group-symbols": "Symboly",
        "special-characters-group-greek": "Grécke",
        "special-characters-group-thai": "Thajské",
        "special-characters-group-lao": "Laoské",
        "special-characters-group-khmer": "Khmer",
+       "special-characters-group-canadianaboriginal": "Písmo pôvodných obyvateľov Kanady",
        "special-characters-title-endash": "pomlčka",
        "special-characters-title-emdash": "dlhá pomlčka",
        "special-characters-title-minus": "mínus",
        "changecredentials": "Zmena prihlasovacích údajov",
        "removecredentials": "Odstránenie prihlasovacích údajov",
        "userjsispublic": "Uvedomte si prosím, že podstránky s JavaScriptom by nemali obsahovať tajné údaje, pretože sú viditeľné ostatným používateľom.",
-       "usercssispublic": "Uvedomte si prosím, že podstránky s CSS by nemali obsahovať tajné údaje, pretože sú viditeľné ostatným používateľom."
+       "usercssispublic": "Uvedomte si prosím, že podstránky s CSS by nemali obsahovať tajné údaje, pretože sú viditeľné ostatným používateľom.",
+       "passwordpolicies": "Pravidlá pre heslá"
 }
index 4288c4a..cc9ea07 100644 (file)
@@ -6,7 +6,8 @@
                        "Mimursal",
                        "Yariiska",
                        "아라",
-                       "Macofe"
+                       "Macofe",
+                       "Amire80"
                ]
        },
        "tog-underline": "Linkiga hoos ka calaamadeysan:",
        "version-poweredby-others": "kuwa kale",
        "redirect-file": "Magaca faylka",
        "fileduplicatesearch-submit": "Raadi",
-       "specialpages": "bogagga khaaska ah",
+       "specialpages": "Bogagga khaaska ah",
        "specialpages-note-top": "Furaha",
        "specialpages-group-pages": "liiska maqaalada",
        "blankpage": "Bog masaxan",
index b015010..6dca443 100644 (file)
        "tog-numberheadings": "Аутоматски нумериши наслове",
        "tog-editondblclick": "Омогући уређивање страница двоструким кликом",
        "tog-editsectiononrightclick": "Омогући уређивање одељака десним кликом на њихове наслове",
-       "tog-watchcreations": "Додај странице које направим и датотеке које отпремим на мој списак надгледања",
-       "tog-watchdefault": "Додај странице и датотеке које уредим на мој списак надгледања",
-       "tog-watchmoves": "Додај странице и датотеке које преместим на мој списак надгледања",
-       "tog-watchdeletion": "Додај странице и датотеке које избришем на мој списак надгледања",
-       "tog-watchuploads": "Додај нове датотеке које отпремим на мој списак надгледања",
-       "tog-watchrollback": "Додај странице на којима сам извршио враћање измена на мој списак надгледања",
+       "tog-watchcreations": "Додаји странице које направим и датотеке које отпремим на мој списак надгледања",
+       "tog-watchdefault": "Додаји странице и датотеке које уредим на мој списак надгледања",
+       "tog-watchmoves": "Додаји странице и датотеке које преместим на мој списак надгледања",
+       "tog-watchdeletion": "Додаји странице и датотеке које избришем на мој списак надгледања",
+       "tog-watchuploads": "Додаји нове датотеке које отпремим на мој списак надгледања",
+       "tog-watchrollback": "Додаји странице на којима сам извршио враћање измена на мој списак надгледања",
        "tog-minordefault": "Подразумевано означавај све измене као мање",
        "tog-previewontop": "Приказуј претпреглед пре оквира за уређивање",
        "tog-previewonfirst": "Приказуј претпреглед при првој измени",
-       "tog-enotifwatchlistpages": "Ð\9fоÑ\88аљи ми е-поруку када се промени страница или датотека са мог списка надгледања",
+       "tog-enotifwatchlistpages": "Шаљи ми е-поруку када се промени страница или датотека са мог списка надгледања",
        "tog-enotifusertalkpages": "Пошаљи ми е-поруку кад се промени моја корисничка страница за разговор",
-       "tog-enotifminoredits": "Ð\9fоÑ\88аљи ми е-поруку и код мањих измена страница и датотека",
-       "tog-enotifrevealaddr": "Ð\9eÑ\82кÑ\80иÑ\98 Ð¼Ð¾Ñ\98Ñ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е у е-порукама за обавештавање",
+       "tog-enotifminoredits": "Шаљи ми е-поруку и код мањих измена страница и датотека",
+       "tog-enotifrevealaddr": "Ð\9eÑ\82кÑ\80иÑ\98 Ð¼Ð¾Ñ\98Ñ\83 Ðµ-адÑ\80еÑ\81Ñ\83 у е-порукама за обавештавање",
        "tog-shownumberswatching": "Прикажи број корисника који надгледају",
        "tog-oldsig": "Ваш постојећи потпис:",
        "tog-fancysig": "Сматрај потпис као викитекст (без аутоматског повезивања)",
        "tog-uselivepreview": "Приказуј претпреглед без поновног учитавања странице",
-       "tog-forceeditsummary": "УпозоÑ\80и ме када не унесем опис измене",
+       "tog-forceeditsummary": "УпозоÑ\80аваÑ\98 ме када не унесем опис измене",
        "tog-watchlisthideown": "Сакривај моје измене са списка надгледања",
        "tog-watchlisthidebots": "Сакривај измене ботова са списка надгледања",
        "tog-watchlisthideminor": "Сакривај мање измене са списка надгледања",
        "tog-watchlisthideliu": "Сакривај измене пријављених корисника са списка надгледања",
-       "tog-watchlistreloadautomatically": "Аутоматски поново учитај списак надгледања кад год се филтер промени (потребан јаваскрипт)",
-       "tog-watchlistunwatchlinks": "Додај означиваче за прекид надгледања/нагледање ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}}) на надгледане странице са променама (за функционалност пребацивања је потребан јаваскрипт)",
+       "tog-watchlistreloadautomatically": "Аутоматски поново учитај списак надгледања кад год се филтер промени (потребан JavaScript)",
+       "tog-watchlistunwatchlinks": "Додаји означиваче за прекид надгледања/нагледање ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}}) на надгледане странице са променама (за функционалност пребацивања је потребан JavaScript)",
        "tog-watchlisthideanons": "Сакривај измене анонимних корисника са списка надгледања",
        "tog-watchlisthidepatrolled": "Сакривај патролиране измене са списка надгледања",
        "tog-watchlisthidecategorization": "Сакривај категоризацију страница",
-       "tog-ccmeonemails": "Ð\9fоÑ\88аљи ми копије е-порука које пошаљем другим корисницима",
+       "tog-ccmeonemails": "Шаљи ми копије е-порука које пошаљем другим корисницима",
        "tog-diffonly": "Не приказуј садржај странице испод разлика",
        "tog-showhiddencats": "Приказуј скривене категорије",
        "tog-norollbackdiff": "Не приказуј разлику након извршеног враћања",
-       "tog-useeditwarning": "УпозоÑ\80и ме када напуштам страницу за уређивање са несачуваним променама",
+       "tog-useeditwarning": "УпозоÑ\80аваÑ\98 ме када напуштам страницу за уређивање са несачуваним променама",
        "tog-prefershttps": "Увек користи безбедну везу док сам пријављен/а.",
-       "underline-always": "Увек",
-       "underline-never": "Ð\9dикад",
-       "underline-default": "Ð\9fрема теми или прегледачу",
+       "underline-always": "увек",
+       "underline-never": "никад",
+       "underline-default": "према теми или прегледачу",
        "editfont-style": "Стил фонта у оквиру за уређивање:",
-       "editfont-monospace": "Сразмерно широк фонт",
-       "editfont-sansserif": "Ð\91есерифни фонт",
-       "editfont-serif": "Серифни фонт",
+       "editfont-monospace": "сразмерно широк фонт",
+       "editfont-sansserif": "бесерифни фонт",
+       "editfont-serif": "серифни фонт",
        "sunday": "недеља",
        "monday": "понедељак",
        "tuesday": "уторак",
        "newwindow": "(отвара се у новом прозору)",
        "cancel": "Откажи",
        "moredotdotdot": "Више…",
-       "morenotlisted": "Овај списак можда није потпун.",
+       "morenotlisted": "Ова листа можда није потпуна.",
        "mypage": "Страница",
        "mytalk": "Разговор",
        "anontalk": "Разговор",
        "nosuchaction": "Нема такве радње",
        "nosuchactiontext": "Радња која је наведена у УРЛ-у није важећа.\nМожда сте погрешно откуцали УРЛ или сте следили покварену везу.\nОво такође може да указује на грешку у софтверу који користи {{SITENAME}}.",
        "nosuchspecialpage": "Нема такве посебне странице",
-       "nospecialpagetext": "<strong>Ð\97аÑ\85Ñ\82евали Ñ\81Ñ\82е Ð½ÐµÐ²Ð°Ð»Ð¸Ð´Ð½Ñ\83 Ð¿Ð¾Ñ\81ебнÑ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83.</strong>\n\nСпиÑ\81ак Ð²Ð°Ð»Ð¸Ð´Ð½Ð¸Ñ\85 Ð¿Ð¾Ñ\81ебниÑ\85 Ñ\81Ñ\82Ñ\80аниÑ\86а Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ñ\81е Ð¿Ñ\80онаÑ\92е на „[[Special:SpecialPages|{{int:specialpages}}]]”.",
+       "nospecialpagetext": "<strong>Ð\97аÑ\85Ñ\82евали Ñ\81Ñ\82е Ð½ÐµÐ²Ð°Ð¶ÐµÑ\9bÑ\83 Ð¿Ð¾Ñ\81ебнÑ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83.</strong>\n\nÐ\9bиÑ\81Ñ\82а Ð²Ð°Ð¶ÐµÑ\9bиÑ\85 Ð¿Ð¾Ñ\81ебниÑ\85 Ñ\81Ñ\82Ñ\80аниÑ\86а Ð¼Ð¾Ð¶Ðµ Ñ\81е Ð¿Ñ\80онаÑ\92и на „[[Special:SpecialPages|{{int:specialpages}}]]”.",
        "error": "Грешка",
        "databaseerror": "Грешка у бази података",
        "databaseerror-text": "Дошло је до грешке у упиту базе података. \nОво може да указује на грешку у софтверу.",
        "userlogin-loggedin": "Већ сте пријављени као {{GENDER:$1|$1}}.\nКористите доњи образац да бисте се пријавили као други корисник.",
        "userlogin-reauth": "Морате да се поново пријавите да бисте верификовали да сте {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Отвори још један налог",
-       "createacct-emailrequired": "Ð\90дÑ\80еÑ\81а Ðµ-поÑ\88Ñ\82е",
-       "createacct-emailoptional": "Ð\90дÑ\80еÑ\81а Ðµ-поÑ\88Ñ\82е (необавезно)",
-       "createacct-email-ph": "УнеÑ\81иÑ\82е Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е",
-       "createacct-another-email-ph": "УнеÑ\81иÑ\82е Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е",
-       "createaccountmail": "Ð\9aоÑ\80иÑ\81Ñ\82и Ð¿Ñ\80ивÑ\80еменÑ\83, Ð½Ð°Ñ\81Ñ\83миÑ\87нÑ\83 Ð»Ð¾Ð·Ð¸Ð½ÐºÑ\83 Ð¸ Ð¿Ð¾Ñ\88аÑ\99и Ñ\98е Ð½Ð° Ð½Ð°Ð²ÐµÐ´ÐµÐ½Ñ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е",
+       "createacct-emailrequired": "Ð\95-адÑ\80еÑ\81а",
+       "createacct-emailoptional": "Ð\95-адÑ\80еÑ\81а (опÑ\86ионално)",
+       "createacct-email-ph": "УнеÑ\81иÑ\82е Ðµ-адÑ\80еÑ\81Ñ\83",
+       "createacct-another-email-ph": "УнеÑ\81иÑ\82е Ðµ-адÑ\80еÑ\81Ñ\83",
+       "createaccountmail": "Ð\9aоÑ\80иÑ\81Ñ\82и Ð¿Ñ\80ивÑ\80еменÑ\83, Ð½Ð°Ñ\81Ñ\83миÑ\87нÑ\83 Ð»Ð¾Ð·Ð¸Ð½ÐºÑ\83 Ð¸ Ð¿Ð¾Ñ\88аÑ\99и Ñ\98е Ð½Ð° Ð½Ð°Ð²ÐµÐ´ÐµÐ½Ñ\83 Ðµ-адÑ\80еÑ\81Ñ\83",
        "createaccountmail-help": "Може се користити да се некоме отвори налог без сазнања лозинке.",
        "createacct-realname": "Право име (опционално)",
        "createacct-reason": "Разлог",
        "mailmypassword": "Ресетуј лозинку",
        "passwordremindertitle": "{{SITENAME}} — привремена лозинка",
        "passwordremindertext": "Неко са IP адресе $1 је затражио нову лозинку на викију {{SITENAME}} ($4).\nСтворена је привремена лозинка за {{GENDER:$2|корисника|корисницу|корисника}} $2 која гласи $3.\nУколико је ово ваш захтев, сада се пријавите и поставите нову лозинку.\nПривремена лозинка истиче за {{PLURAL:$5|један дан|$5 дана}}.\n\nАко је неко други затражио промену лозинке, или сте се сетили ваше лозинке и не желите да је мењате, занемарите ову поруку.",
-       "noemail": "{{GENDER:$1|Ð\9aоÑ\80иÑ\81ник â\80\9e$1â\80\9d Ð½Ð¸Ñ\98е Ð½Ð°Ð²ÐµÐ¾|Ð\9aоÑ\80иÑ\81ниÑ\86а â\80\9e$1â\80\9d Ð½Ð¸Ñ\98е Ð½Ð°Ð²ÐµÐ»Ð°|Ð\9aоÑ\80иÑ\81ник/Ñ\86а â\80\9e$1â\80\9d Ð½Ð¸Ñ\98е Ð½Ð°Ð²ÐµÐ¾/ла}} Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е.",
-       "noemailcreate": "Ð\9cоÑ\80аÑ\82е Ð´Ð° Ð½Ð°Ð²ÐµÐ´ÐµÑ\82е Ð²Ð°Ð¶ÐµÑ\9bÑ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е.",
-       "passwordsent": "Ð\9dова Ð»Ð¾Ð·Ð¸Ð½ÐºÐ° Ñ\98е Ð¿Ð¾Ñ\81лаÑ\82а Ð½Ð° Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е {{GENDER:$1|корисника|кориснице|корисника/це}} $1.\nПоново се пријавите након што је примите.",
+       "noemail": "{{GENDER:$1|Ð\9aоÑ\80иÑ\81ник â\80\9e$1â\80\9d Ð½Ð¸Ñ\98е Ð½Ð°Ð²ÐµÐ¾|Ð\9aоÑ\80иÑ\81ниÑ\86а â\80\9e$1â\80\9d Ð½Ð¸Ñ\98е Ð½Ð°Ð²ÐµÐ»Ð°|Ð\9aоÑ\80иÑ\81ник/Ñ\86а â\80\9e$1â\80\9d Ð½Ð¸Ñ\98е Ð½Ð°Ð²ÐµÐ¾/ла}} Ðµ-адÑ\80еÑ\81Ñ\83.",
+       "noemailcreate": "Ð\9cоÑ\80аÑ\82е Ð´Ð° Ð½Ð°Ð²ÐµÐ´ÐµÑ\82е Ð²Ð°Ð¶ÐµÑ\9bÑ\83 Ðµ-адÑ\80еÑ\81Ñ\83.",
+       "passwordsent": "Ð\9dова Ð»Ð¾Ð·Ð¸Ð½ÐºÐ° Ñ\98е Ð¿Ð¾Ñ\81лаÑ\82а Ð½Ð° Ðµ-адÑ\80еÑ\81Ñ\83 {{GENDER:$1|корисника|кориснице|корисника/це}} $1.\nПоново се пријавите након што је примите.",
        "blocked-mailpassword": "Уређивање са ваше IP адресе је блокирано. Ради спречавања злоупотребе, забрањена је и функција враћања лозинке са ње.",
-       "eauthentsent": "Е-порука о потврди је послата на наведену адресу е-поште.\nПре било којих других слања е-порука на налог, мораћете пратити упутства у е-поруци да бисте потврдили да је налог заиста ваш.",
+       "eauthentsent": "Е-порука са потврдом је послата на наведену е-адресу.\nПре било којих других слања е-порука на налог, мораћете пратити упутства у е-поруци да бисте потврдили да је налог заиста ваш.",
        "throttled-mailpassword": "Порука за промену лозинке је послата у {{PLURAL:$1|1=последњих сат времена|последња $1 сата|последњих $1 сати}}.\nДа бисмо спречили злоупотребу, подсетник шаљемо само једном у року од {{PLURAL:$1|1=сат времена|$1 сата|$1 сати}}.",
        "mailerror": "Грешка при слању поруке: $1",
        "acct_creation_throttle_hit": "Посетиоци овог викија који користе вашу Ај-Пи адресу су отворили {{PLURAL:$1|1=један налог|$1 налога}} у претходних $2, што је највећа дозвољена вредност у овом временском периоду.\nКао резултат тога, ти посетиоци тренутно не могу отварати више налога.",
-       "emailauthenticated": "Ð\92аÑ\88а Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е је потврђена на дан $2 у $3 ч.",
-       "emailnotauthenticated": "Ð\92аÑ\88а Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е још није потврђена.\nНиједна е-порука неће бити послата ни у једном од следећих случајева.",
-       "noemailprefs": "Ð\9dаведиÑ\82е Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е у подешавањима за оспособљавање ових функција.",
-       "emailconfirmlink": "Ð\9fоÑ\82вÑ\80диÑ\82е Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е",
-       "invalidemailaddress": "Ð\9dиÑ\98е Ð¼Ð¾Ð³Ñ\83Ñ\9bе Ð¿Ñ\80иÑ\85ваÑ\82иÑ\82и Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е јер је у неважећем формату.\nУнесите добро форматирану адресу или оставите празно поље.",
-       "cannotchangeemail": "Ð\90дÑ\80еÑ\81е Ðµ-поÑ\88Ñ\82е налога не могу се променити на овом викију.",
+       "emailauthenticated": "Ð\92аÑ\88а Ðµ-адÑ\80еÑ\81а је потврђена на дан $2 у $3 ч.",
+       "emailnotauthenticated": "Ð\92аÑ\88а Ðµ-адÑ\80еÑ\81а још није потврђена.\nНиједна е-порука неће бити послата ни у једном од следећих случајева.",
+       "noemailprefs": "Ð\9dаведиÑ\82е Ðµ-адÑ\80еÑ\81Ñ\83 у подешавањима за оспособљавање ових функција.",
+       "emailconfirmlink": "Ð\9fоÑ\82вÑ\80диÑ\82е Ðµ-адÑ\80еÑ\81Ñ\83",
+       "invalidemailaddress": "Ð\9dиÑ\98е Ð¼Ð¾Ð³Ñ\83Ñ\9bе Ð¿Ñ\80иÑ\85ваÑ\82иÑ\82и Ðµ-адÑ\80еÑ\81Ñ\83 јер је у неважећем формату.\nУнесите добро форматирану адресу или оставите празно поље.",
+       "cannotchangeemail": "Ð\95-адÑ\80еÑ\81е налога не могу се променити на овом викију.",
        "emaildisabled": "Ова локација не може да шаље е-поруке.",
        "accountcreated": "Налог је отворен",
        "accountcreatedtext": "Кориснички налог [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|talk]]) је отворен.",
        "createaccount-title": "Отварање корисничког налога за {{SITENAME}}",
-       "createaccount-text": "Ð\9dеко Ñ\98е Ð¾Ñ\82воÑ\80ио Ð½Ð°Ð»Ð¾Ð³ Ñ\81а Ð²Ð°Ñ\88ом Ð°Ð´Ñ\80еÑ\81ом Ðµ-поÑ\88Ñ\82е на пројекту {{SITENAME}} ($4) под именом „$2” и са лозинком „$3”.\nОдмах требате да се пријавите и промените своју лозинку.\n\nМожете да занемарите ову поруку, ако је овај налог отворен грешком.",
+       "createaccount-text": "Ð\9dеко Ñ\98е Ð¾Ñ\82воÑ\80ио Ð½Ð°Ð»Ð¾Ð³ Ñ\81а Ð²Ð°Ñ\88ом Ðµ-адÑ\80еÑ\81ом на пројекту {{SITENAME}} ($4) под именом „$2” и са лозинком „$3”.\nОдмах требате да се пријавите и промените своју лозинку.\n\nМожете да занемарите ову поруку, ако је овај налог отворен грешком.",
        "login-throttled": "Превише пута сте покушали да се пријавите.\nСачекајте $1 пре него што покушате поново.",
        "login-abort-generic": "Неуспешна пријава – прекинуто",
        "login-migrated-generic": "Ваш налог је мигриран. Ваше корисничко више не постоји на овом викију.",
        "loginlanguagelabel": "Језик: $1",
        "suspicious-userlogout": "Ваш захтев за одјаву је одбијен јер изгледа да га је послао покварени прегледач или кеширани прокси.",
-       "createacct-another-realname-tip": "Ð\9fÑ\80аво Ð¸Ð¼Ðµ Ñ\98е Ð½ÐµÐ¾Ð±Ð°Ð²ÐµÐ·но.\nАко одаберете да га пружите, биће коришћено за приписивање вашег рада.",
+       "createacct-another-realname-tip": "Ð\9fÑ\80аво Ð¸Ð¼Ðµ Ñ\98е Ð¾Ð¿Ñ\86ионално.\nАко одаберете да га пружите, биће коришћено за приписивање вашег рада.",
        "pt-login": "Пријава",
        "pt-login-button": "Пријави ме",
        "pt-login-continue-button": "Настави пријављивање",
        "pt-createaccount": "Отварање налога",
        "pt-userlogout": "Одјави ме",
        "php-mail-error-unknown": "Непозната грешка у функцији PHP mail().",
-       "user-mail-no-addy": "Ð\9fокÑ\83Ñ\88али Ñ\81Ñ\82е Ð´Ð° Ð¿Ð¾Ñ\88аÑ\99еÑ\82е Ðµ-поÑ\80Ñ\83кÑ\83 Ð±ÐµÐ· Ð°Ð´Ñ\80еÑ\81е Ðµ-поÑ\88Ñ\82е.",
+       "user-mail-no-addy": "Ð\9fокÑ\83Ñ\88али Ñ\81Ñ\82е Ð´Ð° Ð¿Ð¾Ñ\88аÑ\99еÑ\82е Ðµ-поÑ\80Ñ\83кÑ\83 Ð±ÐµÐ· Ðµ-адÑ\80еÑ\81е.",
        "user-mail-no-body": "Покушали сте да пошаљете е-поруку са празним или неразумно кратким садржајем.",
-       "changepassword": "Ð\9fÑ\80омена Ð»Ð¾Ð·Ð¸Ð½ÐºÐµ",
+       "changepassword": "Ð\9fÑ\80омениÑ\82е Ð»Ð¾Ð·Ð¸Ð½ÐºÑ\83",
        "resetpass_announce": "Да бисте завршили пријаву, подесите нову лозинку овде.",
        "resetpass_text": "<!-- Овде унесите текст -->",
        "resetpass_header": "Промена лозинке налога",
        "passwordreset-emaildisabled": "Функција е-поште је онемогућена на овом викију.",
        "passwordreset-username": "Корисничко име:",
        "passwordreset-domain": "Домен:",
-       "passwordreset-email": "Ð\90дÑ\80еÑ\81а Ðµ-поÑ\88Ñ\82е:",
+       "passwordreset-email": "Ð\95-адÑ\80еÑ\81а:",
        "passwordreset-emailtitle": "Детаљи налога на викију {{SITENAME}}",
-       "passwordreset-emailtext-ip": "Ð\9dеко (веÑ\80оваÑ\82но Ð²Ð¸, Ñ\81а IP Ð°Ð´Ñ\80еÑ\81е $1) Ð·Ð°Ñ\82Ñ\80ажио Ñ\98е Ñ\80еÑ\81еÑ\82оваÑ\9aе Ð²Ð°Ñ\88е \nлозинке Ð·Ð° Ð¿Ñ\80оÑ\98екаÑ\82 {{SITENAME}} ($4). Ð¡Ð»ÐµÐ´ÐµÑ\9bи ÐºÐ¾Ñ\80иÑ\81ниÑ\87ки {{PLURAL:$3|налог Ñ\98е Ð¿Ð¾Ð²ÐµÐ·Ð°Ð½|налози Ñ\81Ñ\83 Ð¿Ð¾Ð²ÐµÐ·Ð°Ð½Ð¸}} \nÑ\81а Ð¾Ð²Ð¾Ð¼ Ð°Ð´Ñ\80еÑ\81ом Ðµ-поÑ\88Ñ\82е:\n\n$2\n\n{{PLURAL:$3|Ова привремена лозинка|Ове привремене лозинке}} истећи ће за {{PLURAL:$5|један дан|$5 дана}}.\nОдмах требате да се пријавите и одаберите нову лозинку. \nАко је неко други направио овај захтев или сте се сетили \nоригиналне лозинке, а не желите да је промените, \nможете да занемарите ову поруку и наставите да \nкористите своју стару лозинку.",
+       "passwordreset-emailtext-ip": "Ð\9dеко (веÑ\80оваÑ\82но Ð²Ð¸, Ñ\81а IP Ð°Ð´Ñ\80еÑ\81е $1) Ð·Ð°Ñ\82Ñ\80ажио Ñ\98е Ñ\80еÑ\81еÑ\82оваÑ\9aе Ð²Ð°Ñ\88е \nлозинке Ð·Ð° Ð¿Ñ\80оÑ\98екаÑ\82 {{SITENAME}} ($4). Ð¡Ð»ÐµÐ´ÐµÑ\9bи ÐºÐ¾Ñ\80иÑ\81ниÑ\87ки {{PLURAL:$3|налог Ñ\98е Ð¿Ð¾Ð²ÐµÐ·Ð°Ð½|налози Ñ\81Ñ\83 Ð¿Ð¾Ð²ÐµÐ·Ð°Ð½Ð¸}} \nÑ\81а Ð¾Ð²Ð¾Ð¼ Ðµ-адÑ\80еÑ\81ом:\n\n$2\n\n{{PLURAL:$3|Ова привремена лозинка|Ове привремене лозинке}} истећи ће за {{PLURAL:$5|један дан|$5 дана}}.\nОдмах требате да се пријавите и одаберите нову лозинку. \nАко је неко други направио овај захтев или сте се сетили \nоригиналне лозинке, а не желите да је промените, \nможете да занемарите ову поруку и наставите да \nкористите своју стару лозинку.",
        "passwordreset-emailtext-user": "Корисник/ца $1 затражио/ла је ресетовање ваше лозинке на пројекту {{SITENAME}} ($4).\nСледећи кориснички {{PLURAL:$3|налог је повезан|налози су повезани}} са овом адресом е-поште:\n\n$2\n\n{{PLURAL:$3|Привремена лозинка истиче|Привремене лозинке истичу}} за {{PLURAL:$5|један дан|$5 дана}}.\nОдмах требате да се пријавите и одаберите нову лозинку. \nАко је неко други направио овај захтев или сте се сетили \nоригиналне лозинке, а не желите да је промените, \nможете да занемарите ову поруку и наставите да \nкористите своју стару лозинку.",
        "passwordreset-emailelement": "Корисничко име: \n$1\n\nПривремена лозинка: \n$2",
-       "passwordreset-emailsentemail": "Ð\90ко Ñ\98е Ð¾Ð²Ð° Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е повезана са вашим налогом, онда ће е-порука о ресетовању лозинке бити послата.",
-       "passwordreset-emailsentusername": "Ð\90ко Ð¿Ð¾Ñ\81Ñ\82оÑ\98и Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е повезана са овим корисничким именом, онда ће е-порука о ресетовању лозинке бити послата.",
+       "passwordreset-emailsentemail": "Ð\90ко Ñ\98е Ð¾Ð²Ð° Ðµ-адÑ\80еÑ\81а повезана са вашим налогом, онда ће е-порука о ресетовању лозинке бити послата.",
+       "passwordreset-emailsentusername": "Ð\90ко Ð¿Ð¾Ñ\81Ñ\82оÑ\98и Ðµ-адÑ\80еÑ\81а повезана са овим корисничким именом, онда ће е-порука о ресетовању лозинке бити послата.",
        "passwordreset-nocaller": "Позивалац се мора навести",
        "passwordreset-nosuchcaller": "Позивалац не постоји: $1",
        "passwordreset-ignored": "Ресетовање лозинке није успело. Можда послужилац није конфигурисан?",
-       "passwordreset-invalidemail": "Ð\9dеважеÑ\9bа Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е",
-       "passwordreset-nodata": "Ð\9aоÑ\80иÑ\81ниÑ\87ко Ð¸Ð¼Ðµ Ð¸ Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е нису наведени",
-       "changeemail": "Ð\9fÑ\80омена Ð¸Ð»Ð¸ Ñ\83клаÑ\9aаÑ\9aе Ð°Ð´Ñ\80еÑ\81е Ðµ-поÑ\88Ñ\82е",
-       "changeemail-header": "Ð\94овÑ\80Ñ\88иÑ\82е Ð¾Ð²Ð°Ñ\98 Ð¾Ð±Ñ\80азаÑ\86 Ð´Ð° Ð±Ð¸ Ñ\81Ñ\82е Ð¿Ñ\80оменили Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е. Ð\90ко Ð±Ð¸Ñ\81Ñ\82е Ð¶ÐµÐ»ÐµÐ»Ð¸ Ð´Ð° Ñ\83клониÑ\82е Ð¿Ð¾Ð²ÐµÐ·Ð°Ð½Ð¾Ñ\81Ñ\82 Ð±Ð¸Ð»Ð¾ ÐºÐ¾Ñ\98е Ð°Ð´Ñ\80еÑ\81е Ðµ-поÑ\88Ñ\82е Ñ\81а Ð²Ð°Ñ\88ег Ð½Ð°Ð»Ð¾Ð³Ð°, Ð¾Ñ\81Ñ\82авиÑ\82е Ð¿Ñ\80азно Ð¿Ð¾Ñ\99е Ð·Ð° Ð½Ð¾Ð²Ñ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е када шаљете образац.",
+       "passwordreset-invalidemail": "Ð\9dеважеÑ\9bа Ðµ-адÑ\80еÑ\81а",
+       "passwordreset-nodata": "Ð\9aоÑ\80иÑ\81ниÑ\87ко Ð¸Ð¼Ðµ Ð¸ Ðµ-адÑ\80еÑ\81а нису наведени",
+       "changeemail": "Ð\9fÑ\80омена Ð¸Ð»Ð¸ Ñ\83клаÑ\9aаÑ\9aе Ðµ-адÑ\80еÑ\81е",
+       "changeemail-header": "Ð\94овÑ\80Ñ\88иÑ\82е Ð¾Ð²Ð°Ñ\98 Ð¾Ð±Ñ\80азаÑ\86 Ð´Ð° Ð±Ð¸ Ñ\81Ñ\82е Ð¿Ñ\80оменили Ðµ-адÑ\80еÑ\81Ñ\83. Ð\90ко Ð±Ð¸Ñ\81Ñ\82е Ð¶ÐµÐ»ÐµÐ»Ð¸ Ð´Ð° Ñ\83клониÑ\82е Ð¿Ð¾Ð²ÐµÐ·Ð°Ð½Ð¾Ñ\81Ñ\82 Ð±Ð¸Ð»Ð¾ ÐºÐ¾Ñ\98е Ðµ-адÑ\80еÑ\81е Ñ\81а Ð²Ð°Ñ\88ег Ð½Ð°Ð»Ð¾Ð³Ð°, Ð¾Ñ\81Ñ\82авиÑ\82е Ð¿Ñ\80азно Ð¿Ð¾Ñ\99е Ð·Ð° Ð½Ð¾Ð²Ñ\83 Ðµ-адÑ\80еÑ\81Ñ\83 када шаљете образац.",
        "changeemail-no-info": "Морате бити пријављени да бисте приступили овој страници.",
-       "changeemail-oldemail": "ТÑ\80енÑ\83Ñ\82на Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е:",
-       "changeemail-newemail": "Ð\9dова Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е:",
-       "changeemail-newemail-help": "Ð\9eво Ð¿Ð¾Ñ\99е Ñ\82Ñ\80еба Ð´Ð° Ð¾Ñ\81Ñ\82авиÑ\82е Ð¿Ñ\80азно Ð°ÐºÐ¾ Ð¶ÐµÐ»Ð¸Ñ\82е Ð´Ð° Ñ\83клониÑ\82е Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е. Ð\9dеÑ\9bеÑ\82е Ð±Ð¸Ñ\82и Ñ\83 Ð¼Ð¾Ð³Ñ\83Ñ\9bноÑ\81Ñ\82и Ð´Ð° Ñ\80еÑ\81еÑ\82Ñ\83Ñ\98еÑ\82е Ð·Ð°Ð±Ð¾Ñ\80авÑ\99енÑ\83 Ð»Ð¾Ð·Ð¸Ð½ÐºÑ\83 Ð¸ Ð½ÐµÑ\9bеÑ\82е Ð¿Ñ\80имаÑ\82и Ðµ-поÑ\80Ñ\83ке Ñ\81а Ð¾Ð²Ð¾Ð³ Ð²Ð¸ÐºÐ¸Ñ\98а Ð°ÐºÐ¾ Ñ\98е Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е уклоњена.",
+       "changeemail-oldemail": "ТÑ\80енÑ\83Ñ\82на Ðµ-адÑ\80еÑ\81а:",
+       "changeemail-newemail": "Ð\9dова Ðµ-адÑ\80еÑ\81а:",
+       "changeemail-newemail-help": "Ð\9eво Ð¿Ð¾Ñ\99е Ñ\82Ñ\80еба Ð´Ð° Ð¾Ñ\81Ñ\82авиÑ\82е Ð¿Ñ\80азно Ð°ÐºÐ¾ Ð¶ÐµÐ»Ð¸Ñ\82е Ð´Ð° Ñ\83клониÑ\82е Ðµ-адÑ\80еÑ\81Ñ\83. Ð\9dеÑ\9bеÑ\82е Ð±Ð¸Ñ\82и Ñ\83 Ð¼Ð¾Ð³Ñ\83Ñ\9bноÑ\81Ñ\82и Ð´Ð° Ñ\80еÑ\81еÑ\82Ñ\83Ñ\98еÑ\82е Ð·Ð°Ð±Ð¾Ñ\80авÑ\99енÑ\83 Ð»Ð¾Ð·Ð¸Ð½ÐºÑ\83 Ð¸ Ð½ÐµÑ\9bеÑ\82е Ð¿Ñ\80имаÑ\82и Ðµ-поÑ\80Ñ\83ке Ñ\81а Ð¾Ð²Ð¾Ð³ Ð²Ð¸ÐºÐ¸Ñ\98а Ð°ÐºÐ¾ Ñ\98е Ðµ-адÑ\80еÑ\81а уклоњена.",
        "changeemail-none": "(ништа)",
        "changeemail-password": "Ваша лозинка за пројекат {{SITENAME}}:",
        "changeemail-submit": "Промени е-пошту",
        "changeemail-throttled": "Превише пута сте покушали да се пријавите.\nМолимо вас да сачекате $1 пре него што покушате поново.",
-       "changeemail-nochange": "УнеÑ\81иÑ\82е Ð´Ñ\80Ñ\83гÑ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е.",
+       "changeemail-nochange": "УнеÑ\81иÑ\82е Ð´Ñ\80Ñ\83гÑ\83 Ðµ-адÑ\80еÑ\81Ñ\83.",
        "resettokens": "Ресетовање токена",
        "resettokens-text": "Овде можете да ресетујете токене који омогућавају приступ одређеним приватним подацима повезаним са вашим налогом.\n\nТребали бисте то урадити ако их случајно поделите са неким или ако је ваш налог угрожен.",
        "resettokens-no-tokens": "Нема жетона за ресетовање.",
        "blockedtitle": "Корисник је блокиран",
        "blocked-email-user": "<strong>Вашем корисничком имену је блокирано слање е-порука. Још увек можете да уређујете друге странице на овом викију.</strong> Можете да видите потпуне детаље блокаде на [[Special:MyContributions|доприносима налога]].\n\nБлокаду је извршио/ла $1.\n\nНаведен је следећи разлог: <em>$2</em>.\n\n* Почетак блокаде: $8\n* Истек блокаде: $6\n* Намењена кориснику/ци или IP адреси: $7\n* ID блокаде #$5",
        "blockedtext-partial": "<strong>Вашем корисничком имену или IP адреси је блокирано прављење промена на овој страници. Још увек можете да уређујете друге странице на овом викију.</strong> Можете да видите потпуне детаље блокаде на [[Special:MyContributions|доприносима налога]].\n\nБлокаду је извршио/ла $1.\n\nНаведен је следећи разлог: <em>$2</em>.\n\n* Почетак блокаде: $8\n* Истек блокаде: $6\n* Намењена кориснику/ци или IP адреси: $7\n* ID блокаде #$5",
-       "blockedtext": "<strong>Ваше корисничко име или IP адреса је блокирана.</strong>\n\nБлокирање је {{GENDER:$4|извршио|извршила}} $1.\nРазлог је <em>$2</em>.\n\n* Почетак блокирања: $8\n* Истек блокирања: $6\n* Блокирани: $7\n\nМожете да се обратите {{GENDER:$4|кориснику|корисници}} $1 или [[{{MediaWiki:Grouppage-sysop}}|администратору]] ради дискусије о блокирању.\nНе можете да користите могућност „{{int:emailuser}}” осим ако сте унели валидну имејл адресу у својим [[Special:Preferences|подешавањима]] налога и нисте блокирани од коришћења исте.\nВаша тренутна IP адреса је $3, а ID блокирања #$5.\nНаведите све информације одозго при стварању било каквих упита.",
-       "autoblockedtext": "Ð\92аÑ\88а IP Ð°Ð´Ñ\80еÑ\81а Ñ\98е Ð°Ñ\83Ñ\82омаÑ\82Ñ\81ки Ð±Ð»Ð¾ÐºÐ¸Ñ\80ана Ñ\98еÑ\80 Ñ\98Ñ\83 Ñ\98е ÐºÐ¾Ñ\80иÑ\81Ñ\82ио Ð´Ñ\80Ñ\83ги ÐºÐ¾Ñ\80иÑ\81ник, ÐºÐ¾Ð³Ð° Ñ\98е {{GENDER:$4|блокиÑ\80ао|блокиÑ\80ала|блокиÑ\80ао/ла}} $1.\nРазлог:\n\n:<em>$2</em>\n\n* Ð\9fоÑ\87еÑ\82ак Ð±Ð»Ð¾ÐºÐ°Ð´Ðµ: $8\n* Ð\9aÑ\80аÑ\98 Ð±Ð»Ð¾ÐºÐ°Ð´Ðµ: $6\n* Ð\98ме ÐºÐ¾Ñ\80иÑ\81ника: $7\n\nÐ\9cожеÑ\82е Ð´Ð° ÐºÐ¾Ð½Ñ\82акÑ\82иÑ\80аÑ\82е {{GENDER:$4|коÑ\80иÑ\81ника|коÑ\80иÑ\81ниÑ\86Ñ\83|коÑ\80иÑ\81ника/Ñ\86Ñ\83}} $1 Ð¸Ð»Ð¸ Ð´Ñ\80Ñ\83гог [[{{MediaWiki:Grouppage-sysop}}|админиÑ\81Ñ\82Ñ\80аÑ\82оÑ\80а]] Ð´Ð° Ð±Ð¸Ñ\81Ñ\82е Ñ\80аÑ\81пÑ\80авÑ\99али Ð¾ Ð±Ð»Ð¾ÐºÐ°Ð´Ð¸.\n\nÐ\97апамÑ\82иÑ\82е Ð´Ð° Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ\82е Ð´Ð° ÐºÐ¾Ñ\80иÑ\81Ñ\82иÑ\82е Ñ\84Ñ\83нкÑ\86иÑ\98Ñ\83 â\80\9e{{int:emailuser}}â\80\9c Ð¾Ñ\81им Ð°ÐºÐ¾ Ñ\81Ñ\82е Ð½Ð°Ð²ÐµÐ»Ð¸ Ð²Ð°Ð¶ÐµÑ\9bÑ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е у [[Special:Preferences|подешавањима]].\n\nВаша тренутна IP адреса је $3, а ID блокаде $5.\nУкључите све горње детаље при прављењу било каквих упита.",
+       "blockedtext": "<strong>Ваше корисничко име или IP адреса је блокирана.</strong>\n\nБлокирање је {{GENDER:$4|извршио|извршила}} $1.\nРазлог је <em>$2</em>.\n\n* Почетак блокирања: $8\n* Истек блокирања: $6\n* Блокирани: $7\n\nМожете да се обратите {{GENDER:$4|кориснику|корисници}} $1 или [[{{MediaWiki:Grouppage-sysop}}|администратору]] ради дискусије о блокирању.\nНе можете да користите функцију „{{int:emailuser}}” осим ако сте унели важећу е-адресу у својим [[Special:Preferences|подешавањима]] налога и нисте блокирани од коришћења исте.\nВаша тренутна IP адреса је $3, а ID блокирања #$5.\nНаведите све информације одозго при стварању било каквих упита.",
+       "autoblockedtext": "Ð\92аÑ\88а IP Ð°Ð´Ñ\80еÑ\81а Ñ\98е Ð°Ñ\83Ñ\82омаÑ\82Ñ\81ки Ð±Ð»Ð¾ÐºÐ¸Ñ\80ана Ñ\98еÑ\80 Ñ\98Ñ\83 Ñ\98е ÐºÐ¾Ñ\80иÑ\81Ñ\82ио Ð´Ñ\80Ñ\83ги ÐºÐ¾Ñ\80иÑ\81ник, ÐºÐ¾Ð³Ð° Ñ\98е {{GENDER:$4|блокиÑ\80ао|блокиÑ\80ала|блокиÑ\80ао/ла}} $1.\nРазлог:\n\n:<em>$2</em>\n\n* Ð\9fоÑ\87еÑ\82ак Ð±Ð»Ð¾ÐºÐ°Ð´Ðµ: $8\n* Ð\9aÑ\80аÑ\98 Ð±Ð»Ð¾ÐºÐ°Ð´Ðµ: $6\n* Ð\98ме ÐºÐ¾Ñ\80иÑ\81ника: $7\n\nÐ\9cожеÑ\82е Ð´Ð° ÐºÐ¾Ð½Ñ\82акÑ\82иÑ\80аÑ\82е {{GENDER:$4|коÑ\80иÑ\81ника|коÑ\80иÑ\81ниÑ\86Ñ\83|коÑ\80иÑ\81ника/Ñ\86Ñ\83}} $1 Ð¸Ð»Ð¸ Ð´Ñ\80Ñ\83гог [[{{MediaWiki:Grouppage-sysop}}|админиÑ\81Ñ\82Ñ\80аÑ\82оÑ\80а]] Ð´Ð° Ð±Ð¸Ñ\81Ñ\82е Ñ\80аÑ\81пÑ\80авÑ\99али Ð¾ Ð±Ð»Ð¾ÐºÐ°Ð´Ð¸.\n\nÐ\97апамÑ\82иÑ\82е Ð´Ð° Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ\82е Ð´Ð° ÐºÐ¾Ñ\80иÑ\81Ñ\82иÑ\82е Ñ\84Ñ\83нкÑ\86иÑ\98Ñ\83 â\80\9e{{int:emailuser}}â\80\9c Ð¾Ñ\81им Ð°ÐºÐ¾ Ñ\81Ñ\82е Ð½Ð°Ð²ÐµÐ»Ð¸ Ð²Ð°Ð¶ÐµÑ\9bÑ\83 Ðµ-адÑ\80еÑ\81Ñ\83 у [[Special:Preferences|подешавањима]].\n\nВаша тренутна IP адреса је $3, а ID блокаде $5.\nУкључите све горње детаље при прављењу било каквих упита.",
        "blockednoreason": "разлог није наведен",
        "whitelistedittext": "$1 да бисте уређивали странице.",
-       "confirmedittext": "Ð\9cоÑ\80аÑ\82е Ð´Ð° Ð¿Ð¾Ñ\82вÑ\80диÑ\82е Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е пре уређивања страница.\nПоставите и проверите ваљаност адресе преко [[Special:Preferences|подешавања]].",
+       "confirmedittext": "Ð\9cоÑ\80аÑ\82е Ð´Ð° Ð¿Ð¾Ñ\82вÑ\80диÑ\82е Ðµ-адÑ\80еÑ\81Ñ\83 пре уређивања страница.\nПоставите и проверите ваљаност адресе преко [[Special:Preferences|подешавања]].",
        "nosuchsectiontitle": "Није могуће пронаћи одељак",
        "nosuchsectiontext": "Покушали сте да уредите одељак који не постоји.\nМожда је премештен или избрисан док сте прегледали страницу.",
        "loginreqtitle": "Потребна је пријава",
        "revdelete-edit-reasonlist": "Уреди разлоге за брисање",
        "revdelete-offender": "Аутор измене:",
        "suppressionlog": "Дневник сакривања",
-       "suppressionlogtext": "Испод се налази списак брисања и блокирања који укључује садржај сакривен од администратора. Погледајте [[Special:BlockList|списак блокирања]] за списак тренутних операција забрана и блокирања.",
+       "suppressionlogtext": "Испод се налази листа брисања и блокирања који укључује садржај сакривен од администратора. Погледајте [[Special:BlockList|листу блокирања]] за списак тренутних операција забрана и блокирања.",
        "mergehistory": "Обједињавање историја странице",
        "mergehistory-header": "Ова страница вам допушта да обједините историју измена неке изворне странице у новију страницу.\nУверите се да ће ова промена оставити непромењен садржај историје странице.",
        "mergehistory-box": "Обједини измене двеју страница:",
        "mergehistory-revisionrow": "$1 ($2) $3 . . $4 $5 $6",
        "mergelog": "Дневник обједињавања",
        "revertmerge": "растави",
-       "mergelogpagetext": "Ð\98Ñ\81под Ñ\98е Ñ\81пиÑ\81ак најновијих обједињавања историја једне странице у другу.",
+       "mergelogpagetext": "Ð\98Ñ\81под Ñ\81е Ð½Ð°Ð»Ð°Ð·Ð¸ Ð»Ð¸Ñ\81Ñ\82а најновијих обједињавања историја једне странице у другу.",
        "history-title": "Историја измена странице „$1”",
        "difference-title": "Разлика између измена на страници „$1”",
        "difference-title-multipage": "Разлика између страница „$1“ и „$2“",
        "prefs-rc": "Скорашње измене",
        "prefs-watchlist": "Списак надгледања",
        "prefs-editwatchlist": "Уређивање списка надгледања",
-       "prefs-editwatchlist-label": "Уреди уносе на списку надгледања:",
-       "prefs-editwatchlist-edit": "Приказ и уклањање наслова са списка надгледања",
-       "prefs-editwatchlist-raw": "Уређивање необрађеног списка надгледања",
-       "prefs-editwatchlist-clear": "ЧиÑ\88Ñ\9bеÑ\9aе Ñ\81пиÑ\81ка надгледања",
+       "prefs-editwatchlist-label": "Уредите уносе на списку надгледања:",
+       "prefs-editwatchlist-edit": "Погледајте и уклоните наслове са списка надгледања",
+       "prefs-editwatchlist-raw": "Уредите необрађен списак надгледања",
+       "prefs-editwatchlist-clear": "Ð\9eÑ\87иÑ\81Ñ\82иÑ\82е Ñ\81пиÑ\81ак надгледања",
        "prefs-watchlist-days": "Број дана у списку надгледања:",
        "prefs-watchlist-days-max": "Највише $1 {{PLURAL:$1|дан|дана|дана}}",
        "prefs-watchlist-edits": "Највећи број промена приказаних на списку надгледања:",
        "prefs-watchlist-edits-max": "Највећи број: 1000",
        "prefs-watchlist-token": "Токен списка надгледања:",
-       "prefs-watchlist-managetokens": "УпÑ\80авÑ\99аÑ\9aе токенима",
+       "prefs-watchlist-managetokens": "УпÑ\80авÑ\99аÑ\98 токенима",
        "prefs-misc": "Разно",
        "prefs-resetpass": "Промена лозинке",
-       "prefs-changeemail": "Ð\9fÑ\80омена Ð¸Ð»Ð¸ Ñ\83клаÑ\9aаÑ\9aе Ð°Ð´Ñ\80еÑ\81е Ðµ-поÑ\88Ñ\82е",
-       "prefs-setemail": "Ð\9fоÑ\81Ñ\82авÑ\99аÑ\9aе Ð°Ð´Ñ\80еÑ\81е Ðµ-поÑ\88Ñ\82е",
+       "prefs-changeemail": "Ð\9fÑ\80омениÑ\82е Ð¸Ð»Ð¸ Ñ\83клониÑ\82е Ðµ-адÑ\80еÑ\81Ñ\83",
+       "prefs-setemail": "Ð\9fоÑ\81Ñ\82авÑ\99аÑ\9aе Ðµ-адÑ\80еÑ\81е",
        "prefs-email": "Опције е-поште",
        "prefs-rendering": "Изглед",
        "saveprefs": "Сачувај",
        "searchresultshead": "Претрага",
        "stub-threshold": "Праг за форматирање веза као клице ($1):",
        "stub-threshold-sample-link": "пример",
-       "stub-threshold-disabled": "Ð\9eнемогућено",
+       "stub-threshold-disabled": "онемогућено",
        "recentchangesdays": "Број дана у скорашњим изменама:",
        "recentchangesdays-max": "Највише $1 {{PLURAL:$1|дан|дана}}",
        "recentchangescount": "Подразумевани број измена за приказ у скорашњим изменама, историјама страница и дневницима:",
        "savedrights": "Корисничке групе {{GENDER:$1|корисника|кориснице}} $1 су сачуване.",
        "timezonelegend": "Временска зона:",
        "localtime": "Локално време:",
-       "timezoneuseserverdefault": "Ð\9aористи подразумеване вредности викија ($1)",
-       "timezoneuseoffset": "Ð\94руго (наведите одступање испод)",
+       "timezoneuseserverdefault": "користи подразумеване вредности викија ($1)",
+       "timezoneuseoffset": "друго (наведите одступање испод)",
        "timezone-useoffset-placeholder": "Примери вредности: „-07:00” или „01:00”",
        "servertime": "Време на серверу:",
-       "guesstimezone": "Ð\9fопуни из прегледача",
+       "guesstimezone": "попуни из прегледача",
        "timezoneregion-africa": "Африка",
        "timezoneregion-america": "Америка",
        "timezoneregion-antarctica": "Антарктик",
        "timezoneregion-europe": "Европа",
        "timezoneregion-indian": "Индијски океан",
        "timezoneregion-pacific": "Тихи океан",
-       "allowemail": "Дозволи другим корисницима да ми шаљу е-поруке",
-       "email-allow-new-users-label": "Дозволи примање е-порука од новајлија",
-       "email-blacklist-label": "Забрани овим корисницима да ми шаљу е-поруке:",
+       "allowemail": "Дозвољавај другим корисницима да ми шаљу е-поруке",
+       "email-allow-new-users-label": "Дозвољавај е-поруке од потпуно нових корисника",
+       "email-blacklist-label": "Забрањуј следећим корисницима да ми шаљу е-поруке:",
        "prefs-searchoptions": "Претрага",
        "prefs-namespaces": "Именски простори",
        "default": "подразумевано",
        "prefs-files": "Датотеке",
-       "prefs-custom-css": "прилагођени Це-Ес-Ес",
+       "prefs-custom-css": "прилагођени CSS",
        "prefs-custom-json": "Прилагођени ЈСОН",
-       "prefs-custom-js": "прилагођени јаваскрипт",
-       "prefs-common-config": "Дељени Це-Ес-Ес/ЈСОН/јаваскрипт за све теме:",
+       "prefs-custom-js": "прилагођени JavaScript",
+       "prefs-common-config": "Дељени CSS/JSON/JavaScript за све теме:",
        "prefs-reset-intro": "Можете користити ову страницу да поново поставите своја подешавања на подразумеване вредности сајта.\nОво се не може опозвати.",
        "prefs-emailconfirm-label": "Потврда е-поште:",
        "youremail": "Е-пошта:",
        "badsig": "Неважећи необрађени потпис.\nПроверите HTML тагове.",
        "badsiglength": "Ваш потпис је предугачак.\nНе сме бити дужи од $1 {{PLURAL:$1|знака|знака|знакова}}.",
        "yourgender": "Како желите да се представите?",
-       "gender-unknown": "Кад вас спомиње, софтвер ће користити родно неутралне речи кад год је то могуће",
+       "gender-unknown": "Кад вас помиње, софтвер ће користити родно неутралне речи кад год је то могуће",
        "gender-male": "Он уређује вики странице",
        "gender-female": "Она уређује вики странице",
-       "prefs-help-gender": "Ð\9fоÑ\81Ñ\82авÑ\99аÑ\9aе Ð¾Ð²Ð¾Ð³ Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа Ñ\98е Ð½ÐµÐ¾Ð±Ð°Ð²ÐµÐ·Ð½Ð¾.\nСоÑ\84Ñ\82веÑ\80 ÐºÐ¾Ñ\80иÑ\81Ñ\82и Ð´Ð°Ñ\82Ñ\83 Ð²Ñ\80едноÑ\81Ñ\82 Ð´Ð° Ð±Ð¸ Ð²Ð°Ð¼ Ñ\81е Ð¾Ð±Ñ\80аÑ\82ио Ð¸ Ñ\81поменуо вас другима користећи одговарајући граматички род.\nОва информација ће бити јавна.",
+       "prefs-help-gender": "Ð\9fоÑ\81Ñ\82авÑ\99аÑ\9aе Ð¾Ð²Ð¾Ð³ Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа Ñ\98е Ð¾Ð¿Ñ\86ионално.\nСоÑ\84Ñ\82веÑ\80 ÐºÐ¾Ñ\80иÑ\81Ñ\82и Ð´Ð°Ñ\82Ñ\83 Ð²Ñ\80едноÑ\81Ñ\82 Ð´Ð° Ð±Ð¸ Ð²Ð°Ð¼ Ñ\81е Ð¾Ð±Ñ\80аÑ\82ио Ð¸ поменуо вас другима користећи одговарајући граматички род.\nОва информација ће бити јавна.",
        "email": "Е-пошта",
-       "prefs-help-realname": "Ð\9fÑ\80аво Ð¸Ð¼Ðµ Ñ\98е Ð½ÐµÐ¾Ð±Ð°Ð²ÐµÐ·но.\nАко је пружено, биће коришћено за приписивање вашег рада.",
-       "prefs-help-email": "Ð\90дÑ\80еÑ\81а Ðµ-поÑ\88Ñ\82е Ñ\98е Ð½ÐµÐ¾Ð±Ð°Ð²ÐµÐ·на, али је потребна за ресетовање лозинке, ако је заборавите.",
-       "prefs-help-email-others": "ТакоÑ\92е Ð¼Ð¾Ð¶ÐµÑ\82е Ð¾Ð°Ð±Ñ\80аÑ\82и Ð´Ð° Ð´Ð¾Ð¿Ñ\83Ñ\81Ñ\82иÑ\82е Ð´Ñ\80Ñ\83гима Ð´Ð° Ð²Ð°Ñ\81 ÐºÐ¾Ð½Ñ\82акÑ\82иÑ\80аÑ\98Ñ\83 Ð¿Ñ\80еко Ðµ-поÑ\88Ñ\82е Ð¿Ñ\83Ñ\82ем Ð²ÐµÐ·Ðµ Ð½Ð° Ð²Ð°Ñ\88оÑ\98 ÐºÐ¾Ñ\80иÑ\81ниÑ\87коÑ\98 Ñ\81Ñ\82Ñ\80аниÑ\86и Ð¸Ð»Ð¸ Ñ\81Ñ\82Ñ\80аниÑ\86и Ð·Ð° Ñ\80азговоÑ\80.\nÐ\92аÑ\88а Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е неће бити приказана другим корисницима који вас контактирају.",
-       "prefs-help-email-required": "Ð\9fоÑ\82Ñ\80ебна Ñ\98е Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е.",
+       "prefs-help-realname": "Ð\9fÑ\80аво Ð¸Ð¼Ðµ Ñ\98е Ð¾Ð¿Ñ\86ионално.\nАко је пружено, биће коришћено за приписивање вашег рада.",
+       "prefs-help-email": "Ð\95-адÑ\80еÑ\81а Ñ\98е Ð¾Ð¿Ñ\86ионална, али је потребна за ресетовање лозинке, ако је заборавите.",
+       "prefs-help-email-others": "ТакоÑ\92е Ð¼Ð¾Ð¶ÐµÑ\82е Ð¾Ð´Ð°Ð±Ñ\80аÑ\82и Ð´Ð° Ð´Ð¾Ð¿Ñ\83Ñ\81Ñ\82иÑ\82е Ð´Ñ\80Ñ\83гима Ð´Ð° Ð²Ð°Ñ\81 ÐºÐ¾Ð½Ñ\82акÑ\82иÑ\80аÑ\98Ñ\83 Ð¿Ñ\83Ñ\82ем Ðµ-поÑ\88Ñ\82е Ð¿Ñ\80еко Ð²ÐµÐ·Ðµ Ð½Ð° Ð²Ð°Ñ\88оÑ\98 ÐºÐ¾Ñ\80иÑ\81ниÑ\87коÑ\98 Ñ\81Ñ\82Ñ\80аниÑ\86и Ð¸Ð»Ð¸ Ñ\81Ñ\82Ñ\80аниÑ\86и Ð·Ð° Ñ\80азговоÑ\80.\nÐ\92аÑ\88а Ðµ-адÑ\80еÑ\81а неће бити приказана другим корисницима који вас контактирају.",
+       "prefs-help-email-required": "Ð\9fоÑ\82Ñ\80ебна Ñ\98е Ðµ-адÑ\80еÑ\81а.",
        "prefs-info": "Основне информације",
        "prefs-i18n": "Интернационализација",
        "prefs-signature": "Потпис",
        "right-editmyuserjs": "уређивање сопствених јаваскрипт датотека",
        "right-viewmywatchlist": "преглед сопственог списка надгледања",
        "right-editmywatchlist": "уређивање сопственог списка надгледања; неке предузете радње ће свеједно додати странице на списак и без овог права",
-       "right-viewmyprivateinfo": "пÑ\80еглед Ñ\81опÑ\81Ñ\82вениÑ\85 Ð¿Ñ\80иваÑ\82ниÑ\85 Ð¿Ð¾Ð´Ð°Ñ\82ака (нпÑ\80. Ð°Ð´Ñ\80еÑ\81е Ðµ-поÑ\88Ñ\82е, Ð¿Ñ\80аво Ð¸Ð¼Ðµ)",
-       "right-editmyprivateinfo": "Ñ\83Ñ\80еÑ\92иваÑ\9aе Ñ\81опÑ\81Ñ\82вениÑ\85 Ð¿Ñ\80иваÑ\82ниÑ\85 Ð¿Ð¾Ð´Ð°Ñ\82ака (нпÑ\80. Ð°Ð´Ñ\80еÑ\81е Ðµ-поÑ\88Ñ\82е, правог имена)",
+       "right-viewmyprivateinfo": "пÑ\80еглед Ñ\81опÑ\81Ñ\82вениÑ\85 Ð¿Ñ\80иваÑ\82ниÑ\85 Ð¿Ð¾Ð´Ð°Ñ\82ака (нпÑ\80. Ðµ-адÑ\80еÑ\81е, Ð¿Ñ\80авог Ð¸Ð¼ÐµÐ½Ð°)",
+       "right-editmyprivateinfo": "Ñ\83Ñ\80еÑ\92иваÑ\9aе Ñ\81опÑ\81Ñ\82вениÑ\85 Ð¿Ñ\80иваÑ\82ниÑ\85 Ð¿Ð¾Ð´Ð°Ñ\82ака (нпÑ\80. Ðµ-адÑ\80еÑ\81е, правог имена)",
        "right-editmyoptions": "уређивање сопствених подешавања",
        "right-rollback": "брзо враћање измена последњег корисника који је мењао одређену страницу",
        "right-markbotedits": "означавање враћених измена као измене бота",
        "upload-preferred": "Препоручени {{PLURAL:$2|тип|типови}} датотека: $1.",
        "upload-prohibited": "Забрањени {{PLURAL:$2|тип|типови}} датотека: $1.",
        "uploadlogpage": "Дневник отпремања",
-       "uploadlogpagetext": "Ð\98Ñ\81под Ñ\98е Ñ\81пиÑ\81ак Ð½ÐµÐ´Ð°Ð²Ð½Ð¸Ñ\85 Ð¾Ñ\82пÑ\80емаÑ\9aа.\nÐ\9fогледаÑ\98Ñ\82е [[Special:NewFiles|галеÑ\80иÑ\98Ñ\83 Ð½Ð¾Ð²Ð¸Ñ\85 Ð´Ð°Ñ\82оÑ\82ека]] Ð·Ð° Ð»ÐµÐ¿Ñ\88и преглед.",
+       "uploadlogpagetext": "Ð\98Ñ\81под Ñ\81е Ð½Ð°Ð»Ð°Ð·Ð¸ Ð»Ð¸Ñ\81Ñ\82а Ð½Ð°Ñ\98новиÑ\98иÑ\85 Ð¾Ñ\82пÑ\80емаÑ\9aа.\nÐ\9fогледаÑ\98Ñ\82е [[Special:NewFiles|галеÑ\80иÑ\98Ñ\83 Ð½Ð¾Ð²Ð¸Ñ\85 Ð´Ð°Ñ\82оÑ\82ека]] Ð·Ð° Ð²Ð¸Ð·Ñ\83елниÑ\98и преглед.",
        "filename": "Назив датотеке",
        "filedesc": "Опис измене",
        "fileuploadsummary": "Опис измене:",
        "upload-file-error-text": "Дошло је до унутрашње грешке при отварању привремене датотеке на серверу.\nКонтактирајте [[Special:ListUsers/sysop|администратора]].",
        "upload-misc-error": "Непозната грешка при слању датотеке",
        "upload-misc-error-text": "Дошло је до непознате грешке при отпремању датотеке.\nПроверите да ли је УРЛ важећи и доступан, па покушајте поново.\nАко се проблем буде поново јавио, обратите се [[Special:ListUsers/sysop|администратору]].",
-       "upload-too-many-redirects": "Адреса садржи превише преусмерења",
+       "upload-too-many-redirects": "URL садржи превише преусмерења",
        "upload-http-error": "Дошло је до HTTP грешке: $1",
        "upload-copy-upload-invalid-domain": "Примерци отпремања нису доступни на овом домену.",
        "upload-dialog-disabled": "Отпремања датотека коришћењем овог дијалога су онемогућена на овом викију.",
        "uploadstash-nofiles": "Немате сакривене датотеке.",
        "uploadstash-badtoken": "Извршавање ове радње није успело, разлог томе може бити истек времена за уређивање. Покушајте поново.",
        "uploadstash-errclear": "Брисање датотека није успело.",
-       "uploadstash-refresh": "Освежи списак датотека",
+       "uploadstash-refresh": "Освежи листу датотека",
        "uploadstash-thumbnail": "прикажи сличицу",
        "uploadstash-exception": "Не могу сачувати датотеку у складиште ($1): „$2“.",
        "uploadstash-bad-path": "Путања не постоји.",
        "img-auth-public": "Сврха img_auth.php је да прослеђује датотеке из приватних викија.\nОвај вики је постављен као јавни.\nРади сигурности, img_auth.php је онемогућен.",
        "img-auth-noread": "Корисник нема приступ за читање „$1“.",
        "http-invalid-url": "Неважећи УРЛ: $1",
-       "http-invalid-scheme": "Адресе са шемом „$1“ нису подржане.",
+       "http-invalid-scheme": "URL-ови са шемом „$1” нису подржани.",
        "http-request-error": "HTTP захтев није прошао због непознате грешке.",
        "http-read-error": "HTTP грешка при читању.",
        "http-timed-out": "Захтев HTTP је истекао.",
        "listfiles_search_for": "Претражи име медија:",
        "listfiles-userdoesnotexist": "Кориснички налог „$1“ није отворен.",
        "imgfile": "датотека",
-       "listfiles": "СпиÑ\81ак датотека",
+       "listfiles": "Ð\9bиÑ\81Ñ\82а датотека",
        "listfiles_thumb": "Сличица",
        "listfiles_date": "Датум",
        "listfiles_name": "Назив",
        "filehist-comment": "Коментар",
        "imagelinks": "Употреба датотеке",
        "linkstoimage": "{{PLURAL:$1|Следећа страница користи|$1 следеће странице користе|$1 следећих страница користи}} ову датотеку:",
-       "linkstoimage-more": "Ð\92иÑ\88е Ð¾Ð´ $1 {{PLURAL:$1|Ñ\81Ñ\82Ñ\80аниÑ\86а ÐºÐ¾Ñ\80иÑ\81Ñ\82и|Ñ\81Ñ\82Ñ\80аниÑ\86е ÐºÐ¾Ñ\80иÑ\81Ñ\82е|Ñ\81Ñ\82Ñ\80аниÑ\86а ÐºÐ¾Ñ\80иÑ\81Ñ\82и}} Ð¾Ð²Ñ\83 Ð´Ð°Ñ\82оÑ\82екÑ\83.\nСледеÑ\9bи Ñ\81пиÑ\81ак Ð¿Ñ\80иказÑ\83Ñ\98е {{PLURAL:$1|пÑ\80вÑ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 ÐºÐ¾Ñ\98а ÐºÐ¾Ñ\80иÑ\81Ñ\82и|пÑ\80ве $1 Ñ\81Ñ\82Ñ\80аниÑ\86е ÐºÐ¾Ñ\98е ÐºÐ¾Ñ\80иÑ\81Ñ\82е|пÑ\80виÑ\85 $1 Ñ\81Ñ\82Ñ\80аниÑ\86а ÐºÐ¾Ñ\98е ÐºÐ¾Ñ\80иÑ\81Ñ\82е}} Ñ\81амо Ð¾Ð²Ñ\83 Ð´Ð°Ñ\82оÑ\82екÑ\83.\nÐ\94оÑ\81Ñ\82Ñ\83пан Ñ\98е Ð¸ [[Special:WhatLinksHere/$2|поÑ\82пÑ\83ни Ñ\81пиÑ\81ак]].",
+       "linkstoimage-more": "Ð\92иÑ\88е Ð¾Ð´ $1 {{PLURAL:$1|Ñ\81Ñ\82Ñ\80аниÑ\86а ÐºÐ¾Ñ\80иÑ\81Ñ\82и|Ñ\81Ñ\82Ñ\80аниÑ\86е ÐºÐ¾Ñ\80иÑ\81Ñ\82е|Ñ\81Ñ\82Ñ\80аниÑ\86а ÐºÐ¾Ñ\80иÑ\81Ñ\82и}} Ð¾Ð²Ñ\83 Ð´Ð°Ñ\82оÑ\82екÑ\83.\nСледеÑ\9bа Ð»Ð¸Ñ\81Ñ\82а Ð¿Ñ\80иказÑ\83Ñ\98е {{PLURAL:$1|пÑ\80вÑ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 ÐºÐ¾Ñ\98а ÐºÐ¾Ñ\80иÑ\81Ñ\82и|пÑ\80ве $1 Ñ\81Ñ\82Ñ\80аниÑ\86е ÐºÐ¾Ñ\98е ÐºÐ¾Ñ\80иÑ\81Ñ\82е|пÑ\80виÑ\85 $1 Ñ\81Ñ\82Ñ\80аниÑ\86а ÐºÐ¾Ñ\98е ÐºÐ¾Ñ\80иÑ\81Ñ\82е}} Ñ\81амо Ð¾Ð²Ñ\83 Ð´Ð°Ñ\82оÑ\82екÑ\83.\nÐ\94оÑ\81Ñ\82Ñ\83пна Ñ\98е Ð¸ [[Special:WhatLinksHere/$2|поÑ\82пÑ\83на Ð»Ð¸Ñ\81Ñ\82а]].",
        "nolinkstoimage": "Нема страница које користе ову датотеку.",
        "morelinkstoimage": "Погледајте [[Special:WhatLinksHere/$1|више веза]] до ове датотеке.",
        "linkstoimage-redirect": "$1 (преусмерење датотеке) $2",
        "mimetype": "МИМЕ врста:",
        "download": "преузми",
        "unwatchedpages": "Ненадгледане странице",
-       "listredirects": "СпиÑ\81ак преусмерења",
-       "listduplicatedfiles": "СпиÑ\81ак датотека са дупликатима",
-       "listduplicatedfiles-summary": "Ово је списак датотека које су дупликат неких других датотека. Само локалне датотеке су приказане.",
+       "listredirects": "Ð\9bиÑ\81Ñ\82а преусмерења",
+       "listduplicatedfiles": "Ð\9bиÑ\81Ñ\82а датотека са дупликатима",
+       "listduplicatedfiles-summary": "Ово је листа датотека чија је најновија верзија дупликат неких других датотека. Само локалне датотеке су приказане.",
        "listduplicatedfiles-entry": "[[:File:$1|$1]] има [[$3|{{PLURAL:$2|један дупликат|$2 дупликата}}]].",
        "unusedtemplates": "Некоришћени шаблони",
        "unusedtemplatestext": "Ова страница наводи све странице у именском простору {{ns:template}} које нису укључене ни на једној другој страници.\nПре брисања проверите да ли друге странице воде до тих шаблона.",
        "unusedimages": "Некоришћене датотеке",
        "wantedcategories": "Тражене категорије",
        "wantedpages": "Тражене странице",
-       "wantedpages-summary": "СпиÑ\81ак Ð½ÐµÐ¿Ð¾Ñ\81Ñ\82оÑ\98еÑ\9bиÑ\85 Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\81а Ð½Ð°Ñ\98виÑ\88е Ð²ÐµÐ·Ð° Ð´Ð¾ Ñ\9aиÑ\85, Ð½Ð° Ð¾Ð²Ð¾Ð¼ Ñ\81пиÑ\81кÑ\83 Ñ\81е Ð½Ðµ Ð½Ð°Ð»Ð°Ð·Ðµ Ñ\81Ñ\82Ñ\80аниÑ\86е Ð´Ð¾ ÐºÐ¾Ñ\98иÑ\85 Ð²Ð¾Ð´Ðµ Ð¿Ñ\80еÑ\83Ñ\81меÑ\80еÑ\9aа. Ð\97а Ñ\81пиÑ\81ак Ð¿Ð¾ÐºÐ²Ð°Ñ\80ениÑ\85 Ð¿Ñ\80еÑ\83Ñ\81меÑ\80еÑ\9aа Ð¿Ð¾Ð³Ð»ÐµÐ´Ð°Ñ\98Ñ\82е [[{{#special:BrokenRedirects}}|Ñ\81пиÑ\81ак покварених преусмерења]].",
+       "wantedpages-summary": "Ð\9bиÑ\81Ñ\82а Ð½ÐµÐ¿Ð¾Ñ\81Ñ\82оÑ\98еÑ\9bиÑ\85 Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\81а Ð½Ð°Ñ\98виÑ\88е Ð²ÐµÐ·Ð° Ð½Ð° Ñ\9aиÑ\85, Ð½Ð° Ð¾Ð²Ð¾Ð¼ Ñ\81пиÑ\81кÑ\83 Ñ\81е Ð½Ðµ Ð½Ð°Ð»Ð°Ð·Ðµ Ñ\81Ñ\82Ñ\80аниÑ\86е Ð´Ð¾ ÐºÐ¾Ñ\98иÑ\85 Ð²Ð¾Ð´Ðµ Ð¿Ñ\80еÑ\83Ñ\81меÑ\80еÑ\9aа. Ð\97а Ñ\81пиÑ\81ак Ð¿Ð¾ÐºÐ²Ð°Ñ\80ениÑ\85 Ð¿Ñ\80еÑ\83Ñ\81меÑ\80еÑ\9aа, Ð¿Ð¾Ð³Ð»ÐµÐ´Ð°Ñ\98Ñ\82е [[{{#special:BrokenRedirects}}|лиÑ\81Ñ\82Ñ\83 покварених преусмерења]].",
        "wantedpages-badtitle": "Невалидан наслов у скупу резултата: $1",
        "wantedfiles": "Тражене датотеке",
        "wantedfiletext-cat": "Следеће датотеке се користе, али не постоје. Датотеке из других ризница могу бити наведене иако не постоје. Такве датотеке ће бити <del>поништене</del> са списка. Поред тога, странице које садрже непостојеће датотеке се налазе [[:$1|овде]].",
        "protectedpages": "Заштићене странице",
        "protectedpages-filters": "Филтери:",
        "protectedpages-indef": "Само неограничене заштите",
-       "protectedpages-summary": "Ð\9dа Ð¾Ð²Ð¾Ñ\98 Ñ\81Ñ\82Ñ\80аниÑ\86и Ñ\81е Ð½Ð°Ð»Ð°Ð·Ð¸ Ñ\81пиÑ\81ак Ð¿Ð¾Ñ\81Ñ\82оÑ\98еÑ\9bиÑ\85 Ñ\81Ñ\82Ñ\80аниÑ\86а ÐºÐ¾Ñ\98е Ñ\81Ñ\83 Ñ\82Ñ\80енÑ\83Ñ\82но Ð·Ð°Ñ\88Ñ\82иÑ\9bене. Ð\97а Ñ\81пиÑ\81ак наслова који су заштићени од прављења, погледајте [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]].",
+       "protectedpages-summary": "Ð\9eва Ñ\81Ñ\82Ñ\80аниÑ\86а Ð½Ð°Ð²Ð¾Ð´Ð¸ Ð¿Ð¾Ñ\81Ñ\82оÑ\98еÑ\9bе Ñ\81Ñ\82Ñ\80аниÑ\86е ÐºÐ¾Ñ\98е Ñ\81Ñ\83 Ñ\82Ñ\80енÑ\83Ñ\82но Ð·Ð°Ñ\88Ñ\82иÑ\9bене. Ð\97а Ð»Ð¸Ñ\81Ñ\82Ñ\83 наслова који су заштићени од прављења, погледајте [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]].",
        "protectedpages-cascade": "Само преносиве заштите",
        "protectedpages-noredirect": "Сакриј преусмерења",
        "protectedpagesempty": "Нема заштићених страница с овим параметрима.",
        "protectedpages-unknown-timestamp": "нема",
        "protectedpages-unknown-performer": "нема",
        "protectedtitles": "Заштићени наслови",
-       "protectedtitles-summary": "Ð\9dа Ð¾Ð²Ð¾Ñ\98 Ñ\81Ñ\82Ñ\80аниÑ\86и Ñ\81е Ð½Ð°Ð»Ð°Ð·Ðµ Ð½Ð°Ñ\81лови ÐºÐ¾Ñ\98и Ñ\81Ñ\83 Ñ\82Ñ\80енÑ\83Ñ\82но Ð·Ð°Ñ\88Ñ\82иÑ\9bени Ð¾Ð´ Ð¿Ñ\80авÑ\99еÑ\9aа. Ð\97а Ñ\81пиÑ\81ак постојећих страница које су заштићене, погледајте [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
+       "protectedtitles-summary": "Ð\9eва Ñ\81Ñ\82Ñ\80аниÑ\86а Ð½Ð°Ð²Ð¾Ð´Ð¸ Ð½Ð°Ñ\81лове ÐºÐ¾Ñ\98и Ñ\81Ñ\83 Ñ\82Ñ\80енÑ\83Ñ\82но Ð·Ð°Ñ\88Ñ\82иÑ\9bени Ð¾Ð´ Ð¿Ñ\80авÑ\99еÑ\9aа. Ð\97а Ð»Ð¸Ñ\81Ñ\82Ñ\83 постојећих страница које су заштићене, погледајте [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Нема заштићених наслова с овим параметрима.",
        "protectedtitles-submit": "Прикажи наслове",
-       "listusers": "СпиÑ\81ак корисника",
+       "listusers": "Ð\9bиÑ\81Ñ\82а корисника",
        "listusers-editsonly": "Прикажи само кориснике који су уређивали",
        "listusers-temporarygroupsonly": "Прикажи само кориснике у привременим корисничким групама",
        "listusers-creationsort": "Сортирај по датуму прављења",
        "booksources-search-legend": "Претражи штампане изворе",
        "booksources-isbn": "ISBN:",
        "booksources-search": "Претражи",
-       "booksources-text": "Испод се налази списак веза ка сајтовима који се баве продајом нових и половних књига, а који би могли имати додатне податке о књигама које тражите:",
+       "booksources-text": "Испод се налази листа веза на друге сајтове који се баве продајом нових и половних књига, а који би могли имати додатне податке о књигама које тражите:",
        "booksources-invalid-isbn": "Наведени ISBN број није валидан. Проверите да није дошло до грешке при копирању из првобитног извора.",
        "magiclink-tracking-rfc": "Странице са чаробним RFC везама",
        "magiclink-tracking-pmid": "Странице са чаробним PMID везама",
        "listusers-submit": "Прикажи",
        "listusers-noresult": "Корисник није пронађен.",
        "listusers-blocked": "({{GENDER:$1|блокиран|блокирана|блокиран}})",
-       "activeusers": "СпиÑ\81ак активних корисника",
-       "activeusers-intro": "Ово је списак корисника који су били активни {{PLURAL:$1|1=претходни дан|у последња $1 дана|у последњих $1 дана}}.",
+       "activeusers": "Ð\9bиÑ\81Ñ\82а активних корисника",
+       "activeusers-intro": "Ово је листа корисника који су били активни {{PLURAL:$1|1=претходни дан|у последња $1 дана|у последњих $1 дана}}.",
        "activeusers-count": "$1 {{PLURAL:$1|радња|радње|радњи}} {{PLURAL:$3|претходни дан|у последња $3 дана|у последњих $3 дана}}",
        "activeusers-from": "Прикажи кориснике почев од:",
        "activeusers-groups": "Прикажи кориснике који су чланови група:",
        "activeusers-noresult": "Корисник није пронађен.",
        "activeusers-submit": "Прикажи активне кориснике",
        "listgrouprights": "Права корисничких група",
-       "listgrouprights-summary": "Следи списак корисничких група на овом викију, заједно с правима приступа.\nПогледајте [[{{MediaWiki:Listgrouprights-helppage}}|више детаља]] о појединачним правима.",
+       "listgrouprights-summary": "Следи листа корисничких група дефинисаних на овом викију, заједно са повезаним правима приступа.\nМожда постоји [[{{MediaWiki:Listgrouprights-helppage}}|више информација]] о појединачним правима.",
        "listgrouprights-key": "Легенда:\n* <span class=\"listgrouprights-granted\">Додељено право</span>\n* <span class=\"listgrouprights-revoked\">Укинуто право</span>",
        "listgrouprights-group": "Група",
        "listgrouprights-rights": "Права",
        "listgrants-rights": "Права",
        "listgrants-grant-display": "$1 <code>($2)</code>",
        "trackingcategories": "Категорије за праћење",
-       "trackingcategories-summary": "Ова посебна страница је списак категорија које су део Медијавикија, оне се аутоматски ажурирају и њихови називи се могу мењати уређивањем системских порука у именском простору {{ns:8}}.",
+       "trackingcategories-summary": "Ова страница наводи категорије за праћење које аутоматски попуњава софтвер Медијавики. Њихова имена се могу променити изменом одговарајућих системских порука у именском простору {{ns:8}}.",
        "trackingcategories-msg": "Категорије за праћење",
        "trackingcategories-name": "Име поруке",
        "trackingcategories-desc": "Које странице се налазе у категорији",
        "trackingcategories-nodesc": "Опис није доступан.",
        "trackingcategories-disabled": "Категорија је онемогућена",
        "mailnologin": "Нема адресе за слање",
-       "mailnologintext": "Ð\9cоÑ\80аÑ\82е Ð´Ð° Ñ\81е [[Special:UserLogin|пÑ\80иÑ\98авиÑ\82е]] Ð¸ Ð¸Ð¼Ð°Ñ\82е Ð²Ð°Ð¶ÐµÑ\9bи Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е у [[Special:Preferences|подешавањима]] да бисте слали е-поруке другим корисницима.",
+       "mailnologintext": "Ð\9cоÑ\80аÑ\82е Ð´Ð° Ñ\81е [[Special:UserLogin|пÑ\80иÑ\98авиÑ\82е]] Ð¸ Ð¸Ð¼Ð°Ñ\82е Ð²Ð°Ð¶ÐµÑ\9bи Ðµ-адÑ\80еÑ\81Ñ\83 у [[Special:Preferences|подешавањима]] да бисте слали е-поруке другим корисницима.",
        "emailuser": "Пошаљи е-поруку овом кориснику/ци",
        "emailuser-title-target": "Слање е-поруке {{GENDER:$1|кориснику|корисници|кориснику/ци}}",
-       "emailuser-title-notarget": "СлаÑ\9aе Ð¸Ð¼ÐµÑ\98ла кориснику",
-       "emailpagetext": "Ð\9cожеÑ\82е Ð´Ð° ÐºÐ¾Ñ\80иÑ\81Ñ\82иÑ\82е Ð´Ð¾Ñ\9aи Ð¾Ð±Ñ\80азаÑ\86 Ð´Ð° Ð¿Ð¾Ñ\88аÑ\99еÑ\82е Ð¸Ð¼ÐµÑ\98л {{GENDER:$1|овом ÐºÐ¾Ñ\80иÑ\81никÑ\83|овоÑ\98 ÐºÐ¾Ñ\80иÑ\81ниÑ\86и}}.\nÐ\98меÑ\98л ÐºÐ¾Ñ\98и Ñ\81Ñ\82е Ñ\83нели Ñ\83 Ð²Ð°Ñ\88им [[Special:Preferences|подеÑ\88аваÑ\9aима]] Ñ\9bе Ñ\81е Ð¿Ñ\80иказаÑ\82и Ñ\83 Ð¿Ð¾Ñ\99Ñ\83 â\80\9eÐ\9eдâ\80\9c, тако да ће прималац моћи да вам одговори директно.",
+       "emailuser-title-notarget": "СлаÑ\9aе Ðµ-поÑ\80Ñ\83ке кориснику",
+       "emailpagetext": "Ð\9cожеÑ\82е Ð´Ð° ÐºÐ¾Ñ\80иÑ\81Ñ\82иÑ\82е Ð´Ð¾Ñ\9aи Ð¾Ð±Ñ\80азаÑ\86 Ð´Ð° Ð¿Ð¾Ñ\88аÑ\99еÑ\82е Ðµ-поÑ\80Ñ\83кÑ\83 {{GENDER:$1|овом ÐºÐ¾Ñ\80иÑ\81никÑ\83|овоÑ\98 ÐºÐ¾Ñ\80иÑ\81ниÑ\86и}}.\nÐ\95-адÑ\80еÑ\81а ÐºÐ¾Ñ\98и Ñ\81Ñ\82е Ñ\83нели Ñ\83 Ð²Ð°Ñ\88им [[Special:Preferences|подеÑ\88аваÑ\9aима]] Ñ\9bе Ñ\81е Ð¿Ñ\80иказаÑ\82и Ñ\83 Ð¿Ð¾Ñ\99Ñ\83 â\80\9eÐ\9eдâ\80\9d, тако да ће прималац моћи да вам одговори директно.",
        "defemailsubject": "{{SITENAME}} — е-порука од {{GENDER:$1|корисника|кориснице|корисника/це}} „$1”",
        "usermaildisabled": "Корисничка е-пошта је онемогућена",
        "usermaildisabledtext": "Не можете да шаљете е-поруке другим корисницима на овом викију",
-       "noemailtitle": "Ð\9dема Ð°Ð´Ñ\80еÑ\81е Ðµ-поÑ\88Ñ\82е",
-       "noemailtext": "Ð\9eваÑ\98 ÐºÐ¾Ñ\80иÑ\81ник Ð½Ð¸Ñ\98е Ð½Ð°Ð²ÐµÐ¾ Ð²Ð°Ð¶ÐµÑ\9bÑ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е.",
+       "noemailtitle": "Ð\9dема Ðµ-адÑ\80еÑ\81е",
+       "noemailtext": "Ð\9eваÑ\98 ÐºÐ¾Ñ\80иÑ\81ник Ð½Ð¸Ñ\98е Ð½Ð°Ð²ÐµÐ¾ Ð²Ð°Ð¶ÐµÑ\9bÑ\83 Ðµ-адÑ\80еÑ\81Ñ\83.",
        "nowikiemailtext": "Овај корисник је одабрао да не прима е-поруке од других корисника.",
        "emailnotarget": "Непостојеће или наважеће корисничко име примаоца.",
        "emailtarget": "Унос корисничког имена примаоца",
        "emailccsubject": "Копија поруке кориснику/ци $1: $2",
        "emailsent": "Е-порука је послата",
        "emailsenttext": "Ваша е-порука је послата.",
-       "emailuserfooter": "Ð\9eвÑ\83 Ðµ-поÑ\80Ñ\83кÑ\83 Ñ\98е {{GENDER:$1|поÑ\81лао|поÑ\81лала|поÑ\81лао/ла}} $1 {{GENDER:$2|коÑ\80иÑ\81никÑ\83|коÑ\80иÑ\81ниÑ\86и|коÑ\80иÑ\81никÑ\83\86и}} $2 Ð¿Ð¾Ð¼Ð¾Ñ\9bÑ\83 Ð¾Ð¿Ñ\86иÑ\98е â\80\9e{{int:emailuser}}â\80\9d Ð½Ð° Ð¿Ñ\80оÑ\98екÑ\82Ñ\83 {{SITENAME}}. Ð\90ко Ð¾Ð´Ð³Ð¾Ð²Ð¾Ñ\80иÑ\82е Ð½Ð° Ð¾Ð²Ñ\83 Ðµ-поÑ\80Ñ\83кÑ\83, {{GENDER:$2|ваÑ\88а}} Ðµ-поÑ\80Ñ\83ка Ð±Ð¸Ñ\9bе Ð½ÐµÐ¿Ð¾Ñ\81Ñ\80едно Ð¿Ñ\80оÑ\81леÑ\92ена ÐºÐ° {{GENDER:$1|оÑ\80игиналном Ð¿Ð¾Ñ\88иÑ\99аоÑ\86Ñ\83}}, Ñ\87име Ñ\9bеÑ\82е {{GENDER:$2|мÑ\83\98оÑ\98}} Ð¾Ñ\82кÑ\80иÑ\82и {{GENDER:$2|адÑ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е}}.",
+       "emailuserfooter": "Ð\9eвÑ\83 Ðµ-поÑ\80Ñ\83кÑ\83 Ñ\98е {{GENDER:$1|поÑ\81лао|поÑ\81лала|поÑ\81лао/ла}} $1 {{GENDER:$2|коÑ\80иÑ\81никÑ\83|коÑ\80иÑ\81ниÑ\86и|коÑ\80иÑ\81никÑ\83\86и}} $2 Ð¿Ð¾Ð¼Ð¾Ñ\9bÑ\83 Ð¾Ð¿Ñ\86иÑ\98е â\80\9e{{int:emailuser}}â\80\9d Ð½Ð° Ð¿Ñ\80оÑ\98екÑ\82Ñ\83 {{SITENAME}}. Ð\90ко Ð¾Ð´Ð³Ð¾Ð²Ð¾Ñ\80иÑ\82е Ð½Ð° Ð¾Ð²Ñ\83 Ðµ-поÑ\80Ñ\83кÑ\83, {{GENDER:$2|ваÑ\88а}} Ðµ-поÑ\80Ñ\83ка Ð±Ð¸Ñ\9bе Ð½ÐµÐ¿Ð¾Ñ\81Ñ\80едно Ð¿Ñ\80оÑ\81леÑ\92ена ÐºÐ° {{GENDER:$1|оÑ\80игиналном Ð¿Ð¾Ñ\88иÑ\99аоÑ\86Ñ\83}}, Ñ\87име Ñ\9bеÑ\82е {{GENDER:$2|мÑ\83\98оÑ\98}} Ð¾Ñ\82кÑ\80иÑ\82и {{GENDER:$2|е-адÑ\80еÑ\81Ñ\83}}.",
        "usermessage-summary": "Слање системске поруке.",
        "usermessage-editor": "Уређивач системских порука",
        "usermessage-template": "MediaWiki:UserMessage",
        "addwatch": "Додавање на списак надгледања",
        "addedwatchtext": "Страница „[[:$1]]“ и њена страница за разговор је додата на ваш [[Special:Watchlist|списак надгледања]].",
        "addedwatchtext-talk": "Страница „[[:$1]]” и њена придружена страница је додата на ваш [[Special:Watchlist|списак надгледања]]",
-       "addedwatchtext-short": "СÑ\82Ñ\80аниÑ\86а â\80\9e$1â\80\9c Ñ\98е Ð´Ð¾Ð´Ð°Ñ\82а Ð½Ð° Ð²Ð°Ñ\88 списак надгледања.",
+       "addedwatchtext-short": "СÑ\82Ñ\80аниÑ\86а â\80\9e$1â\80\9d Ñ\98е Ð´Ð¾Ð´Ð°Ñ\82а Ð½Ð° списак надгледања.",
        "removewatch": "Уклони са списка надгледања",
        "removedwatchtext": "Страница „[[:$1]]“ и њена страница за разговор је уклоњена са вашег [[Special:Watchlist|списка надгледања]].",
        "removedwatchtext-talk": "\"[[:$1]]\" и његове повезане странице су уклоњене са вашег [[Special:Watchlist|списка надгледања]].",
        "enotif_lastvisited": "За све промене од последње посете, погледајте $1.",
        "enotif_lastdiff": "Да бисте видели ову промену, погледајте $1.",
        "enotif_anon_editor": "анониман корисник $1",
-       "enotif_body": "Ð\9fоÑ\88Ñ\82овани $WATCHINGUSERNAME,\n \t\n$PAGEINTRO $NEWPAGE\n\nРезиме Ñ\83Ñ\80еÑ\92иваÑ\87а: $PAGESUMMARY $PAGEMINOREDIT\n\nÐ\9aонÑ\82акÑ\82:\nмеÑ\98л: $PAGEEDITOR_EMAIL\nвики: $PAGEEDITOR_WIKI\n\nÐ\9dеÑ\9bе Ð±Ð¸Ñ\82и Ð´Ñ\80Ñ\83гиÑ\85 Ð¾Ð±Ð°Ð²ÐµÑ\88Ñ\82еÑ\9aа Ñ\83 Ñ\81лÑ\83Ñ\87аÑ\98Ñ\83 Ð´Ð°Ñ\99иÑ\85 Ð¸Ð·Ð¼ÐµÐ½Ð° Ñ\83колико Ð½Ðµ Ð¿Ð¾Ñ\81еÑ\82иÑ\82е Ð¾Ð²Ñ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 ÐºÐ°Ð´Ð° Ñ\81Ñ\82е Ð¿Ñ\80иÑ\98авÑ\99ени.\nÐ\9cожеÑ\82е Ð¸ Ð´Ð° Ð¿Ð¾Ð½Ð¸Ñ\88Ñ\82иÑ\82е Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа Ð¾Ð±Ð°Ð²ÐµÑ\88Ñ\82еÑ\9aа Ð·Ð° Ñ\81ве Ñ\81Ñ\82Ñ\80аниÑ\86е Ñ\83 Ð²Ð°Ñ\88ем Ñ\81пиÑ\81кÑ\83 Ð½Ð°Ð´Ð³Ð»ÐµÐ´Ð°Ñ\9aа.\n\nСÑ\80даÑ\87ан Ð¿Ð¾Ð·Ð´Ñ\80ав, {{SITENAME}}\n\n--\nÐ\94а Ð±Ð¸Ñ\81Ñ\82е Ð¿Ñ\80оменили Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа Ð¸Ð¼ÐµÑ\98л Ð¾Ð±Ð°Ð²ÐµÑ\88Ñ\82еÑ\9aа, посетите\n{{canonicalurl:{{#special:Preferences}}}}\n\nДа бисте променили подешавања списка надгледања, посетите\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nДа бисте уклонили ову страницу са списка надгледања, посетите\n$UNWATCHURL\n\nПодршка и даља помоћ:\n$HELPPAGE",
+       "enotif_body": "Ð\9fоÑ\88Ñ\82овани $WATCHINGUSERNAME,\n \t\n$PAGEINTRO $NEWPAGE\n\nРезиме Ñ\83Ñ\80еÑ\92иваÑ\87а: $PAGESUMMARY $PAGEMINOREDIT\n\nÐ\9aонÑ\82акÑ\82:\nпоÑ\88Ñ\82а: $PAGEEDITOR_EMAIL\nвики: $PAGEEDITOR_WIKI\n\nÐ\9dеÑ\9bе Ð±Ð¸Ñ\82и Ð´Ñ\80Ñ\83гиÑ\85 Ð¾Ð±Ð°Ð²ÐµÑ\88Ñ\82еÑ\9aа Ñ\83 Ñ\81лÑ\83Ñ\87аÑ\98Ñ\83 Ð´Ð°Ñ\99иÑ\85 Ð¸Ð·Ð¼ÐµÐ½Ð° Ñ\83колико Ð½Ðµ Ð¿Ð¾Ñ\81еÑ\82иÑ\82е Ð¾Ð²Ñ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 ÐºÐ°Ð´Ð° Ñ\81Ñ\82е Ð¿Ñ\80иÑ\98авÑ\99ени.\nÐ\9cожеÑ\82е Ð¸ Ð´Ð° Ð¿Ð¾Ð½Ð¸Ñ\88Ñ\82иÑ\82е Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа Ð¾Ð±Ð°Ð²ÐµÑ\88Ñ\82еÑ\9aа Ð·Ð° Ñ\81ве Ñ\81Ñ\82Ñ\80аниÑ\86е Ñ\83 Ð²Ð°Ñ\88ем Ñ\81пиÑ\81кÑ\83 Ð½Ð°Ð´Ð³Ð»ÐµÐ´Ð°Ñ\9aа.\n\nСÑ\80даÑ\87ан Ð¿Ð¾Ð·Ð´Ñ\80ав, {{SITENAME}}\n\n--\nÐ\94а Ð±Ð¸Ñ\81Ñ\82е Ð¿Ñ\80оменили Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа Ð¾Ð±Ð°Ð²ÐµÑ\88Ñ\82аваÑ\9aа Ð¿Ñ\83Ñ\82ем Ðµ-поÑ\88Ñ\82е, посетите\n{{canonicalurl:{{#special:Preferences}}}}\n\nДа бисте променили подешавања списка надгледања, посетите\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nДа бисте уклонили ову страницу са списка надгледања, посетите\n$UNWATCHURL\n\nПодршка и даља помоћ:\n$HELPPAGE",
        "enotif_minoredit": "Ово је мања измена",
        "created": "направљена",
        "changed": "измењена",
        "actionfailed": "Радња није успела",
        "deletedtext": "Страница „$1“ је избрисана.\nПогледајте $2 за запис недавних брисања.",
        "dellogpage": "Дневник брисања",
-       "dellogpagetext": "Ð\98Ñ\81под Ñ\98е Ñ\81пиÑ\81ак Ð½ÐµÐ´Ð°Ð²Ð½их брисања.",
+       "dellogpagetext": "Ð\98Ñ\81под Ñ\81е Ð½Ð°Ð»Ð°Ð·Ð¸ Ð»Ð¸Ñ\81Ñ\82а Ð½Ð°Ñ\98новиÑ\98их брисања.",
        "deletionlog": "дневник брисања",
        "log-name-create": "Дневник прављења страница",
-       "log-description-create": "Ð\98Ñ\81под Ñ\98е Ñ\81пиÑ\81ак Ð½ÐµÐ´Ð°Ð²Ð½их прављења страница.",
+       "log-description-create": "Ð\98Ñ\81под Ñ\81е Ð½Ð°Ð»Ð°Ð·Ð¸ Ð»Ð¸Ñ\81Ñ\82а Ð½Ð°Ñ\98новиÑ\98их прављења страница.",
        "logentry-create-create": "$1 је {{GENDER:$2|направио|направила}} страницу $3",
        "reverted": "Враћено на ранију измену",
        "deletecomment": "Разлог:",
        "deleting-backlinks-warning": "<strong>Упозорење:</strong> бришете страницу која је укључена у [[Special:WhatLinksHere/{{FULLPAGENAME}}|друге странице]] или друге странице воде на њу.",
        "deleting-subpages-warning": "<strong>Упозорење:</strong> Страница коју желите избрисати има [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|подстраницу|$1 подстранице|$1 подстраница|51=преко 50 подстраница}}]].",
        "rollback": "Врати измене",
+       "rollback-confirmation-confirm": "Потврдите:",
        "rollbacklink": "врати",
        "rollbacklinkcount": "врати $1 {{PLURAL:$1|измену|измене|измена}}",
        "rollbacklinkcount-morethan": "врати више од $1 {{PLURAL:$1|измене|измене|измена}}",
        "logentry-contentmodel-change-revertlink": "врати",
        "logentry-contentmodel-change-revert": "врати",
        "protectlogpage": "Дневник заштите",
-       "protectlogtext": "Ð\98Ñ\81под Ñ\98е Ñ\81пиÑ\81ак Ð·Ð°Ñ\88Ñ\82иÑ\9bениÑ\85 Ñ\81Ñ\82Ñ\80аниÑ\86а.\nÐ\9fогледаÑ\98Ñ\82е [[Special:ProtectedPages|Ñ\81пиÑ\81ак Ð·Ð°Ñ\88Ñ\82иÑ\9bениÑ\85 Ñ\81Ñ\82Ñ\80аниÑ\86а]] Ð·Ð° Ð²Ð¸Ñ\88е Ð´ÐµÑ\82аÑ\99а.",
+       "protectlogtext": "Ð\98Ñ\81под Ñ\81е Ð½Ð°Ð»Ð°Ð·Ð¸ Ð»Ð¸Ñ\81Ñ\82а Ð¿Ñ\80омена Ð·Ð°Ñ\88Ñ\82иÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86а.\nÐ\9fогледаÑ\98Ñ\82е [[Special:ProtectedPages|лиÑ\81Ñ\82Ñ\83 Ð·Ð°Ñ\88Ñ\82иÑ\9bениÑ\85 Ñ\81Ñ\82Ñ\80аниÑ\86а]] Ð·Ð° Ñ\82Ñ\80енÑ\83Ñ\82но Ð¾Ð¿ÐµÑ\80аÑ\82ивне Ð·Ð°Ñ\88Ñ\82иÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86а.",
        "protectedarticle": "је {{GENDER:|заштитио|заштитила}} страницу „[[$1]]“",
        "modifiedarticleprotection": "је {{GENDER:|променио|променила}} ниво заштите странице „[[$1]]“",
        "unprotectedarticle": "је скинуо заштиту са странице „[[$1]]“",
        "blocklist": "Блокирани корисници",
        "autoblocklist": "Аутоблокови",
        "autoblocklist-submit": "Претражи",
-       "autoblocklist-legend": "СпиÑ\81ак аутоблокирања",
+       "autoblocklist-legend": "Ð\9bиÑ\81Ñ\82а аутоблокирања",
        "autoblocklist-localblocks": "{{PLURAL:$1|Локални аутоблок|Локални аутоблокови}}",
        "autoblocklist-total-autoblocks": "Укупно аутоблокова: $1",
-       "autoblocklist-empty": "СпиÑ\81ак Ð°Ñ\83Ñ\82облокиÑ\80аÑ\9aа Ñ\98е Ð¿Ñ\80азан.",
+       "autoblocklist-empty": "Ð\9bиÑ\81Ñ\82а Ð°Ñ\83Ñ\82облокиÑ\80аÑ\9aа Ñ\98е Ð¿Ñ\80азна.",
        "autoblocklist-otherblocks": "{{PLURAL:$1|Други аутоблок|Други аутоблокови}}",
        "ipblocklist": "Блокирани корисници",
        "ipblocklist-legend": "Проналажење блокираног корисника",
        "blocklist-editing-sitewide": "уређивање (на целом сајту)",
        "blocklist-editing-page": "странице",
        "blocklist-editing-ns": "именски простори",
-       "ipblocklist-empty": "СпиÑ\81ак Ð±Ð»Ð¾ÐºÐ¸Ñ\80аÑ\9aа Ñ\98е Ð¿Ñ\80азан.",
+       "ipblocklist-empty": "Ð\9bиÑ\81Ñ\82а Ð±Ð»Ð¾ÐºÐ¸Ñ\80аÑ\9aа Ñ\98е Ð¿Ñ\80азна.",
        "ipblocklist-no-results": "Тражена IP адреса или корисничко име није блокирано.",
        "blocklink": "блокирај",
        "unblocklink": "деблокирај",
        "blocklog-showsuppresslog": "{{GENDER:$1|Овај корисник је раније блокиран и сакривен|Ова корисница је раније блокирана и сакривена}}.\nДневник сакривања је наведен испод као референца:",
        "blocklogentry": "је блокирао [[$1]] са временом истицања од $2 $3",
        "reblock-logentry": "је {{GENDER:|променио|променила}} подешавања блокирања за {{GENDER:$1|корисника|корисницу}} [[$1]] са временом истека од $2 ($3)",
-       "blocklogtext": "Ð\9eво Ñ\98е Ð´Ð½ÐµÐ²Ð½Ð¸Ðº Ñ\80адÑ\9aи Ð±Ð»Ð¾ÐºÐ¸Ñ\80аÑ\9aа Ð¸ Ð´ÐµÐ±Ð»Ð¾ÐºÐ¸Ñ\80аÑ\9aа ÐºÐ¾Ñ\80иÑ\81ника.\nÐ\90Ñ\83Ñ\82омаÑ\82Ñ\81ки Ð±Ð»Ð¾ÐºÐ¸Ñ\80ане IP Ð°Ð´Ñ\80еÑ\81е Ð½Ð¸Ñ\81Ñ\83 Ð½Ð°Ð²ÐµÐ´ÐµÐ½Ðµ.\nÐ\9fогледаÑ\98Ñ\82е [[Special:BlockList|Ñ\81пиÑ\81ак Ð±Ð»Ð¾ÐºÐ¸Ñ\80аÑ\9aа]] Ð·Ð° Ñ\81пиÑ\81ак Ñ\82Ñ\80енÑ\83Ñ\82ниÑ\85 Ð¾Ð¿ÐµÑ\80аÑ\86иÑ\98а Ð·Ð°Ð±Ñ\80ана и блокирања.",
+       "blocklogtext": "Ð\9eво Ñ\98е ÐµÐ²Ð¸Ð´ÐµÐ½Ñ\86иÑ\98а Ñ\80адÑ\9aи Ð±Ð»Ð¾ÐºÐ¸Ñ\80аÑ\9aа Ð¸ Ð´ÐµÐ±Ð»Ð¾ÐºÐ¸Ñ\80аÑ\9aа ÐºÐ¾Ñ\80иÑ\81ника.\nÐ\90Ñ\83Ñ\82омаÑ\82Ñ\81ки Ð±Ð»Ð¾ÐºÐ¸Ñ\80ане IP Ð°Ð´Ñ\80еÑ\81е Ð½Ð¸Ñ\81Ñ\83 Ð½Ð°Ð²ÐµÐ´ÐµÐ½Ðµ.\nÐ\9fогледаÑ\98Ñ\82е [[Special:BlockList|лиÑ\81Ñ\82Ñ\83 Ð±Ð»Ð¾ÐºÐ¸Ñ\80аÑ\9aа]] Ð·Ð° Ñ\82Ñ\80енÑ\83Ñ\82но Ð¾Ð¿ÐµÑ\80аÑ\82ивне Ð·Ð°Ð±Ñ\80ане и блокирања.",
        "unblocklogentry": "је деблокирао $1",
        "block-log-flags-anononly": "само анонимни корисници",
        "block-log-flags-nocreate": "онемогућено отварање налога",
        "movepage-page-unmoved": "Страница $1 не може да се премести на $2.",
        "movepage-max-pages": "Највише $1 {{PLURAL:$1|страница је премештена|странице су премештене|страница је премештено}} и више не може да буде аутоматски премештено.",
        "movelogpage": "Дневник премештања",
-       "movelogpagetext": "Испод се налази списак премештања страница.",
+       "movelogpagetext": "Испод се налази листа свих премештања страница.",
        "movesubpage": "{{PLURAL:$1|Подстраница|Подстранице}}",
        "movesubpagetext": "Ова страница има $1 {{PLURAL:$1|подстраницу приказану|подстранице приказане|подстраница приказаних}} испод.",
        "movenosubpage": "Ова страница нема подстрана.",
        "exportall": "Извези све странице",
        "exportcuronly": "Укључи само тренутну измену, не целу историју",
        "exportnohistory": "----\n'''Напомена:''' извоз пуне историје страница преко овог обрасца је онемогућено из техничких разлога.",
-       "exportlistauthors": "Укључи целокупан списак доприносилаца за сваку страницу",
+       "exportlistauthors": "Укључи потпуну листу доприносилаца за сваку страницу",
        "export-submit": "Извези",
        "export-addcattext": "Додај странице из категорије:",
        "export-addcat": "Додај",
        "allmessagesname": "Назив",
        "allmessagesdefault": "Подразумевани текст",
        "allmessagescurrent": "Актуелни текст поруке",
-       "allmessagestext": "Ово је списак системских порука доступних у именском простору „Медијавики“.\nПосетите [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation Медијавики локализацију] и [https://translatewiki.net translatewiki.net] ако желите да допринесете општој локализацији Медијавикија.",
+       "allmessagestext": "Ово је листа системских порука доступних у именском простору „Медијавики”.\nПосетите [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation локализацију Медијавикија] и [https://translatewiki.net translatewiki.net] ако желите да допринесете општој локализацији Медијавикија.",
        "allmessagesnotsupportedDB": "Ова страница не може да се користи јер је '''$wgUseDatabaseMessages''' онемогућен.",
        "allmessages-filter-legend": "Филтер",
        "allmessages-filter": "Филтрирај по стању:",
        "tooltip-pt-mytalk": "{{GENDER:|Ваша}} страница за разговор",
        "tooltip-pt-anontalk": "Дискусија о изменама са ове IP адресе",
        "tooltip-pt-preferences": "{{GENDER:|Ваша}} подешавања",
-       "tooltip-pt-watchlist": "СпиÑ\81ак страница чије промене надгледате",
-       "tooltip-pt-mycontris": "СпиÑ\81ак {{GENDER:|ваших}} доприноса",
-       "tooltip-pt-anoncontribs": "СпиÑ\81ак измена направљених са ове IP адресе",
+       "tooltip-pt-watchlist": "Ð\9bиÑ\81Ñ\82а страница чије промене надгледате",
+       "tooltip-pt-mycontris": "Ð\9bиÑ\81Ñ\82а {{GENDER:|ваших}} доприноса",
+       "tooltip-pt-anoncontribs": "Ð\9bиÑ\81Ñ\82а измена направљених са ове IP адресе",
        "tooltip-pt-login": "Предлажемо вам да се пријавите, иако то није обавезно",
        "tooltip-pt-login-private": "Морате да се пријавите да бисте користили овај Вики",
        "tooltip-pt-logout": "Одјавите се",
        "tooltip-n-mainpage-description": "Посетите главну страну",
        "tooltip-n-portal": "О пројекту, шта можете да радите и где да пронађете ствари",
        "tooltip-n-currentevents": "Пронађите информације о актуелностима",
-       "tooltip-n-recentchanges": "СпиÑ\81ак недавних промена на викију",
+       "tooltip-n-recentchanges": "Ð\9bиÑ\81Ñ\82а недавних промена на викију",
        "tooltip-n-randompage": "Учитајте случајну страницу",
        "tooltip-n-help": "Место где можете да се информишете",
-       "tooltip-t-whatlinkshere": "СпиÑ\81ак свих вики страница које воде овде",
+       "tooltip-t-whatlinkshere": "Ð\9bиÑ\81Ñ\82а свих вики страница које воде овде",
        "tooltip-t-recentchangeslinked": "Недавне промене на страницама које воде на ову страницу",
        "tooltip-feed-rss": "RSS фид за ову страницу",
        "tooltip-feed-atom": "Atom фид за ову страницу",
-       "tooltip-t-contributions": "СпиÑ\81ак доприноса {{GENDER:$1|овог корисника|ове кориснице|овог корисника}}",
+       "tooltip-t-contributions": "Ð\9bиÑ\81Ñ\82а доприноса {{GENDER:$1|овог корисника|ове кориснице|овог корисника}}",
        "tooltip-t-emailuser": "Пошаљите е-поруку {{GENDER:$1|овом кориснику|овој корисници|кориснику/ци}}",
        "tooltip-t-info": "Више информација о овој страници",
        "tooltip-t-upload": "Отпремите датотеке",
-       "tooltip-t-specialpages": "СпиÑ\81ак свих посебних страница",
+       "tooltip-t-specialpages": "Ð\9bиÑ\81Ñ\82а свих посебних страница",
        "tooltip-t-print": "Верзија ове странице за штампање",
        "tooltip-t-permalink": "Трајна веза ка овој измени странице",
        "tooltip-ca-nstab-main": "Погледајте страницу са садржајем",
        "file-no-thumb-animation": "<strong>Напомена: Због техничких ограничења, сличице ове датотеке неће да се анимирају.</strong>",
        "file-no-thumb-animation-gif": "'''Напомена: због техничких ограничења, минијатуре GIF слика високе резолуције као што је ова неће се анимирати.'''",
        "newimages": "Галерија нових датотека",
-       "imagelisttext": "Ð\98Ñ\81под Ñ\98е Ñ\81пиÑ\81ак Ð¾Ð´ '''$1''' {{PLURAL:$1|даÑ\82оÑ\82еке|даÑ\82оÑ\82еке|даÑ\82оÑ\82ека}} Ð¿Ð¾Ñ\80еÑ\92аних $2.",
+       "imagelisttext": "Ð\98Ñ\81под Ñ\81е Ð½Ð°Ð»Ð°Ð·Ð¸ Ñ\81пиÑ\81ак Ð¾Ð´ <strong>$1</strong> {{PLURAL:$1|даÑ\82оÑ\82еке|даÑ\82оÑ\82еке|даÑ\82оÑ\82ека}} Ñ\81оÑ\80Ñ\82иÑ\80аних $2.",
        "newimages-summary": "Ова посебна страница приказује последње отпремљене датотеке.",
        "newimages-legend": "Филтер",
        "newimages-label": "Назив датотеке (или њен део):",
        "metadata-langitem-default": "$1",
        "namespacesall": "сви",
        "monthsall": "све",
-       "confirmemail": "Ð\9fоÑ\82вÑ\80да Ð°Ð´Ñ\80еÑ\81е Ðµ-поÑ\88Ñ\82е",
-       "confirmemail_noemail": "Ð\9dиÑ\81Ñ\82е Ð¿Ð¾Ñ\81Ñ\82авили Ð²Ð°Ð¶ÐµÑ\9bÑ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е у [[Special:Preferences|корисничким подешавањима]].",
-       "confirmemail_text": "{{SITENAME}} Ð·Ð°Ñ\85Ñ\82ева Ð´Ð° Ð¿Ñ\80овеÑ\80иÑ\82е Ð²Ð°Ñ\99аноÑ\81Ñ\82 Ð¸Ð¼ÐµÑ\98л-адÑ\80еÑ\81е Ð¿Ñ\80е Ð½ÐµÐ³Ð¾ Ñ\88Ñ\82о Ð¿Ð¾Ñ\87неÑ\82е Ð´Ð° ÐºÐ¾Ñ\80иÑ\81Ñ\82иÑ\82е Ñ\84Ñ\83нкÑ\86иÑ\98Ñ\83 Ð¸Ð¼ÐµÑ\98ла.\nÐ\90кÑ\82ивиÑ\80аÑ\98Ñ\82е Ð´Ñ\83гме Ð¸Ñ\81под Ð´Ð° Ð±Ð¸Ñ\81Ñ\82е Ð¿Ð¾Ñ\81лали Ð¼ÐµÑ\98л Ð·Ð° Ð¿Ð¾Ñ\82вÑ\80дÑ\83 Ð½Ð° Ñ\81воÑ\98Ñ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83.\nÐ\9cеÑ\98л Ñ\9bе Ñ\83кÑ\99Ñ\83Ñ\87иваÑ\82и Ð²ÐµÐ·Ñ\83 Ñ\81а ÐºÐ¾Ð´Ð¾Ð¼;\nÑ\83Ñ\87иÑ\82аÑ\98Ñ\82е Ð²ÐµÐ·Ñ\83 Ñ\83 Ñ\81вом Ð¿Ñ\80егледаÑ\87Ñ\83 Ð´Ð° Ð±Ð¸Ñ\81Ñ\82е Ð¿Ð¾Ñ\82вÑ\80дили Ð´Ð° Ñ\98е Ð²Ð°Ñ\88а Ð¸Ð¼ÐµÑ\98л-адреса важећа.",
-       "confirmemail_pending": "Ð\9aод Ð·Ð° Ð¿Ð¾Ñ\82вÑ\80дÑ\83 Ð²Ð°Ð¼ Ñ\98е Ð²ÐµÑ\9b Ð¿Ð¾Ñ\81лаÑ\82 Ð¸Ð¼ÐµÑ\98лом.\nАко сте недавно отворили налог, можда треба да сачекате неколико минута да пристигне пре него што поново затражите нови код.",
+       "confirmemail": "Ð\9fоÑ\82вÑ\80да Ðµ-адÑ\80еÑ\81е",
+       "confirmemail_noemail": "Ð\9dиÑ\81Ñ\82е Ð¿Ð¾Ñ\81Ñ\82авили Ð²Ð°Ð¶ÐµÑ\9bÑ\83 Ðµ-адÑ\80еÑ\81Ñ\83 у [[Special:Preferences|корисничким подешавањима]].",
+       "confirmemail_text": "{{SITENAME}} Ð·Ð°Ñ\85Ñ\82ева Ð´Ð° Ð¿Ñ\80овеÑ\80иÑ\82е Ð²Ð°Ñ\99аноÑ\81Ñ\82 Ðµ-адÑ\80еÑ\81е Ð¿Ñ\80е Ð½ÐµÐ³Ð¾ Ñ\88Ñ\82о Ð¿Ð¾Ñ\87неÑ\82е Ð´Ð° ÐºÐ¾Ñ\80иÑ\81Ñ\82иÑ\82е Ñ\84Ñ\83нкÑ\86иÑ\98Ñ\83 Ðµ-поÑ\88Ñ\82е.\nÐ\90кÑ\82ивиÑ\80аÑ\98Ñ\82е Ð´Ñ\83гме Ð¸Ñ\81под Ð´Ð° Ð±Ð¸Ñ\81Ñ\82е Ð¿Ð¾Ñ\81лали Ð¿Ð¾Ñ\80Ñ\83кÑ\83â\80\82Ñ\81а Ð¿Ð¾Ñ\82вÑ\80дом Ð½Ð° Ñ\81воÑ\98Ñ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83.\nÐ\9fоÑ\80Ñ\83ка Ñ\9bе Ñ\83кÑ\99Ñ\83Ñ\87иваÑ\82и Ð²ÐµÐ·Ñ\83 Ñ\81а ÐºÐ¾Ð´Ð¾Ð¼;\nÑ\83Ñ\87иÑ\82аÑ\98Ñ\82е Ð²ÐµÐ·Ñ\83 Ñ\83 Ñ\81вом Ð¿Ñ\80егледаÑ\87Ñ\83 Ð´Ð° Ð±Ð¸Ñ\81Ñ\82е Ð¿Ð¾Ñ\82вÑ\80дили Ð´Ð° Ñ\98е Ð²Ð°Ñ\88а Ðµ-адреса важећа.",
+       "confirmemail_pending": "Ð\9aод Ð·Ð° Ð¿Ð¾Ñ\82вÑ\80дÑ\83 Ð²Ð°Ð¼ Ñ\98е Ð²ÐµÑ\9b Ð¿Ð¾Ñ\81лаÑ\82 Ðµ-поÑ\88Ñ\82ом.\nАко сте недавно отворили налог, можда треба да сачекате неколико минута да пристигне пре него што поново затражите нови код.",
        "confirmemail_send": "Пошаљи код за потврду",
        "confirmemail_sent": "Потврдна порука је послата.",
-       "confirmemail_oncreate": "Ð\9aôд Ð·Ð° Ð¿Ð¾Ñ\82вÑ\80дÑ\83 Ñ\98е Ð¿Ð¾Ñ\81лаÑ\82 Ð½Ð° Ð²Ð°Ñ\88Ñ\83 Ð¸Ð¼ÐµÑ\98л-адÑ\80еÑ\81Ñ\83.\nÐ\9eваÑ\98 ÐºÃ´Ð´ Ð½Ð¸Ñ\98е Ð½ÐµÐ¾Ð¿Ñ\85одан Ð·Ð° Ð¿Ñ\80иÑ\98авÑ\99иваÑ\9aе, Ð°Ð»Ð¸ Ñ\9bеÑ\82е Ð¼Ð¾Ñ\80аÑ\82и Ð´Ð° Ð³Ð° Ð½Ð°Ð²ÐµÐ´ÐµÑ\82е Ð¿Ñ\80е Ð¾Ð¼Ð¾Ð³Ñ\83Ñ\9bаваÑ\9aа Ð±Ð¸Ð»Ð¾ ÐºÐ°ÐºÐ²Ð¸Ñ\85 Ñ\84Ñ\83нкÑ\86иÑ\98а Ð·Ð°Ñ\81нованиÑ\85 Ð½Ð° Ð¸Ð¼ÐµÑ\98лÑ\83 на викију.",
-       "confirmemail_sendfailed": "{{SITENAME}} Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð¿Ð¾Ñ\88аÑ\99е Ðµ-поÑ\80Ñ\83кÑ\83 Ð·Ð° Ð¿Ð¾Ñ\82вÑ\80дÑ\83.\nÐ\9fÑ\80овеÑ\80иÑ\82е Ð´Ð° Ð»Ð¸ Ñ\98е Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е Ñ\81адÑ\80жи Ð½ÐµÐ²Ð°Ð¶ÐµÑ\9bе Ð·Ð½Ð°Ðºе.\n\nПошиљалац је вратио грешку: $1",
+       "confirmemail_oncreate": "Ð\9aôд Ð·Ð° Ð¿Ð¾Ñ\82вÑ\80дÑ\83 Ñ\98е Ð¿Ð¾Ñ\81лаÑ\82 Ð½Ð° Ð²Ð°Ñ\88Ñ\83 Ðµ-адÑ\80еÑ\81Ñ\83.\nÐ\9eваÑ\98 ÐºÃ´Ð´ Ð½Ð¸Ñ\98е Ð½ÐµÐ¾Ð¿Ñ\85одан Ð·Ð° Ð¿Ñ\80иÑ\98авÑ\99иваÑ\9aе, Ð°Ð»Ð¸ Ñ\9bеÑ\82е Ð¼Ð¾Ñ\80аÑ\82и Ð´Ð° Ð³Ð° Ð½Ð°Ð²ÐµÐ´ÐµÑ\82е Ð¿Ñ\80е Ð¾Ð¼Ð¾Ð³Ñ\83Ñ\9bаваÑ\9aа Ð±Ð¸Ð»Ð¾ ÐºÐ°ÐºÐ²Ð¸Ñ\85 Ñ\84Ñ\83нкÑ\86иÑ\98а Ð·Ð°Ñ\81нованиÑ\85 Ð½Ð° Ðµ-поÑ\88Ñ\82и на викију.",
+       "confirmemail_sendfailed": "{{SITENAME}} Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð¿Ð¾Ñ\88аÑ\99е Ð¿Ð¾Ñ\80Ñ\83кÑ\83 Ñ\81а Ð¿Ð¾Ñ\82вÑ\80дом.\nÐ\9fÑ\80овеÑ\80иÑ\82е Ð´Ð° Ð»Ð¸ Ðµ-адÑ\80еÑ\81а Ñ\81адÑ\80жи Ð½ÐµÐ²Ð°Ð¶ÐµÑ\9bе Ð·Ð½Ð°ÐºÐ¾Ð²е.\n\nПошиљалац је вратио грешку: $1",
        "confirmemail_invalid": "Неважећи код за потврду.\nКод је можда истекао.",
-       "confirmemail_needlogin": "$1 Ð´Ð° Ð±Ð¸Ñ\81Ñ\82е Ð¿Ð¾Ñ\82вÑ\80дили Ð°Ð´Ñ\80еÑ\81Ñ\83 Ðµ-поÑ\88Ñ\82е.",
-       "confirmemail_success": "Ð\92аÑ\88а Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е је потврђена.\nСада можете да се [[Special:UserLogin|пријавите]] и уживате у викију.",
-       "confirmemail_loggedin": "Ð\92аÑ\88а Ð°Ð´Ñ\80еÑ\81а Ðµ-поÑ\88Ñ\82е је сада потврђена.",
-       "confirmemail_subject": "{{SITENAME}} â\80\93 Ð¿Ð¾Ñ\82вÑ\80да Ð°Ð´Ñ\80еÑ\81е Ðµ-поÑ\88Ñ\82е",
-       "confirmemail_body": "Ð\9dеко, Ð²ÐµÑ\80оваÑ\82но Ð\92и, Ñ\81а IP Ð°Ð´Ñ\80еÑ\81е $1,\nÑ\80егиÑ\81Ñ\82Ñ\80овао Ñ\98е Ð½Ð°Ð»Ð¾Ð³ â\80\9e$2â\80\9c Ñ\81а Ð¾Ð²Ð¾Ð¼ Ð¸Ð¼ÐµÑ\98л Ð°Ð´Ñ\80еÑ\81ом Ð½Ð° Ð¿Ñ\80оÑ\98екÑ\82Ñ\83 {{SITENAME}}.\n\nÐ\94а Ð±Ð¸Ñ\81Ñ\82е Ð¿Ð¾Ñ\82вÑ\80дили Ð´Ð° Ð¾Ð²Ð°Ñ\98 Ð½Ð°Ð»Ð¾Ð³ Ñ\81Ñ\82ваÑ\80но Ð¿Ñ\80ипада Ð²Ð°Ð¼Ð° Ð¸ Ð°ÐºÑ\82ивиÑ\80али Ñ\84Ñ\83нкÑ\86иÑ\98Ñ\83 Ð¸Ð¼ÐµÑ\98ла Ð½Ð° Ð¿Ñ\80оÑ\98екÑ\82Ñ\83 {{SITENAME}}, Ð¾Ñ\82воÑ\80иÑ\82е Ð¾Ð²Ð° Ñ\83 Ð¿Ñ\80егледаÑ\87Ñ\83:\n\n$3\n\nÐ\90ко Ð²Ð¸ *ниÑ\81Ñ\82е* Ñ\80егиÑ\81Ñ\82Ñ\80овали Ð½Ð°Ð»Ð¾Ð³, Ð¿Ñ\80аÑ\82иÑ\82е Ð¾Ð²Ñ\83 Ð²ÐµÐ·Ñ\83\nда Ð±Ð¸Ñ\81Ñ\82е Ð¾Ñ\82казали Ð¿Ð¾Ñ\82вÑ\80дÑ\83 Ð¸Ð¼ÐµÑ\98л адресе:\n\n$5\n\nОвај код за потврду истиче у $4.",
-       "confirmemail_body_changed": "Неко, вероватно Ви, с IP адресе $1,\nпроменио је имејл адресу налога „$2“ у ову адресу на пројекту {{SITENAME}}.\n\nДа бисте потврдили да овај налог стварно припада вама и поново активирали функцију имејла, отворите следећу везу у прегледачу:\n\n$3\n\nАко налог *не* припада вама, пратите следећу везу да откажете потврду имејл адресе:\n\n$5\n\nОвај код за потврду истиче $6 у $7",
-       "confirmemail_body_set": "Неко, вероватно Ви, с IP адресе $1,\nпроменио је имејл адресу налога „$2“ у ову адресу на {{SITENAME}}.\n\nДа бисмо потврдили да овај налог стварно припада вама и поново активирали\nфункцију имејла на {{SITENAME}}, отворите следећу везу у прегледачу:\n\n$3\n\nАко налог *не* припада вама, пратите следећу везу да откажете потврду имејл адресе:\n\n$5\n\nОвај код за потврду истиче $4.",
-       "confirmemail_invalidated": "Ð\9fоÑ\82вÑ\80да Ð°Ð´Ñ\80еÑ\81е Ðµ-поÑ\88Ñ\82е је отказана",
+       "confirmemail_needlogin": "$1 Ð´Ð° Ð±Ð¸Ñ\81Ñ\82е Ð¿Ð¾Ñ\82вÑ\80дили Ðµ-адÑ\80еÑ\81Ñ\83.",
+       "confirmemail_success": "Ð\92аÑ\88а Ðµ-адÑ\80еÑ\81а је потврђена.\nСада можете да се [[Special:UserLogin|пријавите]] и уживате у викију.",
+       "confirmemail_loggedin": "Ð\92аÑ\88а Ðµ-адÑ\80еÑ\81а је сада потврђена.",
+       "confirmemail_subject": "{{SITENAME}} â\80\93 Ð¿Ð¾Ñ\82вÑ\80да Ðµ-адÑ\80еÑ\81е",
+       "confirmemail_body": "Ð\9dеко, Ð²ÐµÑ\80оваÑ\82но Ð\92и, Ñ\81а IP Ð°Ð´Ñ\80еÑ\81е $1,\nÑ\80егиÑ\81Ñ\82Ñ\80овао Ñ\98е Ð½Ð°Ð»Ð¾Ð³ â\80\9e$2â\80\9c Ñ\81а Ð¾Ð²Ð¾Ð¼ Ðµ-адÑ\80еÑ\81ом Ð½Ð° Ð¿Ñ\80оÑ\98екÑ\82Ñ\83 {{SITENAME}}.\n\nÐ\94а Ð±Ð¸Ñ\81Ñ\82е Ð¿Ð¾Ñ\82вÑ\80дили Ð´Ð° Ð¾Ð²Ð°Ñ\98 Ð½Ð°Ð»Ð¾Ð³ Ñ\81Ñ\82ваÑ\80но Ð¿Ñ\80ипада Ð²Ð°Ð¼Ð° Ð¸ Ð°ÐºÑ\82ивиÑ\80али Ñ\84Ñ\83нкÑ\86иÑ\98е Ðµ-поÑ\88Ñ\82е Ð½Ð° Ð¿Ñ\80оÑ\98екÑ\82Ñ\83 {{SITENAME}}, Ð¾Ñ\82воÑ\80иÑ\82е Ð¾Ð²Ñ\83 Ð²ÐµÐ·Ñ\83 Ñ\83 Ð¿Ñ\80егледаÑ\87Ñ\83:\n\n$3\n\nÐ\90ко Ð²Ð¸ *ниÑ\81Ñ\82е* Ñ\80егиÑ\81Ñ\82Ñ\80овали Ð½Ð°Ð»Ð¾Ð³, Ð¿Ñ\80аÑ\82иÑ\82е Ð¾Ð²Ñ\83 Ð²ÐµÐ·Ñ\83\nда Ð±Ð¸Ñ\81Ñ\82е Ð¾Ñ\82казали Ð¿Ð¾Ñ\82вÑ\80дÑ\83 Ðµ-адресе:\n\n$5\n\nОвај код за потврду истиче у $4.",
+       "confirmemail_body_changed": "Неко, вероватно Ви, са IP адресе $1,\nпроменио је е-адресу налога „$2” у ову адресу на пројекту {{SITENAME}}.\n\nДа бисте потврдили да овај налог стварно припада вама и поново активирали функције е-поште, отворите следећу везу у прегледачу:\n\n$3\n\nАко налог *не* припада вама, пратите следећу везу да откажете потврду е-адресе:\n\n$5\n\nОвај код за потврду истиче $6 у $7",
+       "confirmemail_body_set": "Неко, вероватно Ви, са IP адресе $1,\nпроменио је е-адресу налога „$2” у ову адресу на {{SITENAME}}.\n\nДа бисмо потврдили да овај налог стварно припада вама и поново активирали\nфункције е-поште на пројекту {{SITENAME}}, отворите следећу везу у прегледачу:\n\n$3\n\nАко налог *не* припада вама, пратите следећу везу да откажете потврду е-адресе:\n\n$5\n\nОвај код за потврду истиче $4.",
+       "confirmemail_invalidated": "Ð\9fоÑ\82вÑ\80да Ðµ-адÑ\80еÑ\81е је отказана",
        "invalidateemail": "Отказивање потврде е-поште",
-       "notificationemail_subject_changed": "РегиÑ\81Ñ\82Ñ\80ована Ð¸Ð¼ÐµÑ\98л адреса на пројекту {{SITENAME}} је промењена",
-       "notificationemail_subject_removed": "РегиÑ\81Ñ\82Ñ\80ована Ð¸Ð¼ÐµÑ\98л адреса на пројекту {{SITENAME}} је уклоњена",
-       "notificationemail_body_changed": "Неко, вероватно Ви је променио имејл адресу налога из $2“ у „$3“ са IP адресе $1 на сајту {{SITENAME}}.\n\nАко ово нисте били Ви, одмах обавестите администраторе сајта.",
-       "notificationemail_body_removed": "Неко, вероватно Ви, с IP адресе $1, \nуклонио је имејл адресу за налог „$2“ на {{SITENAME}}.\n\nАко ово нисте били Ви, контактирајте администраторе сајта одмах.",
+       "notificationemail_subject_changed": "РегиÑ\81Ñ\82Ñ\80ована Ðµ-адреса на пројекту {{SITENAME}} је промењена",
+       "notificationemail_subject_removed": "РегиÑ\81Ñ\82Ñ\80ована Ðµ-адреса на пројекту {{SITENAME}} је уклоњена",
+       "notificationemail_body_changed": "Неко, вероватно Ви, са IP адресе $1,\nпроменио је е-адресу налога из „$2” у „$3” на пројекту {{SITENAME}}.\n\nАко ово нисте били Ви, одмах се обратите администратору сајта.",
+       "notificationemail_body_removed": "Неко, вероватно Ви, са IP адресе $1, \nуклонио је е-адресу налога „$2” на пројекту {{SITENAME}}.\n\nАко ово нисте били Ви, одмах се обратите администратору сајта.",
        "scarytranscludedisabled": "[Међувики укључивање шаблона је онемогућено]",
        "scarytranscludefailed": "[Добављање шаблона за $1 није успело]",
        "scarytranscludefailed-httpstatus": "[Не могу да преузмем шаблон $1: HTTP $2]",
        "tag-mw-undo": "поништење",
        "tag-mw-undo-description": "Измене које поништавају претходне измене",
        "tags-title": "Ознаке",
-       "tags-intro": "Ð\9dа Ð¾Ð²Ð¾Ñ\98 Ñ\81Ñ\82Ñ\80аниÑ\86и Ñ\98е Ð½Ð°Ð²ÐµÐ´ÐµÐ½ Ñ\81пиÑ\81ак Ð¾Ð·Ð½Ð°ÐºÐ° Ñ\81 ÐºÐ¾Ñ\98има Ð¿Ñ\80огÑ\80ам Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð¾Ð·Ð½Ð°Ñ\87и Ð¸Ð·Ð¼ÐµÐ½Ðµ Ð¸ Ñ\9aегово значење.",
-       "tags-tag": "Ð\9dазив ознаке",
+       "tags-intro": "Ð\9eва Ñ\81Ñ\82Ñ\80аниÑ\86а Ð½Ð°Ð²Ð¾Ð´Ð¸ Ð¾Ð·Ð½Ð°ÐºÐµ ÐºÐ¾Ñ\98има Ñ\81оÑ\84Ñ\82веÑ\80 Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð¾Ð·Ð½Ð°Ñ\87и Ð¸Ð·Ð¼ÐµÐ½Ðµ, Ñ\82е Ñ\9aиÑ\85ово значење.",
+       "tags-tag": "Ð\98ме ознаке",
        "tags-display-header": "Изглед на списковима промена",
        "tags-description-header": "Опис значења",
        "tags-source-header": "Извор",
        "logentry-newusers-newusers": "$1 је {{GENDER:$2|отворио|отворила}} кориснички налог",
        "logentry-newusers-create": "$1 је {{GENDER:$2|отворио|отворила}} кориснички налог",
        "logentry-newusers-create2": "$1 је {{GENDER:$2|отворио|отворила}} кориснички налог $3",
-       "logentry-newusers-byemail": "$1 Ñ\98е {{GENDER:$2|оÑ\82воÑ\80ио|оÑ\82воÑ\80ила}} ÐºÐ¾Ñ\80иÑ\81ниÑ\87ки Ð½Ð°Ð»Ð¾Ð³ $3 Ð¸ Ð»Ð¾Ð·Ð¸Ð½ÐºÐ° Ñ\98е Ð¿Ð¾Ñ\81лаÑ\82а Ð½Ð° Ð¸Ð¼ÐµÑ\98л",
+       "logentry-newusers-byemail": "$1 Ñ\98е {{GENDER:$2|оÑ\82воÑ\80ио|оÑ\82воÑ\80ила}} ÐºÐ¾Ñ\80иÑ\81ниÑ\87ки Ð½Ð°Ð»Ð¾Ð³ $3 Ð¸ Ð»Ð¾Ð·Ð¸Ð½ÐºÐ° Ñ\98е Ð¿Ð¾Ñ\81лаÑ\82а Ð½Ð° Ðµ-поÑ\88Ñ\82Ñ\83",
        "logentry-newusers-autocreate": "$1 је аутоматски {{GENDER:$2|отворио|отворила}} кориснички налог",
        "logentry-protect-move_prot": "$1 је {{GENDER:$2|преместио|преместила}} подешавања заштите са $4 на $3",
        "logentry-protect-unprotect": "$1 je {{GENDER:$2|скинуо|скинула}} заштиту са странице $3",
        "logentry-upload-overwrite": "$1 је {{GENDER:$2|отпремио|отпремила}} нову верзију датотеке $3",
        "logentry-upload-revert": "$1 је {{GENDER:$2|вратио|вратила}} датотеку $3 на старију верзију",
        "log-name-managetags": "Дневник управљања ознакама",
-       "log-description-managetags": "Ð\9dа Ð¾Ð²Ð¾Ñ\98 Ñ\81Ñ\82Ñ\80аниÑ\86и Ñ\81е Ð½Ð°Ð»Ð°Ð·Ð¸ Ñ\81пиÑ\81ак Ð¸Ð·Ð¼ÐµÐ½Ð° Ñ\83 Ð²ÐµÐ·Ð¸ [[Special:Tags|ознака]]. Ð\94невник Ñ\81адÑ\80жи Ñ\81амо Ñ\80адÑ\9aе ÐºÐ¾Ñ\98е Ñ\81Ñ\83 Ñ\80Ñ\83Ñ\87но Ð¸Ð·Ð²Ñ\80Ñ\88или Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñ\81Ñ\82Ñ\80аÑ\82оÑ\80и; Ñ\83ноÑ\81и Ð·Ð° Ð¾Ð·Ð½Ð°ÐºÐµ ÐºÐ¾Ñ\98е Ñ\98е Ð½Ð°Ð¿Ñ\80авио Ð¸Ð»Ð¸ Ð¸Ð·Ð±Ñ\80иÑ\81ао Ð²Ð¸ÐºÐ¸ Ñ\81оÑ\84Ñ\82веÑ\80, Ð° Ð½Ðµ Ð½Ð°Ð»Ð°Ð·Ðµ Ñ\81е Ñ\83 Ð¾Ð²Ð¾Ð¼ Ð´Ð½ÐµÐ²Ð½Ð¸ÐºÑ\83.",
+       "log-description-managetags": "Ð\9eва Ñ\81Ñ\82Ñ\80аниÑ\86а Ð½Ð°Ð²Ð¾Ð´Ð¸ Ð·Ð°Ð´Ð°Ñ\82ке Ñ\83пÑ\80авÑ\99аÑ\9aа Ñ\83 Ð²ÐµÐ·Ð¸ Ñ\81а [[Special:Tags|ознакама]]. Ð\95виденÑ\86иÑ\98а Ñ\81адÑ\80жи Ñ\81амо Ñ\80адÑ\9aе ÐºÐ¾Ñ\98е Ñ\81Ñ\83 Ñ\80Ñ\83Ñ\87но Ð¸Ð·Ð²Ñ\80Ñ\88или Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñ\81Ñ\82Ñ\80аÑ\82оÑ\80и; Ñ\83ноÑ\81и Ð·Ð° Ð¾Ð·Ð½Ð°ÐºÐµ ÐºÐ¾Ñ\98е Ñ\98е Ð½Ð°Ð¿Ñ\80авио Ð¸Ð»Ð¸ Ð¸Ð·Ð±Ñ\80иÑ\81ао Ð²Ð¸ÐºÐ¸ Ñ\81оÑ\84Ñ\82веÑ\80, Ð° Ð½Ðµ Ð½Ð°Ð»Ð°Ð·Ðµ Ñ\81е Ñ\83 Ð¾Ð²Ð¾Ñ\98 ÐµÐ²Ð¸Ð´ÐµÐ½Ñ\86иÑ\98и.",
        "logentry-managetags-create": "$1 је {{GENDER:$2|направио|направила}} ознаку „$4“",
        "logentry-managetags-delete": "$1 је {{GENDER:$2|избрисао|избрисала}} ознаку „$4” (уклоњена је из $5 {{PLURAL:$5|измене или уноса у дневнику|измена и/или уноса у дневнику}})",
        "logentry-managetags-activate": "$1 је {{GENDER:$2|активирао|активирала}} ознаку „$4“ за употребу од стране корисника и ботова",
        "authmanager-domain-help": "Домен за спољашњу потврду идентитета.",
        "authmanager-retype-help": "Поновите лозинку да би сте потврдили.",
        "authmanager-email-label": "Е-пошта",
-       "authmanager-email-help": "Ð\90дÑ\80еÑ\81а Ðµ-поÑ\88Ñ\82е:",
+       "authmanager-email-help": "Ð\95-адÑ\80еÑ\81а",
        "authmanager-realname-label": "Право име",
        "authmanager-realname-help": "Право име корисника",
        "authmanager-provider-password": "Потврда идентитета лозинком",
        "pagedata-bad-title": "Невалидан наслов: $1.",
        "unregistered-user-config": "Из безбедоносних разлога, јаваскрипт, Це-Ес-Ес и ЈСОН корисничке подстранице не могу бити учитане за нерегистроване кориснике.",
        "passwordpolicies": "Правила за лозинке",
-       "passwordpolicies-summary": "Ово је списак делотворних смерница за лозинке за корисничке групе одређене на овом викију.",
+       "passwordpolicies-summary": "Ово је листа делотворних смерница за лозинке за корисничке групе дефинисане на овом викију.",
        "passwordpolicies-group": "Група",
        "passwordpolicies-policies": "Правила",
        "passwordpolicies-policy-display": "<span class=\"passwordpolicies-policy\">$1 <code>($2)</code></span>",
index e87f2ea..01d6bd6 100644 (file)
        "tog-useeditwarning": "Varna mig om jag lämnar en redigeringssida med osparade ändringar",
        "tog-prefershttps": "Använd alltid en säker anslutning medan jag är inloggad",
        "tog-showrollbackconfirmation": "Visa en bekräftelsedialog när man klickar på en tillbakarullningslänk",
+       "tog-showrollbackconfirmation-prerelease-warning": "OBS: Denna funktion är ännu inte tillgänglig. Om du ändrar denna inställning nu kommer ditt val kommas ihåg [https://meta.wikimedia.org/wiki/WMDE_Technical_Wishes/Rollback#Status när funktionen släpps].",
        "underline-always": "Alltid",
        "underline-never": "Aldrig",
        "underline-default": "Webbläsarens eller utseendets standardinställning",
        "deleting-backlinks-warning": "<strong>Varning:</strong>\n[[Special:WhatLinksHere/{{FULLPAGENAME}}|Andra sidor]] länkar till eller inkluderar sidan som du är på väg att radera.",
        "deleting-subpages-warning": "<strong>Varning:</strong> Sidan du håller på att radera har [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|en undersida|$1 undersidor|51=över 50 undersidor}}]].",
        "rollback": "Rulla tillbaka ändringar",
+       "rollback-confirmation-confirm": "Var god bekräfta:",
+       "rollback-confirmation-yes": "Rulla tillbaka",
+       "rollback-confirmation-no": "Avbryt",
        "rollbacklink": "rulla tillbaka",
        "rollbacklinkcount": "rulla tillbaka $1 {{PLURAL:$1|redigering|redigeringar}}",
        "rollbacklinkcount-morethan": "rulla tillbaka mer än $1 {{PLURAL:$1|redigering|redigeringar}}",
        "confirm-unwatch-top": "Ta bort denna sida från din bevakningslista?",
        "confirm-rollback-button": "OK",
        "confirm-rollback-top": "Återställ redigeringar på denna sida?",
+       "confirm-rollback-bottom": "Denna åtgärd kommer rulla tillbaka de markerade ändringarna på denna sida direkt.",
        "confirm-mcrrestore-title": "Återställ en sidversion",
        "confirm-mcrundo-title": "Ångra en ändring",
        "mcrundofailed": "Misslyckades att ångra",
index 2ad4915..7373150 100644 (file)
        "watchlistanontext": "Lütfen izleme listenizdeki maddeleri görmek ya da değiştirmek için oturum açın.",
        "watchnologin": "Oturum açık değil.",
        "addwatch": "İzleme listesine ekle",
-       "addedwatchtext": "\"[[:$1]]\" sayfası [[Special:Watchlist|izleme listenize]] eklenmiştir.\nBundan sonra, bu sayfaya ve ilgili tartışma sayfasına yapılacak değişiklikler burada listelenecek.",
+       "addedwatchtext": "\"[[:$1]]\" ve ona ait tartışma sayfası [[Special:Watchlist|izleme listenize]] eklenmiştir.",
        "addedwatchtext-short": "\"$1\" sayfası izleme listenize eklendi.",
        "removewatch": "İzleme listesinden kaldır",
-       "removedwatchtext": "\"[[:$1]]\" sayfası, tartışma sayfası ile birlikte [[Special:Watchlist|izleme listenizden]] silinmiştir.",
+       "removedwatchtext": "\"[[:$1]]\" ve ona ait tartışma sayfası ile birlikte [[Special:Watchlist|izleme listenizden]] silinmiştir.",
        "removedwatchtext-short": "\"$1\" sayfası izleme listenizden çıkarıldı.",
        "watch": "izle",
        "watchthispage": "Sayfayı izle",
        "expand_templates_generate_xml": "XML derleyici ağacını göster",
        "expand_templates_generate_rawhtml": "Ham HTML göster",
        "expand_templates_preview": "Önizleme",
-       "expand_templates_preview_fail_html": "<em>{{SITENAME}} işlenmemiş HTML koduna izin verdiği ve oturum verilerinde kayıp yaşandığı için, ön izleme, JavaScript saldırılarına karşı önlem olarak gizlendi.</em>\n\n<strong>Eğer meşru bir ön izleme girişimi idiyse, tekrar deneyiniz.</strong>\nYine de çalışmıyorsa, [[Special:UserLogout|oturum kapamayı]] ve tekrar açmayı deneyin.",
+       "expand_templates_preview_fail_html": "<em>{{SITENAME}} işlenmemiş HTML koduna izin verdiği ve oturum verilerinde kayıp yaşandığı için, ön izleme, JavaScript saldırılarına karşı önlem olarak gizlendi.</em>\n\n<strong>Eğer bu meşru bir ön izleme girişimi idiyse, tekrar deneyiniz.</strong>\nYine de çalışmıyorsa, [[Special:UserLogout|oturum kapatıp]] tekrar açmayı deneyin ve tarayıcınızın bu siteden çerezlere izin verip vermediğini kontrol edin.",
        "expand_templates_preview_fail_html_anon": "<em>{{SITENAME}} işlenmemiş HTML koduna izin verdiği ve oturum verilerinde kayıp yaşandığı için, ön izleme, JavaScript saldırılarına karşı önlem olarak gizlendi.</em>\n\n<strong>Eğer meşru bir ön izleme girişimi idiyse, lütfen  [[Special:UserLogin|oturum açarak]] tekrar deneyin.</strong>",
        "expand_templates_input_missing": "En azından bazı giriş viki metni sağlamak zorundasınız.",
        "pagelanguage": "Sayfanın dilini değiştir",
index fad362d..34b32bf 100644 (file)
        "tog-useeditwarning": "Попереджати мене, якщо я залишаю сторінку редагування з незбереженими змінами",
        "tog-prefershttps": "Завжди використовувати безпечне з'єднання при вході в систему",
        "tog-showrollbackconfirmation": "Показати підтверджувальне вікно при натисканні на посилання відкоту",
+       "tog-showrollbackconfirmation-prerelease-warning": "Зверніть увагу: ця функція ще не доступна. Якщо Ви оберете зараз цю опцію, Ваш вибір буде збережено, [https://meta.wikimedia.org/wiki/WMDE_Technical_Wishes/Rollback#Status коли функцію буде реалізовано].",
        "underline-always": "Завжди",
        "underline-never": "Ніколи",
        "underline-default": "Використовувати налаштування браузера",
        "deleting-backlinks-warning": "<strong>Попередження:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|інші сторінки]] посилаються або містять сторінку, яку Ви маєте намір видалити.",
        "deleting-subpages-warning": "<strong>Попередження:</strong> Сторінка, яку Ви маєте намір вилучити, має [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|$1 підсторінку|$1 підсторінки|$1 підсторінок|51=понад 50 підсторінок}}]].",
        "rollback": "Відкинуто редагування",
+       "rollback-confirmation-confirm": "Будь ласка, підтвердіть:",
+       "rollback-confirmation-yes": "Відкинути",
+       "rollback-confirmation-no": "Скасувати",
        "rollbacklink": "відкинути",
        "rollbacklinkcount": "відкинути $1 {{PLURAL:$1|редагування|редагування|редагувань}}",
        "rollbacklinkcount-morethan": "відкинути понад $1 {{PLURAL:$1|редагування|редагування|редагувань}}",
        "confirm-unwatch-top": "Вилучити цю сторінку з вашого списку спостереження?",
        "confirm-rollback-button": "Гаразд",
        "confirm-rollback-top": "Відкотити редагування цієї сторінки?",
+       "confirm-rollback-bottom": "Ця дія миттєво відкотить обрані зміни на цій сторінці.",
        "confirm-mcrrestore-title": "Відновити версію",
        "confirm-mcrundo-title": "Скасувати зміну",
        "mcrundofailed": "Помилка скасування",
index 5e2cb46..c1b21b7 100644 (file)
        "spam_blanking": "所有修訂均包含 $1 連結,清空中",
        "spam_deleting": "所有修訂均包含 $1 連結,刪除中",
        "simpleantispam-label": "防垃圾訊息檢查用。\n請 <strong>勿</strong> 填寫此欄位!",
-       "pageinfo-title": "\"$1\" 的資訊",
+       "pageinfo-title": "「$1」的資訊",
        "pageinfo-not-current": "抱歉,無法提供先前修訂的資訊。",
        "pageinfo-header-basic": "基本資訊",
        "pageinfo-header-edits": "編輯歷史",
        "logentry-newusers-autocreate": "已自動{{GENDER:$2|建立}}使用者帳號 $1",
        "logentry-protect-move_prot": "$1 {{GENDER:$2|已移動}}保護設定從 $4 至 $3",
        "logentry-protect-unprotect": "$1 {{GENDER:$2|已移除}} $3 的保護",
-       "logentry-protect-protect": "$1 {{GENDER:$2|已保護}} $3 $4",
+       "logentry-protect-protect": "$1 {{GENDER:$2|pó-hō͘ liáu}} $3 $4",
        "logentry-protect-protect-cascade": "$1 {{GENDER:$2|已保護}} $3 $4 [連鎖]",
        "logentry-protect-modify": "$1 {{GENDER:$2|已變更}} $3 的保護層級 $4",
        "logentry-protect-modify-cascade": "$1 {{GENDER:$2|已變更}} $3 的保護層級 $4 [連鎖]",
index 855e014..4de60f3 100644 (file)
 
 $rtl = true;
 
+$namespaceNames = [
+       NS_MEDIA            => 'ߟߊߛߋߢߊߥߙߍ',
+       NS_SPECIAL          => 'ߞߙߍߞߙߍߣߍ߲',
+       NS_TALK             => 'ߢߊߝߐߞߣߍ',
+       NS_USER             => 'ߟߊߓߊ߯ߙߟߊ',
+       NS_USER_TALK        => 'ߟߊߓߊ߯ߙߟߊ ߟߊ߫ ߢߊߝߐߞߣߍ',
+       NS_PROJECT_TALK     => '$1 ߢߊߝߐߞߣߍ',
+       NS_FILE             => 'ߞߐߕߐ߮',
+       NS_FILE_TALK        => 'ߞߐߕߐ߮ ߢߊߝߐߞߣߍ',
+       NS_MEDIAWIKI        => 'ߡߘߌߦߊߥߞߌ',
+       NS_MEDIAWIKI_TALK   => 'ߡߘߌߦߊߥߞߌ ߢߊߝߐߞߣߍ',
+       NS_TEMPLATE         => 'ߞߙߊߞߏ',
+       NS_TEMPLATE_TALK    => 'ߞߙߊߞߏ ߢߊߝߐߞߣߍ',
+       NS_HELP             => 'ߡߊ߬ߘߍ߬ߡߍ߲߬ߠߌ߲',
+       NS_HELP_TALK        => 'ߡߊ߬ߘߍ߬ߡߍ߲߬ߠߌ߲ ߢߊߝߐߞߣߍ',
+       NS_CATEGORY         => 'ߦߌߟߡߊ',
+       NS_CATEGORY_TALK    => 'ߦߌߟߡߊ ߢߊߝߐߞߣߍ',
+];
+
 $digitTransformTable = [
        '0' => '߀', # U+07C0
        '1' => '߁', # U+07C1
index 57e04e0..e81e197 100644 (file)
@@ -117,7 +117,7 @@ class FindOrphanedFiles extends Maintenance {
                                                $oiWheres ? $dbr->makeList( $oiWheres, LIST_OR ) : '1=0'
                                        )
                                ],
-                               true // UNION ALL (performance)
+                               $dbr::UNION_ALL
                        ),
                        __METHOD__
                );
index 3395458..1dd1909 100644 (file)
@@ -129,6 +129,11 @@ class CommandLineInstaller extends Maintenance {
                        $installer->execute();
                        $installer->writeConfigurationFile( $this->getOption( 'confpath', $IP ) );
                }
+               $installer->showMessage(
+                       'config-install-success',
+                       $installer->getVar( 'wgServer' ),
+                       $installer->getVar( 'wgScriptPath' )
+               );
        }
 
        private function setDbPassOption() {
diff --git a/maintenance/mediawiki.Title/generateJsToUpperCaseList.js b/maintenance/mediawiki.Title/generateJsToUpperCaseList.js
new file mode 100644 (file)
index 0000000..fd742f6
--- /dev/null
@@ -0,0 +1,8 @@
+/* eslint-env node, es6 */
+var i, chars = [];
+
+for ( i = 0; i < 65536; i++ ) {
+       chars.push( String.fromCharCode( i ).toUpperCase() );
+}
+// eslint-disable-next-line no-console
+console.log( JSON.stringify( chars ) );
diff --git a/maintenance/mediawiki.Title/generatePhpCharToUpperMappings.php b/maintenance/mediawiki.Title/generatePhpCharToUpperMappings.php
new file mode 100755 (executable)
index 0000000..a04958c
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Utility to generate mapping file used in mw.Title (phpCharToUpper.json)
+ *
+ * Compares output of String.toUpperCase in JavaScript with
+ * mb_strtoupper in PHP, and outputs a list of lower:upper
+ * mappings where they differ. This is then used by Title.js
+ * to provide the same normalization in the client as on
+ * the server.
+ */
+
+$data = [];
+
+// phpcs:disable MediaWiki.Usage.ForbiddenFunctions.exec
+$jsUpperChars = json_decode( exec( 'node generateJsToUpperCaseList.js' ) );
+// phpcs:enable MediaWiki.Usage.ForbiddenFunctions.exec
+
+for ( $i = 0; $i < 65536; $i++ ) {
+       if ( $i >= 0xd800 && $i <= 0xdfff ) {
+               // Skip surrogate pairs
+               continue;
+       }
+       $char = mb_convert_encoding( '&#' . $i . ';', 'UTF-8', 'HTML-ENTITIES' );
+       $phpUpper = mb_strtoupper( $char );
+       $jsUpper = $jsUpperChars[$i];
+       if ( $jsUpper !== $phpUpper ) {
+               $data[$char] = $phpUpper;
+       }
+}
+
+echo str_replace( '    ', "\t",
+       json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE )
+) . "\n";
index d000972..dccdd38 100644 (file)
@@ -412,6 +412,7 @@ $filter = $_REQUEST['filter'] ?? '';
        $queries = [];
        $sqltotal = 0.0;
 
+       /** @var profile_point|false $last */
        $last = false;
        foreach ( $res as $o ) {
                $next = new profile_point( $o->pf_name, $o->pf_count, $o->pf_time, $o->pf_memory );
@@ -435,7 +436,7 @@ $filter = $_REQUEST['filter'] ?? '';
                }
        }
 
-       $s = new profile_point( 'SQL Queries', 0, $sqltotal, 0, 0 );
+       $s = new profile_point( 'SQL Queries', 0, $sqltotal, 0 );
        foreach ( $queries as $q ) {
                $s->add_child( $q );
        }
index 718cd83..ce36105 100644 (file)
@@ -591,19 +591,6 @@ return [
                ],
                'group' => 'jquery.ui',
        ],
-       'jquery.ui.spinner' => [
-               'deprecated' => 'Please use "jquery.spinner" instead.',
-               'scripts' => 'resources/lib/jquery.ui/jquery.ui.spinner.js',
-               'dependencies' => [
-                       'jquery.ui.core',
-                       'jquery.ui.widget',
-                       'jquery.ui.button',
-               ],
-               'skinStyles' => [
-                       'default' => 'resources/lib/jquery.ui/themes/smoothness/jquery.ui.spinner.css',
-               ],
-               'group' => 'jquery.ui',
-       ],
        'jquery.ui.tabs' => [
                'scripts' => 'resources/lib/jquery.ui/jquery.ui.tabs.js',
                'dependencies' => [
@@ -1136,9 +1123,11 @@ return [
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.Title' => [
-               'scripts' => [
-                       'resources/src/mediawiki.Title/Title.js',
-                       'resources/src/mediawiki.Title/phpCharToUpper.js',
+               'localBasePath' => "$IP/resources/src/mediawiki.Title",
+               'remoteBasePath' => "$wgResourceBasePath/resources/src/mediawiki.Title",
+               'packageFiles' => [
+                       'Title.js',
+                       'phpCharToUpper.json'
                ],
                'dependencies' => [
                        'mediawiki.String',
@@ -1457,6 +1446,7 @@ return [
                'skinStyles' => [
                        'default' => 'resources/src/mediawiki.action/mediawiki.action.history.styles.css',
                ],
+               'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.action.view.dblClickEdit' => [
                'scripts' => 'resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js',
@@ -1508,6 +1498,7 @@ return [
                'scripts' => 'resources/src/mediawiki.action/mediawiki.action.view.rightClickEdit.js',
        ],
        'mediawiki.action.edit.editWarning' => [
+               'targets' => [ 'desktop', 'mobile' ],
                'scripts' => 'resources/src/mediawiki.action/mediawiki.action.edit.editWarning.js',
                'dependencies' => [
                        'jquery.textSelection',
diff --git a/resources/lib/jquery.ui/jquery.ui.spinner.js b/resources/lib/jquery.ui/jquery.ui.spinner.js
deleted file mode 100644 (file)
index 98dc9df..0000000
+++ /dev/null
@@ -1,478 +0,0 @@
-/*!
- * jQuery UI Spinner 1.9.2
- * http://jqueryui.com
- *
- * Copyright 2012 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/spinner/
- *
- * Depends:
- *  jquery.ui.core.js
- *  jquery.ui.widget.js
- *  jquery.ui.button.js
- */
-(function( $ ) {
-
-function modifier( fn ) {
-       return function() {
-               var previous = this.element.val();
-               fn.apply( this, arguments );
-               this._refresh();
-               if ( previous !== this.element.val() ) {
-                       this._trigger( "change" );
-               }
-       };
-}
-
-$.widget( "ui.spinner", {
-       version: "1.9.2",
-       defaultElement: "<input>",
-       widgetEventPrefix: "spin",
-       options: {
-               culture: null,
-               icons: {
-                       down: "ui-icon-triangle-1-s",
-                       up: "ui-icon-triangle-1-n"
-               },
-               incremental: true,
-               max: null,
-               min: null,
-               numberFormat: null,
-               page: 10,
-               step: 1,
-
-               change: null,
-               spin: null,
-               start: null,
-               stop: null
-       },
-
-       _create: function() {
-               // handle string values that need to be parsed
-               this._setOption( "max", this.options.max );
-               this._setOption( "min", this.options.min );
-               this._setOption( "step", this.options.step );
-
-               // format the value, but don't constrain
-               this._value( this.element.val(), true );
-
-               this._draw();
-               this._on( this._events );
-               this._refresh();
-
-               // turning off autocomplete prevents the browser from remembering the
-               // value when navigating through history, so we re-enable autocomplete
-               // if the page is unloaded before the widget is destroyed. #7790
-               this._on( this.window, {
-                       beforeunload: function() {
-                               this.element.removeAttr( "autocomplete" );
-                       }
-               });
-       },
-
-       _getCreateOptions: function() {
-               var options = {},
-                       element = this.element;
-
-               $.each( [ "min", "max", "step" ], function( i, option ) {
-                       var value = element.attr( option );
-                       if ( value !== undefined && value.length ) {
-                               options[ option ] = value;
-                       }
-               });
-
-               return options;
-       },
-
-       _events: {
-               keydown: function( event ) {
-                       if ( this._start( event ) && this._keydown( event ) ) {
-                               event.preventDefault();
-                       }
-               },
-               keyup: "_stop",
-               focus: function() {
-                       this.previous = this.element.val();
-               },
-               blur: function( event ) {
-                       if ( this.cancelBlur ) {
-                               delete this.cancelBlur;
-                               return;
-                       }
-
-                       this._refresh();
-                       if ( this.previous !== this.element.val() ) {
-                               this._trigger( "change", event );
-                       }
-               },
-               mousewheel: function( event, delta ) {
-                       if ( !delta ) {
-                               return;
-                       }
-                       if ( !this.spinning && !this._start( event ) ) {
-                               return false;
-                       }
-
-                       this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
-                       clearTimeout( this.mousewheelTimer );
-                       this.mousewheelTimer = this._delay(function() {
-                               if ( this.spinning ) {
-                                       this._stop( event );
-                               }
-                       }, 100 );
-                       event.preventDefault();
-               },
-               "mousedown .ui-spinner-button": function( event ) {
-                       var previous;
-
-                       // We never want the buttons to have focus; whenever the user is
-                       // interacting with the spinner, the focus should be on the input.
-                       // If the input is focused then this.previous is properly set from
-                       // when the input first received focus. If the input is not focused
-                       // then we need to set this.previous based on the value before spinning.
-                       previous = this.element[0] === this.document[0].activeElement ?
-                               this.previous : this.element.val();
-                       function checkFocus() {
-                               var isActive = this.element[0] === this.document[0].activeElement;
-                               if ( !isActive ) {
-                                       this.element.focus();
-                                       this.previous = previous;
-                                       // support: IE
-                                       // IE sets focus asynchronously, so we need to check if focus
-                                       // moved off of the input because the user clicked on the button.
-                                       this._delay(function() {
-                                               this.previous = previous;
-                                       });
-                               }
-                       }
-
-                       // ensure focus is on (or stays on) the text field
-                       event.preventDefault();
-                       checkFocus.call( this );
-
-                       // support: IE
-                       // IE doesn't prevent moving focus even with event.preventDefault()
-                       // so we set a flag to know when we should ignore the blur event
-                       // and check (again) if focus moved off of the input.
-                       this.cancelBlur = true;
-                       this._delay(function() {
-                               delete this.cancelBlur;
-                               checkFocus.call( this );
-                       });
-
-                       if ( this._start( event ) === false ) {
-                               return;
-                       }
-
-                       this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
-               },
-               "mouseup .ui-spinner-button": "_stop",
-               "mouseenter .ui-spinner-button": function( event ) {
-                       // button will add ui-state-active if mouse was down while mouseleave and kept down
-                       if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
-                               return;
-                       }
-
-                       if ( this._start( event ) === false ) {
-                               return false;
-                       }
-                       this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
-               },
-               // TODO: do we really want to consider this a stop?
-               // shouldn't we just stop the repeater and wait until mouseup before
-               // we trigger the stop event?
-               "mouseleave .ui-spinner-button": "_stop"
-       },
-
-       _draw: function() {
-               var uiSpinner = this.uiSpinner = this.element
-                       .addClass( "ui-spinner-input" )
-                       .attr( "autocomplete", "off" )
-                       .wrap( this._uiSpinnerHtml() )
-                       .parent()
-                               // add buttons
-                               .append( this._buttonHtml() );
-
-               this.element.attr( "role", "spinbutton" );
-
-               // button bindings
-               this.buttons = uiSpinner.find( ".ui-spinner-button" )
-                       .attr( "tabIndex", -1 )
-                       .button()
-                       .removeClass( "ui-corner-all" );
-
-               // IE 6 doesn't understand height: 50% for the buttons
-               // unless the wrapper has an explicit height
-               if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
-                               uiSpinner.height() > 0 ) {
-                       uiSpinner.height( uiSpinner.height() );
-               }
-
-               // disable spinner if element was already disabled
-               if ( this.options.disabled ) {
-                       this.disable();
-               }
-       },
-
-       _keydown: function( event ) {
-               var options = this.options,
-                       keyCode = $.ui.keyCode;
-
-               switch ( event.keyCode ) {
-               case keyCode.UP:
-                       this._repeat( null, 1, event );
-                       return true;
-               case keyCode.DOWN:
-                       this._repeat( null, -1, event );
-                       return true;
-               case keyCode.PAGE_UP:
-                       this._repeat( null, options.page, event );
-                       return true;
-               case keyCode.PAGE_DOWN:
-                       this._repeat( null, -options.page, event );
-                       return true;
-               }
-
-               return false;
-       },
-
-       _uiSpinnerHtml: function() {
-               return "<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>";
-       },
-
-       _buttonHtml: function() {
-               return "" +
-                       "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" +
-                               "<span class='ui-icon " + this.options.icons.up + "'>&#9650;</span>" +
-                       "</a>" +
-                       "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" +
-                               "<span class='ui-icon " + this.options.icons.down + "'>&#9660;</span>" +
-                       "</a>";
-       },
-
-       _start: function( event ) {
-               if ( !this.spinning && this._trigger( "start", event ) === false ) {
-                       return false;
-               }
-
-               if ( !this.counter ) {
-                       this.counter = 1;
-               }
-               this.spinning = true;
-               return true;
-       },
-
-       _repeat: function( i, steps, event ) {
-               i = i || 500;
-
-               clearTimeout( this.timer );
-               this.timer = this._delay(function() {
-                       this._repeat( 40, steps, event );
-               }, i );
-
-               this._spin( steps * this.options.step, event );
-       },
-
-       _spin: function( step, event ) {
-               var value = this.value() || 0;
-
-               if ( !this.counter ) {
-                       this.counter = 1;
-               }
-
-               value = this._adjustValue( value + step * this._increment( this.counter ) );
-
-               if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
-                       this._value( value );
-                       this.counter++;
-               }
-       },
-
-       _increment: function( i ) {
-               var incremental = this.options.incremental;
-
-               if ( incremental ) {
-                       return $.isFunction( incremental ) ?
-                               incremental( i ) :
-                               Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
-               }
-
-               return 1;
-       },
-
-       _precision: function() {
-               var precision = this._precisionOf( this.options.step );
-               if ( this.options.min !== null ) {
-                       precision = Math.max( precision, this._precisionOf( this.options.min ) );
-               }
-               return precision;
-       },
-
-       _precisionOf: function( num ) {
-               var str = num.toString(),
-                       decimal = str.indexOf( "." );
-               return decimal === -1 ? 0 : str.length - decimal - 1;
-       },
-
-       _adjustValue: function( value ) {
-               var base, aboveMin,
-                       options = this.options;
-
-               // make sure we're at a valid step
-               // - find out where we are relative to the base (min or 0)
-               base = options.min !== null ? options.min : 0;
-               aboveMin = value - base;
-               // - round to the nearest step
-               aboveMin = Math.round(aboveMin / options.step) * options.step;
-               // - rounding is based on 0, so adjust back to our base
-               value = base + aboveMin;
-
-               // fix precision from bad JS floating point math
-               value = parseFloat( value.toFixed( this._precision() ) );
-
-               // clamp the value
-               if ( options.max !== null && value > options.max) {
-                       return options.max;
-               }
-               if ( options.min !== null && value < options.min ) {
-                       return options.min;
-               }
-
-               return value;
-       },
-
-       _stop: function( event ) {
-               if ( !this.spinning ) {
-                       return;
-               }
-
-               clearTimeout( this.timer );
-               clearTimeout( this.mousewheelTimer );
-               this.counter = 0;
-               this.spinning = false;
-               this._trigger( "stop", event );
-       },
-
-       _setOption: function( key, value ) {
-               if ( key === "culture" || key === "numberFormat" ) {
-                       var prevValue = this._parse( this.element.val() );
-                       this.options[ key ] = value;
-                       this.element.val( this._format( prevValue ) );
-                       return;
-               }
-
-               if ( key === "max" || key === "min" || key === "step" ) {
-                       if ( typeof value === "string" ) {
-                               value = this._parse( value );
-                       }
-               }
-
-               this._super( key, value );
-
-               if ( key === "disabled" ) {
-                       if ( value ) {
-                               this.element.prop( "disabled", true );
-                               this.buttons.button( "disable" );
-                       } else {
-                               this.element.prop( "disabled", false );
-                               this.buttons.button( "enable" );
-                       }
-               }
-       },
-
-       _setOptions: modifier(function( options ) {
-               this._super( options );
-               this._value( this.element.val() );
-       }),
-
-       _parse: function( val ) {
-               if ( typeof val === "string" && val !== "" ) {
-                       val = window.Globalize && this.options.numberFormat ?
-                               Globalize.parseFloat( val, 10, this.options.culture ) : +val;
-               }
-               return val === "" || isNaN( val ) ? null : val;
-       },
-
-       _format: function( value ) {
-               if ( value === "" ) {
-                       return "";
-               }
-               return window.Globalize && this.options.numberFormat ?
-                       Globalize.format( value, this.options.numberFormat, this.options.culture ) :
-                       value;
-       },
-
-       _refresh: function() {
-               this.element.attr({
-                       "aria-valuemin": this.options.min,
-                       "aria-valuemax": this.options.max,
-                       // TODO: what should we do with values that can't be parsed?
-                       "aria-valuenow": this._parse( this.element.val() )
-               });
-       },
-
-       // update the value without triggering change
-       _value: function( value, allowAny ) {
-               var parsed;
-               if ( value !== "" ) {
-                       parsed = this._parse( value );
-                       if ( parsed !== null ) {
-                               if ( !allowAny ) {
-                                       parsed = this._adjustValue( parsed );
-                               }
-                               value = this._format( parsed );
-                       }
-               }
-               this.element.val( value );
-               this._refresh();
-       },
-
-       _destroy: function() {
-               this.element
-                       .removeClass( "ui-spinner-input" )
-                       .prop( "disabled", false )
-                       .removeAttr( "autocomplete" )
-                       .removeAttr( "role" )
-                       .removeAttr( "aria-valuemin" )
-                       .removeAttr( "aria-valuemax" )
-                       .removeAttr( "aria-valuenow" );
-               this.uiSpinner.replaceWith( this.element );
-       },
-
-       stepUp: modifier(function( steps ) {
-               this._stepUp( steps );
-       }),
-       _stepUp: function( steps ) {
-               this._spin( (steps || 1) * this.options.step );
-       },
-
-       stepDown: modifier(function( steps ) {
-               this._stepDown( steps );
-       }),
-       _stepDown: function( steps ) {
-               this._spin( (steps || 1) * -this.options.step );
-       },
-
-       pageUp: modifier(function( pages ) {
-               this._stepUp( (pages || 1) * this.options.page );
-       }),
-
-       pageDown: modifier(function( pages ) {
-               this._stepDown( (pages || 1) * this.options.page );
-       }),
-
-       value: function( newVal ) {
-               if ( !arguments.length ) {
-                       return this._parse( this.element.val() );
-               }
-               modifier( this._value ).call( this, newVal );
-       },
-
-       widget: function() {
-               return this.uiSpinner;
-       }
-});
-
-}( jQuery ) );
diff --git a/resources/lib/jquery.ui/themes/smoothness/jquery.ui.spinner.css b/resources/lib/jquery.ui/themes/smoothness/jquery.ui.spinner.css
deleted file mode 100644 (file)
index e89b720..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*!
- * jQuery UI Spinner 1.9.2
- * http://jqueryui.com
- *
- * Copyright 2012 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Spinner#theming
- */
-.ui-spinner { position:relative; display: inline-block; overflow: hidden; padding: 0; vertical-align: middle; }
-.ui-spinner-input { border: none; background: none; padding: 0; margin: .2em 0; vertical-align: middle; margin-left: .4em; margin-right: 22px; }
-.ui-spinner-button { width: 16px; height: 50%; font-size: .5em; padding: 0; margin: 0; text-align: center; position: absolute; cursor: default; display: block; overflow: hidden; right: 0; }
-.ui-spinner a.ui-spinner-button { border-top: none; border-bottom: none; border-right: none; } /* more specificity required here to overide default borders */
-.ui-spinner .ui-icon { position: absolute; margin-top: -8px; top: 50%; left: 0; } /* vertical centre icon */
-.ui-spinner-up { top: 0; }
-.ui-spinner-down { bottom: 0; }
-
-/* TR overrides */
-.ui-spinner .ui-icon-triangle-1-s {
-       /* need to fix icons sprite */
-       background-position:-65px -16px;
-}
index 6bb3bce..78ae135 100644 (file)
@@ -34,6 +34,8 @@
        var
                mwString = require( 'mediawiki.String' ),
 
+               toUpperMapping = require( './phpCharToUpper.json' ),
+
                namespaceIds = mw.config.get( 'wgNamespaceIds' ),
 
                /**
                }
        };
 
+       /**
+        * PHP's strtoupper differs from String.toUpperCase in a number of cases (T147646).
+        *
+        * @param {string} chr Unicode character
+        * @return {string} Unicode character, in upper case, according to the same rules as in PHP
+        */
+       Title.phpCharToUpper = function ( chr ) {
+               var mapped = toUpperMapping[ chr ];
+               return mapped || chr.toUpperCase();
+       };
+
        /* Public members */
 
        Title.prototype = {
                        ) {
                                return this.title;
                        }
-                       // PHP's strtoupper differs from String.toUpperCase in a number of cases
-                       // Bug: T147646
                        return mw.Title.phpCharToUpper( this.title[ 0 ] ) + this.title.slice( 1 );
                },
 
diff --git a/resources/src/mediawiki.Title/phpCharToUpper.js b/resources/src/mediawiki.Title/phpCharToUpper.js
deleted file mode 100644 (file)
index ed700f0..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-// This file can't be parsed by JSDuck due to <https://github.com/tenderlove/rkelly/issues/35>.
-// (It is excluded in jsduck.json.)
-// ESLint suggests unquoting some object keys, which would render the file unparseable by Opera 12.
-/* eslint-disable quote-props */
-( function () {
-       var toUpperMapping = {
-               'ß': 'ß',
-               'ʼn': 'ʼn',
-               'Dž': 'Dž',
-               'dž': 'Dž',
-               'Lj': 'Lj',
-               'lj': 'Lj',
-               'Nj': 'Nj',
-               'nj': 'Nj',
-               'ǰ': 'ǰ',
-               'Dz': 'Dz',
-               'dz': 'Dz',
-               'ʝ': 'Ʝ',
-               'ͅ': 'ͅ',
-               'ΐ': 'ΐ',
-               'ΰ': 'ΰ',
-               'և': 'և',
-               'ᏸ': 'Ᏸ',
-               'ᏹ': 'Ᏹ',
-               'ᏺ': 'Ᏺ',
-               'ᏻ': 'Ᏻ',
-               'ᏼ': 'Ᏼ',
-               'ᏽ': 'Ᏽ',
-               'ẖ': 'ẖ',
-               'ẗ': 'ẗ',
-               'ẘ': 'ẘ',
-               'ẙ': 'ẙ',
-               'ẚ': 'ẚ',
-               'ὐ': 'ὐ',
-               'ὒ': 'ὒ',
-               'ὔ': 'ὔ',
-               'ὖ': 'ὖ',
-               'ᾀ': 'ᾈ',
-               'ᾁ': 'ᾉ',
-               'ᾂ': 'ᾊ',
-               'ᾃ': 'ᾋ',
-               'ᾄ': 'ᾌ',
-               'ᾅ': 'ᾍ',
-               'ᾆ': 'ᾎ',
-               'ᾇ': 'ᾏ',
-               'ᾈ': 'ᾈ',
-               'ᾉ': 'ᾉ',
-               'ᾊ': 'ᾊ',
-               'ᾋ': 'ᾋ',
-               'ᾌ': 'ᾌ',
-               'ᾍ': 'ᾍ',
-               'ᾎ': 'ᾎ',
-               'ᾏ': 'ᾏ',
-               'ᾐ': 'ᾘ',
-               'ᾑ': 'ᾙ',
-               'ᾒ': 'ᾚ',
-               'ᾓ': 'ᾛ',
-               'ᾔ': 'ᾜ',
-               'ᾕ': 'ᾝ',
-               'ᾖ': 'ᾞ',
-               'ᾗ': 'ᾟ',
-               'ᾘ': 'ᾘ',
-               'ᾙ': 'ᾙ',
-               'ᾚ': 'ᾚ',
-               'ᾛ': 'ᾛ',
-               'ᾜ': 'ᾜ',
-               'ᾝ': 'ᾝ',
-               'ᾞ': 'ᾞ',
-               'ᾟ': 'ᾟ',
-               'ᾠ': 'ᾨ',
-               'ᾡ': 'ᾩ',
-               'ᾢ': 'ᾪ',
-               'ᾣ': 'ᾫ',
-               'ᾤ': 'ᾬ',
-               'ᾥ': 'ᾭ',
-               'ᾦ': 'ᾮ',
-               'ᾧ': 'ᾯ',
-               'ᾨ': 'ᾨ',
-               'ᾩ': 'ᾩ',
-               'ᾪ': 'ᾪ',
-               'ᾫ': 'ᾫ',
-               'ᾬ': 'ᾬ',
-               'ᾭ': 'ᾭ',
-               'ᾮ': 'ᾮ',
-               'ᾯ': 'ᾯ',
-               'ᾲ': 'ᾲ',
-               'ᾳ': 'ᾼ',
-               'ᾴ': 'ᾴ',
-               'ᾶ': 'ᾶ',
-               'ᾷ': 'ᾷ',
-               'ᾼ': 'ᾼ',
-               'ῂ': 'ῂ',
-               'ῃ': 'ῌ',
-               'ῄ': 'ῄ',
-               'ῆ': 'ῆ',
-               'ῇ': 'ῇ',
-               'ῌ': 'ῌ',
-               'ῒ': 'ῒ',
-               'ΐ': 'ΐ',
-               'ῖ': 'ῖ',
-               'ῗ': 'ῗ',
-               'ῢ': 'ῢ',
-               'ΰ': 'ΰ',
-               'ῤ': 'ῤ',
-               'ῦ': 'ῦ',
-               'ῧ': 'ῧ',
-               'ῲ': 'ῲ',
-               'ῳ': 'ῼ',
-               'ῴ': 'ῴ',
-               'ῶ': 'ῶ',
-               'ῷ': 'ῷ',
-               'ῼ': 'ῼ',
-               'ⅰ': 'ⅰ',
-               'ⅱ': 'ⅱ',
-               'ⅲ': 'ⅲ',
-               'ⅳ': 'ⅳ',
-               'ⅴ': 'ⅴ',
-               'ⅵ': 'ⅵ',
-               'ⅶ': 'ⅶ',
-               'ⅷ': 'ⅷ',
-               'ⅸ': 'ⅸ',
-               'ⅹ': 'ⅹ',
-               'ⅺ': 'ⅺ',
-               'ⅻ': 'ⅻ',
-               'ⅼ': 'ⅼ',
-               'ⅽ': 'ⅽ',
-               'ⅾ': 'ⅾ',
-               'ⅿ': 'ⅿ',
-               'ⓐ': 'ⓐ',
-               'ⓑ': 'ⓑ',
-               'ⓒ': 'ⓒ',
-               'ⓓ': 'ⓓ',
-               'ⓔ': 'ⓔ',
-               'ⓕ': 'ⓕ',
-               'ⓖ': 'ⓖ',
-               'ⓗ': 'ⓗ',
-               'ⓘ': 'ⓘ',
-               'ⓙ': 'ⓙ',
-               'ⓚ': 'ⓚ',
-               'ⓛ': 'ⓛ',
-               'ⓜ': 'ⓜ',
-               'ⓝ': 'ⓝ',
-               'ⓞ': 'ⓞ',
-               'ⓟ': 'ⓟ',
-               'ⓠ': 'ⓠ',
-               'ⓡ': 'ⓡ',
-               'ⓢ': 'ⓢ',
-               'ⓣ': 'ⓣ',
-               'ⓤ': 'ⓤ',
-               'ⓥ': 'ⓥ',
-               'ⓦ': 'ⓦ',
-               'ⓧ': 'ⓧ',
-               'ⓨ': 'ⓨ',
-               'ⓩ': 'ⓩ',
-               'ꞵ': 'Ꞵ',
-               'ꞷ': 'Ꞷ',
-               'ꭓ': 'Ꭓ',
-               'ꭰ': 'Ꭰ',
-               'ꭱ': 'Ꭱ',
-               'ꭲ': 'Ꭲ',
-               'ꭳ': 'Ꭳ',
-               'ꭴ': 'Ꭴ',
-               'ꭵ': 'Ꭵ',
-               'ꭶ': 'Ꭶ',
-               'ꭷ': 'Ꭷ',
-               'ꭸ': 'Ꭸ',
-               'ꭹ': 'Ꭹ',
-               'ꭺ': 'Ꭺ',
-               'ꭻ': 'Ꭻ',
-               'ꭼ': 'Ꭼ',
-               'ꭽ': 'Ꭽ',
-               'ꭾ': 'Ꭾ',
-               'ꭿ': 'Ꭿ',
-               'ꮀ': 'Ꮀ',
-               'ꮁ': 'Ꮁ',
-               'ꮂ': 'Ꮂ',
-               'ꮃ': 'Ꮃ',
-               'ꮄ': 'Ꮄ',
-               'ꮅ': 'Ꮅ',
-               'ꮆ': 'Ꮆ',
-               'ꮇ': 'Ꮇ',
-               'ꮈ': 'Ꮈ',
-               'ꮉ': 'Ꮉ',
-               'ꮊ': 'Ꮊ',
-               'ꮋ': 'Ꮋ',
-               'ꮌ': 'Ꮌ',
-               'ꮍ': 'Ꮍ',
-               'ꮎ': 'Ꮎ',
-               'ꮏ': 'Ꮏ',
-               'ꮐ': 'Ꮐ',
-               'ꮑ': 'Ꮑ',
-               'ꮒ': 'Ꮒ',
-               'ꮓ': 'Ꮓ',
-               'ꮔ': 'Ꮔ',
-               'ꮕ': 'Ꮕ',
-               'ꮖ': 'Ꮖ',
-               'ꮗ': 'Ꮗ',
-               'ꮘ': 'Ꮘ',
-               'ꮙ': 'Ꮙ',
-               'ꮚ': 'Ꮚ',
-               'ꮛ': 'Ꮛ',
-               'ꮜ': 'Ꮜ',
-               'ꮝ': 'Ꮝ',
-               'ꮞ': 'Ꮞ',
-               'ꮟ': 'Ꮟ',
-               'ꮠ': 'Ꮠ',
-               'ꮡ': 'Ꮡ',
-               'ꮢ': 'Ꮢ',
-               'ꮣ': 'Ꮣ',
-               'ꮤ': 'Ꮤ',
-               'ꮥ': 'Ꮥ',
-               'ꮦ': 'Ꮦ',
-               'ꮧ': 'Ꮧ',
-               'ꮨ': 'Ꮨ',
-               'ꮩ': 'Ꮩ',
-               'ꮪ': 'Ꮪ',
-               'ꮫ': 'Ꮫ',
-               'ꮬ': 'Ꮬ',
-               'ꮭ': 'Ꮭ',
-               'ꮮ': 'Ꮮ',
-               'ꮯ': 'Ꮯ',
-               'ꮰ': 'Ꮰ',
-               'ꮱ': 'Ꮱ',
-               'ꮲ': 'Ꮲ',
-               'ꮳ': 'Ꮳ',
-               'ꮴ': 'Ꮴ',
-               'ꮵ': 'Ꮵ',
-               'ꮶ': 'Ꮶ',
-               'ꮷ': 'Ꮷ',
-               'ꮸ': 'Ꮸ',
-               'ꮹ': 'Ꮹ',
-               'ꮺ': 'Ꮺ',
-               'ꮻ': 'Ꮻ',
-               'ꮼ': 'Ꮼ',
-               'ꮽ': 'Ꮽ',
-               'ꮾ': 'Ꮾ',
-               'ꮿ': 'Ꮿ',
-               'ff': 'ff',
-               'fi': 'fi',
-               'fl': 'fl',
-               'ffi': 'ffi',
-               'ffl': 'ffl',
-               'ſt': 'ſt',
-               'st': 'st',
-               'ﬓ': 'ﬓ',
-               'ﬔ': 'ﬔ',
-               'ﬕ': 'ﬕ',
-               'ﬖ': 'ﬖ',
-               'ﬗ': 'ﬗ'
-       };
-       mw.Title.phpCharToUpper = function ( chr ) {
-               var mapped = toUpperMapping[ chr ];
-               return mapped || chr.toUpperCase();
-       };
-}() );
diff --git a/resources/src/mediawiki.Title/phpCharToUpper.json b/resources/src/mediawiki.Title/phpCharToUpper.json
new file mode 100644 (file)
index 0000000..b0887fa
--- /dev/null
@@ -0,0 +1,245 @@
+{
+       "ß": "ß",
+       "ʼn": "ʼn",
+       "Dž": "Dž",
+       "dž": "Dž",
+       "Lj": "Lj",
+       "lj": "Lj",
+       "Nj": "Nj",
+       "nj": "Nj",
+       "ǰ": "ǰ",
+       "Dz": "Dz",
+       "dz": "Dz",
+       "ʝ": "Ʝ",
+       "ͅ": "ͅ",
+       "ΐ": "ΐ",
+       "ΰ": "ΰ",
+       "և": "և",
+       "ᏸ": "Ᏸ",
+       "ᏹ": "Ᏹ",
+       "ᏺ": "Ᏺ",
+       "ᏻ": "Ᏻ",
+       "ᏼ": "Ᏼ",
+       "ᏽ": "Ᏽ",
+       "ẖ": "ẖ",
+       "ẗ": "ẗ",
+       "ẘ": "ẘ",
+       "ẙ": "ẙ",
+       "ẚ": "ẚ",
+       "ὐ": "ὐ",
+       "ὒ": "ὒ",
+       "ὔ": "ὔ",
+       "ὖ": "ὖ",
+       "ᾀ": "ᾈ",
+       "ᾁ": "ᾉ",
+       "ᾂ": "ᾊ",
+       "ᾃ": "ᾋ",
+       "ᾄ": "ᾌ",
+       "ᾅ": "ᾍ",
+       "ᾆ": "ᾎ",
+       "ᾇ": "ᾏ",
+       "ᾈ": "ᾈ",
+       "ᾉ": "ᾉ",
+       "ᾊ": "ᾊ",
+       "ᾋ": "ᾋ",
+       "ᾌ": "ᾌ",
+       "ᾍ": "ᾍ",
+       "ᾎ": "ᾎ",
+       "ᾏ": "ᾏ",
+       "ᾐ": "ᾘ",
+       "ᾑ": "ᾙ",
+       "ᾒ": "ᾚ",
+       "ᾓ": "ᾛ",
+       "ᾔ": "ᾜ",
+       "ᾕ": "ᾝ",
+       "ᾖ": "ᾞ",
+       "ᾗ": "ᾟ",
+       "ᾘ": "ᾘ",
+       "ᾙ": "ᾙ",
+       "ᾚ": "ᾚ",
+       "ᾛ": "ᾛ",
+       "ᾜ": "ᾜ",
+       "ᾝ": "ᾝ",
+       "ᾞ": "ᾞ",
+       "ᾟ": "ᾟ",
+       "ᾠ": "ᾨ",
+       "ᾡ": "ᾩ",
+       "ᾢ": "ᾪ",
+       "ᾣ": "ᾫ",
+       "ᾤ": "ᾬ",
+       "ᾥ": "ᾭ",
+       "ᾦ": "ᾮ",
+       "ᾧ": "ᾯ",
+       "ᾨ": "ᾨ",
+       "ᾩ": "ᾩ",
+       "ᾪ": "ᾪ",
+       "ᾫ": "ᾫ",
+       "ᾬ": "ᾬ",
+       "ᾭ": "ᾭ",
+       "ᾮ": "ᾮ",
+       "ᾯ": "ᾯ",
+       "ᾲ": "ᾲ",
+       "ᾳ": "ᾼ",
+       "ᾴ": "ᾴ",
+       "ᾶ": "ᾶ",
+       "ᾷ": "ᾷ",
+       "ᾼ": "ᾼ",
+       "ῂ": "ῂ",
+       "ῃ": "ῌ",
+       "ῄ": "ῄ",
+       "ῆ": "ῆ",
+       "ῇ": "ῇ",
+       "ῌ": "ῌ",
+       "ῒ": "ῒ",
+       "ΐ": "ΐ",
+       "ῖ": "ῖ",
+       "ῗ": "ῗ",
+       "ῢ": "ῢ",
+       "ΰ": "ΰ",
+       "ῤ": "ῤ",
+       "ῦ": "ῦ",
+       "ῧ": "ῧ",
+       "ῲ": "ῲ",
+       "ῳ": "ῼ",
+       "ῴ": "ῴ",
+       "ῶ": "ῶ",
+       "ῷ": "ῷ",
+       "ῼ": "ῼ",
+       "ⅰ": "ⅰ",
+       "ⅱ": "ⅱ",
+       "ⅲ": "ⅲ",
+       "ⅳ": "ⅳ",
+       "ⅴ": "ⅴ",
+       "ⅵ": "ⅵ",
+       "ⅶ": "ⅶ",
+       "ⅷ": "ⅷ",
+       "ⅸ": "ⅸ",
+       "ⅹ": "ⅹ",
+       "ⅺ": "ⅺ",
+       "ⅻ": "ⅻ",
+       "ⅼ": "ⅼ",
+       "ⅽ": "ⅽ",
+       "ⅾ": "ⅾ",
+       "ⅿ": "ⅿ",
+       "ⓐ": "ⓐ",
+       "ⓑ": "ⓑ",
+       "ⓒ": "ⓒ",
+       "ⓓ": "ⓓ",
+       "ⓔ": "ⓔ",
+       "ⓕ": "ⓕ",
+       "ⓖ": "ⓖ",
+       "ⓗ": "ⓗ",
+       "ⓘ": "ⓘ",
+       "ⓙ": "ⓙ",
+       "ⓚ": "ⓚ",
+       "ⓛ": "ⓛ",
+       "ⓜ": "ⓜ",
+       "ⓝ": "ⓝ",
+       "ⓞ": "ⓞ",
+       "ⓟ": "ⓟ",
+       "ⓠ": "ⓠ",
+       "ⓡ": "ⓡ",
+       "ⓢ": "ⓢ",
+       "ⓣ": "ⓣ",
+       "ⓤ": "ⓤ",
+       "ⓥ": "ⓥ",
+       "ⓦ": "ⓦ",
+       "ⓧ": "ⓧ",
+       "ⓨ": "ⓨ",
+       "ⓩ": "ⓩ",
+       "ꞵ": "Ꞵ",
+       "ꞷ": "Ꞷ",
+       "ꭓ": "Ꭓ",
+       "ꭰ": "Ꭰ",
+       "ꭱ": "Ꭱ",
+       "ꭲ": "Ꭲ",
+       "ꭳ": "Ꭳ",
+       "ꭴ": "Ꭴ",
+       "ꭵ": "Ꭵ",
+       "ꭶ": "Ꭶ",
+       "ꭷ": "Ꭷ",
+       "ꭸ": "Ꭸ",
+       "ꭹ": "Ꭹ",
+       "ꭺ": "Ꭺ",
+       "ꭻ": "Ꭻ",
+       "ꭼ": "Ꭼ",
+       "ꭽ": "Ꭽ",
+       "ꭾ": "Ꭾ",
+       "ꭿ": "Ꭿ",
+       "ꮀ": "Ꮀ",
+       "ꮁ": "Ꮁ",
+       "ꮂ": "Ꮂ",
+       "ꮃ": "Ꮃ",
+       "ꮄ": "Ꮄ",
+       "ꮅ": "Ꮅ",
+       "ꮆ": "Ꮆ",
+       "ꮇ": "Ꮇ",
+       "ꮈ": "Ꮈ",
+       "ꮉ": "Ꮉ",
+       "ꮊ": "Ꮊ",
+       "ꮋ": "Ꮋ",
+       "ꮌ": "Ꮌ",
+       "ꮍ": "Ꮍ",
+       "ꮎ": "Ꮎ",
+       "ꮏ": "Ꮏ",
+       "ꮐ": "Ꮐ",
+       "ꮑ": "Ꮑ",
+       "ꮒ": "Ꮒ",
+       "ꮓ": "Ꮓ",
+       "ꮔ": "Ꮔ",
+       "ꮕ": "Ꮕ",
+       "ꮖ": "Ꮖ",
+       "ꮗ": "Ꮗ",
+       "ꮘ": "Ꮘ",
+       "ꮙ": "Ꮙ",
+       "ꮚ": "Ꮚ",
+       "ꮛ": "Ꮛ",
+       "ꮜ": "Ꮜ",
+       "ꮝ": "Ꮝ",
+       "ꮞ": "Ꮞ",
+       "ꮟ": "Ꮟ",
+       "ꮠ": "Ꮠ",
+       "ꮡ": "Ꮡ",
+       "ꮢ": "Ꮢ",
+       "ꮣ": "Ꮣ",
+       "ꮤ": "Ꮤ",
+       "ꮥ": "Ꮥ",
+       "ꮦ": "Ꮦ",
+       "ꮧ": "Ꮧ",
+       "ꮨ": "Ꮨ",
+       "ꮩ": "Ꮩ",
+       "ꮪ": "Ꮪ",
+       "ꮫ": "Ꮫ",
+       "ꮬ": "Ꮬ",
+       "ꮭ": "Ꮭ",
+       "ꮮ": "Ꮮ",
+       "ꮯ": "Ꮯ",
+       "ꮰ": "Ꮰ",
+       "ꮱ": "Ꮱ",
+       "ꮲ": "Ꮲ",
+       "ꮳ": "Ꮳ",
+       "ꮴ": "Ꮴ",
+       "ꮵ": "Ꮵ",
+       "ꮶ": "Ꮶ",
+       "ꮷ": "Ꮷ",
+       "ꮸ": "Ꮸ",
+       "ꮹ": "Ꮹ",
+       "ꮺ": "Ꮺ",
+       "ꮻ": "Ꮻ",
+       "ꮼ": "Ꮼ",
+       "ꮽ": "Ꮽ",
+       "ꮾ": "Ꮾ",
+       "ꮿ": "Ꮿ",
+       "ff": "ff",
+       "fi": "fi",
+       "fl": "fl",
+       "ffi": "ffi",
+       "ffl": "ffl",
+       "ſt": "ſt",
+       "st": "st",
+       "ﬓ": "ﬓ",
+       "ﬔ": "ﬔ",
+       "ﬕ": "ﬕ",
+       "ﬖ": "ﬖ",
+       "ﬗ": "ﬗ"
+}
index ffa9e42..71b343c 100644 (file)
@@ -3,9 +3,7 @@
  */
 ( function () {
        $( function () {
-               var $preferences, tabs, wrapper, previousTab, switchingNoHash;
-
-               $preferences = $( '#preferences' );
+               var tabs, previousTab, switchingNoHash;
 
                // Make sure the accessibility tip is focussable so that keyboard users take notice,
                // but hide it by default to reduce visual clutter.
                        } )
                        .insertBefore( '.mw-htmlform-ooui-wrapper' );
 
-               tabs = new OO.ui.IndexLayout( {
-                       expanded: false,
-                       // Do not remove focus from the tabs menu after choosing a tab
-                       autoFocus: false
-               } );
-
-               mw.config.get( 'wgPreferencesTabs' ).forEach( function ( tabConfig ) {
-                       var panel, $panelContents;
-
-                       panel = new OO.ui.TabPanelLayout( tabConfig.name, {
-                               expanded: false,
-                               label: tabConfig.label
-                       } );
-                       $panelContents = $( '#mw-prefsection-' + tabConfig.name );
-
-                       // Hide the unnecessary PHP PanelLayouts
-                       // (Do not use .remove(), as that would remove event handlers for everything inside them)
-                       $panelContents.parent().detach();
+               tabs = OO.ui.infuse( $( '.mw-prefs-tabs' ) );
 
-                       panel.$element.append( $panelContents );
-                       tabs.addTabPanels( [ panel ] );
-
-                       // Remove duplicate labels
-                       // (This must be after .addTabPanels(), otherwise the tab item doesn't exist yet)
-                       $panelContents.children( 'legend' ).remove();
-                       $panelContents.attr( 'aria-labelledby', panel.getTabItem().getElementId() );
-               } );
-
-               wrapper = new OO.ui.PanelLayout( {
-                       expanded: false,
-                       padded: false,
-                       framed: true
-               } );
-               wrapper.$element.append( tabs.$element );
-               $preferences.prepend( wrapper.$element );
-               $( '.mw-prefs-faketabs' ).remove();
+               tabs.$element.addClass( 'mw-prefs-tabs-infused' );
 
                function enhancePanel( panel ) {
                        if ( !panel.$element.data( 'mw-section-infused' ) ) {
-                               // mw-htmlform-autoinfuse-lazy class has been removed by replacing faketabs
+                               panel.$element.removeClass( 'mw-htmlform-autoinfuse-lazy' );
                                mw.hook( 'htmlform.enhance' ).fire( panel.$element );
                                panel.$element.data( 'mw-section-infused', true );
                        }
@@ -75,7 +40,7 @@
                        // Changing the hash apparently causes keyboard focus to be lost?
                        // Save and restore it. This makes no sense though.
                        active = document.activeElement;
-                       location.hash = '#mw-prefsection-' + panel.getName();
+                       location.hash = '#' + panel.getName();
                        if ( active ) {
                                active.focus();
                        }
@@ -86,7 +51,7 @@
 
                /**
                 * @ignore
-                * @param {string} name the name of a tab without the prefix ("mw-prefsection-")
+                * @param {string} name The name of a tab
                 * @param {boolean} [noHash] A hash will be set according to the current
                 *  open section. Use this flag to suppress this.
                 */
                                matchedElement, parentSection;
                        if ( hash.match( /^#mw-prefsection-[\w]+$/ ) ) {
                                mw.storage.session.remove( 'mwpreferences-prevTab' );
-                               switchPrefTab( hash.replace( '#mw-prefsection-', '' ) );
+                               switchPrefTab( hash.slice( 1 ) );
                        } else if ( hash.match( /^#mw-[\w-]+$/ ) ) {
                                matchedElement = document.getElementById( hash.slice( 1 ) );
                                parentSection = $( matchedElement ).parent().closest( '[id^="mw-prefsection-"]' );
                                if ( parentSection.length ) {
                                        mw.storage.session.remove( 'mwpreferences-prevTab' );
                                        // Switch to proper tab and scroll to selected item.
-                                       switchPrefTab( parentSection.attr( 'id' ).replace( 'mw-prefsection-', '' ), true );
+                                       switchPrefTab( parentSection.attr( 'id' ), true );
                                        matchedElement.scrollIntoView();
                                }
                        }
                        if ( hash.match( /^#mw-[\w-]+/ ) ) {
                                detectHash();
                        } else if ( hash === '' ) {
-                               switchPrefTab( 'personal', true );
+                               switchPrefTab( 'mw-prefsection-personal', true );
                        }
                } )
                        // Run the function immediately to select the proper tab on startup.
index b1931f4..532b9ca 100644 (file)
        overflow: hidden;
 }
 
-/* Most outer Panellayout:
- * Decrease contrast of `border` slightly as padding/border combination is sufficient
- * accessibility wise and focus of content is more important here. */
-#preferences .oo-ui-panelLayout-framed {
-       border-color: #c8ccd1;
-}
+.mw-prefs-tabs {
+       .mw-prefs-fieldset-wrapper {
+               padding-left: 0;
+               padding-right: 0;
+
+               &:first-child {
+                       padding-top: 0;
+               }
 
-#preferences .oo-ui-menuLayout .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed {
-       border-width: 0;
-       border-radius: 0;
-       padding-left: 0;
-       padding-right: 0;
-       box-shadow: none;
+               &:last-child {
+                       padding-bottom: 0;
+               }
+       }
 }
 
-.mw-prefs-faketabs > .oo-ui-menuLayout > .oo-ui-menuLayout-menu a {
-       color: inherit;
-       text-decoration: none;
+.mw-prefs-tabs-wrapper.oo-ui-panelLayout-framed,
+.mw-prefs-tabs > .oo-ui-menuLayout-content > .oo-ui-indexLayout-stackLayout > .oo-ui-tabPanelLayout {
+       /* Decrease contrast of `border` slightly as padding/border combination is sufficient
+        * accessibility wise and focus of content is more important here. */
+       border-color: #c8ccd1;
 }
 
-/* Disabled JavaScript */
+/* JavaScript disabled */
 .client-nojs {
-       /* Adjust the borders: frame each prefsection instead of the
-        * whole tabLayout wrapper */
-       #preferences .oo-ui-menuLayout .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed:first-child {
-               border-color: #c8ccd1;
-               border-width: 1px 0 0;
-       }
-
-       #preferences .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed:last-child {
-               padding-bottom: 0;
-               margin-bottom: 0;
-       }
-
-       /* Fake Tabs to address reflow */
-       .mw-prefs-faketabs {
+       // Disable .oo-ui-panelLayout-framed on outer wrapper
+       .mw-prefs-tabs-wrapper {
                border-width: 0;
                border-radius: 0;
-               .box-shadow( none );
+       }
 
-               > .oo-ui-menuLayout > .oo-ui-menuLayout-content > .oo-ui-stackLayout {
-                       margin-bottom: 1em;
+       .mw-prefs-tabs {
+               // Hide the tab menu when JS is disabled as we can't use this feature
+               > .oo-ui-menuLayout-menu {
+                       display: none;
                }
 
-               /* Hide the tab menu when JS is disabled as we can't use this feature */
-               > .oo-ui-menuLayout > .oo-ui-menuLayout-menu {
-                       display: none;
+               .mw-prefs-section-fieldset {
+                       // <legend> is hard to style, so apply border to top of group
+                       > .oo-ui-fieldsetLayout-group {
+                               padding-top: 1.5em;
+                               border-top: 1px solid #c8ccd1;
+                       }
+
+                       // Remove spacing between legend and underline
+                       &.oo-ui-labelElement > .oo-ui-fieldsetLayout-header > .oo-ui-labelElement-label {
+                               margin-bottom: 0;
+                       }
+               }
+
+               // Spacing between sections
+               > .oo-ui-menuLayout-content > .oo-ui-indexLayout-stackLayout > .oo-ui-tabPanelLayout {
+                       margin-bottom: 1em;
                }
        }
 }
 
-/* Enabled JavaScript
- * Hide top level legends when JS is enabled, as they will not be visible
- * when the real tabLayout is built */
-.client-js #preferences {
+/* JavaScript enabled */
+.client-js .mw-prefs-tabs {
        .oo-ui-tabPanelLayout {
-               padding-top: 0.5em;
+               // Panels don't need borders as the IndexLayout is inside a framed wrapper.
+               border: 0;
 
-               & > fieldset > legend {
+               // Hide section legend, only used in nojs mode
+               > fieldset > legend {
                        display: none;
                }
        }
 
-       .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed {
-               margin-top: 2.286em; /* equals `32px` at `font-size: 14px;` */
-               margin-bottom: 0;
-               border-width: 0;
-               border-radius: 0;
-               padding: 0;
-               box-shadow: none;
-
-               &:first-child {
-                       margin-top: 0.85714286em;
-               }
-
-               .oo-ui-panelLayout-framed:first-child {
-                       margin-top: 0;
+       // Hide all but the first panel before infusion
+       &:not( .mw-prefs-tabs-infused ) {
+               .oo-ui-tabPanelLayout:not( :first-child ) {
+                       display: none;
                }
        }
-
-       > .oo-ui-panelLayout > .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-header {
-               margin-bottom: 1em;
-       }
 }
 
 /* Make the "Basic information" section more compact */
index 3a71760..c25db2f 100644 (file)
                        focusout: this.onBlur.bind( this )
                } );
                this.calendar.$element.on( {
+                       focusout: this.onBlur.bind( this ),
                        click: this.onCalendarClick.bind( this ),
                        keypress: this.onCalendarKeyPress.bind( this )
                } );
index 6b1ed7f..e0b8111 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use Wikimedia\Rdbms\Database;
 use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\DatabasePostgres;
 use Wikimedia\ScopedCallback;
@@ -179,4 +180,10 @@ class DatabasePostgresTest extends MediaWikiTestCase {
                $this->doTestInsertSelectIgnore();
        }
 
+       /**
+        * @covers \Wikimedia\Rdbms\DatabasePostgres::getAttributes
+        */
+       public function testAttributes() {
+               $this->assertTrue( DatabasePostgres::getAttributes()[Database::ATTR_SCHEMAS_AS_TABLE_GROUPS] );
+       }
 }
index d0360a9..f953319 100644 (file)
@@ -11,7 +11,7 @@ class CachedBagOStuffTest extends PHPUnit\Framework\TestCase {
 
        /**
         * @covers CachedBagOStuff::__construct
-        * @covers CachedBagOStuff::doGet
+        * @covers CachedBagOStuff::get
         */
        public function testGetFromBackend() {
                $backend = new HashBagOStuff;
@@ -36,6 +36,7 @@ class CachedBagOStuffTest extends PHPUnit\Framework\TestCase {
                        $cache->set( "key$i", 1 );
                        $this->assertEquals( 1, $cache->get( "key$i" ) );
                        $this->assertEquals( 1, $backend->get( "key$i" ) );
+
                        $cache->delete( "key$i" );
                        $this->assertEquals( false, $cache->get( "key$i" ) );
                        $this->assertEquals( false, $backend->get( "key$i" ) );
@@ -67,7 +68,7 @@ class CachedBagOStuffTest extends PHPUnit\Framework\TestCase {
        }
 
        /**
-        * @covers CachedBagOStuff::doGet
+        * @covers CachedBagOStuff::get
         */
        public function testCacheBackendMisses() {
                $backend = new HashBagOStuff;
index b28a5b9..414042d 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use Wikimedia\Rdbms\Database;
 use Wikimedia\Rdbms\DatabaseMssql;
 
 class DatabaseMssqlTest extends PHPUnit\Framework\TestCase {
@@ -52,4 +53,10 @@ class DatabaseMssqlTest extends PHPUnit\Framework\TestCase {
                $mockDb->buildSubstring( 'foo', $start, $length );
        }
 
+       /**
+        * @covers \Wikimedia\Rdbms\DatabaseMssql::getAttributes
+        */
+       public function testAttributes() {
+               $this->assertTrue( DatabaseMssql::getAttributes()[Database::ATTR_SCHEMAS_AS_TABLE_GROUPS] );
+       }
 }