Merge "Use User::getDefaultOption() instead of $wgDefaultUserOptions"
authorAaron Schulz <aschulz@wikimedia.org>
Sun, 22 Jul 2012 09:45:18 +0000 (09:45 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sun, 22 Jul 2012 09:45:18 +0000 (09:45 +0000)
354 files changed:
.jshintignore
.jshintrc
RELEASE-NOTES-1.20
UPGRADE
docs/hooks.txt
includes/AutoLoader.php
includes/CacheHelper.php
includes/ChangesList.php
includes/Cookie.php
includes/DataUpdate.php
includes/DefaultSettings.php
includes/DeferredUpdates.php
includes/Defines.php
includes/EditPage.php
includes/Exception.php
includes/Export.php
includes/Feed.php
includes/GlobalFunctions.php
includes/HTMLForm.php
includes/Html.php
includes/HttpFunctions.php
includes/ImagePage.php
includes/Import.php
includes/Linker.php
includes/LinksUpdate.php
includes/MagicWord.php
includes/OutputPage.php
includes/Pager.php
includes/Preferences.php
includes/RecentChange.php
includes/RevisionList.php
includes/Sanitizer.php
includes/Skin.php
includes/SkinLegacy.php
includes/SkinTemplate.php
includes/SpecialPage.php
includes/SqlDataUpdate.php
includes/StringUtils.php
includes/Title.php
includes/User.php
includes/WebRequest.php
includes/Wiki.php
includes/WikiMap.php
includes/WikiPage.php
includes/Xml.php
includes/ZhConversion.php
includes/actions/HistoryAction.php
includes/actions/InfoAction.php
includes/api/ApiBase.php
includes/api/ApiBlock.php
includes/api/ApiDelete.php
includes/api/ApiDisabled.php
includes/api/ApiEditPage.php
includes/api/ApiEmailUser.php
includes/api/ApiExpandTemplates.php
includes/api/ApiFeedWatchlist.php
includes/api/ApiFileRevert.php
includes/api/ApiFormatBase.php
includes/api/ApiFormatDbg.php
includes/api/ApiFormatJson.php
includes/api/ApiFormatPhp.php
includes/api/ApiFormatRaw.php
includes/api/ApiFormatTxt.php
includes/api/ApiFormatWddx.php
includes/api/ApiFormatXml.php
includes/api/ApiFormatYaml.php
includes/api/ApiHelp.php
includes/api/ApiImport.php
includes/api/ApiLogin.php
includes/api/ApiLogout.php
includes/api/ApiMain.php
includes/api/ApiMove.php
includes/api/ApiOpenSearch.php
includes/api/ApiPageSet.php
includes/api/ApiParamInfo.php
includes/api/ApiParse.php
includes/api/ApiPatrol.php
includes/api/ApiProtect.php
includes/api/ApiQuery.php
includes/api/ApiQueryAllCategories.php
includes/api/ApiQueryAllImages.php
includes/api/ApiQueryAllLinks.php
includes/api/ApiQueryAllMessages.php
includes/api/ApiQueryAllPages.php
includes/api/ApiQueryAllUsers.php
includes/api/ApiQueryBacklinks.php
includes/api/ApiQueryBase.php
includes/api/ApiQueryBlocks.php
includes/api/ApiQueryCategories.php
includes/api/ApiQueryCategoryInfo.php
includes/api/ApiQueryCategoryMembers.php
includes/api/ApiQueryDeletedrevs.php
includes/api/ApiQueryDisabled.php
includes/api/ApiQueryDuplicateFiles.php
includes/api/ApiQueryExtLinksUsage.php
includes/api/ApiQueryExternalLinks.php
includes/api/ApiQueryFilearchive.php
includes/api/ApiQueryIWBacklinks.php
includes/api/ApiQueryIWLinks.php
includes/api/ApiQueryImageInfo.php
includes/api/ApiQueryImages.php
includes/api/ApiQueryInfo.php
includes/api/ApiQueryLangBacklinks.php
includes/api/ApiQueryLangLinks.php
includes/api/ApiQueryLinks.php
includes/api/ApiQueryLogEvents.php
includes/api/ApiQueryProtectedTitles.php
includes/api/ApiQueryQueryPage.php
includes/api/ApiQueryRecentChanges.php
includes/api/ApiQueryRevisions.php
includes/api/ApiQuerySearch.php
includes/api/ApiQuerySiteinfo.php
includes/api/ApiQueryStashImageInfo.php
includes/api/ApiQueryUserContributions.php
includes/api/ApiQueryUserInfo.php
includes/api/ApiQueryUsers.php
includes/api/ApiQueryWatchlist.php
includes/api/ApiQueryWatchlistRaw.php
includes/api/ApiResult.php
includes/api/ApiRollback.php
includes/api/ApiUnblock.php
includes/api/ApiUndelete.php
includes/api/ApiUpload.php
includes/api/ApiUserrights.php
includes/api/ApiWatch.php
includes/cache/ProcessCacheLRU.php [new file with mode: 0644]
includes/db/Database.php
includes/db/DatabaseOracle.php
includes/db/DatabasePostgres.php
includes/filerepo/FileRepo.php
includes/filerepo/LocalRepo.php
includes/filerepo/RepoGroup.php
includes/filerepo/backend/FSFileBackend.php
includes/filerepo/backend/FileBackend.php
includes/filerepo/backend/FileBackendMultiWrite.php
includes/filerepo/backend/FileBackendStore.php
includes/filerepo/backend/FileOp.php
includes/filerepo/backend/FileOpBatch.php
includes/filerepo/backend/SwiftFileBackend.php
includes/filerepo/backend/lockmanager/LSLockManager.php
includes/filerepo/backend/lockmanager/MemcLockManager.php
includes/filerepo/file/File.php
includes/filerepo/file/LocalFile.php
includes/installer/Installer.php
includes/installer/WebInstallerOutput.php
includes/installer/WebInstallerPage.php
includes/job/DoubleRedirectJob.php
includes/json/FormatJson.php
includes/logging/LogEventsList.php
includes/logging/LogFormatter.php
includes/logging/LogPage.php
includes/logging/PatrolLog.php
includes/media/Bitmap.php
includes/media/ExifBitmap.php
includes/media/FormatMetadata.php
includes/media/Generic.php
includes/media/MediaTransformOutput.php
includes/media/SVGMetadataExtractor.php
includes/media/XMP.php
includes/objectcache/APCBagOStuff.php
includes/objectcache/BagOStuff.php
includes/objectcache/DBABagOStuff.php
includes/objectcache/EhcacheBagOStuff.php
includes/objectcache/EmptyBagOStuff.php
includes/objectcache/HashBagOStuff.php
includes/objectcache/MemcachedBagOStuff.php
includes/objectcache/MemcachedClient.php
includes/objectcache/MemcachedPeclBagOStuff.php
includes/objectcache/MemcachedPhpBagOStuff.php
includes/objectcache/MultiWriteBagOStuff.php
includes/objectcache/ObjectCache.php
includes/objectcache/SqlBagOStuff.php
includes/objectcache/WinCacheBagOStuff.php
includes/parser/LinkHolderArray.php
includes/parser/Parser.php
includes/parser/ParserCache.php
includes/parser/ParserOutput.php
includes/parser/Preprocessor.php
includes/parser/Preprocessor_DOM.php
includes/parser/Preprocessor_Hash.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/search/SearchOracle.php
includes/specials/SpecialAllmessages.php
includes/specials/SpecialContributions.php
includes/specials/SpecialFileDuplicateSearch.php
includes/specials/SpecialListusers.php
includes/specials/SpecialLog.php
includes/specials/SpecialNewimages.php
includes/specials/SpecialProtectedpages.php
includes/specials/SpecialRevisiondelete.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUploadStash.php
includes/specials/SpecialUserlogin.php
includes/specials/SpecialVersion.php
includes/upload/UploadBase.php
includes/upload/UploadStash.php
includes/zhtable/simp2trad.manual
includes/zhtable/trad2simp.manual
languages/Language.php
languages/Names.php
languages/messages/MessagesArc.php
languages/messages/MessagesBa.php
languages/messages/MessagesBcl.php
languages/messages/MessagesBe_tarask.php
languages/messages/MessagesBg.php
languages/messages/MessagesBpy.php
languages/messages/MessagesBr.php
languages/messages/MessagesCa.php
languages/messages/MessagesCe.php
languages/messages/MessagesCrh_cyrl.php
languages/messages/MessagesCrh_latn.php
languages/messages/MessagesCs.php
languages/messages/MessagesDe.php
languages/messages/MessagesDsb.php
languages/messages/MessagesEn.php
languages/messages/MessagesEs.php
languages/messages/MessagesEt.php
languages/messages/MessagesFa.php
languages/messages/MessagesFi.php
languages/messages/MessagesFr.php
languages/messages/MessagesFrr.php
languages/messages/MessagesGl.php
languages/messages/MessagesHe.php
languages/messages/MessagesHif_latn.php
languages/messages/MessagesHsb.php
languages/messages/MessagesHu.php
languages/messages/MessagesIa.php
languages/messages/MessagesIlo.php
languages/messages/MessagesIt.php
languages/messages/MessagesKa.php
languages/messages/MessagesKbd_cyrl.php
languages/messages/MessagesKk_cyrl.php
languages/messages/MessagesKk_latn.php
languages/messages/MessagesKo.php
languages/messages/MessagesKrc.php
languages/messages/MessagesKsh.php
languages/messages/MessagesLb.php
languages/messages/MessagesLez.php
languages/messages/MessagesLt.php
languages/messages/MessagesMdf.php
languages/messages/MessagesMhr.php
languages/messages/MessagesMk.php
languages/messages/MessagesMl.php
languages/messages/MessagesMrj.php
languages/messages/MessagesMyv.php
languages/messages/MessagesNb.php
languages/messages/MessagesNds_nl.php
languages/messages/MessagesNl.php
languages/messages/MessagesNn.php
languages/messages/MessagesOr.php
languages/messages/MessagesOs.php
languages/messages/MessagesPfl.php
languages/messages/MessagesPms.php
languages/messages/MessagesPt.php
languages/messages/MessagesQqq.php
languages/messages/MessagesRo.php
languages/messages/MessagesRoa_tara.php
languages/messages/MessagesRu.php
languages/messages/MessagesSah.php
languages/messages/MessagesSe.php
languages/messages/MessagesShi.php
languages/messages/MessagesSk.php
languages/messages/MessagesSl.php
languages/messages/MessagesSv.php
languages/messages/MessagesTa.php
languages/messages/MessagesTl.php
languages/messages/MessagesTly.php
languages/messages/MessagesTyv.php
languages/messages/MessagesUk.php
languages/messages/MessagesUz.php
languages/messages/MessagesVep.php
languages/messages/MessagesVi.php
languages/messages/MessagesXal.php
languages/messages/MessagesYi.php
languages/messages/MessagesZh_hant.php
maintenance/cleanupPreferences.php
maintenance/copyFileBackend.php
maintenance/deleteArchivedRevisions.inc
maintenance/deleteBatch.php
maintenance/deleteDefaultMessages.php
maintenance/deleteImageMemcached.php
maintenance/deleteOldRevisions.php
maintenance/deleteOrphanedRevisions.php
maintenance/deleteRevision.php
maintenance/deleteSelfExternals.php
maintenance/dumpIterator.php
maintenance/dumpLinks.php
maintenance/dumpSisterSites.php
maintenance/dumpUploads.php
maintenance/edit.php
maintenance/fetchText.php
maintenance/fileOpPerfTest.php
maintenance/findHooks.php
maintenance/fixDoubleRedirects.php
maintenance/fixExtLinksProtocolRelative.php
maintenance/fixSlaveDesync.php
maintenance/fixTimestamps.php
maintenance/fixUserRegistration.php
maintenance/formatInstallDoc.php
maintenance/jsparse.php
maintenance/language/messages.inc
maintenance/locking/LockServerDaemon.php
maintenance/mergeMessageFileList.php
maintenance/rebuildImages.php
maintenance/syncFileBackend.php
maintenance/updateCollation.php
resources/jquery/jquery.arrowSteps.css
resources/jquery/jquery.arrowSteps.js
resources/jquery/jquery.autoEllipsis.js
resources/jquery/jquery.byteLength.js
resources/jquery/jquery.byteLimit.js
resources/jquery/jquery.checkboxShiftClick.js
resources/jquery/jquery.client.js
resources/jquery/jquery.collapsibleTabs.js
resources/jquery/jquery.color.js
resources/jquery/jquery.colorUtil.js
resources/jquery/jquery.delayedBind.js
resources/jquery/jquery.expandableField.js
resources/jquery/jquery.getAttrs.js
resources/jquery/jquery.highlightText.js
resources/jquery/jquery.localize.js
resources/jquery/jquery.makeCollapsible.js
resources/jquery/jquery.messageBox.js
resources/jquery/jquery.mwExtension.js
resources/jquery/jquery.placeholder.js
resources/jquery/jquery.qunit.completenessTest.js
resources/jquery/jquery.spinner.js
resources/jquery/jquery.suggestions.js
resources/jquery/jquery.tabIndex.js
resources/jquery/jquery.tablesorter.js
resources/jquery/jquery.textSelection.js
resources/mediawiki.action/mediawiki.action.edit.js
resources/mediawiki.api/mediawiki.api.edit.js
resources/mediawiki.api/mediawiki.api.js
resources/mediawiki.api/mediawiki.api.parse.js
skins/common/commonElements.css
tests/phpunit/MediaWikiLangTestCase.php
tests/phpunit/docs/ExportDemoTest.php [new file with mode: 0644]
tests/phpunit/includes/BlockTest.php
tests/phpunit/includes/WikiPageTest.php
tests/phpunit/includes/XmlTest.php
tests/phpunit/includes/cache/ProcessCacheLRUTest.php [new file with mode: 0644]
tests/phpunit/includes/filerepo/FileBackendTest.php
tests/phpunit/includes/parser/NewParserTest.php
tests/phpunit/languages/LanguageTest.php
tests/phpunit/maintenance/backupTextPassTest.php
tests/qunit/QUnitTestResources.php
tests/qunit/data/testrunner.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.parse.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js

index fb2a42f..8ba7fc3 100644 (file)
@@ -5,6 +5,7 @@ resources/jquery/jquery.cycle.all.js
 resources/jquery/jquery.cookie.js
 resources/jquery/jquery.farbtastic.js
 resources/jquery/jquery.form.js
+resources/jquery/jquery.hoverIntent.js
 resources/jquery/jquery.js
 resources/jquery/jquery.json.js
 resources/jquery/jquery.mockjax.js
@@ -12,7 +13,9 @@ resources/jquery/jquery.qunit.js
 resources/jquery/jquery.validate.js
 resources/jquery/jquery.xmldom.js
 resources/jquery.effects
+resources/jquery.tipsy
 resources/jquery.ui
+resources/mediawiki.libs/mediawiki.libs.jpegmeta.js
 tests/jasmine/lib/jasmine-1.0.1/jasmine-html.js
 tests/jasmine/lib/jasmine-1.0.1/jasmine.js
 
index 4e82155..3c801c2 100644 (file)
--- a/.jshintrc
+++ b/.jshintrc
@@ -4,7 +4,19 @@
                "jQuery",
                "QUnit"
        ],
-       "browser": true,
+
+       "bitwise": true,
+       "curly": true,
+       "eqeqeq": true,
+       "immed": true,
+       "latedef": true,
+       "newcap": true,
+       "noempty": true,
+       "undef": true,
+       "trailing": true,
+
+       "laxbreak": true,
        "smarttabs": true,
-       "laxbreak": true
+
+       "browser": true
 }
index f1ecf08..2f463a7 100644 (file)
@@ -23,6 +23,8 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
 * The user right 'upload_by_url' is no longer given to sysops by default.
   This only affects installations which have $wgAllowCopyUploads set to true.
 * Removed f-prot support from $wgAntivirusSetup.
+* $wgDBerrorLogInUTC to log error in $wgDBerrorLog using an UTC date instead
+  of the wiki timezone set by $wgLocalTimezone.
 
 === New features in 1.20 ===
 * Added TitleIsAlwaysKnown hook which gets called when determining if a page exists.
@@ -92,6 +94,11 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
   replacement, and so on.
 * (bug 34678) Added InternalParseBeforeSanitize hook which gets called during Parser's
   internalParse method just before the parser removes unwanted/dangerous HTML tags.
+* (bug 36783) Implement jQuery Promise interface in mediawiki.api module.
+* Make dates in sortable tables sort according to the page content language
+  instead of the site content language
+* (bug 37926) Deleterevision will no longer allow users to delete log entries,
+  the new deletelogentry permission is required for this.
 
 === Bug fixes in 1.20 ===
 * (bug 30245) Use the correct way to construct a log page title.
@@ -143,6 +150,7 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
   who don't have access to /tmp can specify an alternative.
 * (bug 27283) SqlBagOStuff breaks PostgreSQL transactions.
 * (bug 35727) mw.Api ajax() should put token parameter last.
+* (bug 260) Handle <pre> overflow automatically with a scroll bar.
 * (bug 37708) mw.Uri.clone() should make a deep copy.
 * (bug 38024) ResourceLoader should not create empty stylesheets for modules
   that don't have stylesheets.
@@ -160,6 +168,7 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
 * (bug 38152) jquery.tablesorter: Use .data() instead of .attr(), so that live
   values are used instead of just the fixed values from when the tablesorter
   was initialized.
+* (bug 38093) Gender of changed user groups missing in Special:Log/rights
 
 === API changes in 1.20 ===
 * (bug 34316) Add ability to retrieve maximum upload size from MediaWiki API.
@@ -171,21 +180,24 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
 * (bug 32497) API now allows changing of protection level using pageid.
 * (bug 32498) API now allows comparing pages using pageids.
 * (bug 30975) API import of pages with invalid characters in this wiki leads to Fatal Error.
-* (bug 30488) API now allows listing of backlinks/embeddedin/imageusage per pageid
-* (bug 34927) Output media_type for list=filearchive
-* (bug 28814) add properties to output of action=parse
-* (bug 33224) add variants of content language to meta=siteinfo
-* (bug 36761) "Mark pages as visited" now submits previously established filter options
-* (bug 32643) action=purge with forcelinkupdate no longer crashes when ratelimit is reached
-* The paraminfo module now also contains result properties for most modules
-* (bug 32348) Allow descending order for list=alllinks
-* (bug 31777) Upload unknown error ``fileexists-forbidden''
-* (bug 32382) Allow descending order for list=iwbacklinks
-* (bug 32381) Allow descending order for list=backlinks, list=embeddedin and list=imageusage
-* (bug 32383) Allow descending order for list=langbacklinks
-* API meta=siteinfo can now return the list of known variable IDs
+* (bug 30488) API now allows listing of backlinks/embeddedin/imageusage per pageid.
+* (bug 34927) Output media_type for list=filearchive.
+* (bug 28814) add properties to output of action=parse.
+* (bug 33224) add variants of content language to meta=siteinfo.
+* (bug 36761) "Mark pages as visited" now submits previously established filter options.
+* (bug 32643) action=purge with forcelinkupdate no longer crashes when ratelimit is reached.
+* The paraminfo module now also contains result properties for most modules.
+* (bug 32348) Allow descending order for list=alllinks.
+* (bug 31777) Upload unknown error ``fileexists-forbidden''.
+* (bug 32382) Allow descending order for list=iwbacklinks.
+* (bug 32381) Allow descending order for list=backlinks, list=embeddedin and list=imageusage.
+* (bug 32383) Allow descending order for list=langbacklinks.
+* API meta=siteinfo can now return the list of known variable IDs.
+* (bug 35980) list=deletedrevs now honors drdir correctly in "all" mode (mode #3).
+* (bug 29290) API avoids mangling fields in continuation parameters
+* (bug 36987) API avoids mangling fields in continuation parameters
 * (bug 30836) siteinfo prop=specialpagealiases will no longer return nonexistent special pages
-* (bug 35980) list=deletedrevs now honors drdir correctly in "all" mode (mode #3)
+* (bug 38190) Add "required" flag to some token params for hint in api docs.
 
 === Languages updated in 1.20 ===
 
diff --git a/UPGRADE b/UPGRADE
index 9e7d035..cdaf4f9 100644 (file)
--- a/UPGRADE
+++ b/UPGRADE
@@ -76,7 +76,7 @@ behaviour of MediaWiki.
 
 Extensions usually need to be upgraded at the same time as the MediaWiki core.
 
-In MediaWiki 1.14 some extensions are migrated into the core. Please see the
+In MediaWiki 1.14 some extensions were migrated into the core. Please see the
 HISTORY section "Migrated extensions" and disable these extensions in your
 LocalSettings.php
 
index 47654ce..e844e89 100644 (file)
@@ -245,6 +245,10 @@ $block: The block from which the autoblock is coming.
 'AbortDiffCache': Can be used to cancel the caching of a diff
 &$diffEngine: DifferenceEngine object
 
+'AbortEmailNotification': Can be used to cancel email notifications for an edit.
+$editor: The User who made the change.
+$title: The Title of the page that was edited.
+
 'AbortLogin': Return false to cancel account login.
 $user: the User object being authenticated against
 $password: the password being submitted, not yet checked for validity
@@ -1622,6 +1626,11 @@ $out: OutputPage object
 'RecentChange_save': called at the end of RecentChange::save()
 $recentChange: RecentChange object
 
+'RedirectSpecialArticleRedirectParams': lets you alter the set of
+parameter names such as "oldid" that are preserved when using
+redirecting special pages such as Special:MyPage and Special:MyTalk.
+&$redirectParams: An array of parameters preserved by redirecting special pages.
+
 'RequestContextCreateSkin': Called when RequestContext::getSkin creates a skin instance.
 Can be used by an extension override what skin is used in certain contexts.
 IContextSource $context: The RequestContext the skin is being created for.
index 11cf616..6c37d1a 100644 (file)
@@ -419,6 +419,7 @@ $wgAutoloadLocalClasses = array(
        'LinkCache' => 'includes/cache/LinkCache.php',
        'MessageCache' => 'includes/cache/MessageCache.php',
        'ObjectFileCache' => 'includes/cache/ObjectFileCache.php',
+       'ProcessCacheLRU' => 'includes/cache/ProcessCacheLRU.php',
        'ResourceFileCache' => 'includes/cache/ResourceFileCache.php',
        'SquidUpdate' => 'includes/cache/SquidUpdate.php',
        'TitleDependency' => 'includes/cache/CacheDependency.php',
index 5209857..8199cb4 100644 (file)
@@ -73,7 +73,8 @@ interface ICacheHelper {
        function saveCache();
 
        /**
-        * Sets the time to live for the cache, in seconds or a unix timestamp indicating the point of expiry..
+        * Sets the time to live for the cache, in seconds or a unix timestamp
+        * indicating the point of expiry...
         *
         * @since 1.20
         *
@@ -319,7 +320,8 @@ class CacheHelper implements ICacheHelper {
        }
 
        /**
-        * Sets the time to live for the cache, in seconds or a unix timestamp indicating the point of expiry..
+        * Sets the time to live for the cache, in seconds or a unix timestamp
+        * indicating the point of expiry...
         *
         * @since 1.20
         *
index fe0db10..96a11e1 100644 (file)
@@ -108,7 +108,7 @@ class ChangesList extends ContextSource {
        }
 
        /**
-        * Sets the list to use a <li class="watchlist-(namespace)-(page)"> tag
+        * Sets the list to use a "<li class='watchlist-(namespace)-(page)'>" tag
         * @param $value Boolean
         */
        public function setWatchlistDivs( $value = true ) {
@@ -145,7 +145,7 @@ class ChangesList extends ContextSource {
        }
 
        /**
-        * Provide the <abbr> element appropriate to a given abbreviated flag,
+        * Provide the "<abbr>" element appropriate to a given abbreviated flag,
         * namely the flag indicating a new page, a minor edit, a bot edit, or an
         * unpatrolled edit.  By default in English it will contain "N", "m", "b",
         * "!" respectively, plus it will have an appropriate title and class.
@@ -564,7 +564,9 @@ class OldChangesList extends ChangesList {
        /**
         * Format a line using the old system (aka without any javascript).
         *
-        * @param $rc RecentChange
+        * @param $rc RecentChange, passed by reference
+        * @param $watched Bool (default false)
+        * @param $linenumber Int (default null)
         * @return string
         */
        public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
@@ -1104,7 +1106,7 @@ class EnhancedChangesList extends ChangesList {
         * @param $dir String: one of '', 'd', 'l', 'r'
         * @param $alt String: text
         * @param $title String: text
-        * @return String: HTML <img> tag
+        * @return String: HTML "<img>" tag
         */
        protected function arrow( $dir, $alt='', $title='' ) {
                global $wgStylePath;
@@ -1117,7 +1119,7 @@ class EnhancedChangesList extends ChangesList {
        /**
         * Generate HTML for a right- or left-facing arrow,
         * depending on language direction.
-        * @return String: HTML <img> tag
+        * @return String: HTML "<img>" tag
         */
        protected function sideArrow() {
                $dir = $this->getLanguage()->isRTL() ? 'l' : 'r';
@@ -1127,7 +1129,7 @@ class EnhancedChangesList extends ChangesList {
        /**
         * Generate HTML for a down-facing arrow
         * depending on language direction.
-        * @return String: HTML <img> tag
+        * @return String: HTML "<img>" tag
         */
        protected function downArrow() {
                return $this->arrow( 'd', '-', $this->msg( 'rc-enhanced-hide' )->text() );
@@ -1135,7 +1137,7 @@ class EnhancedChangesList extends ChangesList {
 
        /**
         * Generate HTML for a spacer image
-        * @return String: HTML <img> tag
+        * @return String: HTML "<img>" tag
         */
        protected function spacerArrow() {
                return $this->arrow( '', codepointToUtf8( 0xa0 ) ); // non-breaking space
index 1ca02b5..7984d63 100644 (file)
@@ -80,8 +80,8 @@ class Cookie {
         * A better method might be to use a blacklist like
         * http://publicsuffix.org/
         *
-        * @fixme fails to detect 3-letter top-level domains
-        * @fixme fails to detect 2-letter top-level domains for single-domain use (probably not a big problem in practice, but there are test cases)
+        * @todo fixme fails to detect 3-letter top-level domains
+        * @todo fixme fails to detect 2-letter top-level domains for single-domain use (probably not a big problem in practice, but there are test cases)
         *
         * @param $domain String: the domain to validate
         * @param $originDomain String: (optional) the domain the cookie originates from
index 7203c3b..793d335 100644 (file)
 /**
  * Abstract base class for update jobs that do something with some secondary
  * data extracted from article.
+ *
+ * @note: subclasses should NOT start or commit transactions in their doUpdate() method,
+ *        a transaction will automatically be wrapped around the update. If need be,
+ *        subclasses can override the beginTransaction() and commitTransaction() methods.
  */
 abstract class DataUpdate implements DeferrableUpdate {
 
index 7441932..3311fb1 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Default values for configuration settings.
+ * Default values for MediaWiki configuration settings.
  *
  *
  *                 NEVER EDIT THIS FILE
@@ -17,6 +17,9 @@
  * Documentation is in the source and on:
  * http://www.mediawiki.org/wiki/Manual:Configuration_settings
  *
+ * @warning  Note: this (and other things) will break if the autoloader is not
+ * enabled. Please include includes/AutoLoader.php before including this file.
+ *
  * 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
  * @file
  */
 
+/**
+ * @defgroup Globalsettings Global settings
+ */
+
 /**
  * @cond file_level_code
- * This is not a valid entry point, perform no further processing unless MEDIAWIKI is defined
+ * This is not a valid entry point, perform no further processing unless
+ * MEDIAWIKI is defined
  */
 if( !defined( 'MEDIAWIKI' ) ) {
        echo "This file is part of MediaWiki and is not a valid entry point\n";
        die( 1 );
 }
 
-# Create a site configuration object. Not used for much in a default install.
-# Note: this (and other things) will break if the autoloader is not enabled.
-# Please include includes/AutoLoader.php before including this file.
+/**
+ * wgConf hold the site configuration.
+ * Not used for much in a default install.
+ */
 $wgConf = new SiteConfiguration;
-/** @endcond */
 
 /** MediaWiki version number */
 $wgVersion = '1.20alpha';
@@ -59,10 +67,10 @@ $wgSitename = 'MediaWiki';
 /**
  * URL of the server.
  *
- * Example:
- * <code>
+ * @par Example:
+ * @code
  * $wgServer = 'http://example.com';
- * </code>
+ * @endcode
  *
  * This is usually detected correctly by MediaWiki. If MediaWiki detects the
  * wrong server, it will redirect incorrectly after you save a page. In that
@@ -273,11 +281,16 @@ $wgUploadStashScalerBaseUrl = false;
 
 /**
  * To set 'pretty' URL paths for actions other than
- * plain page views, add to this array. For instance:
+ * plain page views, add to this array.
+ *
+ * @par Example:
+ * Set pretty URL for the edit action:
+ * @code
  *   'edit' => "$wgScriptPath/edit/$1"
+ * @endcode
  *
- * There must be an appropriate script or rewrite rule
- * in place to handle these URLs.
+ * There must be an appropriate script or rewrite rule in place to handle these
+ * URLs.
  */
 $wgActionPaths = array();
 
@@ -300,7 +313,7 @@ $wgUploadStashMaxAge = 6 * 3600; // 6 hours
 $wgAllowImageMoving = true;
 
 /**
- * These are additional characters that should be replaced with '-' in file names
+ * These are additional characters that should be replaced with '-' in filenames
  */
 $wgIllegalFileChars = ":";
 
@@ -310,9 +323,10 @@ $wgIllegalFileChars = ":";
 $wgFileStore = array();
 
 /**
- * What directory to place deleted uploads in
+ * What directory to place deleted uploads in.
+ * Defaults to "{$wgUploadDirectory}/deleted".
  */
-$wgDeletedDirectory = false; //  Defaults to $wgUploadDirectory/deleted
+$wgDeletedDirectory = false;
 
 /**
  * Set this to true if you use img_auth and want the user to see details on why access failed.
@@ -347,7 +361,7 @@ $wgImgAuthPublicTest = true;
  *                          container : backend container name the zone is in
  *                          directory : root path within container for the zone
  *                          url       : base URL to the root of the zone
- *                      Zones default to using <repo name>-<zone name> as the container name
+ *                      Zones default to using "<repo name>-<zone name>" as the container name
  *                      and default to using the container root as the zone's root directory.
  *                      Nesting of zone locations within other zones should be avoided.
  *   - url              Public zone URL. The 'zones' settings take precedence.
@@ -367,8 +381,8 @@ $wgImgAuthPublicTest = true;
  *                      is 0644.
  *   - directory        The local filesystem directory where public files are stored. Not used for
  *                      some remote repos.
- *   - thumbDir         The base thumbnail directory. Defaults to <directory>/thumb.
- *   - thumbUrl         The base thumbnail URL. Defaults to <url>/thumb.
+ *   - thumbDir         The base thumbnail directory. Defaults to "<directory>/thumb".
+ *   - thumbUrl         The base thumbnail URL. Defaults to "<url>/thumb".
  *
  *
  * These settings describe a foreign MediaWiki installation. They are optional, and will be ignored
@@ -418,10 +432,11 @@ $wgUseInstantCommons = false;
  * File backend structure configuration.
  * This is an array of file backend configuration arrays.
  * Each backend configuration has the following parameters:
- *     'name'        : A unique name for the backend
- *     'class'       : The file backend class to use
- *     'wikiId'      : A unique string that identifies the wiki (container prefix)
- *     'lockManager' : The name of a lock manager (see $wgLockManagers)
+ *  - 'name'        : A unique name for the backend
+ *  - 'class'       : The file backend class to use
+ *  - 'wikiId'      : A unique string that identifies the wiki (container prefix)
+ *  - 'lockManager' : The name of a lock manager (see $wgLockManagers)
+ *
  * Additional parameters are specific to the class used.
  */
 $wgFileBackends = array();
@@ -429,8 +444,8 @@ $wgFileBackends = array();
 /**
  * Array of configuration arrays for each lock manager.
  * Each backend configuration has the following parameters:
- *     'name'        : A unique name for the lock manger
- *     'class'       : The lock manger class to use
+ *  - 'name'        : A unique name for the lock manager
+ *  - 'class'       : The lock manger class to use
  * Additional parameters are specific to the class used.
  */
 $wgLockManagers = array();
@@ -439,12 +454,13 @@ $wgLockManagers = array();
  * Show EXIF data, on by default if available.
  * Requires PHP's EXIF extension: http://www.php.net/manual/en/ref.exif.php
  *
- * NOTE FOR WINDOWS USERS:
- * To enable EXIF functions, add the following lines to the
- * "Windows extensions" section of php.ini:
- *
+ * @note FOR WINDOWS USERS:
+ * To enable EXIF functions, add the following lines to the "Windows
+ * extensions" section of php.ini:
+ * @code{.ini}
  * extension=extensions/php_mbstring.dll
  * extension=extensions/php_exif.dll
+ * @endcode
  */
 $wgShowEXIF = function_exists( 'exif_read_data' );
 
@@ -512,11 +528,13 @@ $wgCopyUploadsDomains = array();
  * file and url keys. If the * key is set this value will be used as maximum
  * for non-specified types.
  *
- * For example:
+ * @par Example:
+ * @code
  * $wgMaxUploadSize = array(
  *     '*' => 250 * 1024,
  *     'url' => 500 * 1024,
  * );
+ * @endcode
  * Sets the maximum for all uploads to 250 kB except for upload-by-url, which
  * will have a maximum of 500 kB.
  *
@@ -526,27 +544,37 @@ $wgMaxUploadSize = 1024*1024*100; # 100MB
 /**
  * Point the upload navigation link to an external URL
  * Useful if you want to use a shared repository by default
- * without disabling local uploads (use $wgEnableUploads = false for that)
- * e.g. $wgUploadNavigationUrl = 'http://commons.wikimedia.org/wiki/Special:Upload';
+ * without disabling local uploads (use $wgEnableUploads = false for that).
+ *
+ * @par Example:
+ * @code
+ * $wgUploadNavigationUrl = 'http://commons.wikimedia.org/wiki/Special:Upload';
+ * @endcode
  */
 $wgUploadNavigationUrl = false;
 
 /**
  * Point the upload link for missing files to an external URL, as with
- * $wgUploadNavigationUrl. The URL will get (?|&)wpDestFile=<filename>
+ * $wgUploadNavigationUrl. The URL will get "(?|&)wpDestFile=<filename>"
  * appended to it as appropriate.
  */
 $wgUploadMissingFileUrl = false;
 
 /**
- * Give a path here to use thumb.php for thumbnail generation on client request, instead of
- * generating them on render and outputting a static URL. This is necessary if some of your
- * apache servers don't have read/write access to the thumbnail path.
+ * Give a path here to use thumb.php for thumbnail generation on client
+ * request, instead of generating them on render and outputting a static URL.
+ * This is necessary if some of your apache servers don't have read/write
+ * access to the thumbnail path.
  *
- * Example:
+ * @par Example:
+ * @code
  *   $wgThumbnailScriptPath = "{$wgScriptPath}/thumb{$wgScriptExtension}";
+ * @endcode
  */
 $wgThumbnailScriptPath = false;
+/**
+ * @see $wgThumbnailScriptPath
+ */
 $wgSharedThumbnailScriptPath = false;
 
 /**
@@ -559,7 +587,8 @@ $wgSharedThumbnailScriptPath = false;
  * maintenance/rebuildImages.php to register them in the database. This is no
  * longer recommended, use maintenance/importImages.php instead.
  *
- * Note that this variable may be ignored if $wgLocalFileRepo is set.
+ * @note That this variable may be ignored if $wgLocalFileRepo is set.
+ * @todo Deprecate the setting and ultimately remove it from Core.
  */
 $wgHashedUploadDirectory = true;
 
@@ -584,13 +613,17 @@ $wgRepositoryBaseUrl = "http://commons.wikimedia.org/wiki/File:";
  * This is the list of preferred extensions for uploading files. Uploading files
  * with extensions not in this list will trigger a warning.
  *
- * WARNING: If you add any OpenOffice or Microsoft Office file formats here,
+ * @warning If you add any OpenOffice or Microsoft Office file formats here,
  * such as odt or doc, and untrusted users are allowed to upload files, then
  * your wiki will be vulnerable to cross-site request forgery (CSRF).
  */
 $wgFileExtensions = array( 'png', 'gif', 'jpg', 'jpeg' );
 
-/** Files with these extensions will never be allowed as uploads. */
+/**
+ * Files with these extensions will never be allowed as uploads.
+ * An array of file extensions to blacklist. You should append to this array
+ * if you want to blacklist additional files.
+ * */
 $wgFileBlacklist = array(
        # HTML may contain cookie-stealing JavaScript and web bugs
        'html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht',
@@ -628,7 +661,7 @@ $wgAllowJavaUploads = false;
 /**
  * This is a flag to determine whether or not to check file extensions on upload.
  *
- * WARNING: setting this to false is insecure for public wikis.
+ * @warning Setting this to false is insecure for public wikis.
  */
 $wgCheckFileExtensions = true;
 
@@ -636,18 +669,21 @@ $wgCheckFileExtensions = true;
  * If this is turned off, users may override the warning for files not covered
  * by $wgFileExtensions.
  *
- * WARNING: setting this to false is insecure for public wikis.
+ * @warning Setting this to false is insecure for public wikis.
  */
 $wgStrictFileExtensions = true;
 
 /**
  * Setting this to true will disable the upload system's checks for HTML/JavaScript.
- * THIS IS VERY DANGEROUS on a publicly editable site, so USE wgGroupPermissions
- * TO RESTRICT UPLOADING to only those that you trust
+ *
+ * @warning THIS IS VERY DANGEROUS on a publicly editable site, so USE
+ * $wgGroupPermissions TO RESTRICT UPLOADING to only those that you trust
  */
 $wgDisableUploadScriptChecks = false;
 
-/** Warn if uploaded files are larger than this (in bytes), or false to disable*/
+/**
+ * Warn if uploaded files are larger than this (in bytes), or false to disable
+ */
 $wgUploadSizeWarning = false;
 
 /**
@@ -674,18 +710,18 @@ $wgTrustedMediaFormats = array(
  * Each entry in the array maps a MIME type to a class name
  */
 $wgMediaHandlers = array(
-       'image/jpeg' => 'JpegHandler',
-       'image/png' => 'PNGHandler',
-       'image/gif' => 'GIFHandler',
-       'image/tiff' => 'TiffHandler',
+       'image/jpeg'     => 'JpegHandler',
+       'image/png'      => 'PNGHandler',
+       'image/gif'      => 'GIFHandler',
+       'image/tiff'     => 'TiffHandler',
        'image/x-ms-bmp' => 'BmpHandler',
-       'image/x-bmp' => 'BmpHandler',
-       'image/x-xcf' => 'XCFHandler',
-       'image/svg+xml' => 'SvgHandler', // official
-       'image/svg' => 'SvgHandler', // compat
+       'image/x-bmp'    => 'BmpHandler',
+       'image/x-xcf'    => 'XCFHandler',
+       'image/svg+xml'  => 'SvgHandler', // official
+       'image/svg'      => 'SvgHandler', // compat
        'image/vnd.djvu' => 'DjVuHandler', // official
-       'image/x.djvu' => 'DjVuHandler', // compat
-       'image/x-djvu' => 'DjVuHandler', // compat
+       'image/x.djvu'   => 'DjVuHandler', // compat
+       'image/x-djvu'   => 'DjVuHandler', // compat
 );
 
 /**
@@ -719,17 +755,18 @@ $wgImageMagickTempDir = false;
  * %s will be replaced with the source path, %d with the destination
  * %w and %h will be replaced with the width and height.
  *
- * Example for GraphicMagick:
- * <code>
+ * @par Example for GraphicMagick:
+ * @code
  * $wgCustomConvertCommand = "gm convert %s -resize %wx%h %d"
- * </code>
+ * @endcode
  *
  * Leave as false to skip this.
  */
 $wgCustomConvertCommand = false;
 
 /**
- * Some tests and extensions use exiv2 to manipulate the EXIF metadata in some image formats.
+ * Some tests and extensions use exiv2 to manipulate the EXIF metadata in some
+ * image formats.
  */
 $wgExiv2Command = '/usr/bin/exiv2';
 
@@ -767,11 +804,15 @@ $wgSVGMaxSize = 2048;
 $wgSVGMetadataCutoff = 262144;
 
 /**
- * MediaWiki will reject HTMLesque tags in uploaded files due to idiotic browsers which can't
- * perform basic stuff like MIME detection and which are vulnerable to further idiots uploading
- * crap files as images. When this directive is on, <title> will be allowed in files with
- * an "image/svg+xml" MIME type. You should leave this disabled if your web server is misconfigured
- * and doesn't send appropriate MIME types for SVG images.
+ * Disallow <title> element in SVG files.
+ *
+ * MediaWiki will reject HTMLesque tags in uploaded files due to idiotic
+ * browsers which can not perform basic stuff like MIME detection and which are
+ * vulnerable to further idiots uploading crap files as images.
+ *
+ * When this directive is on, "<title>" will be allowed in files with an
+ * "image/svg+xml" MIME type. You should leave this disabled if your web server
+ * is misconfigured and doesn't send appropriate MIME types for SVG images.
  */
 $wgAllowTitlesInSVG = false;
 
@@ -801,13 +842,13 @@ $wgMaxAnimatedGifArea = 1.25e7;
  * For inline display, we need to convert to PNG or JPEG.
  * Note scaling should work with ImageMagick, but may not with GD scaling.
  *
- * Example:
- * <code>
+ * @par Example:
+ * @code
  *  // PNG is lossless, but inefficient for photos
  *  $wgTiffThumbnailType = array( 'png', 'image/png' );
  *  // JPEG is good for photos, but has no transparency support. Bad for diagrams.
  *  $wgTiffThumbnailType = array( 'jpg', 'image/jpeg' );
- * </code>
+ * @endcode
  */
  $wgTiffThumbnailType = false;
 
@@ -820,7 +861,7 @@ $wgMaxAnimatedGifArea = 1.25e7;
 $wgThumbnailEpoch = '20030516000000';
 
 /**
- * If set, inline scaled images will still produce <img> tags ready for
+ * If set, inline scaled images will still produce "<img>" tags ready for
  * output instead of showing an error message.
  *
  * This may be useful if errors are transitory, especially if the site
@@ -941,10 +982,11 @@ $wgLoadFileinfoExtension = false;
  * the mime type to standard output.
  * The name of the file to process will be appended to the command given here.
  * If not set or NULL, mime_content_type will be used if available.
- * Example:
- * <code>
+ *
+ * @par Example:
+ * @code
  * #$wgMimeDetectorCommand = "file -bi"; # use external mime detector (Linux)
- * </code>
+ * @endcode
  */
 $wgMimeDetectorCommand = null;
 
@@ -998,7 +1040,7 @@ $wgThumbLimits = array(
 );
 
 /**
- * Default parameters for the <gallery> tag
+ * Default parameters for the "<gallery>" tag
  */
 $wgGalleryOptions = array (
        'imagesPerRow' => 0, // Default number of images per-row in the gallery. 0 -> Adapt to screensize
@@ -1021,7 +1063,10 @@ $wgThumbUpright = 0.75;
 $wgDirectoryMode = 0777;
 
 /**
- * DJVU settings
+ * @name DJVU settings
+ * @{
+ */
+/**
  * Path of the djvudump executable
  * Enable this and $wgDjvuRenderer to enable djvu rendering
  */
@@ -1046,15 +1091,18 @@ $wgDjvuTxt = null;
  * Path of the djvutoxml executable
  * This works like djvudump except much, much slower as of version 3.5.
  *
- * For now I recommend you use djvudump instead. The djvuxml output is
+ * For now we  recommend you use djvudump instead. The djvuxml output is
  * probably more stable, so we'll switch back to it as soon as they fix
  * the efficiency problem.
  * http://sourceforge.net/tracker/index.php?func=detail&aid=1704049&group_id=32953&atid=406583
+ *
+ * @par Example:
+ * @code
+ * $wgDjvuToXML = 'djvutoxml';
+ * @endcode
  */
-# $wgDjvuToXML = 'djvutoxml';
 $wgDjvuToXML = null;
 
-
 /**
  * Shell command for the DJVU post processor
  * Default: pnmtopng, since ddjvu generates ppm output
@@ -1065,6 +1113,7 @@ $wgDjvuPostProcessor = 'pnmtojpeg';
  * File extension for the DJVU post processor output
  */
 $wgDjvuOutputExtension = 'jpg';
+/** @} */ # end of DJvu }
 
 /** @} */ # end of file uploads }
 
@@ -1141,17 +1190,21 @@ $wgNewPasswordExpiry = 3600 * 24 * 7;
 $wgUserEmailConfirmationTokenExpiry = 7 * 24 * 60 * 60;
 
 /**
- * SMTP Mode
+ * SMTP Mode.
+ *
  * For using a direct (authenticated) SMTP server connection.
  * Default to false or fill an array :
- * <code>
- * "host" => 'SMTP domain',
- * "IDHost" => 'domain for MessageID',
- * "port" => "25",
- * "auth" => true/false,
- * "username" => user,
- * "password" => password
- * </code>
+ *
+ * @code
+ * $wgSMTP = array(
+ *     'host'     => 'SMTP domain',
+ *     'IDHost'   => 'domain for MessageID',
+ *     'port'     => '25',
+ *     'auth'     => [true|false],
+ *     'username' => [SMTP username],
+ *     'password' => [SMTP password],
+ * );
+ * @endcode
  */
 $wgSMTP = false;
 
@@ -1173,9 +1226,9 @@ $wgEnotifFromEditor = false;
 # It call this to be a "user-preferences-option (UPO)"
 
 /**
- * Require email authentication before sending mail to an email addres. This is
- * highly recommended. It prevents MediaWiki from being used as an open spam
- * relay.
+ * Require email authentication before sending mail to an email address.
+ * This is highly recommended. It prevents MediaWiki from being used as an open
+ * spam relay.
  */
 $wgEmailAuthentication = true;
 
@@ -1354,9 +1407,9 @@ $wgSharedTables = array( 'user', 'user_properties' );
  * accidental misconfiguration or MediaWiki bugs, set read_only=1 on all your
  * slaves in my.cnf. You can set read_only mode at runtime using:
  *
- * <code>
+ * @code
  *     SET @@read_only=1;
- * </code>
+ * @endcode
  *
  * Since the effect of writing to a slave is so damaging and difficult to clean
  * up, we at Wikimedia set read_only=1 in my.cnf on all our DB servers, even
@@ -1381,13 +1434,19 @@ $wgMasterWaitTimeout = 10;
 
 /** File to log database errors to */
 $wgDBerrorLog = false;
+/**
+ * Override wiki timezone to UTC for wgDBerrorLog
+ * @since 1.20
+ */
+$wgDBerrorLogInUTC = false;
 
 /** When to give an error message */
 $wgDBClusterTimeout = 10;
 
 /**
- * Scale load balancer polling time so that under overload conditions, the database server
- * receives a SHOW STATUS query at an average interval of this many microseconds
+ * Scale load balancer polling time so that under overload conditions, the
+ * database server receives a SHOW STATUS query at an average interval of this
+ * many microseconds
  */
 $wgDBAvgStatusPoll = 2000;
 
@@ -1395,7 +1454,7 @@ $wgDBAvgStatusPoll = 2000;
  * Set to true to engage MySQL 4.1/5.0 charset-related features;
  * for now will just cause sending of 'SET NAMES=utf8' on connect.
  *
- * WARNING: THIS IS EXPERIMENTAL!
+ * @warning THIS IS EXPERIMENTAL!
  *
  * May break if you're not using the table defs from mysql5/tables.sql.
  * May break if you're upgrading an existing wiki if set differently.
@@ -1448,19 +1507,30 @@ $wgCompressRevisions = false;
 
 /**
  * External stores allow including content
- * from non database sources following URL links
+ * from non database sources following URL links.
  *
  * Short names of ExternalStore classes may be specified in an array here:
+ * @code
  * $wgExternalStores = array("http","file","custom")...
+ * @endcode
  *
  * CAUTION: Access to database might lead to code execution
  */
 $wgExternalStores = false;
 
 /**
- * An array of external mysql servers, e.g.
- * $wgExternalServers = array( 'cluster1' => array( 'srv28', 'srv29', 'srv30' ) );
- * Used by LBFactory_Simple, may be ignored if $wgLBFactoryConf is set to another class.
+ * An array of external MySQL servers.
+ *
+ * @par Example:
+ * Create a cluster named 'cluster1' containing three servers:
+ * @code
+ * $wgExternalServers = array(
+ *     'cluster1' => array( 'srv28', 'srv29', 'srv30' )
+ * );
+ * @endcode
+ *
+ * Used by LBFactory_Simple, may be ignored if $wgLBFactoryConf is set to
+ * another class.
  */
 $wgExternalServers = array();
 
@@ -1469,9 +1539,12 @@ $wgExternalServers = array();
  * Part of a URL, e.g. DB://cluster1
  *
  * Can be an array instead of a single string, to enable data distribution. Keys
- * must be consecutive integers, starting at zero. Example:
+ * must be consecutive integers, starting at zero.
  *
+ * @par Example:
+ * @code
  * $wgDefaultExternalStore = array( 'DB://cluster1', 'DB://cluster2' );
+ * @endcode
  *
  * @var array
  */
@@ -1634,8 +1707,8 @@ $wgObjectCaches = array(
 );
 
 /**
- * The expiry time for the parser cache, in seconds. The default is 86.4k
- * seconds, otherwise known as a day.
+ * The expiry time for the parser cache, in seconds.
+ * The default is 86400 (one day).
  */
 $wgParserCacheExpireTime = 86400;
 
@@ -1684,9 +1757,9 @@ $wgMemCachedTimeout = 100000;
 $wgUseLocalMessageCache = false;
 
 /**
- * Defines format of local cache
- * true - Serialized object
- * false - PHP source file (Warning - security risk)
+ * Defines format of local cache.
+ *  - true: Serialized object
+ *  - false: PHP source file (Warning - security risk)
  */
 $wgLocalMessageCacheSerialized = true;
 
@@ -1699,23 +1772,23 @@ $wgAdaptiveMessageCache = false;
 
 /**
  * Localisation cache configuration. Associative array with keys:
- *     class:       The class to use. May be overridden by extensions.
+ * class:       The class to use. May be overridden by extensions.
  *
- *     store:       The location to store cache data. May be 'files', 'db' or
- *                  'detect'. If set to "files", data will be in CDB files. If set
- *                  to "db", data will be stored to the database. If set to
- *                  "detect", files will be used if $wgCacheDirectory is set,
- *                  otherwise the database will be used.
+ * store:       The location to store cache data. May be 'files', 'db' or
+ *              'detect'. If set to "files", data will be in CDB files. If set
+ *              to "db", data will be stored to the database. If set to
+ *              "detect", files will be used if $wgCacheDirectory is set,
+ *              otherwise the database will be used.
  *
- *     storeClass:  The class name for the underlying storage. If set to a class
- *                  name, it overrides the "store" setting.
+ * storeClass:  The class name for the underlying storage. If set to a class
+ *              name, it overrides the "store" setting.
  *
- *     storeDirectory:  If the store class puts its data in files, this is the
- *                      directory it will use. If this is false, $wgCacheDirectory
- *                      will be used.
+ * storeDirectory:  If the store class puts its data in files, this is the
+ *                  directory it will use. If this is false, $wgCacheDirectory
+ *                  will be used.
  *
- *     manualRecache:   Set this to true to disable cache updates on web requests.
- *                      Use maintenance/rebuildLocalisationCache.php instead.
+ * manualRecache:   Set this to true to disable cache updates on web requests.
+ *                  Use maintenance/rebuildLocalisationCache.php instead.
  */
 $wgLocalisationCacheConf = array(
        'class' => 'LocalisationCache',
@@ -1730,14 +1803,17 @@ $wgCachePages = true;
 
 /**
  * Set this to current time to invalidate all prior cached pages. Affects both
- * client- and server-side caching.
+ * client-side and server-side caching.
  * You can get the current date on your server by using the command:
+ * @verbatim
  *   date +%Y%m%d%H%M%S
+ * @endverbatim
  */
 $wgCacheEpoch = '20030516000000';
 
 /**
  * Bump this number when changing the global style sheets and JavaScript.
+ *
  * It should be appended in the query string of static CSS and JS includes,
  * to ensure that client-side caches do not keep obsolete copies of global
  * styles.
@@ -1863,10 +1939,12 @@ $wgUseXVO = false;
 $wgVaryOnXFP = false;
 
 /**
- * Internal server name as known to Squid, if different. Example:
- * <code>
+ * Internal server name as known to Squid, if different.
+ *
+ * @par Example:
+ * @code
  * $wgInternalServer = 'http://yourinternal.tld:8000';
- * </code>
+ * @endcode
  */
 $wgInternalServer = false;
 
@@ -1914,6 +1992,7 @@ $wgMaxSquidPurgeTitles = 400;
  *
  * Example configuration to send purges for upload.wikimedia.org to one
  * multicast group and all other purges to another:
+ * @code
  * $wgHTCPMulticastRouting = array(
  *         '|^https?://upload\.wikimedia\.org|' => array(
  *                 'host' => '239.128.0.113',
@@ -1924,6 +2003,7 @@ $wgMaxSquidPurgeTitles = 400;
  *                 'port' => 4827,
  *         ),
  * );
+ * @endcode
  *
  * @see $wgHTCPMulticastTTL
  */
@@ -1972,11 +2052,12 @@ $wgLanguageCode = 'en';
 
 /**
  * Some languages need different word forms, usually for different cases.
- * Used in Language::convertGrammar(). Example:
+ * Used in Language::convertGrammar().
  *
- * <code>
+ * @par Example:
+ * @code
  * $wgGrammarForms['en']['genitive']['car'] = 'car\'s';
- * </code>
+ * @endcode
  */
 $wgGrammarForms = array();
 
@@ -2059,7 +2140,7 @@ $wgAllUnicodeFixes = false;
  * converting a wiki from MediaWiki 1.4 or earlier to UTF-8 without the
  * burdensome mass conversion of old text data.
  *
- * NOTE! This DOES NOT touch any fields other than old_text.Titles, comments,
+ * @note This DOES NOT touch any fields other than old_text. Titles, comments,
  * user names, etc still must be converted en masse in the database before
  * continuing as a UTF-8 wiki.
  */
@@ -2168,28 +2249,27 @@ $wgCanonicalLanguageLinks = true;
 $wgDefaultLanguageVariant = false;
 
 /**
- * Disabled variants array of language variant conversion. Example:
- * <code>
+ * Disabled variants array of language variant conversion.
+ *
+ * @par Example:
+ * @code
  *  $wgDisabledVariants[] = 'zh-mo';
  *  $wgDisabledVariants[] = 'zh-my';
- * </code>
- *
- * or:
- *
- * <code>
- *  $wgDisabledVariants = array('zh-mo', 'zh-my');
- * </code>
+ * @endcode
  */
 $wgDisabledVariants = array();
 
 /**
  * Like $wgArticlePath, but on multi-variant wikis, this provides a
  * path format that describes which parts of the URL contain the
- * language variant.  For Example:
+ * language variant.
  *
- *   $wgLanguageCode = 'sr';
- *   $wgVariantArticlePath = '/$2/$1';
- *   $wgArticlePath = '/wiki/$1';
+ * @par Example:
+ * @code
+ *     $wgLanguageCode = 'sr';
+ *     $wgVariantArticlePath = '/$2/$1';
+ *     $wgArticlePath = '/wiki/$1';
+ * @endcode
  *
  * A link to /wiki/ would be redirected to /sr/Главна_страна
  *
@@ -2215,10 +2295,14 @@ $wgLoginLanguageSelector = false;
  * wfMsg(). The code behaves this way by default. However, sites like the
  * Wikimedia Commons do offer different versions of 'mainpage' and the like for
  * different languages. This array provides a way to override the default
- * behavior. For example, to allow language-specific main page and community
- * portal, set
+ * behavior.
  *
- * $wgForceUIMsgAsContentMsg = array( 'mainpage', 'portal-url' );
+ * @par Example:
+ * To allow language-specific main page and community
+ * portal:
+ * @code
+ *     $wgForceUIMsgAsContentMsg = array( 'mainpage', 'portal-url' );
+ * @endcode
  */
 $wgForceUIMsgAsContentMsg = array();
 
@@ -2233,13 +2317,13 @@ $wgForceUIMsgAsContentMsg = array();
  * Timezones can be translated by editing MediaWiki messages of type
  * timezone-nameinlowercase like timezone-utc.
  *
- * Examples:
- * <code>
+ * @par Examples:
+ * @code
  * $wgLocaltimezone = 'GMT';
  * $wgLocaltimezone = 'PST8PDT';
  * $wgLocaltimezone = 'Europe/Sweden';
  * $wgLocaltimezone = 'CET';
- * </code>
+ * @endcode
  */
 $wgLocaltimezone = null;
 
@@ -2260,7 +2344,7 @@ $wgLocalTZoffset = null;
  * language variant conversion is disabled in interface messages. Setting this
  * to true re-enables it.
  *
- * This variable should be removed (implicitly false) in 1.20 or earlier.
+ * @todo This variable should be removed (implicitly false) in 1.20 or earlier.
  */
 $wgBug34832TransitionalRollback = true;
 
@@ -2357,10 +2441,14 @@ $wgWellFormedXml = true;
 
 /**
  * Permit other namespaces in addition to the w3.org default.
- * Use the prefix for the key and the namespace for the value. For
- * example:
+ *
+ * Use the prefix for the key and the namespace for the value.
+ *
+ * @par Example:
+ * @code
  * $wgXhtmlNamespaces['svg'] = 'http://www.w3.org/2000/svg';
- * Normally we wouldn't have to define this in the root <html>
+ * @endCode
+ * Normally we wouldn't have to define this in the root "<html>"
  * element, but IE needs it there in some circumstances.
  *
  * This is ignored if $wgHtml5 is true, for the same reason as
@@ -2371,7 +2459,7 @@ $wgXhtmlNamespaces = array();
 /**
  * Show IP address, for non-logged in users. It's necessary to switch this off
  * for some forms of caching.
- * Will disable file cache.
+ * @warning Will disable file cache.
  */
 $wgShowIPinHeader = true;
 
@@ -2530,15 +2618,16 @@ $wgExperimentalHtmlIds = false;
  * The value should be either a string or an array. If it is a string it will be output
  * directly as html, however some skins may choose to ignore it. An array is the preferred format
  * for the icon, the following keys are used:
- *   src: An absolute url to the image to use for the icon, this is recommended
+ *  - src: An absolute url to the image to use for the icon, this is recommended
  *        but not required, however some skins will ignore icons without an image
- *   url: The url to use in the <a> arround the text or icon, if not set an <a> will not be outputted
- *   alt: This is the text form of the icon, it will be displayed without an image in
+ * - url: The url to use in the a element arround the text or icon, if not set an a element will not be outputted
+ * - alt: This is the text form of the icon, it will be displayed without an image in
  *        skins like Modern or if src is not set, and will otherwise be used as
  *        the alt="" for the image. This key is required.
- *   width and height: If the icon specified by src is not of the standard size
+ * - width and height: If the icon specified by src is not of the standard size
  *                     you can specify the size of image to use with these keys.
  *                     Otherwise they will default to the standard 88x31.
+ * @todo Reformat documentation.
  */
 $wgFooterIcons = array(
        "copyright" => array(
@@ -2554,23 +2643,24 @@ $wgFooterIcons = array(
 );
 
 /**
- * Login / create account link behavior when it's possible for anonymous users to create an account
- * true = use a combined login / create account link
- * false = split login and create account into two separate links
+ * Login / create account link behavior when it's possible for anonymous users
+ * to create an account.
+ *  - true = use a combined login / create account link
+ *  - false = split login and create account into two separate links
  */
 $wgUseCombinedLoginLink = true;
 
 /**
- * Search form behavior for Vector skin only
- * true = use an icon search button
- * false = use Go & Search buttons
+ * Search form behavior for Vector skin only.
+ *  - true = use an icon search button
+ *  - false = use Go & Search buttons
  */
 $wgVectorUseSimpleSearch = false;
 
 /**
- * Watch and unwatch as an icon rather than a link for Vector skin only
- * true = use an icon watch/unwatch button
- * false = use watch/unwatch text link
+ * Watch and unwatch as an icon rather than a link for Vector skin only.
+ *  - true = use an icon watch/unwatch button
+ *  - false = use watch/unwatch text link
  */
 $wgVectorUseIconWatch = false;
 
@@ -2608,10 +2698,13 @@ $wgSend404Code = true;
  */
 
 /**
- * Client-side resource modules. Extensions should add their module definitions
- * here.
+ * Client-side resource modules.
  *
- * Example:
+ * Extensions should add their resource loader module definitions
+ * to the $wgResourceModules variable.
+ *
+ * @par Example:
+ * @code
  *   $wgResourceModules['ext.myExtension'] = array(
  *      'scripts' => 'myExtension.js',
  *      'styles' => 'myExtension.css',
@@ -2619,6 +2712,7 @@ $wgSend404Code = true;
  *      'localBasePath' => dirname( __FILE__ ),
  *      'remoteExtPath' => 'MyExtension',
  *   );
+ * @endcode
  */
 $wgResourceModules = array();
 
@@ -2627,11 +2721,13 @@ $wgResourceModules = array();
  * built-in source that is not in this array, but defined by
  * ResourceLoader::__construct() so that it cannot be unset.
  *
- * Example:
+ * @par Example:
+ * @code
  *   $wgResourceLoaderSources['foo'] = array(
  *       'loadScript' => 'http://example.org/w/load.php',
  *       'apiScript' => 'http://example.org/w/api.php'
  *   );
+ * @endcode
  */
 $wgResourceLoaderSources = array();
 
@@ -2642,7 +2738,9 @@ $wgResourceLoaderSources = array();
 $wgResourceBasePath = null;
 
 /**
- * Maximum time in seconds to cache resources served by the resource loader
+ * Maximum time in seconds to cache resources served by the resource loader.
+ *
+ * @todo Document array structure
  */
 $wgResourceLoaderMaxage = array(
        'versioned' => array(
@@ -2658,8 +2756,9 @@ $wgResourceLoaderMaxage = array(
 );
 
 /**
- * The default debug mode (on/off) for of ResourceLoader requests. This will still
- * be overridden when the debug URL parameter is used.
+ * The default debug mode (on/off) for of ResourceLoader requests.
+ *
+ * This will still be overridden when the debug URL parameter is used.
  */
 $wgResourceLoaderDebug = false;
 
@@ -2685,33 +2784,54 @@ $wgResourceLoaderMinifierMaxLineLength = 1000;
 
 /**
  * Whether to include the mediawiki.legacy JS library (old wikibits.js), and its
- * dependencies
+ * dependencies.
  */
 $wgIncludeLegacyJavaScript = true;
 
 /**
- * Whether to preload the mediawiki.util module as blocking module in the top queue.
- * Before MediaWiki 1.19, modules used to load slower/less asynchronous which allowed
- * modules to lack dependencies on 'popular' modules that were likely loaded already.
+ * Whether to preload the mediawiki.util module as blocking module in the top
+ * queue.
+ *
+ * Before MediaWiki 1.19, modules used to load slower/less asynchronous which
+ * allowed modules to lack dependencies on 'popular' modules that were likely
+ * loaded already.
+ *
  * This setting is to aid scripts during migration by providing mediawiki.util
  * unconditionally (which was the most commonly missed dependency).
- * It doesn't cover all missing dependencies obviously but should fix most of them.
+ * It doesn't cover all missing dependencies obviously but should fix most of
+ * them.
+ *
  * This should be removed at some point after site/user scripts have been fixed.
- * Enable this if your wiki has a large amount of user/site scripts that are lacking
- * dependencies.
+ * Enable this if your wiki has a large amount of user/site scripts that are
+ * lacking dependencies.
+ * @todo Deprecate
  */
 $wgPreloadJavaScriptMwUtil = false;
 
 /**
- * Whether or not to assing configuration variables to the global window object.
- * If this is set to false, old code using deprecated variables like:
- * " if ( window.wgRestrictionEdit ) ..."
+ * Whether or not to assign configuration variables to the global window object.
+ *
+ * If this is set to false, old code using deprecated variables will no longer
+ * work.
+ *
+ * @par Example of legacy code:
+ * @code{,js}
+ *     if ( window.wgRestrictionEdit ) { ... }
+ * @endcode
  * or:
- * " if ( wgIsArticle ) ..."
- * will no longer work and needs to use mw.config instead. For example:
- * " if ( mw.config.exists('wgRestrictionEdit') )"
- * or
- * " if ( mw.config.get('wgIsArticle') )".
+ * @code{,js}
+ *     if ( wgIsArticle ) { ... }
+ * @endcode
+ *
+ * Instead, one needs to use mw.config.
+ * @par Example using mw.config global configuration:
+ * @code{,js}
+ *     if ( mw.config.exists('wgRestrictionEdit') ) { ... }
+ * @endcode
+ * or:
+ * @code{,js}
+ *     if ( mw.config.get('wgIsArticle') ) { ... }
+ * @endcode
  */
 $wgLegacyJavaScriptGlobals = true;
 
@@ -2729,8 +2849,8 @@ $wgLegacyJavaScriptGlobals = true;
 $wgResourceLoaderMaxQueryLength = -1;
 
 /**
- * If set to true, JavaScript modules loaded from wiki pages will be parsed prior
- * to minification to validate it.
+ * If set to true, JavaScript modules loaded from wiki pages will be parsed
+ * prior to minification to validate it.
  *
  * Parse errors will result in a JS exception being thrown during module load,
  * which avoids breaking other modules loaded in the same request.
@@ -2748,7 +2868,7 @@ $wgResourceLoaderValidateJS = true;
 $wgResourceLoaderValidateStaticJS = false;
 
 /**
- * If set to true, asynchronous loading of bottom-queue scripts in the <head>
+ * If set to true, asynchronous loading of bottom-queue scripts in the "<head>"
  * will be enabled. This is an experimental feature that's supposed to make
  * JavaScript load faster.
  */
@@ -2784,19 +2904,25 @@ $wgMetaNamespaceTalk = false;
  * names of existing namespaces. Extensions developers should use
  * $wgCanonicalNamespaceNames.
  *
- * PLEASE  NOTE: Once you delete a namespace, the pages in that namespace will
+ * @warning Once you delete a namespace, the pages in that namespace will
  * no longer be accessible. If you rename it, then you can access them through
  * the new namespace name.
  *
  * Custom namespaces should start at 100 to avoid conflicting with standard
  * namespaces, and should always follow the even/odd main/talk pattern.
+ *
+ * @par Example:
+ * @code
+ * $wgExtraNamespaces = array(
+ *    100 => "Hilfe",
+ *    101 => "Hilfe_Diskussion",
+ *    102 => "Aide",
+ *    103 => "Discussion_Aide"
+ * );
+ * @endcode
+ *
+ * @todo Add a note about maintenance/namespaceDupes.php
  */
-# $wgExtraNamespaces = array(
-#     100 => "Hilfe",
-#     101 => "Hilfe_Diskussion",
-#     102 => "Aide",
-#     103 => "Discussion_Aide"
-# );
 $wgExtraNamespaces = array();
 
 /**
@@ -2808,18 +2934,22 @@ $wgExtraNamespaces = array();
 $wgExtraGenderNamespaces = array();
 
 /**
- * Namespace aliases
+ * Namespace aliases.
+ *
  * These are alternate names for the primary localised namespace names, which
  * are defined by $wgExtraNamespaces and the language file. If a page is
  * requested with such a prefix, the request will be redirected to the primary
  * name.
  *
  * Set this to a map from namespace names to IDs.
- * Example:
+ *
+ * @par Example:
+ * @code
  *    $wgNamespaceAliases = array(
  *        'Wikipedian' => NS_USER,
  *        'Help' => 100,
  *    );
+ * @endcode
  */
 $wgNamespaceAliases = array();
 
@@ -2834,8 +2964,8 @@ $wgNamespaceAliases = array();
  *   -  +         Enabled by default, but doesn't work with path to query rewrite rules, corrupted by apache
  *   -  ?         Enabled by default, but doesn't work with path to PATH_INFO rewrites
  *
- * All three of these punctuation problems can be avoided by using an alias, instead of a
- * rewrite rule of either variety.
+ * All three of these punctuation problems can be avoided by using an alias,
+ * instead of a rewrite rule of either variety.
  *
  * The problem with % is that when using a path to query rewrite rule, URLs are
  * double-unescaped: once by Apache's path conversion code, and again by PHP. So
@@ -2861,33 +2991,47 @@ $wgLocalInterwiki = false;
  */
 $wgInterwikiExpiry = 10800;
 
-/** Interwiki caching settings.
-       $wgInterwikiCache specifies path to constant database file
-               This cdb database is generated by dumpInterwiki from maintenance
-               and has such key formats:
-                       dbname:key - a simple key (e.g. enwiki:meta)
-                       _sitename:key - site-scope key (e.g. wiktionary:meta)
-                       __global:key - global-scope key (e.g. __global:meta)
-                       __sites:dbname - site mapping (e.g. __sites:enwiki)
-               Sites mapping just specifies site name, other keys provide
-                       "local url" data layout.
-       $wgInterwikiScopes specify number of domains to check for messages:
-               1 - Just wiki(db)-level
-               2 - wiki and global levels
-               3 - site levels
-       $wgInterwikiFallbackSite - if unable to resolve from cache
+/**
+ * @name Interwiki caching settings.
+ * @{
+ */
+/**
+ *$wgInterwikiCache specifies path to constant database file.
+ *
+ * This cdb database is generated by dumpInterwiki from maintenance and has
+ * such key formats:
+ *  - dbname:key - a simple key (e.g. enwiki:meta)
+ *  - _sitename:key - site-scope key (e.g. wiktionary:meta)
+ *  - __global:key - global-scope key (e.g. __global:meta)
+ *  - __sites:dbname - site mapping (e.g. __sites:enwiki)
+ *
+ * Sites mapping just specifies site name, other keys provide "local url"
+ * data layout.
  */
 $wgInterwikiCache = false;
+/**
+ * Specify number of domains to check for messages.
+ *     - 1: Just wiki(db)-level
+ *     - 2: wiki and global levels
+ *     - 3: site levels
+ */
 $wgInterwikiScopes = 3;
+/**
+ *     $wgInterwikiFallbackSite - if unable to resolve from cache
+ */
 $wgInterwikiFallbackSite = 'wiki';
+/** @} */ # end of Interwiki caching settings.
 
 /**
  * If local interwikis are set up which allow redirects,
  * set this regexp to restrict URLs which will be displayed
  * as 'redirected from' links.
  *
+ * @par Example:
  * It might look something like this:
+ * @code
  * $wgRedirectSources = '!^https?://[a-z-]+\.wikipedia\.org/!';
+ * @endcode
  *
  * Leave at false to avoid displaying any incoming redirect markers.
  * This does not affect intra-wiki redirects, which don't change
@@ -2897,7 +3041,8 @@ $wgRedirectSources = false;
 
 /**
  * Set this to false to avoid forcing the first letter of links to capitals.
- * WARNING: may break links! This makes links COMPLETELY case-sensitive. Links
+ *
+ * @warning may break links! This makes links COMPLETELY case-sensitive. Links
  * appearing with a capital at the beginning of a sentence will *not* go to the
  * same place as links in the middle of a sentence using a lowercase initial.
  */
@@ -2911,7 +3056,11 @@ $wgCapitalLinks = true;
  * associated content namespaces, the values for those are ignored in favor of the
  * subject namespace's setting. Setting for NS_MEDIA is taken automatically from
  * NS_FILE.
- * EX: $wgCapitalLinkOverrides[ NS_FILE ] = false;
+ *
+ * @par Example:
+ * @code
+ *     $wgCapitalLinkOverrides[ NS_FILE ] = false;
+ * @endcode
  */
 $wgCapitalLinkOverrides = array();
 
@@ -3044,11 +3193,11 @@ $wgAllowExternalImages = false;
  * You can use this to set up a trusted, simple repository of images.
  * You may also specify an array of strings to allow multiple sites
  *
- * Examples:
- * <code>
+ * @par Examples:
+ * @code
  * $wgAllowExternalImagesFrom = 'http://127.0.0.1/';
  * $wgAllowExternalImagesFrom = array( 'http://127.0.0.1/', 'http://example.com' );
- * </code>
+ * @endcode
  */
 $wgAllowExternalImagesFrom = '';
 
@@ -3063,7 +3212,7 @@ $wgAllowExternalImagesFrom = '';
 $wgEnableImageWhitelist = true;
 
 /**
- * A different approach to the above: simply allow the <img> tag to be used.
+ * A different approach to the above: simply allow the "<img>" tag to be used.
  * This allows you to specify alt text and other attributes, copy-paste HTML to
  * your wiki more easily, etc.  However, allowing external images in any manner
  * will allow anyone with editing rights to snoop on your visitors' IP
@@ -3105,7 +3254,7 @@ $wgTidyInternal = extension_loaded( 'tidy' );
  */
 $wgDebugTidy = false;
 
-/** Allow raw, unchecked HTML in <html>...</html> sections.
+/** Allow raw, unchecked HTML in "<html>...</html>" sections.
  * THIS IS VERY DANGEROUS on a publicly editable site, so USE wgGroupPermissions
  * TO RESTRICT EDITING to only those that you trust
  */
@@ -3373,7 +3522,7 @@ $wgInvalidUsernameCharacters = '@';
 /**
  * Character used as a delimiter when testing for interwiki userrights
  * (In Special:UserRights, it is possible to modify users on different
- * databases if the delimiter is used, e.g. Someuser@enwiki).
+ * databases if the delimiter is used, e.g. "Someuser@enwiki").
  *
  * It is recommended that you have this delimiter in
  * $wgInvalidUsernameCharacters above, or you will not be able to
@@ -3495,18 +3644,19 @@ $wgBlockCIDRLimit = array(
 $wgBlockDisablesLogin = false;
 
 /**
- * Pages anonymous user may see as an array, e.g.
+ * Pages anonymous user may see, set as an array of pages titles.
  *
- * <code>
+ * @par Example:
+ * @code
  * $wgWhitelistRead = array ( "Main Page", "Wikipedia:Help");
- * </code>
+ * @endcode
  *
  * Special:Userlogin and Special:ChangePassword are always whitelisted.
  *
- * NOTE: This will only work if $wgGroupPermissions['*']['read'] is false --
+ * @note This will only work if $wgGroupPermissions['*']['read'] is false --
  * see below. Otherwise, ALL pages are accessible, regardless of this setting.
  *
- * Also note that this will only protect _pages in the wiki_. Uploaded files
+ * @note Also that this will only protect _pages in the wiki_. Uploaded files
  * will remain readable. You can use img_auth.php to protect uploaded files,
  * see http://www.mediawiki.org/wiki/Manual:Image_Authorization
  */
@@ -3520,6 +3670,7 @@ $wgEmailConfirmToEdit = false;
 
 /**
  * Permission keys given to users in each group.
+ *
  * This is an array where the keys are all groups and each value is an
  * array of the format (right => boolean).
  *
@@ -3630,6 +3781,7 @@ $wgGroupPermissions['bureaucrat']['noratelimit'] = true;
 // Permission to export pages including linked pages regardless of $wgExportMaxLinkDepth
 #$wgGroupPermissions['bureaucrat']['override-export-depth'] = true;
 
+#$wgGroupPermissions['sysop']['deletelogentry']  = true;
 #$wgGroupPermissions['sysop']['deleterevision']  = true;
 // To hide usernames from users and Sysops
 #$wgGroupPermissions['suppress']['hideuser'] = true;
@@ -3650,6 +3802,7 @@ $wgGroupPermissions['bureaucrat']['noratelimit'] = true;
 
 /**
  * Permission keys revoked from users in each group.
+ *
  * This acts the same way as wgGroupPermissions above, except that
  * if the user is in a group here, the permission will be removed from them.
  *
@@ -3667,16 +3820,20 @@ $wgImplicitGroups = array( '*', 'user', 'autoconfirmed' );
  * A map of group names that the user is in, to group names that those users
  * are allowed to add or revoke.
  *
- * Setting the list of groups to add or revoke to true is equivalent to "any group".
- *
- * For example, to allow sysops to add themselves to the "bot" group:
+ * Setting the list of groups to add or revoke to true is equivalent to "any
+ * group".
  *
+ * @par Example:
+ * To allow sysops to add themselves to the "bot" group:
+ * @code
  *    $wgGroupsAddToSelf = array( 'sysop' => array( 'bot' ) );
+ * @endcode
  *
+ * @par Example:
  * Implicit groups may be used for the source group, for instance:
- *
+ * @code
  *    $wgGroupsRemoveFromSelf = array( '*' => true );
- *
+ * @endcode
  * This allows users in the '*' group (i.e. any user) to remove themselves from
  * any group that they happen to be in.
  *
@@ -3712,13 +3869,16 @@ $wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' );
  * namespace.  If you list more than one permission, a user must
  * have all of them to edit pages in that namespace.
  *
- * Note: NS_MEDIAWIKI is implicitly restricted to editinterface.
+ * @note NS_MEDIAWIKI is implicitly restricted to 'editinterface'.
  */
 $wgNamespaceProtection = array();
 
 /**
  * Pages in namespaces in this array can not be used as templates.
- * Elements must be numeric namespace ids.
+ *
+ * Elements MUST be numeric namespace ids, you can safely use the MediaWiki
+ * namespaces constants (NS_USER, NS_MAIN...).
+ *
  * Among other things, this may be useful to enforce read-restrictions
  * which may otherwise be bypassed by using the template machanism.
  */
@@ -3734,11 +3894,15 @@ $wgNonincludableNamespaces = array();
  *
  * When left at 0, all registered accounts will pass.
  *
- * Example:
- * <code>
+ * @par Example:
+ * Set automatic confirmation to 10 minutes (which is 600 seconds):
+ * @code
  *  $wgAutoConfirmAge = 600;     // ten minutes
+ * @endcode
+ * Set age to one day:
+ * @code
  *  $wgAutoConfirmAge = 3600*24; // one day
- * </code>
+ * @endcode
  */
 $wgAutoConfirmAge = 0;
 
@@ -3746,14 +3910,18 @@ $wgAutoConfirmAge = 0;
  * Number of edits an account requires before it is autoconfirmed.
  * Passing both this AND the time requirement is needed. Example:
  *
- * <code>
+ * @par Example:
+ * @code
  * $wgAutoConfirmCount = 50;
- * </code>
+ * @endcode
  */
 $wgAutoConfirmCount = 0;
 
 /**
  * Automatically add a usergroup to any user who matches certain conditions.
+ *
+ * @todo Redocument $wgAutopromote
+ *
  * The format is
  *   array( '&' or '|' or '^' or '!', cond1, cond2, ... )
  * where cond1, cond2, ... are themselves conditions; *OR*
@@ -3781,14 +3949,19 @@ $wgAutopromote = array(
 
 /**
  * Automatically add a usergroup to any user who matches certain conditions.
+ *
  * Does not add the user to the group again if it has been removed.
  * Also, does not remove the group if the user no longer meets the criteria.
  *
- * The format is
+ * The format is:
+ * @code
  *     array( event => criteria, ... )
- * where event is
- *     'onEdit' (when user edits) or 'onView' (when user views the wiki)
- * and criteria has the same format as $wgAutopromote
+ * @endcode
+ * Where event is either:
+ *     - 'onEdit' (when user edits)
+ *     - 'onView' (when user views the wiki)
+ *
+ * Criteria has the same format as $wgAutopromote
  *
  * @see $wgAutopromote
  * @since 1.18
@@ -3806,16 +3979,23 @@ $wgAutopromoteOnceLogInRC = true;
 
 /**
  * $wgAddGroups and $wgRemoveGroups can be used to give finer control over who
- * can assign which groups at Special:Userrights.  Example configuration:
+ * can assign which groups at Special:Userrights.
  *
+ * @par Example:
+ * Bureaucrats can add any group:
  * @code
- * // Bureaucrat can add any group
  * $wgAddGroups['bureaucrat'] = true;
- * // Bureaucrats can only remove bots and sysops
+ * @endcode
+ * Bureaucrats can only remove bots and sysops:
+ * @code
  * $wgRemoveGroups['bureaucrat'] = array( 'bot', 'sysop' );
- * // Sysops can make bots
+ * @endcode
+ * Sysops can make bots:
+ * @code
  * $wgAddGroups['sysop'] = array( 'bot' );
- * // Sysops can disable other sysops in an emergency, and disable bots
+ * @endcode
+ * Sysops can disable other sysops in an emergency, and disable bots:
+ * @code
  * $wgRemoveGroups['sysop'] = array( 'sysop', 'bot' );
  * @endcode
  */
@@ -3835,8 +4015,10 @@ $wgAvailableRights = array();
  */
 $wgDeleteRevisionsLimit = 0;
 
-/** Number of accounts each IP address may create, 0 to disable.
- * Requires memcached */
+/**
+ * Number of accounts each IP address may create, 0 to disable.
+ *
+ * @warning Requires memcached */
 $wgAccountCreationThrottle = 0;
 
 /**
@@ -3846,8 +4028,9 @@ $wgAccountCreationThrottle = 0;
  * There's no administrator override on-wiki, so be careful what you set. :)
  * May be an array of regexes or a single string for backwards compatibility.
  *
- * See http://en.wikipedia.org/wiki/Regular_expression
- * Note that each regex needs a beginning/end delimiter, eg: # or /
+ * @see http://en.wikipedia.org/wiki/Regular_expression
+ *
+ * @note Each regex needs a beginning/end delimiter, eg: # or /
  */
 $wgSpamRegex = array();
 
@@ -3855,39 +4038,46 @@ $wgSpamRegex = array();
 $wgSummarySpamRegex = array();
 
 /**
- * Whether to use DNS blacklists in $wgDnsBlacklistUrls to check for open proxies
+ * Whether to use DNS blacklists in $wgDnsBlacklistUrls to check for open
+ * proxies
  * @since 1.16
  */
 $wgEnableDnsBlacklist = false;
 
 /**
- * @deprecated since 1.17 Use $wgEnableDnsBlacklist instead, only kept for backward
- *  compatibility
+ * @deprecated since 1.17 Use $wgEnableDnsBlacklist instead, only kept for
+ * backward compatibility.
  */
 $wgEnableSorbs = false;
 
 /**
- * List of DNS blacklists to use, if $wgEnableDnsBlacklist is true. This is an
- * array of either a URL or an array with the URL and a key (should the blacklist
- * require a key). For example:
+ * List of DNS blacklists to use, if $wgEnableDnsBlacklist is true.
+ *
+ * This is an array of either a URL or an array with the URL and a key (should
+ * the blacklist require a key).
+ *
+ * @par Example:
  * @code
  * $wgDnsBlacklistUrls = array(
  *   // String containing URL
- *   'http.dnsbl.sorbs.net',
+ *   'http.dnsbl.sorbs.net.',
  *   // Array with URL and key, for services that require a key
- *   array( 'dnsbl.httpbl.net', 'mykey' ),
+ *   array( 'dnsbl.httpbl.net.', 'mykey' ),
  *   // Array with just the URL. While this works, it is recommended that you
  *   // just use a string as shown above
- *   array( 'opm.tornevall.org' )
+ *   array( 'opm.tornevall.org.' )
  * );
  * @endcode
+ *
+ * @note You should end the domain name with a . to avoid searching your
+ * eventual domain search suffixes.
  * @since 1.16
  */
 $wgDnsBlacklistUrls = array( 'http.dnsbl.sorbs.net.' );
 
 /**
- * @deprecated since 1.17 Use $wgDnsBlacklistUrls instead, only kept for backward
- *  compatibility
+ * @deprecated since 1.17 Use $wgDnsBlacklistUrls instead, only kept for
+ * backward compatibility.
  */
 $wgSorbsUrl = array();
 
@@ -3898,13 +4088,24 @@ $wgSorbsUrl = array();
 $wgProxyWhitelist = array();
 
 /**
- * Simple rate limiter options to brake edit floods.  Maximum number actions
- * allowed in the given number of seconds; after that the violating client re-
- * ceives HTTP 500 error pages until the period elapses.
+ * Simple rate limiter options to brake edit floods.
+ *
+ * Maximum number actions allowed in the given number of seconds; after that
+ * the violating client receives HTTP 500 error pages until the period
+ * elapses.
+ *
+ * @par Example:
+ * To set a generic maximum of 4 hits in 60 seconds:
+ * @code
+ * $wgRateLimits = array( 4, 60 );
+ * @endcode
  *
- * array( 4, 60 ) for a maximum of 4 hits in 60 seconds.
+ * You could also limit per action and then type of users. See the inline
+ * code for a template to use.
  *
- * This option set is experimental and likely to change. Requires memcached.
+ * This option set is experimental and likely to change.
+ *
+ * @warning Requires memcached.
  */
 $wgRateLimits = array(
        'edit' => array(
@@ -3953,7 +4154,8 @@ $wgQueryPageDefaultLimit = 50;
 
 /**
  * Limit password attempts to X attempts per Y seconds per IP per account.
- * Requires memcached.
+ *
+ * @warning Requires memcached.
  */
 $wgPasswordAttemptThrottle = array( 'count' => 5, 'seconds' => 300 );
 
@@ -3968,10 +4170,10 @@ $wgPasswordAttemptThrottle = array( 'count' => 5, 'seconds' => 300 );
  * If you enable this, every editor's IP address will be scanned for open HTTP
  * proxies.
  *
- * Don't enable this. Many sysops will report "hostile TCP port scans" to your
- * ISP and ask for your server to be shut down.
- *
+ * @warning Don't enable this. Many sysops will report "hostile TCP port scans"
+ * to your ISP and ask for your server to be shut down.
  * You have been warned.
+ *
  */
 $wgBlockOpenProxies = false;
 /** Port we want to scan for a proxy */
@@ -4105,18 +4307,18 @@ $wgDebugRedirects = false;
 
 /**
  * If true, log debugging data from action=raw and load.php.
- * This is normally false to avoid overlapping debug entries due to gen=css and
- * gen=js requests.
+ * This is normally false to avoid overlapping debug entries due to gen=css
+ * and gen=js requests.
  */
 $wgDebugRawPage = false;
 
 /**
  * Send debug data to an HTML comment in the output.
  *
- * This may occasionally be useful when supporting a non-technical end-user. It's
- * more secure than exposing the debug log file to the web, since the output only
- * contains private data for the current user. But it's not ideal for development
- * use since data is lost on fatal errors and redirects.
+ * This may occasionally be useful when supporting a non-technical end-user.
+ * It's more secure than exposing the debug log file to the web, since the
+ * output only contains private data for the current user. But it's not ideal
+ * for development use since data is lost on fatal errors and redirects.
  */
 $wgDebugComments = false;
 
@@ -4432,12 +4634,13 @@ $wgMWSuggestTemplate = false;
 $wgDisableSearchUpdate = false;
 
 /**
- * List of namespaces which are searched by default. Example:
+ * List of namespaces which are searched by default.
  *
- * <code>
+ * @par Example:
+ * @code
  * $wgNamespacesToBeSearchedDefault[NS_MAIN] = true;
  * $wgNamespacesToBeSearchedDefault[NS_PROJECT] = true;
- * </code>
+ * @endcode
  */
 $wgNamespacesToBeSearchedDefault = array(
        NS_MAIN => true,
@@ -4445,9 +4648,9 @@ $wgNamespacesToBeSearchedDefault = array(
 
 /**
  * Namespaces to be searched when user clicks the "Help" tab
- * on Special:Search
+ * on Special:Search.
  *
- * Same format as $wgNamespacesToBeSearchedDefault
+ * Same format as $wgNamespacesToBeSearchedDefault.
  */
 $wgNamespacesToBeSearchedHelp = array(
        NS_PROJECT => true,
@@ -4455,8 +4658,10 @@ $wgNamespacesToBeSearchedHelp = array(
 );
 
 /**
- * If set to true the 'searcheverything' preference will be effective only for logged-in users.
- * Useful for big wikis to maintain different search profiles for anonymous and logged-in users.
+ * If set to true the 'searcheverything' preference will be effective only for
+ * logged-in users.
+ * Useful for big wikis to maintain different search profiles for anonymous and
+ * logged-in users.
  *
  */
 $wgSearchEverythingOnlyLoggedIn = false;
@@ -4472,18 +4677,22 @@ $wgDisableInternalSearch = false;
  * If the URL includes '$1', this will be replaced with the URL-encoded
  * search term.
  *
- * For example, to forward to Google you'd have something like:
- * $wgSearchForwardUrl = 'http://www.google.com/search?q=$1' .
- *                       '&domains=http://example.com' .
- *                       '&sitesearch=http://example.com' .
- *                       '&ie=utf-8&oe=utf-8';
+ * @par Example:
+ * To forward to Google you'd have something like:
+ * @code
+ * $wgSearchForwardUrl =
+ *     'http://www.google.com/search?q=$1' .
+ *     '&domains=http://example.com' .
+ *     '&sitesearch=http://example.com' .
+ *     '&ie=utf-8&oe=utf-8';
+ * @endcode
  */
 $wgSearchForwardUrl = null;
 
 /**
- * Search form behavior
- * true = use Go & Search buttons
- * false = use Go button & Advanced search link
+ * Search form behavior.
+ * true = use Go & Search buttons
+ * false = use Go button & Advanced search link
  */
 $wgUseTwoButtonsSearchForm = true;
 
@@ -4500,11 +4709,13 @@ $wgSitemapNamespaces = false;
  * maintenance/generateSitemap.php script.
  *
  * This should be a map of namespace IDs to priority
- * Example:
+ * @par Example:
+ * @code
  *  $wgSitemapNamespacesPriorities = array(
  *      NS_USER => '0.9',
  *      NS_HELP => '0.0',
  *  );
+ * @endcode
  */
 $wgSitemapNamespacesPriorities = false;
 
@@ -4733,18 +4944,23 @@ $wgFeedDiffCutoff = 32768;
 /** Override the site's default RSS/ATOM feed for recentchanges that appears on
  * every page. Some sites might have a different feed they'd like to promote
  * instead of the RC feed (maybe like a "Recent New Articles" or "Breaking news" one).
- * Ex: $wgSiteFeed['format'] = "http://example.com/somefeed.xml"; Format can be one
- * of either 'rss' or 'atom'.
+ * Should be a format as key (either 'rss' or 'atom') and an URL to the feed
+ * as value.
+ * @par Example:
+ * Configure the 'atom' feed to http://example.com/somefeed.xml
+ * @code
+ * $wgSiteFeed['atom'] = "http://example.com/somefeed.xml";
+ * @endcode
  */
 $wgOverrideSiteFeed = array();
 
 /**
- * Available feeds objects
+ * Available feeds objects.
  * Should probably only be defined when a page is syndicated ie when
- * $wgOut->isSyndicated() is true
+ * $wgOut->isSyndicated() is true.
  */
 $wgFeedClasses = array(
-       'rss' => 'RSSFeed',
+       'rss'  => 'RSSFeed',
        'atom' => 'AtomFeed',
 );
 
@@ -4903,9 +5119,9 @@ $wgExportAllowListContributors = false;
  * can become *insanely large* and could easily break your wiki,
  * it's disabled by default for now.
  *
- * There's a HARD CODED limit of 5 levels of recursion to prevent a
- * crazy-big export from being done by someone setting the depth
- * number too high. In other words, last resort safety net.
+ * @warning There's a HARD CODED limit of 5 levels of recursion to prevent a
+ * crazy-big export from being done by someone setting the depth number too
+ * high. In other words, last resort safety net.
  */
 $wgExportMaxLinkDepth = 0;
 
@@ -4927,7 +5143,8 @@ $wgExportAllowAll = false;
  */
 
 /**
- * A list of callback functions which are called once MediaWiki is fully initialised
+ * A list of callback functions which are called once MediaWiki is fully
+ * initialised
  */
 $wgExtensionFunctions = array();
 
@@ -4942,9 +5159,10 @@ $wgExtensionFunctions = array();
  * Variables defined in extensions will override conflicting variables defined
  * in the core.
  *
- * Example:
+ * @par Example:
+ * @code
  *    $wgExtensionMessagesFiles['ConfirmEdit'] = dirname(__FILE__).'/ConfirmEdit.i18n.php';
- *
+ * @endcode
  */
 $wgExtensionMessagesFiles = array();
 
@@ -4958,7 +5176,9 @@ $wgExtensionMessagesFiles = array();
  * Registration is done with $pout->addOutputHook( $tag, $data ).
  *
  * The callback has the form:
+ * @code
  *    function outputHook( $outputPage, $parserOutput, $data ) { ... }
+ * @endcode
  */
 $wgParserOutputHooks = array();
 
@@ -4989,7 +5209,7 @@ $wgAutoloadClasses = array();
  * urls, descriptions and pointers to localized description msgs. Note that
  * the version, url, description and descriptionmsg key can be omitted.
  *
- * <code>
+ * @code
  * $wgExtensionCredits[$type][] = array(
  *     'name' => 'Example extension',
  *     'version' => 1.9,
@@ -4999,7 +5219,7 @@ $wgAutoloadClasses = array();
  *     'description' => 'An example extension',
  *     'descriptionmsg' => 'exampleextension-desc',
  * );
- * </code>
+ * @endcode
  *
  * Where $type is 'specialpage', 'parserhook', 'variable', 'media' or 'other'.
  * Where 'descriptionmsg' can be an array with message key and parameters:
@@ -5015,12 +5235,30 @@ $wgAuth = null;
 
 /**
  * Global list of hooks.
- * Add a hook by doing:
+ *
+ * The key is one of the events made available by MediaWiki, you can find
+ * a description for most of them in docs/hooks.txt. The array is used
+ * internally by Hook:run().
+ *
+ * The value can be one of:
+ *
+ * - A function name:
+ * @code
  *     $wgHooks['event_name'][] = $function;
- * or:
+ * @endcode
+ * - A function with some data:
+ * @code
  *     $wgHooks['event_name'][] = array($function, $data);
- * or:
+ * @endcode
+ * - A an object method:
+ * @code
  *     $wgHooks['event_name'][] = array($object, 'method');
+ * @endcode
+ *
+ * @warning You should always append to an event array or you will end up
+ * deleting a previous registered hook.
+ *
+ * @todo Does it support PHP closures?
  */
 $wgHooks = array();
 
@@ -5174,17 +5412,19 @@ $wgLogRestrictions = array(
  *
  * See $wgLogTypes for a list of available log types.
  *
- * For example:
+ * @par Example:
+ * @code
  *   $wgFilterLogTypes => array(
  *      'move' => true,
  *      'import' => false,
  *   );
+ * @endcode
  *
  * Will display show/hide links for the move and import logs. Move logs will be
  * hidden by default unless the link is clicked. Import logs will be shown by
  * default, and hidden when the link is clicked.
  *
- * A message of the form log-show-hide-<type> should be added, and will be used
+ * A message of the form log-show-hide-[type] should be added, and will be used
  * for the link text.
  */
 $wgFilterLogTypes = array(
@@ -5197,7 +5437,7 @@ $wgFilterLogTypes = array(
  *
  * Extensions with custom log types may add to this array.
  *
- * Since 1.19, if you follow the naming convention log-name-TYPE,
+ * @since 1.19, if you follow the naming convention log-name-TYPE,
  * where TYPE is your log type, yoy don't need to use this array.
  */
 $wgLogNames = array(
@@ -5220,7 +5460,7 @@ $wgLogNames = array(
  *
  * Extensions with custom log types may add to this array.
  *
- * Since 1.19, if you follow the naming convention log-description-TYPE,
+ * @since 1.19, if you follow the naming convention log-description-TYPE,
  * where TYPE is your log type, yoy don't need to use this array.
  */
 $wgLogHeaders = array(
@@ -5434,7 +5674,7 @@ $wgMaxRedirectLinksRetrieved = 500;
  */
 
 /**
- * Array of allowed values for the title=foo&action=<action> parameter. Syntax is:
+ * Array of allowed values for the "title=foo&action=<action>" parameter. Syntax is:
  *     'foo' => 'ClassName'    Load the specified class which subclasses Action
  *     'foo' => true           Load the class FooAction which subclasses Action
  *                             If something is specified in the getActionOverrides()
@@ -5499,8 +5739,10 @@ $wgDefaultRobotPolicy = 'index,follow';
  * URLs, so search engine spiders risk getting lost in a maze of twisty special
  * pages, all alike, and never reaching your actual content.
  *
- * Example:
+ * @par Example:
+ * @code
  *   $wgNamespaceRobotPolicies = array( NS_TALK => 'noindex' );
+ * @endcode
  */
 $wgNamespaceRobotPolicies = array();
 
@@ -5508,10 +5750,18 @@ $wgNamespaceRobotPolicies = array();
  * Robot policies per article. These override the per-namespace robot policies.
  * Must be in the form of an array where the key part is a properly canonical-
  * ised text form title and the value is a robot policy.
- * Example:
- *   $wgArticleRobotPolicies = array( 'Main Page' => 'noindex,follow',
- *     'User:Bob' => 'index,follow' );
- * Example that DOES NOT WORK because the names are not canonical text forms:
+ *
+ * @par Example:
+ * @code
+ * $wgArticleRobotPolicies = array(
+ *             'Main Page' => 'noindex,follow',
+ *             'User:Bob' => 'index,follow',
+ * );
+ * @endcode
+ *
+ * @par Example that DOES NOT WORK because the names are not canonical text
+ * forms:
+ * @code
  *   $wgArticleRobotPolicies = array(
  *     # Underscore, not space!
  *     'Main_Page' => 'noindex,follow',
@@ -5520,6 +5770,7 @@ $wgNamespaceRobotPolicies = array();
  *     # Needs to be "Abc", not "abc" (unless $wgCapitalLinks is false for that namespace)!
  *     'abc' => 'noindex,nofollow'
  *   );
+ * @endcode
  */
 $wgArticleRobotPolicies = array();
 
@@ -5527,8 +5778,11 @@ $wgArticleRobotPolicies = array();
  * An array of namespace keys in which the __INDEX__/__NOINDEX__ magic words
  * will not function, so users can't decide whether pages in that namespace are
  * indexed by search engines.  If set to null, default to $wgContentNamespaces.
- * Example:
+ *
+ * @par Example:
+ * @code
  *   $wgExemptFromUserRobotsControl = array( NS_MAIN, NS_TALK, NS_PROJECT );
+ * @endcode
  */
 $wgExemptFromUserRobotsControl = null;
 
@@ -5558,9 +5812,10 @@ $wgEnableAPI = true;
 $wgEnableWriteAPI = true;
 
 /**
- * API module extensions
+ * API module extensions.
  * Associative array mapping module name to class name.
  * Extension modules may override the core modules.
+ * @todo Describe each of the variables, group them and add examples
  */
 $wgAPIModules = array();
 $wgAPIMetaModules = array();
@@ -5575,7 +5830,7 @@ $wgAPIMaxDBRows = 5000;
 
 /**
  * The maximum size (in bytes) of an API result.
- * Don't set this lower than $wgMaxArticleSize*1024
+ * @warning Do not set this lower than $wgMaxArticleSize*1024
  */
 $wgAPIMaxResultSize = 8388608;
 
@@ -5630,17 +5885,18 @@ $wgAjaxLicensePreview = true;
  * This is currently only used by the API (requests to api.php)
  * $wgCrossSiteAJAXdomains can be set using a wildcard syntax:
  *
- * '*' matches any number of characters
- * '?' matches any 1 character
- *
- * Example:
- $wgCrossSiteAJAXdomains = array(
-  'www.mediawiki.org',
-  '*.wikipedia.org',
-  '*.wikimedia.org',
-  '*.wiktionary.org',
- );
+ * - '*' matches any number of characters
+ * - '?' matches any 1 character
  *
+ * @par Example:
+ * @code
+ * $wgCrossSiteAJAXdomains = array(
+ *     'www.mediawiki.org',
+ *     '*.wikipedia.org',
+ *     '*.wikimedia.org',
+ *     '*.wiktionary.org',
+ * );
+ * @endcode
  */
 $wgCrossSiteAJAXdomains = array();
 
@@ -5744,7 +6000,7 @@ $wgUpdateRowsPerQuery = 100;
 
 /**
  * The build directory for HipHop compilation.
- * Defaults to $IP/maintenance/hiphop/build.
+ * Defaults to '$IP/maintenance/hiphop/build'.
  */
 $wgHipHopBuildDirectory = false;
 
@@ -5764,8 +6020,9 @@ $wgHipHopCompilerProcs = 'detect';
  *
  * To compile extensions with HipHop, set $wgExtensionsDirectory correctly,
  * and use code like:
- *
+ * @code
  *    require( MWInit::extensionSetupPath( 'Extension/Extension.php' ) );
+ * @endcode
  *
  * to include the extension setup file from LocalSettings.php. It is not
  * necessary to set this variable unless you use MWInit::extensionSetupPath().
@@ -5810,9 +6067,11 @@ $wgExternalDiffEngine = false;
 
 /**
  * Disable redirects to special pages and interwiki redirects, which use a 302
- * and have no "redirected from" link. Note this is only for articles with #Redirect
- * in them. URL's containing a local interwiki prefix (or a non-canonical special
- * page name) are still hard redirected regardless of this setting.
+ * and have no "redirected from" link.
+ *
+ * @note This is only for articles with #REDIRECT in them. URL's containing a
+ * local interwiki prefix (or a non-canonical special page name) are still hard
+ * redirected regardless of this setting.
  */
 $wgDisableHardRedirects = false;
 
@@ -5823,8 +6082,8 @@ $wgDisableHardRedirects = false;
 $wgLinkHolderBatchSize = 1000;
 
 /**
- * By default MediaWiki does not register links pointing to same server in externallinks dataset,
- * use this value to override:
+ * By default MediaWiki does not register links pointing to same server in
+ * externallinks dataset, use this value to override:
  */
 $wgRegisterInternalExternals = false;
 
@@ -5852,8 +6111,10 @@ $wgRedirectOnLogin = null;
  * This configuration array maps pool types to an associative array. The only
  * defined key in the associative array is "class", which gives the class name.
  * The remaining elements are passed through to the class as constructor
- * parameters. Example:
+ * parameters.
  *
+ * @par Example:
+ * @code
  *   $wgPoolCounterConf = array( 'ArticleView' => array(
  *     'class' => 'PoolCounter_Client',
  *     'timeout' => 15, // wait timeout in seconds
@@ -5861,6 +6122,7 @@ $wgRedirectOnLogin = null;
  *     'maxqueue' => 50, // maximum number of total threads in each pool
  *     ... any extension-specific options...
  *   );
+ * @endcode
  */
 $wgPoolCounterConf = null;
 
index 00af974..b4989a6 100644 (file)
@@ -88,10 +88,19 @@ class DeferredUpdates {
                }
 
                foreach ( $updates as $update ) {
-                       $update->doUpdate();
+                       try {
+                               $update->doUpdate();
 
-                       if ( $doCommit && $dbw->trxLevel() ) {
-                               $dbw->commit( __METHOD__ );
+                               if ( $doCommit && $dbw->trxLevel() ) {
+                                       $dbw->commit( __METHOD__ );
+                               }
+                       } catch ( MWException $e ) {
+                               // We don't want exceptions thrown during deferred updates to
+                               // be reported to the user since the output is already sent.
+                               // Instead we just log them.
+                               if ( !$e instanceof ErrorPageError ) {
+                                       wfDebugLog( 'exception', $e->getLogMessage() );
+                               }
                        }
                }
 
index d0f0c26..f7b41b8 100644 (file)
@@ -25,7 +25,7 @@
  */
 
 /**
- * @defgroup Constants
+ * @defgroup Constants MediaWiki constants
  */
 
 /**
index 1f526cc..03f5ba7 100644 (file)
@@ -591,8 +591,8 @@ class EditPage {
                                wfProfileOut( get_class( $this ) . "::importContentFormData" );
                        }
 
-                       # Truncate for whole multibyte characters. +5 bytes for ellipsis
-                       $this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 250 );
+                       # Truncate for whole multibyte characters
+                       $this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 255 );
 
                        # If the summary consists of a heading, e.g. '==Foobar==', extract the title from the
                        # header syntax, e.g. 'Foobar'. This is mainly an issue when we are using wpSummary for
@@ -604,7 +604,7 @@ class EditPage {
                        # currently doing double duty as both edit summary and section title. Right now this
                        # is just to allow API edits to work around this limitation, but this should be
                        # incorporated into the actual edit form when EditPage is rewritten (Bugs 18654, 26312).
-                       $this->sectiontitle = $wgLang->truncate( $request->getText( 'wpSectionTitle' ), 250 );
+                       $this->sectiontitle = $wgLang->truncate( $request->getText( 'wpSectionTitle' ), 255 );
                        $this->sectiontitle = preg_replace( '/^\s*=+\s*(.*?)\s*=+\s*$/', '$1', $this->sectiontitle );
 
                        $this->edittime = $request->getVal( 'wpEdittime' );
@@ -1515,7 +1515,7 @@ class EditPage {
         * @private
         * @todo document
         *
-        * @parma $editText string
+        * @param $editText string
         *
         * @return bool
         */
@@ -1582,7 +1582,7 @@ class EditPage {
        /**
         * Check given input text against $wgSpamRegex, and return the text of the first match.
         *
-        * @parma $text string
+        * @param $text string
         *
         * @return string|bool  matching string or false
         */
@@ -1837,6 +1837,10 @@ class EditPage {
                        $wgOut->addHTML( Html::hidden( 'wpIgnoreBlankSummary', true ) );
                }
 
+               if ( $this->undidRev ) {
+                       $wgOut->addHTML( Html::hidden( 'wpUndidRevision', $this->undidRev ) );
+               }
+
                if ( $this->hasPresetSummary ) {
                        // If a summary has been preset using &summary= we dont want to prompt for
                        // a different summary. Only prompt for a summary if the summary is blanked.
@@ -2109,7 +2113,7 @@ class EditPage {
         * @return array An array in the format array( $label, $input )
         */
        function getSummaryInput( $summary = "", $labelText = null, $inputAttrs = null, $spanLabelAttrs = null ) {
-               // Note: the maxlength is overriden in JS to 250 and to make it use UTF-8 bytes, not characters.
+               // Note: the maxlength is overriden in JS to 255 and to make it use UTF-8 bytes, not characters.
                $inputAttrs = ( is_array( $inputAttrs ) ? $inputAttrs : array() ) + array(
                        'id' => 'wpSummary',
                        'maxlength' => '200',
@@ -2992,8 +2996,8 @@ HTML
         * failure, etc).
         *
         * @todo This doesn't include category or interlanguage links.
-        *       Would need to enhance it a bit, <s>maybe wrap them in XML
-        *       or something...</s> that might also require more skin
+        *       Would need to enhance it a bit, "<s>maybe wrap them in XML
+        *       or something...</s>" that might also require more skin
         *       initialization, so check whether that's a problem.
         */
        function livePreview() {
@@ -3136,12 +3140,14 @@ HTML
         * @private
         */
        function checkUnicodeCompliantBrowser() {
-               global $wgBrowserBlackList;
-               if ( empty( $_SERVER["HTTP_USER_AGENT"] ) ) {
+               global $wgBrowserBlackList, $wgRequest;
+
+               $currentbrowser = $wgRequest->getHeader( 'User-Agent' );
+               if ( $currentbrowser === false ) {
                        // No User-Agent header sent? Trust it by default...
                        return true;
                }
-               $currentbrowser = $_SERVER["HTTP_USER_AGENT"];
+
                foreach ( $wgBrowserBlackList as $browser ) {
                        if ( preg_match( $browser, $currentbrowser ) ) {
                                return false;
index 6e1325f..0fc5cd7 100644 (file)
@@ -33,7 +33,8 @@ class MWException extends Exception {
        var $logId;
 
        /**
-        * Should the exception use $wgOut to output the error ?
+        * Should the exception use $wgOut to output the error?
+        *
         * @return bool
         */
        function useOutputPage() {
@@ -44,7 +45,8 @@ class MWException extends Exception {
        }
 
        /**
-        * Can the extension use wfMsg() to get i18n messages ?
+        * Can the extension use wfMsg() to get i18n messages?
+        *
         * @return bool
         */
        function useMessageCache() {
@@ -62,9 +64,9 @@ class MWException extends Exception {
        /**
         * Run hook to allow extensions to modify the text of the exception
         *
-        * @param $name String: class name of the exception
-        * @param $args Array: arguments to pass to the callback functions
-        * @return Mixed: string to output or null if any hook has been called
+        * @param $name string: class name of the exception
+        * @param $args array: arguments to pass to the callback functions
+        * @return string|null string to output or null if any hook has been called
         */
        function runHooks( $name, $args = array() ) {
                global $wgExceptionHooks;
@@ -97,11 +99,11 @@ class MWException extends Exception {
        /**
         * Get a message from i18n
         *
-        * @param $key String: message name
-        * @param $fallback String: default message if the message cache can't be
+        * @param $key string: message name
+        * @param $fallback string: default message if the message cache can't be
         *                  called by the exception
         * The function also has other parameters that are arguments for the message
-        * @return String message with arguments replaced
+        * @return string message with arguments replaced
         */
        function msg( $key, $fallback /*[, params...] */ ) {
                $args = array_slice( func_get_args(), 2 );
@@ -118,7 +120,7 @@ class MWException extends Exception {
         * backtrace to the error, otherwise show a message to ask to set it to true
         * to show that information.
         *
-        * @return String html to output
+        * @return string html to output
         */
        function getHTML() {
                global $wgShowExceptionDetails;
@@ -140,8 +142,10 @@ class MWException extends Exception {
        }
 
        /**
+        * Get the text to display when reporting the error on the command line.
         * If $wgShowExceptionDetails is true, return a text message with a
         * backtrace to the error.
+        *
         * @return string
         */
        function getText() {
@@ -157,13 +161,21 @@ class MWException extends Exception {
        }
 
        /**
-        * Return titles of this error page
-        * @return String
+        * Return the title of the page when reporting this error in a HTTP response.
+        *
+        * @return string
         */
        function getPageTitle() {
                return $this->msg( 'internalerror', "Internal error" );
        }
 
+       /**
+        * Get a random ID for this error.
+        * This allows to link the exception to its correspoding log entry when
+        * $wgShowExceptionDetails is set to false.
+        *
+        * @return string
+        */
        function getLogId() {
                if ( $this->logId === null ) {
                        $this->logId = wfRandomString( 8 );
@@ -175,7 +187,7 @@ class MWException extends Exception {
         * Return the requested URL and point to file and line number from which the
         * exception occured
         *
-        * @return String
+        * @return string
         */
        function getLogMessage() {
                global $wgRequest;
@@ -197,7 +209,9 @@ class MWException extends Exception {
                return "[$id] $url   Exception from line $line of $file: $message";
        }
 
-       /** Output the exception report using HTML */
+       /**
+        * Output the exception report using HTML.
+        */
        function reportHTML() {
                global $wgOut;
                if ( $this->useOutputPage() ) {
@@ -261,7 +275,9 @@ class MWException extends Exception {
        }
 
        /**
-        * @static
+        * Check whether we are in command line mode or not to report the exception
+        * in the correct format.
+        *
         * @return bool
         */
        static function isCommandLine() {
@@ -272,6 +288,7 @@ class MWException extends Exception {
 /**
  * Exception class which takes an HTML error message, and does not
  * produce a backtrace. Replacement for OutputPage::fatalError().
+ *
  * @ingroup Exception
  */
 class FatalError extends MWException {
@@ -292,7 +309,8 @@ class FatalError extends MWException {
 }
 
 /**
- * An error page which can definitely be safely rendered using the OutputPage
+ * An error page which can definitely be safely rendered using the OutputPage.
+ *
  * @ingroup Exception
  */
 class ErrorPageError extends MWException {
@@ -331,6 +349,8 @@ class ErrorPageError extends MWException {
  * Show an error page on a badtitle.
  * Similar to ErrorPage, but emit a 400 HTTP error code to let mobile
  * browser it is not really a valid content.
+ *
+ * @ingroup Exception
  */
 class BadTitleError extends ErrorPageError {
 
@@ -360,6 +380,7 @@ class BadTitleError extends ErrorPageError {
 /**
  * Show an error when a user tries to do something they do not have the necessary
  * permissions for.
+ *
  * @ingroup Exception
  */
 class PermissionsError extends ErrorPageError {
@@ -396,7 +417,8 @@ class PermissionsError extends ErrorPageError {
 
 /**
  * Show an error when the wiki is locked/read-only and the user tries to do
- * something that requires write access
+ * something that requires write access.
+ *
  * @ingroup Exception
  */
 class ReadOnlyError extends ErrorPageError {
@@ -410,7 +432,8 @@ class ReadOnlyError extends ErrorPageError {
 }
 
 /**
- * Show an error when the user hits a rate limit
+ * Show an error when the user hits a rate limit.
+ *
  * @ingroup Exception
  */
 class ThrottledError extends ErrorPageError {
@@ -429,7 +452,8 @@ class ThrottledError extends ErrorPageError {
 }
 
 /**
- * Show an error when the user tries to do something whilst blocked
+ * Show an error when the user tries to do something whilst blocked.
+ *
  * @ingroup Exception
  */
 class UserBlockedError extends ErrorPageError {
@@ -495,15 +519,18 @@ class UserBlockedError extends ErrorPageError {
  * }
  * @endcode
  *
- * @param $reasonMsg A message key containing the reason for the error.
- *        Optional, default: 'exception-nologin-text'
- * @param $titleMsg A message key to set the page title.
- *        Optional, default: 'exception-nologin'
- * @param $params Parameters to wfMsg().
- *        Optiona, default: null
+ * @ingroup Exception
  */
 class UserNotLoggedIn extends ErrorPageError {
 
+       /**
+        * @param $reasonMsg A message key containing the reason for the error.
+        *        Optional, default: 'exception-nologin-text'
+        * @param $titleMsg A message key to set the page title.
+        *        Optional, default: 'exception-nologin'
+        * @param $params Parameters to wfMsg().
+        *        Optiona, default: null
+        */
        public function __construct(
                $reasonMsg = 'exception-nologin-text',
                $titleMsg  = 'exception-nologin',
@@ -628,7 +655,8 @@ class MWExceptionHandler {
        /**
         * Print a message, if possible to STDERR.
         * Use this in command line mode only (see isCommandLine)
-        * @param $message String Failure text
+        *
+        * @param $message string Failure text
         */
        public static function printError( $message ) {
                # NOTE: STDERR may not be available, especially if php-cgi is used from the command line (bug #15602).
@@ -642,8 +670,9 @@ class MWExceptionHandler {
 
        /**
         * Print a message after escaping it and converting newlines to <br>
-        * Use this for non-command line failures
-        * @param $message String Failure text
+        * Use this for non-command line failures.
+        *
+        * @param $message string Failure text
         */
        private static function escapeEchoAndDie( $message ) {
                echo nl2br( htmlspecialchars( $message ) ) . "\n";
index 36d98d6..f01fb23 100644 (file)
@@ -58,6 +58,14 @@ class WikiExporter {
         */
        var $sink;
 
+       /**
+        * Returns the export schema version.
+        * @return string
+        */
+       public static function schemaVersion() {
+               return "0.7";
+       }
+
        /**
         * If using WikiExporter::STREAM to stream a large amount of data,
         * provide a database connection which is not managed by
@@ -465,14 +473,16 @@ class WikiExporter {
 class XmlDumpWriter {
        /**
         * Returns the export schema version.
+        * @deprecated in 1.20; use WikiExporter::schemaVersion() instead
         * @return string
         */
        function schemaVersion() {
-               return "0.7";
+               wfDeprecated( __METHOD__, '1.20' );
+               return WikiExporter::schemaVersion();
        }
 
        /**
-        * Opens the XML output stream's root <mediawiki> element.
+        * Opens the XML output stream's root "<mediawiki>" element.
         * This does not include an xml directive, so is safe to include
         * as a subelement in a larger XML stream. Namespace and XML Schema
         * references are included.
@@ -483,7 +493,7 @@ class XmlDumpWriter {
         */
        function openStream() {
                global $wgLanguageCode;
-               $ver = $this->schemaVersion();
+               $ver = WikiExporter::schemaVersion();
                return Xml::element( 'mediawiki', array(
                        'xmlns'              => "http://www.mediawiki.org/xml/export-$ver/",
                        'xmlns:xsi'          => "http://www.w3.org/2001/XMLSchema-instance",
@@ -572,7 +582,7 @@ class XmlDumpWriter {
        }
 
        /**
-        * Opens a <page> section on the output stream, with data
+        * Opens a "<page>" section on the output stream, with data
         * from the given database row.
         *
         * @param $row object
@@ -604,7 +614,7 @@ class XmlDumpWriter {
        }
 
        /**
-        * Closes a <page> section on the output stream.
+        * Closes a "<page>" section on the output stream.
         *
         * @access private
         * @return string
@@ -614,7 +624,7 @@ class XmlDumpWriter {
        }
 
        /**
-        * Dumps a <revision> section on the output stream, with
+        * Dumps a "<revision>" section on the output stream, with
         * data filled in from the given database row.
         *
         * @param $row object
@@ -678,7 +688,7 @@ class XmlDumpWriter {
        }
 
        /**
-        * Dumps a <logitem> section on the output stream, with
+        * Dumps a "<logitem>" section on the output stream, with
         * data filled in from the given database row.
         *
         * @param $row object
@@ -726,6 +736,7 @@ class XmlDumpWriter {
 
        /**
         * @param $timestamp string
+        * @param $indent string Default to six spaces
         * @return string
         */
        function writeTimestamp( $timestamp, $indent = "      " ) {
@@ -736,6 +747,7 @@ class XmlDumpWriter {
        /**
         * @param $id
         * @param $text string
+        * @param $indent string Default to six spaces
         * @return string
         */
        function writeContributor( $id, $text, $indent = "      " ) {
@@ -815,7 +827,7 @@ class XmlDumpWriter {
         * Return prefixed text form of title, but using the content language's
         * canonical namespace. This skips any special-casing such as gendered
         * user namespaces -- which while useful, are not yet listed in the
-        * XML <siteinfo> data so are unsafe in export.
+        * XML "<siteinfo>" data so are unsafe in export.
         *
         * @param Title $title
         * @return string
index 950b437..f9dbf5b 100644 (file)
@@ -183,34 +183,35 @@ class FeedItem {
  * @todo document (needs one-sentence top-level class description).
  * @ingroup Feed
  */
-class ChannelFeed extends FeedItem {
-       /**#@+
-        * Abstract function, override!
-        * @abstract
-        */
-
+abstract class ChannelFeed extends FeedItem {
        /**
         * Generate Header of the feed
+        * @par Example:
+        * @code
+        * print "<feed>";
+        * @endcode
+        * @param $item
         */
-       function outHeader() {
-               # print "<feed>";
-       }
+       abstract public function outHeader();
 
        /**
         * Generate an item
+        * @par Example:
+        * @code
+        * print "<item>...</item>";
+        * @endcode
         * @param $item
         */
-       function outItem( $item ) {
-               # print "<item>...</item>";
-       }
+       abstract public function outItem( $item );
 
        /**
         * Generate Footer of the feed
+        * @par Example:
+        * @code
+        * print "</feed>";
+        * @endcode
         */
-       function outFooter() {
-               # print "</feed>";
-       }
-       /**#@-*/
+       abstract public function outFooter();
 
        /**
         * Setup and send HTTP headers. Don't send any content;
index 7e75958..35887a4 100644 (file)
@@ -1053,11 +1053,22 @@ function wfDebugLog( $logGroup, $text, $public = true ) {
  * @param $text String: database error message.
  */
 function wfLogDBError( $text ) {
-       global $wgDBerrorLog;
+       global $wgDBerrorLog, $wgDBerrorLogInUTC;
        if ( $wgDBerrorLog ) {
                $host = wfHostname();
                $wiki = wfWikiID();
-               $text = date( 'D M j G:i:s T Y' ) . "\t$host\t$wiki\t$text";
+
+               if( $wgDBerrorLogInUTC ) {
+                       $wikiTimezone = date_default_timezone_get();
+                       date_default_timezone_set( 'UTC' );
+               }
+               $date = date( 'D M j G:i:s T Y' );
+               if( $wgDBerrorLogInUTC ) {
+                       // Restore timezone
+                       date_default_timezone_set( $wikiTimezone );
+               }
+
+               $text = "$date\t$host\t$wiki\t$text";
                wfErrorLog( $text, $wgDBerrorLog );
        }
 }
@@ -2063,7 +2074,7 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
  * Escapes the given text so that it may be output using addWikiText()
  * without any linking, formatting, etc. making its way through. This
  * is achieved by substituting certain characters with HTML entities.
- * As required by the callers, <nowiki> is not used.
+ * As required by the callers, "<nowiki>" is not used.
  *
  * @param $text String: text to be escaped
  * @return String
@@ -2874,9 +2885,11 @@ function wfEscapeShellArg( ) {
  *                 (non-zero is usually failure)
  * @param $environ Array optional environment variables which should be
  *                 added to the executed command environment.
+ * @param $limits Array optional array with limits(filesize, memory, time)
+ *                 this overwrites the global wgShellMax* limits.
  * @return string collected stdout as a string (trailing newlines stripped)
  */
-function wfShellExec( $cmd, &$retval = null, $environ = array() ) {
+function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array() ) {
        global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime;
 
        static $disabled;
@@ -2924,9 +2937,9 @@ function wfShellExec( $cmd, &$retval = null, $environ = array() ) {
        $cmd = $envcmd . $cmd;
 
        if ( php_uname( 's' ) == 'Linux' ) {
-               $time = intval( $wgMaxShellTime );
-               $mem = intval( $wgMaxShellMemory );
-               $filesize = intval( $wgMaxShellFileSize );
+               $time = intval ( isset($limits['time']) ? $limits['time'] : $wgMaxShellTime );
+               $mem = intval ( isset($limits['memory']) ? $limits['memory'] : $wgMaxShellMemory );
+               $filesize = intval ( isset($limits['filesize']) ? $limits['filesize'] : $wgMaxShellFileSize );
 
                if ( $time > 0 && $mem > 0 ) {
                        $script = "$IP/bin/ulimit4.sh";
@@ -3192,11 +3205,11 @@ function wfUseMW( $req_ver ) {
 
 /**
  * Return the final portion of a pathname.
- * Reimplemented because PHP5's basename() is buggy with multibyte text.
+ * Reimplemented because PHP5's "basename()" is buggy with multibyte text.
  * http://bugs.php.net/bug.php?id=33898
  *
  * PHP's basename() only considers '\' a pathchar on Windows and Netware.
- * We'll consider it so always, as we don't want \s in our Unix paths either.
+ * We'll consider it so always, as we don't want '\s' in our Unix paths either.
  *
  * @param $path String
  * @param $suffix String: to remove if present
index 36008fb..38b15b9 100644 (file)
@@ -546,7 +546,7 @@ class HTMLForm extends ContextSource {
        }
 
        /**
-        * Wrap the form innards in an actual <form> element
+        * Wrap the form innards in an actual "<form>" element
         * @param $html String HTML contents to wrap.
         * @return String wrapped HTML.
         */
@@ -688,7 +688,7 @@ class HTMLForm extends ContextSource {
        /**
         * Format a stack of error messages into a single HTML string
         * @param $errors Array of message keys/values
-        * @return String HTML, a <ul> list of errors
+        * @return String HTML, a "<ul>" list of errors
         */
        public static function formatErrors( $errors ) {
                $errorstr = '';
@@ -761,16 +761,16 @@ class HTMLForm extends ContextSource {
                $this->mId = $id;
        }
        /**
-        * Prompt the whole form to be wrapped in a <fieldset>, with
-        * this text as its <legend> element.
-        * @param $legend String HTML to go inside the <legend> element.
+        * Prompt the whole form to be wrapped in a "<fieldset>", with
+        * this text as its "<legend>" element.
+        * @param $legend String HTML to go inside the "<legend>" element.
         *       Will be escaped
         */
        public function setWrapperLegend( $legend ) { $this->mWrapperLegend = $legend; }
 
        /**
-        * Prompt the whole form to be wrapped in a <fieldset>, with
-        * this message as its <legend> element.
+        * Prompt the whole form to be wrapped in a "<fieldset>", with
+        * this message as its "<legend>" element.
         * @since 1.19
         * @param $msg String message key
         */
@@ -780,7 +780,7 @@ class HTMLForm extends ContextSource {
 
        /**
         * Set the prefix for various default messages
-        * TODO: currently only used for the <fieldset> legend on forms
+        * @todo currently only used for the "<fieldset>" legend on forms
         * with multiple sections; should be used elsewhre?
         * @param $p String
         */
@@ -819,10 +819,10 @@ class HTMLForm extends ContextSource {
        }
 
        /**
-        * TODO: Document
+        * @todo Document
         * @param $fields array[]|HTMLFormField[] array of fields (either arrays or objects)
-        * @param $sectionName string ID attribute of the <table> tag for this section, ignored if empty
-        * @param $fieldsetIDPrefix string ID prefix for the <fieldset> tag of each subsection, ignored if empty
+        * @param $sectionName string ID attribute of the "<table>" tag for this section, ignored if empty
+        * @param $fieldsetIDPrefix string ID prefix for the "<fieldset>" tag of each subsection, ignored if empty
         * @return String
         */
        public function displaySection( $fields, $sectionName = '', $fieldsetIDPrefix = '' ) {
@@ -938,8 +938,8 @@ class HTMLForm extends ContextSource {
        }
 
        /**
-        * Get a string to go in the <legend> of a section fieldset.  Override this if you
-        * want something more complicated
+        * Get a string to go in the "<legend>" of a section fieldset.
+        * Override this if you want something more complicated.
         * @param $key String
         * @return String
         */
@@ -1350,7 +1350,7 @@ abstract class HTMLFormField {
 
        /**
         * flatten an array of options to a single array, for instance,
-        * a set of <options> inside <optgroups>.
+        * a set of "<options>" inside "<optgroups>".
         * @param $options array Associative Array with values either Strings
         *       or Arrays
         * @return Array flattened input
@@ -1889,7 +1889,7 @@ class HTMLMultiSelectField extends HTMLFormField {
  *     ** <option value>
  *     * New Optgroup header
  * Plus a text field underneath for an additional reason.  The 'value' of the field is
- * ""<select>: <extra reason>"", or "<extra reason>" if nothing has been selected in the
+ * "<select>: <extra reason>", or "<extra reason>" if nothing has been selected in the
  * select dropdown.
  * @todo FIXME: If made 'required', only the text field should be compulsory.
  */
@@ -1991,7 +1991,7 @@ class HTMLSelectAndOtherField extends HTMLSelectField {
 
        /**
         * @param  $request WebRequest
-        * @return Array( <overall message>, <select value>, <text field value> )
+        * @return Array("<overall message>","<select value>","<text field value>")
         */
        function loadDataFromRequest( $request ) {
                if ( $request->getCheck( $this->mName ) ) {
index 3986a7b..7fa98e1 100644 (file)
@@ -548,9 +548,10 @@ class Html {
        }
 
        /**
-        * Output a <script> tag with the given contents.  TODO: do some useful
-        * escaping as well, like if $contents contains literal '</script>' or (for
-        * XML) literal "]]>".
+        * Output a "<script>" tag with the given contents.
+        *
+        * @todo do some useful escaping as well, like if $contents contains
+        * literal "</script>" or (for XML) literal "]]>".
         *
         * @param $contents string JavaScript
         * @return string Raw HTML
@@ -572,8 +573,8 @@ class Html {
        }
 
        /**
-        * Output a <script> tag linking to the given URL, e.g.,
-        * <script src=foo.js></script>.
+        * Output a "<script>" tag linking to the given URL, e.g.,
+        * "<script src=foo.js></script>".
         *
         * @param $url string
         * @return string Raw HTML
@@ -591,9 +592,9 @@ class Html {
        }
 
        /**
-        * Output a <style> tag with the given contents for the given media type
+        * Output a "<style>" tag with the given contents for the given media type
         * (if any).  TODO: do some useful escaping as well, like if $contents
-        * contains literal '</style>' (admittedly unlikely).
+        * contains literal "</style>" (admittedly unlikely).
         *
         * @param $contents string CSS
         * @param $media mixed A media type string, like 'screen'
@@ -613,7 +614,7 @@ class Html {
        }
 
        /**
-        * Output a <link rel=stylesheet> linking to the given URL for the given
+        * Output a "<link rel=stylesheet>" linking to the given URL for the given
         * media type (if any).
         *
         * @param $url string
@@ -630,7 +631,7 @@ class Html {
        }
 
        /**
-        * Convenience function to produce an <input> element.  This supports the
+        * Convenience function to produce an "<input>" element.  This supports the
         * new HTML5 input types and attributes, and will silently strip them if
         * $wgHtml5 is false.
         *
@@ -663,11 +664,12 @@ class Html {
        }
 
        /**
-        * Convenience function to produce an <input> element.  This supports leaving
-        * out the cols= and rows= which Xml requires and are required by HTML4/XHTML
-        * but not required by HTML5 and will silently set cols="" and rows="" if
-        * $wgHtml5 is false and cols and rows are omitted (HTML4 validates present
-        * but empty cols="" and rows="" as valid).
+        * Convenience function to produce an "<input>" element.
+        *
+        * This supports leaving out the cols= and rows= which Xml requires and are
+        * required by HTML4/XHTML but not required by HTML5 and will silently set
+        * cols="" and rows="" if $wgHtml5 is false and cols and rows are omitted
+        * (HTML4 validates present but empty cols="" and rows="" as valid).
         *
         * @param $name    string name attribute
         * @param $value   string value attribute
@@ -706,7 +708,7 @@ class Html {
         *
         * @param $params array:
         * - selected: [optional] Id of namespace which should be pre-selected
-        * - all: [optional] Value of item for "all namespaces". If null or unset, no <option> is generated to select all namespaces
+        * - all: [optional] Value of item for "all namespaces". If null or unset, no "<option>" is generated to select all namespaces
         * - label: text for label to add before the field
         * - exclude: [optional] Array of namespace ids to exclude
         * - disable: [optional] Array of namespace ids for which the option should be disabled in the selector
index 201a9e5..66b6e72 100644 (file)
@@ -156,7 +156,7 @@ class Http {
         *
         * file:// should not be allowed here for security purpose (r67684)
         *
-        * @fixme this is wildly inaccurate and fails to actually check most stuff
+        * @todo FIXME this is wildly inaccurate and fails to actually check most stuff
         *
         * @param $uri Mixed: URI to check for validity
         * @return Boolean
index 8f683e9..fead0f5 100644 (file)
@@ -155,7 +155,7 @@ class ImagePage extends Article {
                        # should be in page content language
                        $pageLang = $this->getTitle()->getPageLanguage();
                        $out->addHTML( Xml::openElement( 'div', array( 'id' => 'mw-imagepage-content',
-                               'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(),
+                               'lang' => $pageLang->getHtmlCode(), 'dir' => $pageLang->getDir(),
                                'class' => 'mw-content-'.$pageLang->getDir() ) ) );
                        parent::view();
                        $out->addHTML( Xml::closeElement( 'div' ) );
@@ -412,12 +412,11 @@ class ImagePage extends Article {
 
                                        if ( $page > 1 ) {
                                                $label = $out->parse( wfMsg( 'imgmultipageprev' ), false );
-                                               $link = Linker::link(
+                                               $link = Linker::linkKnown(
                                                        $this->getTitle(),
                                                        $label,
                                                        array(),
-                                                       array( 'page' => $page - 1 ),
-                                                       array( 'known', 'noclasses' )
+                                                       array( 'page' => $page - 1 )
                                                );
                                                $thumb1 = Linker::makeThumbLinkObj( $this->getTitle(), $this->displayImg, $link, $label, 'none',
                                                        array( 'page' => $page - 1 ) );
@@ -427,12 +426,11 @@ class ImagePage extends Article {
 
                                        if ( $page < $count ) {
                                                $label = wfMsg( 'imgmultipagenext' );
-                                               $link = Linker::link(
+                                               $link = Linker::linkKnown(
                                                        $this->getTitle(),
                                                        $label,
                                                        array(),
-                                                       array( 'page' => $page + 1 ),
-                                                       array( 'known', 'noclasses' )
+                                                       array( 'page' => $page + 1 )
                                                );
                                                $thumb2 = Linker::makeThumbLinkObj( $this->getTitle(), $this->displayImg, $link, $label, 'none',
                                                        array( 'page' => $page + 1 ) );
@@ -644,7 +642,7 @@ EOT
 
                # External editing link
                if ( $wgUseExternalEditor ) {
-                       $elink = Linker::link(
+                       $elink = Linker::linkKnown(
                                $this->getTitle(),
                                wfMsgHtml( 'edit-externally' ),
                                array(),
@@ -652,8 +650,7 @@ EOT
                                        'action' => 'edit',
                                        'externaledit' => 'true',
                                        'mode' => 'file'
-                               ),
-                               array( 'known', 'noclasses' )
+                               )
                        );
                        $out->addHTML(
                                '<li id="mw-imagepage-edit-external">' . $elink . ' <small>' .
@@ -828,13 +825,7 @@ EOT
                foreach ( $dupes as $file ) {
                        $fromSrc = '';
                        if ( $file->isLocal() ) {
-                               $link = Linker::link(
-                                       $file->getTitle(),
-                                       null,
-                                       array(),
-                                       array(),
-                                       array( 'known', 'noclasses' )
-                               );
+                               $link = Linker::linkKnown( $file->getTitle() );
                        } else {
                                $link = Linker::makeExternalLink( $file->getDescriptionUrl(),
                                        $file->getTitle()->getPrefixedText() );
@@ -1004,10 +995,10 @@ class ImageHistoryList extends ContextSource {
                                if ( !$iscur ) {
                                        $q['oldimage'] = $img;
                                }
-                               $row .= Linker::link(
+                               $row .= Linker::linkKnown(
                                        $this->title,
                                        wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ),
-                                       array(), $q, array( 'known' )
+                                       array(), $q
                                );
                        }
                        # Link to hide content. Don't show useless link to people who cannot hide revisions.
@@ -1044,7 +1035,7 @@ class ImageHistoryList extends ContextSource {
                        if ( $file->isDeleted( File::DELETED_FILE ) ) {
                                $row .= wfMsgHtml( 'filehist-revert' );
                        } else {
-                               $row .= Linker::link(
+                               $row .= Linker::linkKnown(
                                        $this->title,
                                        wfMsgHtml( 'filehist-revert' ),
                                        array(),
@@ -1052,8 +1043,7 @@ class ImageHistoryList extends ContextSource {
                                                'action' => 'revert',
                                                'oldimage' => $img,
                                                'wpEditToken' => $user->getEditToken( $img )
-                                       ),
-                                       array( 'known', 'noclasses' )
+                                       )
                                );
                        }
                }
@@ -1072,7 +1062,7 @@ class ImageHistoryList extends ContextSource {
                                $this->preventClickjacking();
                                $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
                                # Make a link to review the image
-                               $url = Linker::link(
+                               $url = Linker::linkKnown(
                                        $revdel,
                                        $lang->timeanddate( $timestamp, true ),
                                        array(),
@@ -1080,8 +1070,7 @@ class ImageHistoryList extends ContextSource {
                                                'target' => $this->title->getPrefixedText(),
                                                'file' => $img,
                                                'token' => $user->getEditToken( $img )
-                                       ),
-                                       array( 'known', 'noclasses' )
+                                       )
                                );
                        } else {
                                $url = $lang->timeanddate( $timestamp, true );
index 9ebc34c..491ec09 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * MediaWiki page data importer
+ * MediaWiki page data importer.
  *
  * Copyright © 2003,2005 Brion Vibber <brion@pobox.com>
  * http://www.mediawiki.org/
@@ -275,7 +275,7 @@ class WikiImporter {
        }
 
        /**
-        * Notify the callback function when a new <page> is reached.
+        * Notify the callback function when a new "<page>" is reached.
         * @param $title Title
         */
        function pageCallback( $title ) {
@@ -285,7 +285,7 @@ class WikiImporter {
        }
 
        /**
-        * Notify the callback function when a </page> is closed.
+        * Notify the callback function when a "</page>" is closed.
         * @param $title Title
         * @param $origTitle Title
         * @param $revCount Integer
index 526ad0c..5355140 100644 (file)
@@ -407,6 +407,11 @@ class Linker {
         * despite $query not being used.
         *
         * @param $nt Title
+        * @param $html String [optional]
+        * @param $query String [optional]
+        * @param $trail String [optional]
+        * @param $prefix String [optional]
+        *
         *
         * @return string
         */
@@ -1723,7 +1728,7 @@ class Linker {
                        }
                        $outText .= "</div><ul>\n";
 
-                       usort( $templates, array( 'Title', 'compare' ) );
+                       usort( $templates, 'Title::compare' );
                        foreach ( $templates as $titleObj ) {
                                $r = $titleObj->getRestrictions( 'edit' );
                                if ( in_array( 'sysop', $r ) ) {
@@ -1923,10 +1928,10 @@ class Linker {
         * Creates a (show/hide) link for deleting revisions/log entries
         *
         * @param $query Array: query parameters to be passed to link()
-        * @param $restricted Boolean: set to true to use a <strong> instead of a <span>
+        * @param $restricted Boolean: set to true to use a "<strong>" instead of a "<span>"
         * @param $delete Boolean: set to true to use (show/hide) rather than (show)
         *
-        * @return String: HTML <a> link to Special:Revisiondelete, wrapped in a
+        * @return String: HTML "<a>" link to Special:Revisiondelete, wrapped in a
         * span to allow for customization of appearance with CSS
         */
        public static function revDeleteLink( $query = array(), $restricted = false, $delete = true ) {
index 0712ac8..1d0bcf7 100644 (file)
@@ -822,9 +822,7 @@ class LinksDeletionUpdate extends SqlDataUpdate {
        /**
         * Constructor
         *
-        * @param $title Title of the page we're updating
-        * @param $parserOutput ParserOutput: output from a full parse of this page
-        * @param $recursive Boolean: queue jobs for recursive updates?
+        * @param $page WikiPage Page we are updating
         */
        function __construct( WikiPage $page ) {
                parent::__construct( );
index 9745b9a..997034c 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 /**
- * This class encapsulates "magic words" such as #redirect, __NOTOC__, etc.
+ * This class encapsulates "magic words" such as "#redirect", __NOTOC__, etc.
  *
  * @par Usage:
  * @code
@@ -42,7 +42,7 @@
  *
  * To add magic words in an extension, use $magicWords in a file listed in
  * $wgExtensionMessagesFiles[].
- * 
+ *
  * @par Example:
  * @code
  * $magicWords = array();
index c00cfbe..1b4bc49 100644 (file)
  * @todo document
  */
 class OutputPage extends ContextSource {
-       /// Should be private. Used with addMeta() which adds <meta>
+       /// Should be private. Used with addMeta() which adds "<meta>"
        var $mMetatags = array();
 
-       /// <meta keywords="stuff"> most of the time the first 10 links to an article
+       /// "<meta keywords='stuff'>" most of the time the first 10 links to an article
        var $mKeywords = array();
 
        var $mLinktags = array();
@@ -50,7 +50,7 @@ class OutputPage extends ContextSource {
        /// Should be private - has getter and setter. Contains the HTML title
        var $mPagetitle = '';
 
-       /// Contains all of the <body> content. Should be private we got set/get accessors and the append() method.
+       /// Contains all of the "<body>" content. Should be private we got set/get accessors and the append() method.
        var $mBodytext = '';
 
        /**
@@ -60,7 +60,7 @@ class OutputPage extends ContextSource {
         */
        public $mDebugtext = ''; // TODO: we might want to replace it by wfDebug() wfDebugLog()
 
-       /// Should be private. Stores contents of <title> tag
+       /// Should be private. Stores contents of "<title>" tag
        var $mHTMLtitle = '';
 
        /// Should be private. Is the displayed content related to the source of the corresponding wiki article.
@@ -116,8 +116,8 @@ class OutputPage extends ContextSource {
        /**
         * Should be private. Used for JavaScript (pre resource loader)
         * We should split js / css.
-        * mScripts content is inserted as is in <head> by Skin. This might contains
-        * either a link to a stylesheet or inline css.
+        * mScripts content is inserted as is in "<head>" by Skin. This might
+        * contains either a link to a stylesheet or inline css.
         */
        var $mScripts = '';
 
@@ -135,7 +135,7 @@ class OutputPage extends ContextSource {
         */
        var $mPageLinkTitle = '';
 
-       /// Array of elements in <head>. Parser might add its own headers!
+       /// Array of elements in "<head>". Parser might add its own headers!
        var $mHeadItems = array();
 
        // @todo FIXME: Next variables probably comes from the resource loader
@@ -197,7 +197,7 @@ class OutputPage extends ContextSource {
 
        /**
         * Comes from the parser. This was probably made to load CSS/JS only
-        * if we had <gallery>. Used directly in CategoryPage.php
+        * if we had "<gallery>". Used directly in CategoryPage.php
         * Looks like resource loader can replace this.
         */
        var $mNoGallery = false;
@@ -292,7 +292,7 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Add a new <meta> tag
+        * Add a new "<meta>" tag
         * To add an http-equiv meta tag, precede the name with "http:"
         *
         * @param $name String tag name
@@ -405,7 +405,7 @@ class OutputPage extends ContextSource {
        /**
         * Add a self-contained script tag with the given contents
         *
-        * @param $script String: JavaScript text, no <script> tags
+        * @param $script String: JavaScript text, no "<script>" tags
         */
        public function addInlineScript( $script ) {
                $this->mScripts .= Html::inlineScript( "\n$script\n" ) . "\n";
@@ -779,7 +779,7 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * "HTML title" means the contents of <title>.
+        * "HTML title" means the contents of "<title>".
         * It is stored as plain, unescaped text and will be run through htmlspecialchars in the skin file.
         *
         * @param $name string
@@ -793,7 +793,7 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Return the "HTML title", i.e. the content of the <title> tag.
+        * Return the "HTML title", i.e. the content of the "<title>" tag.
         *
         * @return String
         */
@@ -804,7 +804,7 @@ class OutputPage extends ContextSource {
        /**
         * Set $mRedirectedFrom, the Title of the page which redirected us to the current page.
         *
-        * param @t Title
+        * @param $t Title
         */
        public function setRedirectedFrom( $t ) {
                $this->mRedirectedFrom = $t;
@@ -1362,11 +1362,11 @@ class OutputPage extends ContextSource {
         * Set the timestamp of the revision which will be displayed. This is used
         * to avoid a extra DB call in Skin::lastModified().
         *
-        * @param $revid Mixed: string, or null
+        * @param $timestamp Mixed: string, or null
         * @return Mixed: previous value
         */
-       public function setRevisionTimestamp( $timestmap ) {
-               return wfSetVar( $this->mRevisionTimestamp, $timestmap );
+       public function setRevisionTimestamp( $timestamp) {
+               return wfSetVar( $this->mRevisionTimestamp, $timestamp );
        }
 
        /**
@@ -2023,11 +2023,11 @@ class OutputPage extends ContextSource {
        /**
         * Prepare this object to display an error page; disable caching and
         * indexing, clear the current text and redirect, set the page's title
-        * and optionally an custom HTML title (content of the <title> tag).
+        * and optionally an custom HTML title (content of the "<title>" tag).
         *
         * @param $pageTitle String|Message will be passed directly to setPageTitle()
         * @param $htmlTitle String|Message will be passed directly to setHTMLTitle();
-        *                   optional, if not passed the <title> attribute will be
+        *                   optional, if not passed the "<title>" attribute will be
         *                   based on $pageTitle
         */
        public function prepareErrorPage( $pageTitle, $htmlTitle = false ) {
@@ -2391,7 +2391,7 @@ $templates
        /**
         * @param $sk Skin The given Skin
         * @param $includeStyle Boolean: unused
-        * @return String: The doctype, opening <html>, and head element.
+        * @return String: The doctype, opening "<html>", and head element.
         */
        public function headElement( Skin $sk, $includeStyle = true ) {
                global $wgContLang;
@@ -2516,8 +2516,8 @@ $templates
         * @param $only String ResourceLoaderModule TYPE_ class constant
         * @param $useESI boolean
         * @param $extraQuery Array with extra query parameters to add to each request. array( param => value )
-        * @param $loadCall boolean If true, output an (asynchronous) mw.loader.load() call rather than a <script src="..."> tag
-        * @return string html <script> and <style> tags
+        * @param $loadCall boolean If true, output an (asynchronous) mw.loader.load() call rather than a "<script src='...'>" tag
+        * @return string html "<script>" and "<style>" tags
         */
        protected function makeResourceLoaderLink( $modules, $only, $useESI = false, array $extraQuery = array(), $loadCall = false ) {
                global $wgResourceLoaderUseESI;
@@ -2696,7 +2696,7 @@ $templates
        }
 
        /**
-        * JS stuff to put in the <head>. This is the startup module, config
+        * JS stuff to put in the "<head>". This is the startup module, config
         * vars and modules marked with position 'top'
         *
         * @return String: HTML fragment
@@ -2744,12 +2744,12 @@ $templates
        }
 
        /**
-        * JS stuff to put at the 'bottom', which can either be the bottom of the <body>
-        * or the bottom of the <head> depending on $wgResourceLoaderExperimentalAsyncLoading:
+        * JS stuff to put at the 'bottom', which can either be the bottom of the "<body>"
+        * or the bottom of the "<head>" depending on $wgResourceLoaderExperimentalAsyncLoading:
         * modules marked with position 'bottom', legacy scripts ($this->mScripts),
         * user preferences, site JS and user JS
         *
-        * @param $inHead boolean If true, this HTML goes into the <head>, if false it goes into the <body>
+        * @param $inHead boolean If true, this HTML goes into the "<head>", if false it goes into the "<body>"
         * @return string
         */
        function getScriptsForBottomQueue( $inHead ) {
@@ -2865,7 +2865,7 @@ $templates
        }
 
        /**
-        * JS stuff to put at the bottom of the <body>
+        * JS stuff to put at the bottom of the "<body>"
         * @return string
         */
        function getBottomScripts() {
@@ -2880,7 +2880,7 @@ $templates
        /**
         * Add one or more variables to be set in mw.config in JavaScript.
         *
-        * @param $key {String|Array} Key or array of key/value pars.
+        * @param $keys {String|Array} Key or array of key/value pairs.
         * @param $value {Mixed} [optional] Value of the configuration variable.
         */
        public function addJsConfigVars( $keys, $value = null ) {
@@ -2963,6 +2963,9 @@ $templates
                        'wgPageContentLanguage' => $lang->getCode(),
                        'wgSeparatorTransformTable' => $compactSeparatorTransTable,
                        'wgDigitTransformTable' => $compactDigitTransTable,
+                       'wgDefaultDateFormat' => $lang->getDefaultDateFormat(),
+                       'wgMonthNames' => $lang->getMonthNamesArray(),
+                       'wgMonthNamesShort' => $lang->getMonthAbbreviationsArray(),
                        'wgRelevantPageName' => $relevantTitle->getPrefixedDBKey(),
                );
                if ( $wgContLang->hasVariants() ) {
@@ -3016,7 +3019,7 @@ $templates
        }
 
        /**
-        * @param $addContentType bool: Whether <meta> specifying content type should be returned
+        * @param $addContentType bool: Whether "<meta>" specifying content type should be returned
         *
         * @return array in format "link name or number => 'link html'".
         */
@@ -3250,7 +3253,7 @@ $templates
 
        /**
         * @param $unused
-        * @param $addContentType bool: Whether <meta> specifying content type should be returned
+        * @param $addContentType bool: Whether "<meta>" specifying content type should be returned
         *
         * @return string HTML tag links to be put in the header.
         */
@@ -3259,7 +3262,7 @@ $templates
        }
 
        /**
-        * Generate a <link rel/> for a feed.
+        * Generate a "<link rel/>" for a feed.
         *
         * @param $type String: feed type
         * @param $url String: URL to the feed
@@ -3314,7 +3317,7 @@ $templates
        }
 
        /**
-        * Build a set of <link>s for the stylesheets specified in the $this->styles array.
+        * Build a set of "<link>" elements for the stylesheets specified in the $this->styles array.
         * These will be applied to various media & IE conditionals.
         *
         * @return string
index 750636d..d82f957 100644 (file)
@@ -175,6 +175,15 @@ abstract class IndexPager extends ContextSource implements Pager {
                }
        }
 
+       /**
+        * Get the Database object in use
+        *
+        * @return DatabaseBase
+        */
+       public function getDatabase() {
+               return $this->mDb;
+       }
+
        /**
         * Do the query, using information from the object context. This function
         * has been kept minimal to make it overridable if necessary, to allow for
@@ -993,7 +1002,7 @@ abstract class TablePager extends IndexPager {
         * @protected
         *
         * @param $row Object: the database result row
-        * @return Array of <attr> => <value>
+        * @return Array of attribute => value
         */
        function getRowAttrs( $row ) {
                $class = $this->getRowClass( $row );
@@ -1108,7 +1117,7 @@ abstract class TablePager extends IndexPager {
        }
 
        /**
-        * Get a <select> element which has options for each of the allowed limits
+        * Get a "<select>" element which has options for each of the allowed limits
         *
         * @return String: HTML fragment
         */
@@ -1138,7 +1147,7 @@ abstract class TablePager extends IndexPager {
        }
 
        /**
-        * Get <input type="hidden"> elements for use in a method="get" form.
+        * Get \<input type="hidden"\> elements for use in a method="get" form.
         * Resubmits all defined elements of the query string, except for a
         * blacklist, passed in the $blacklist parameter.
         *
index bf63d65..eaf781b 100644 (file)
@@ -939,7 +939,7 @@ class Preferences {
 
                if ( $wgEnableAPI ) {
                        # Some random gibberish as a proposed default
-                       // @fixme This should use CryptRand but we may not want to read urandom on every view
+                       // @todo Fixme: this should use CryptRand but we may not want to read urandom on every view
                        $hash = sha1( mt_rand() . microtime( true ) );
 
                        $defaultPreferences['watchlisttoken'] = array(
@@ -1587,7 +1587,7 @@ class PreferencesForm extends HTMLForm {
        }
 
        /**
-        * Get the <legend> for a given section key. Normally this is the
+        * Get the "<legend>" for a given section key. Normally this is the
         * prefs-$key message but we'll allow extensions to override it.
         * @param $key string
         * @return string
index 3327826..20e7909 100644 (file)
@@ -158,7 +158,7 @@ class RecentChange {
        }
 
        /**
-        * @return bool|\Title
+        * @return bool|Title
         */
        public function getMovedToTitle() {
                if( $this->mMovedToTitle === false ) {
@@ -231,13 +231,15 @@ class RecentChange {
                        }
                        $title = Title::makeTitle( $this->mAttribs['rc_namespace'], $this->mAttribs['rc_title'] );
 
-                       # @todo FIXME: This would be better as an extension hook
-                       $enotif = new EmailNotification();
-                       $status = $enotif->notifyOnPageChange( $editor, $title,
-                               $this->mAttribs['rc_timestamp'],
-                               $this->mAttribs['rc_comment'],
-                               $this->mAttribs['rc_minor'],
-                               $this->mAttribs['rc_last_oldid'] );
+                       if ( wfRunHooks( 'AbortEmailNotification', array($editor, $title) ) ) {
+                               # @todo FIXME: This would be better as an extension hook
+                               $enotif = new EmailNotification();
+                               $status = $enotif->notifyOnPageChange( $editor, $title,
+                                       $this->mAttribs['rc_timestamp'],
+                                       $this->mAttribs['rc_comment'],
+                                       $this->mAttribs['rc_minor'],
+                                       $this->mAttribs['rc_last_oldid'] );
+                       }
                }
        }
 
index fc777b5..3c5cfa8 100644 (file)
@@ -245,7 +245,7 @@ abstract class RevisionItemBase {
        abstract public function canViewContent();
 
        /**
-        * Get the HTML of the list item. Should be include <li></li> tags.
+        * Get the HTML of the list item. Should be include "<li></li>" tags.
         * This is used to show the list in HTML form, by the special page.
         */
        abstract public function getHTML();
index fed5762..a0c77da 100644 (file)
@@ -1253,7 +1253,7 @@ class Sanitizer {
         * a. named char refs can only be &lt; &gt; &amp; &quot;, others are
         *   numericized (this way we're well-formed even without a DTD)
         * b. any numeric char refs must be legal chars, not invalid or forbidden
-        * c. use &#x, not &#X
+        * c. use lower cased "&#x", not "&#X"
         * d. fix or reject non-valid attributes
         *
         * @param $text String
@@ -1421,7 +1421,7 @@ class Sanitizer {
        /**
         * If the named entity is defined in the HTML 4.0/XHTML 1.0 DTD,
         * return the UTF-8 encoding of that character. Otherwise, returns
-        * pseudo-entity source (eg &foo;)
+        * pseudo-entity source (eg "&foo;")
         *
         * @param $name String
         * @return String
index 8d47b83..f4960c1 100644 (file)
@@ -326,7 +326,7 @@ abstract class Skin extends ContextSource {
        }
 
        /**
-        * Make a <script> tag containing global variables
+        * Make a "<script>" tag containing global variables
         *
         * @deprecated in 1.19
         * @param $unused
@@ -363,7 +363,7 @@ abstract class Skin extends ContextSource {
         * inside ->getOutput() is deprecated. The $out arg is kept
         * for compatibility purposes with skins.
         * @param $out OutputPage
-        * @delete
+        * @todo delete
         */
        abstract function setupSkinUserCss( OutputPage $out );
 
@@ -397,7 +397,7 @@ abstract class Skin extends ContextSource {
 
        /**
         * This will be called by OutputPage::headElement when it is creating the
-        * <body> tag, skins can override it if they have a need to add in any
+        * "<body>" tag, skins can override it if they have a need to add in any
         * body attributes or classes of their own.
         * @param $out OutputPage
         * @param $bodyAttrs Array
@@ -636,9 +636,9 @@ abstract class Skin extends ContextSource {
        }
 
        /**
-        * This gets called shortly before the </body> tag.
+        * This gets called shortly before the "</body>" tag.
         *
-        * @return String HTML-wrapped JS code to be put before </body>
+        * @return String HTML-wrapped JS code to be put before "</body>"
         */
        function bottomScripts() {
                // TODO and the suckage continues. This function is really just a wrapper around
index 18bc29f..6385250 100644 (file)
@@ -95,7 +95,7 @@ class LegacyTemplate extends BaseTemplate {
        }
 
        /**
-        * This will be called immediately after the <body> tag.  Split into
+        * This will be called immediately after the "<body>" tag.  Split into
         * two functions to make it easier to subclass.
         * @return string
         */
@@ -159,8 +159,8 @@ class LegacyTemplate extends BaseTemplate {
        }
 
        /**
-        * This gets called shortly before the </body> tag.
-        * @return String HTML to be put before </body>
+        * This gets called shortly before the "</body>" tag.
+        * @return String HTML to be put before "</body>"
         */
        function afterContent() {
                return $this->doAfterContent();
index 4028e78..3f07dd6 100644 (file)
@@ -91,7 +91,7 @@ class SkinTemplate extends Skin {
        var $template = 'QuickTemplate';
 
        /**
-        * Whether this skin use OutputPage::headElement() to generate the <head>
+        * Whether this skin use OutputPage::headElement() to generate the "<head>"
         * tag
         */
        var $useHeadElement = false;
@@ -1605,26 +1605,39 @@ abstract class BaseTemplate extends QuickTemplate {
         * Makes a link, usually used by makeListItem to generate a link for an item
         * in a list used in navigation lists, portlets, portals, sidebars, etc...
         *
-        * $key is a string, usually a key from the list you are generating this link from
-        * $item is an array containing some of a specific set of keys.
-        * The text of the link will be generated either from the contents of the "text"
-        * key in the $item array, if a "msg" key is present a message by that name will
-        * be used, and if neither of those are set the $key will be used as a message name.
+        * @param $key string usually a key from the list you are generating this
+        * link from.
+        * @param $item array contains some of a specific set of keys.
+        *
+        * The text of the link will be generated either from the contents of the
+        * "text" key in the $item array, if a "msg" key is present a message by
+        * that name will be used, and if neither of those are set the $key will be
+        * used as a message name.
+        *
         * If a "href" key is not present makeLink will just output htmlescaped text.
-        * The href, id, class, rel, and type keys are used as attributes for the link if present.
-        * If an "id" or "single-id" (if you don't want the actual id to be output on the link)
-        * is present it will be used to generate a tooltip and accesskey for the link.
+        * The "href", "id", "class", "rel", and "type" keys are used as attributes
+        * for the link if present.
+        *
+        * If an "id" or "single-id" (if you don't want the actual id to be output
+        * on the link) is present it will be used to generate a tooltip and
+        * accesskey for the link.
+        *
         * If you don't want an accesskey, set $item['tooltiponly'] = true;
-        * $options can be used to affect the output of a link:
-        *   You can use a text-wrapper key to specify a list of elements to wrap the
-        *     text of a link in. This should be an array of arrays containing a 'tag' and
-        *     optionally an 'attributes' key. If you only have one element you don't need
-        *     to wrap it in another array. eg: To use <a><span>...</span></a> in all links
-        *     use array( 'text-wrapper' => array( 'tag' => 'span' ) ) for your options.
-        *   A link-class key can be used to specify additional classes to apply to all links.
-        *   A link-fallback can be used to specify a tag to use instead of <a> if there is
-        *   no link. eg: If you specify 'link-fallback' => 'span' than any non-link will
-        *   output a <span> instead of just text.
+        *
+        * @param $options array can be used to affect the output of a link.
+        * Possible options are:
+        *   - 'text-wrapper' key to specify a list of elements to wrap the text of
+        *   a link in. This should be an array of arrays containing a 'tag' and
+        *   optionally an 'attributes' key. If you only have one element you don't
+        *   need to wrap it in another array. eg: To use <a><span>...</span></a>
+        *   in all links use array( 'text-wrapper' => array( 'tag' => 'span' ) )
+        *   for your options.
+        *   - 'link-class' key can be used to specify additional classes to apply
+        *   to all links.
+        *   - 'link-fallback' can be used to specify a tag to use instead of "<a>"
+        *   if there is no link. eg: If you specify 'link-fallback' => 'span' than
+        *   any non-link will output a "<span>" instead of just text.
+        *
         * @return string
         */
        function makeLink( $key, $item, $options = array() ) {
@@ -1686,17 +1699,22 @@ abstract class BaseTemplate extends QuickTemplate {
        }
 
        /**
-        * Generates a list item for a navigation, portlet, portal, sidebar... etc list
-        * $key is a string, usually a key from the list you are generating this link from
-        * $item is an array of list item data containing some of a specific set of keys.
+        * Generates a list item for a navigation, portlet, portal, sidebar... list
+        *
+        * @param $key string, usually a key from the list you are generating this link from.
+        * @param $item array, of list item data containing some of a specific set of keys.
         * The "id" and "class" keys will be used as attributes for the list item,
         * if "active" contains a value of true a "active" class will also be appended to class.
-        * If you want something other than a <li> you can pass a tag name such as
+        *
+        * @param $options array
+        *
+        * If you want something other than a "<li>" you can pass a tag name such as
         * "tag" => "span" in the $options array to change the tag used.
         * link/content data for the list item may come in one of two forms
         * A "links" key may be used, in which case it should contain an array with
-        * a list of links to include inside the list item, see makeLink for the format
-        * of individual links array items.
+        * a list of links to include inside the list item, see makeLink for the
+        * format of individual links array items.
+        *
         * Otherwise the relevant keys from the list item $item array will be passed
         * to makeLink instead. Note however that "id" and "class" are used by the
         * list item directly so they will not be passed to makeLink
@@ -1704,6 +1722,7 @@ abstract class BaseTemplate extends QuickTemplate {
         * If you need an id or class on a single link you should include a "links"
         * array with just one link item inside of it.
         * $options is also passed on to makeLink calls
+        *
         * @return string
         */
        function makeListItem( $key, $item, $options = array() ) {
index ba6db79..629e06d 100644 (file)
@@ -1136,17 +1136,103 @@ class SpecialCreateAccount extends SpecialRedirectToSpecial {
  * users.
  */
 
+/**
+ * Superclass for any RedirectSpecialPage which redirects the user
+ * to a particular article (as opposed to user contributions, logs, etc.).
+ *
+ * For security reasons these special pages are restricted to pass on
+ * the following subset of GET parameters to the target page while
+ * removing all others:
+ *
+ * - useskin, uselang, printable: to alter the appearance of the resulting page
+ *
+ * - redirect: allows viewing one's user page or talk page even if it is a
+ * redirect.
+ *
+ * - rdfrom: allows redirecting to one's user page or talk page from an
+ * external wiki with the "Redirect from..." notice.
+ *
+ * - limit, offset: Useful for linking to history of one's own user page or
+ * user talk page. For example, this would be a link to "the last edit to your
+ * user talk page in the year 2010":
+ * http://en.wikipedia.org/w/index.php?title=Special:MyPage&offset=20110000000000&limit=1&action=history
+ *
+ * - feed: would allow linking to the current user's RSS feed for their user
+ * talk page:
+ * http://en.wikipedia.org/w/index.php?title=Special:MyTalk&action=history&feed=rss
+ *
+ * - preloadtitle: Can be used to provide a default section title for a
+ * preloaded new comment on one's own talk page.
+ *
+ * - summary : Can be used to provide a default edit summary for a preloaded
+ * edit to one's own user page or talk page.
+ *
+ * - preview: Allows showing/hiding preview on first edit regardless of user
+ * preference, useful for preloaded edits where you know preview wouldn't be
+ * useful.
+ *
+ * - internaledit, externaledit, mode: Allows forcing the use of the
+ * internal/external editor, e.g. to force the internal editor for
+ * short/simple preloaded edits.
+ *
+ * - redlink: Affects the message the user sees if their talk page/user talk
+ * page does not currently exist. Avoids confusion for newbies with no user
+ * pages over why they got a "permission error" following this link:
+ * http://en.wikipedia.org/w/index.php?title=Special:MyPage&redlink=1
+ *
+ * - debug: determines whether the debug parameter is passed to load.php,
+ * which disables reformatting and allows scripts to be debugged. Useful
+ * when debugging scripts that manipulate one's own user page or talk page.
+ *
+ * @par Hook extension:
+ * Extensions can add to the redirect parameters list by using the hook
+ * RedirectSpecialArticleRedirectParams
+ *
+ * This hook allows extensions which add GET parameters like FlaggedRevs to
+ * retain those parameters when redirecting using special pages.
+ *
+ * @par Hook extension example:
+ * @code
+ *     $wgHooks['RedirectSpecialArticleRedirectParams'][] =
+ *             'MyExtensionHooks::onRedirectSpecialArticleRedirectParams';
+ *     public static function onRedirectSpecialArticleRedirectParams( &$redirectParams ) {
+ *             $redirectParams[] = 'stable';
+ *             return true;
+ *     }
+ * @endcode
+ * @ingroup SpecialPage
+ */
+abstract class RedirectSpecialArticle extends RedirectSpecialPage {
+       function __construct( $name ) {
+               parent::__construct( $name );
+               $redirectParams = array(
+                       'action',
+                       'redirect', 'rdfrom',
+                       # Options for preloaded edits
+                       'preload', 'editintro', 'preloadtitle', 'summary',
+                       # Options for overriding user settings
+                       'preview', 'internaledit', 'externaledit', 'mode',
+                       # Options for history/diffs
+                       'section', 'oldid', 'diff', 'dir',
+                       'limit', 'offset', 'feed',
+                       # Misc options
+                       'redlink', 'debug',
+                       # Options for action=raw; missing ctype can break JS or CSS in some browsers
+                       'ctype', 'maxage', 'smaxage',
+               );
+
+               wfRunHooks( "RedirectSpecialArticleRedirectParams", array(&$redirectParams) );
+               $this->mAllowedRedirectParams = $redirectParams;
+       }
+}
+
 /**
  * Shortcut to construct a special page pointing to current user user's page.
  * @ingroup SpecialPage
  */
-class SpecialMypage extends RedirectSpecialPage {
+class SpecialMypage extends RedirectSpecialArticle {
        function __construct() {
                parent::__construct( 'Mypage' );
-               $this->mAllowedRedirectParams = array( 'action', 'preload', 'preloadtitle', 'editintro',
-                       'section', 'oldid', 'diff', 'dir',
-                       // Options for action=raw; missing ctype can break JS or CSS in some browsers
-                       'ctype', 'maxage', 'smaxage' );
        }
 
        function getRedirect( $subpage ) {
@@ -1162,11 +1248,9 @@ class SpecialMypage extends RedirectSpecialPage {
  * Shortcut to construct a special page pointing to current user talk page.
  * @ingroup SpecialPage
  */
-class SpecialMytalk extends RedirectSpecialPage {
+class SpecialMytalk extends RedirectSpecialArticle {
        function __construct() {
                parent::__construct( 'Mytalk' );
-               $this->mAllowedRedirectParams = array( 'action', 'preload', 'preloadtitle', 'editintro',
-                       'section', 'oldid', 'diff', 'dir' );
        }
 
        function getRedirect( $subpage ) {
index 6d36a43..aeb9ba4 100644 (file)
 /**
  * Abstract base class for update jobs that put some secondary data extracted
  * from article content into the database.
+ *
+ * @note: subclasses should NOT start or commit transactions in their doUpdate() method,
+ *        a transaction will automatically be wrapped around the update. Starting another
+ *        one would break the outer transaction bracket. If need be, subclasses can override
+ *        the beginTransaction() and commitTransaction() methods.
  */
 abstract class SqlDataUpdate extends DataUpdate {
 
index 3b500ae..43275a6 100644 (file)
@@ -213,7 +213,7 @@ class StringUtils {
         * Returns an Iterator
         * @param $separator
         * @param $subject
-        * @return ArrayIterator|\ExplodeIterator
+        * @return ArrayIterator|ExplodeIterator
         */
        static function explode( $separator, $subject ) {
                if ( substr_count( $subject, $separator ) > 1000 ) {
index 2b1a513..4f7984e 100644 (file)
@@ -2859,7 +2859,7 @@ class Title {
         * @return Int or 0 if the page doesn't exist
         */
        public function getLatestRevID( $flags = 0 ) {
-               if ( $this->mLatestID !== false ) {
+               if ( !( $flags & Title::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
                        return intval( $this->mLatestID );
                }
                # Calling getArticleID() loads the field from cache as needed
@@ -3474,6 +3474,10 @@ class Title {
                        $wgUser->spreadAnyEditBlock();
                        return $err;
                }
+               // Check suppressredirect permission
+               if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
+                       $createRedirect = true;
+               }
 
                // If it is a file, move it first.
                // It is done before all other moving stuff is done because it's hard to revert.
@@ -3570,8 +3574,8 @@ class Title {
         *
         * @param $nt Title the page to move to, which should be a redirect or nonexistent
         * @param $reason String The reason for the move
-        * @param $createRedirect Bool Whether to leave a redirect at the old title.  Ignored
-        *   if the user doesn't have the suppressredirect right
+        * @param $createRedirect Bool Whether to leave a redirect at the old title. Does not check
+        *   if the user has the suppressredirect right
         * @throws MWException
         */
        private function moveToInternal( &$nt, $reason = '', $createRedirect = true ) {
@@ -3585,7 +3589,7 @@ class Title {
                        $logType = 'move';
                }
 
-               $redirectSuppressed = !$createRedirect && $wgUser->isAllowed( 'suppressredirect' );
+               $redirectSuppressed = !$createRedirect;
 
                $logEntry = new ManualLogEntry( 'move', $logType );
                $logEntry->setPerformer( $wgUser );
index 3279a38..ca8ce8f 100644 (file)
@@ -119,6 +119,7 @@ class User {
                'delete',
                'deletedhistory',
                'deletedtext',
+               'deletelogentry',
                'deleterevision',
                'edit',
                'editinterface',
@@ -527,7 +528,7 @@ class User {
         * as 300.300.300.300 will return true because it looks like an IP
         * address, despite not being strictly valid.
         *
-        * We match \d{1,3}\.\d{1,3}\.\d{1,3}\.xxx as an anonymous IP
+        * We match "\d{1,3}\.\d{1,3}\.\d{1,3}\.xxx" as an anonymous IP
         * address because the usemod software would "cloak" anonymous IP
         * addresses like this, if we allowed accounts like this to be created
         * new users could get the old edits of these anonymous users.
index 87526fc..766db46 100644 (file)
@@ -504,9 +504,9 @@ class WebRequest {
         * Fetch a text string from the given array or return $default if it's not
         * set. Carriage returns are stripped from the text, and with some language
         * modules there is an input transliteration applied. This should generally
-        * be used for form <textarea> and <input> fields. Used for user-supplied
-        * freeform text input (for which input transformations may be required - e.g.
-        * Esperanto x-coding).
+        * be used for form "<textarea>" and "<input>" fields. Used for
+        * user-supplied freeform text input (for which input transformations may
+        * be required - e.g.  Esperanto x-coding).
         *
         * @param $name String
         * @param $default String: optional
index 7efb082..5f593e7 100644 (file)
@@ -392,23 +392,6 @@ class MediaWiki {
                return $article;
        }
 
-       /**
-        * Cleaning up request by doing deferred updates, DB transaction, and the output
-        */
-       public function finalCleanup() {
-               wfProfileIn( __METHOD__ );
-               // Now commit any transactions, so that unreported errors after
-               // output() don't roll back the whole DB transaction
-               $factory = wfGetLBFactory();
-               $factory->commitMasterChanges();
-               // Output everything!
-               $this->context->getOutput()->output();
-               // Do any deferred jobs
-               DeferredUpdates::doUpdates( 'commit' );
-               $this->doJobs();
-               wfProfileOut( __METHOD__ );
-       }
-
        /**
         * Do a job from the job queue
         */
@@ -447,12 +430,23 @@ class MediaWiki {
         * Ends this task peacefully
         */
        public function restInPeace() {
+               // Do any deferred jobs
+               DeferredUpdates::doUpdates( 'commit' );
+
+               // Execute a job from the queue
+               $this->doJobs();
+
+               // Log message usage, if $wgAdaptiveMessageCache is set to true
                MessageCache::logMessages();
+
+               // Log profiling data, e.g. in the database or UDP
                wfLogProfilingData();
+
                // Commit and close up!
                $factory = wfGetLBFactory();
                $factory->commitMasterChanges();
                $factory->shutdown();
+
                wfDebug( "Request ended normally\n" );
        }
 
@@ -599,7 +593,13 @@ class MediaWiki {
                }
 
                $this->performRequest();
-               $this->finalCleanup();
+
+               // Now commit any transactions, so that unreported errors after
+               // output() don't roll back the whole DB transaction
+               wfGetLBFactory()->commitMasterChanges();
+
+               // Output everything!
+               $this->context->getOutput()->output();
 
                wfProfileOut( __METHOD__ );
        }
index 1190172..7dd85b6 100644 (file)
@@ -126,6 +126,13 @@ class WikiReference {
        private $mServer; ///< server URL, may be protocol-relative, e.g. '//www.mediawiki.org'
        private $mPath;   ///< path, '/wiki/$1'
 
+       /**
+        * @param $major string
+        * @param $minor string
+        * @param $canonicalServer string
+        * @param $path string
+        * @param $server null|string
+        */
        public function __construct( $major, $minor, $canonicalServer, $path, $server = null ) {
                $this->mMajor = $major;
                $this->mMinor = $minor;
@@ -186,8 +193,17 @@ class WikiReference {
                return $this->mCanonicalServer . $this->getLocalUrl( $page );
        }
 
+       /**
+        * Get a canonical server URL
+        * @return string
+        */
+       public function getCanonicalServer() {
+               return $this->mCanonicalServer;
+       }
+
        /**
         * Alias for getCanonicalUrl(), for backwards compatibility.
+        * @param $page string
         * @return String
         */
        public function getUrl( $page ) {
index 43932a7..6d9170a 100644 (file)
@@ -1487,6 +1487,10 @@ class WikiPage extends Page {
 
                                wfProfileOut( __METHOD__ );
                                return $status;
+                       } elseif ( $oldtext === false ) {
+                               # Sanity check for bug 37225
+                               wfProfileOut( __METHOD__ );
+                               throw new MWException( "Could not find text for current revision {$oldid}." );
                        }
 
                        $revision = new Revision( array(
@@ -2602,7 +2606,7 @@ class WikiPage extends Page {
                if ( is_object( $rt ) && ( !is_object( $ot ) || !$rt->equals( $ot ) || $ot->getFragment() != $rt->getFragment() ) ) {
                        $truncatedtext = $wgContLang->truncate(
                                str_replace( "\n", ' ', $newtext ),
-                               max( 0, 250
+                               max( 0, 255
                                        - strlen( wfMsgForContent( 'autoredircomment' ) )
                                        - strlen( $rt->getFullText() )
                                ) );
index 6e4bb3a..505cb7f 100644 (file)
@@ -210,15 +210,18 @@ class Xml {
         * @param string $selected The language code of the selected language
         * @param boolean $customisedOnly If true only languages which have some content are listed
         * @param string $inLanguage The ISO code of the language to display the select list in (optional)
+        * @param array $overrideAttrs Override the attributes of the select tag (since 1.20)
+        * @param Message|null $msg Label message key (since 1.20)
         * @return array containing 2 items: label HTML and select list HTML
         */
-       public static function languageSelector( $selected, $customisedOnly = true, $inLanguage = null ) {
+       public static function languageSelector( $selected, $customisedOnly = true, $inLanguage = null, $overrideAttrs = array(), Message $msg = null ) {
                global $wgLanguageCode;
 
-               $languages = Language::fetchLanguageNames( $inLanguage, $customisedOnly ? 'mwfile' : 'mw' );
+               $include = $customisedOnly ? 'mwfile' : 'mw';
+               $languages = Language::fetchLanguageNames( $inLanguage, $include );
 
-               // Make sure the site language is in the list; a custom language code might not have a
-               // defined name...
+               // Make sure the site language is in the list;
+               // a custom language code might not have a defined name...
                if( !array_key_exists( $wgLanguageCode, $languages ) ) {
                        $languages[$wgLanguageCode] = $wgLanguageCode;
                }
@@ -228,7 +231,7 @@ class Xml {
                /**
                 * If a bogus value is set, default to the content language.
                 * Otherwise, no default is selected and the user ends up
-                * with an Afrikaans interface since it's first in the list.
+                * with Afrikaans since it's first in the list.
                 */
                $selected = isset( $languages[$selected] ) ? $selected : $wgLanguageCode;
                $options = "\n";
@@ -236,12 +239,15 @@ class Xml {
                        $options .= Xml::option( "$code - $name", $code, ($code == $selected) ) . "\n";
                }
 
+               $attrs = array( 'id' => 'wpUserLanguage', 'name' => 'wpUserLanguage' );
+               $attrs = array_merge( $attrs, $overrideAttrs );
+
+               if( $msg === null ) {
+                       $msg = wfMessage( 'yourlanguage' );
+               }
                return array(
-                       Xml::label( wfMsg('yourlanguage'), 'wpUserLanguage' ),
-                       Xml::tags( 'select',
-                               array( 'id' => 'wpUserLanguage', 'name' => 'wpUserLanguage' ),
-                               $options
-                       )
+                       Xml::label( $msg->text(), $attrs['id'] ),
+                       Xml::tags( 'select', $attrs, $options )
                );
 
        }
index 2a2d31e..0595923 100644 (file)
@@ -2570,7 +2570,6 @@ $zh2Hant = array(
 '龚' => '龔',
 '龛' => '龕',
 '龟' => '龜',
-'' => '棡',
 '𠮶' => '嗰',
 '𡒄' => '壈',
 '𦈖' => '䌈',
@@ -10389,7 +10388,6 @@ $zh2Hans = array(
 '棖' => '枨',
 '棗' => '枣',
 '棟' => '栋',
-'棡' => '',
 '棧' => '栈',
 '棲' => '栖',
 '棶' => '梾',
index 7393315..1dacabc 100644 (file)
@@ -618,10 +618,8 @@ class HistoryPager extends ReverseChronologicalPager {
                $tools = array();
 
                # Rollback and undo links
-               if ( $prevRev &&
-                       !count( $this->getTitle()->getUserPermissionsErrors( 'edit', $this->getUser() ) ) )
-               {
-                       if ( $latest && !count( $this->getTitle()->getUserPermissionsErrors( 'rollback', $this->getUser() ) ) ) {
+               if ( $prevRev && $this->getTitle()->quickUserCan( 'edit', $user ) ) {
+                       if ( $latest && $this->getTitle()->quickUserCan( 'rollback', $user ) ) {
                                $this->preventClickjacking();
                                $tools[] = '<span class="mw-rollback-link">' .
                                        Linker::buildRollbackLink( $rev, $this->getContext() ) . '</span>';
index 543beb7..89e2cfe 100644 (file)
@@ -107,6 +107,7 @@ class InfoAction extends FormlessAction {
         * @return mixed array or boolean false
         */
        public static function pageCountInfo( $title ) {
+               wfProfileIn( __METHOD__ );
                $id = $title->getArticleID();
                $dbr = wfGetDB( DB_SLAVE );
 
@@ -114,8 +115,8 @@ class InfoAction extends FormlessAction {
                        'watchlist',
                        'COUNT(*)',
                        array(
+                               'wl_namespace' => $title->getNamespace(),
                                'wl_title'     => $title->getDBkey(),
-                               'wl_namespace' => $title->getNamespace()
                        ),
                        __METHOD__
                );
@@ -133,15 +134,21 @@ class InfoAction extends FormlessAction {
                        array( 'rev_page' => $id ),
                        __METHOD__
                );
+               $result = array( 'watchers' => $watchers, 'edits' => $edits,
+                       'authors' => $authors );
 
-               $views = (int)$dbr->selectField(
-                       'page',
-                       'page_counter',
-                       array( 'page_id' => $id ),
-                       __METHOD__
-               );
-
-               return array( 'watchers' => $watchers, 'edits' => $edits,
-                       'authors' => $authors, 'views' => $views );
+               global $wgDisableCounters;
+               if ( !$wgDisableCounters ) {
+                       $views = (int)$dbr->selectField(
+                               'page',
+                               'page_counter',
+                               array( 'page_id' => $id ),
+                               __METHOD__
+                       );
+                       $result['views'] = $views;
+               }
+
+               wfProfileOut( __METHOD__ );
+               return $result;
        }
 }
index a6e3363..bc94ee4 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 5, 2006
  *
- * Copyright © 2006, 2010 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006, 2010 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -134,7 +134,7 @@ abstract class ApiBase extends ContextSource {
        /**
         * Get the name of the module as shown in the profiler log
         *
-        * @param $db DatabaseBase
+        * @param $db DatabaseBase|bool
         *
         * @return string
         */
@@ -1184,7 +1184,8 @@ abstract class ApiBase extends ContextSource {
         * @param $errorCode string Brief, arbitrary, stable string to allow easy
         *   automated identification of the error, e.g., 'unknown_action'
         * @param $httpRespCode int HTTP response code
-        * @param $extradata array Data to add to the <error> element; array in ApiResult format
+        * @param $extradata array Data to add to the "<error>" element; array in ApiResult format
+        * @throws UsageException
         */
        public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) {
                Profiler::instance()->close();
index 6cd31ae..c879b35 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 4, 2007
  *
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -72,9 +72,9 @@ class ApiBlock extends ApiBase {
                $data = array(
                        'Target' => $params['user'],
                        'Reason' => array(
-                               is_null( $params['reason'] ) ? '' : $params['reason'],
+                               $params['reason'],
                                'other',
-                               is_null( $params['reason'] ) ? '' : $params['reason']
+                               $params['reason']
                        ),
                        'Expiry' => $params['expiry'] == 'never' ? 'infinite' : $params['expiry'],
                        'HardBlock' => !$params['anononly'],
@@ -156,7 +156,7 @@ class ApiBlock extends ApiBase {
                                ApiBase::PARAM_DEPRECATED => true,
                        ),
                        'expiry' => 'never',
-                       'reason' => null,
+                       'reason' => '',
                        'anononly' => false,
                        'nocreate' => false,
                        'autoblock' => false,
@@ -174,7 +174,7 @@ class ApiBlock extends ApiBase {
                        'token' => 'A block token previously obtained through prop=info',
                        'gettoken' => 'If set, a block token will be returned, and no other action will be taken',
                        'expiry' => 'Relative expiry time, e.g. \'5 months\' or \'2 weeks\'. If set to \'infinite\', \'indefinite\' or \'never\', the block will never expire.',
-                       'reason' => 'Reason for block (optional)',
+                       'reason' => 'Reason for block',
                        'anononly' => 'Block anonymous users only (i.e. disable anonymous edits for this IP)',
                        'nocreate' => 'Prevent account creation',
                        'autoblock' => 'Automatically block the last used IP address, and any subsequent IP addresses they try to login from',
index 91406af..da13a22 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Jun 30, 2007
  *
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -52,7 +52,7 @@ class ApiDelete extends ApiBase {
                }
 
                $titleObj = $pageObj->getTitle();
-               $reason = ( isset( $params['reason'] ) ? $params['reason'] : null );
+               $reason = $params['reason'];
                $user = $this->getUser();
 
                if ( $titleObj->getNamespace() == NS_FILE ) {
@@ -62,7 +62,7 @@ class ApiDelete extends ApiBase {
                }
 
                if ( !$status->isGood() ) {
-                       $errors = $this->getErrorsArray();
+                       $errors = $status->getErrorsArray();
                        $this->dieUsageMsg( $errors[0] ); // We don't care about multiple errors, just report one of them
                }
 
@@ -180,7 +180,10 @@ class ApiDelete extends ApiBase {
                        'pageid' => array(
                                ApiBase::PARAM_TYPE => 'integer'
                        ),
-                       'token' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
                        'reason' => null,
                        'watch' => array(
                                ApiBase::PARAM_DFLT => false,
@@ -221,7 +224,8 @@ class ApiDelete extends ApiBase {
                return array(
                        '' => array(
                                'title' => 'string',
-                               'reason' => 'string'
+                               'reason' => 'string',
+                               'logid' => 'integer'
                        )
                );
        }
index 5575489..13975ae 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 25, 2008
  *
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
index 9a16007..e75c4ad 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on August 16, 2007
  *
- * Copyright © 2007 Iker Labarga <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Iker Labarga "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -413,7 +413,10 @@ class ApiEditPage extends ApiBase {
                                ApiBase::PARAM_REQUIRED => false,
                        ),
                        'text' => null,
-                       'token' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
                        'summary' => null,
                        'minor' => false,
                        'notminor' => false,
index 4b6ba00..4fa0343 100644 (file)
@@ -98,7 +98,10 @@ class ApiEmailUser extends ApiBase {
                                ApiBase::PARAM_TYPE => 'string',
                                ApiBase::PARAM_REQUIRED => true
                        ),
-                       'token' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
                        'ccme' => false,
                );
        }
index 2ed118f..160f5b9 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 05, 2007
  *
- * Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index eee8fa1..7f79835 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 13, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 7cac0eb..08a9c8e 100644 (file)
@@ -126,7 +126,10 @@ class ApiFileRevert extends ApiBase {
                                ApiBase::PARAM_TYPE => 'string',
                                ApiBase::PARAM_REQUIRED => true,
                        ),
-                       'token' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
                );
 
        }
index 7dfdffc..1c6a871 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 19, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 92619f7..3d2a39c 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 22, 2006
  *
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
index e728d05..acbc7d3 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 19, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 60552c4..fac2ca5 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 22, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index db81aac..184f0a3 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Feb 2, 2009
  *
- * Copyright © 2009 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2009 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
index e26b82b..7141459 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 22, 2006
  *
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
index 1bc9d02..65056e4 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 22, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index ef2c54f..5ccf185 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 19, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -83,16 +83,40 @@ class ApiFormatXml extends ApiFormatBase {
 
        /**
         * This method takes an array and converts it to XML.
+        *
         * There are several noteworthy cases:
         *
-        *  If array contains a key '_element', then the code assumes that ALL other keys are not important and replaces them with the value['_element'].
-        *      Example:        name='root',  value = array( '_element'=>'page', 'x', 'y', 'z') creates <root>  <page>x</page>  <page>y</page>  <page>z</page> </root>
+        * If array contains a key '_element', then the code assumes that ALL
+        * other keys are not important and replaces them with the
+        * value['_element'].
+        *
+        * @par Example:
+        * @verbatim
+        * name='root',  value = array( '_element'=>'page', 'x', 'y', 'z')
+        * @endverbatim
+        * creates:
+        * @verbatim
+        * <root>  <page>x</page>  <page>y</page>  <page>z</page> </root>
+        * @endverbatim
+        *
+        * If any of the array's element key is '*', then the code treats all
+        * other key->value pairs as attributes, and the value['*'] as the
+        * element's content.
+        *
+        * @par Example:
+        * @verbatim
+        * name='root',  value = array( '*'=>'text', 'lang'=>'en', 'id'=>10)
+        * @endverbatim
+        * creates:
+        * @verbatim
+        * <root lang='en' id='10'>text</root>
+        * @endverbatim
         *
-        *  If any of the array's element key is '*', then the code treats all other key->value pairs as attributes, and the value['*'] as the element's content.
-        *      Example:        name='root',  value = array( '*'=>'text', 'lang'=>'en', 'id'=>10)   creates  <root lang='en' id='10'>text</root>
+        * Finally neither key is found, all keys become element names, and values
+        * become element content.
         *
-        * If neither key is found, all keys become element names, and values become element content.
-        * The method is recursive, so the same rules apply to any sub-arrays.
+        * @note The method is recursive, so the same rules apply to any
+        * sub-arrays.
         *
         * @param $elemName
         * @param $elemValue
index dbcdb21..730ad8e 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 19, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 97da786..2b5de21 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 6, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 6663d97..abd2777 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Feb 4, 2009
  *
- * Copyright © 2009 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2009 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -98,7 +98,10 @@ class ApiImport extends ApiBase {
        public function getAllowedParams() {
                global $wgImportSources;
                return array(
-                       'token' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
                        'summary' => null,
                        'xml' => null,
                        'interwikisource' => array(
index 2ad2653..1f91fe9 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 19, 2006
  *
- * Copyright © 2006-2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com,
+ * Copyright © 2006-2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com",
  * Daniel Cannon (cannon dot danielc at gmail dot com)
  *
  * This program is free software; you can redistribute it and/or modify
index cab2430..b2f634d 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Jan 4, 2008
  *
- * Copyright © 2008 Yuri Astrakhan <Firstname><Lastname>@gmail.com,
+ * Copyright © 2008 Yuri Astrakhan "<Firstname><Lastname>@gmail.com",
  *
  * 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
index 172a77b..05f6652 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 4, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -967,11 +967,11 @@ class ApiMain extends ApiBase {
        protected function getCredits() {
                return array(
                        'API developers:',
-                       '    Roan Kattouw <Firstname>.<Lastname>@gmail.com (lead developer Sep 2007-present)',
+                       '    Roan Kattouw "<Firstname>.<Lastname>@gmail.com" (lead developer Sep 2007-present)',
                        '    Victor Vasiliev - vasilvv at gee mail dot com',
                        '    Bryan Tong Minh - bryan . tongminh @ gmail . com',
                        '    Sam Reed - sam @ reedyboy . net',
-                       '    Yuri Astrakhan <Firstname><Lastname>@gmail.com (creator, lead developer Sep 2006-Sep 2007)',
+                       '    Yuri Astrakhan "<Firstname><Lastname>@gmail.com" (creator, lead developer Sep 2006-Sep 2007)',
                        '',
                        'Please send your comments, suggestions and questions to mediawiki-api@lists.wikimedia.org',
                        'or file a bug report at https://bugzilla.wikimedia.org/'
@@ -1160,8 +1160,18 @@ class ApiMain extends ApiBase {
 class UsageException extends MWException {
 
        private $mCodestr;
+
+       /**
+        * @var null|array
+        */
        private $mExtraData;
 
+       /**
+        * @param $message string
+        * @param $codestr string
+        * @param $code int
+        * @param $extradata array|null
+        */
        public function __construct( $message, $codestr, $code = 0, $extradata = null ) {
                parent::__construct( $message, $code );
                $this->mCodestr = $codestr;
index c89f59b..55148b1 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 31, 2007
  *
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -37,9 +37,6 @@ class ApiMove extends ApiBase {
        public function execute() {
                $user = $this->getUser();
                $params = $this->extractRequestParams();
-               if ( is_null( $params['reason'] ) ) {
-                       $params['reason'] = '';
-               }
 
                $this->requireOnlyOneParameter( $params, 'from', 'fromid' );
 
@@ -180,8 +177,11 @@ class ApiMove extends ApiBase {
                                ApiBase::PARAM_TYPE => 'string',
                                ApiBase::PARAM_REQUIRED => true
                        ),
-                       'token' => null,
-                       'reason' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
+                       'reason' => '',
                        'movetalk' => false,
                        'movesubpages' => false,
                        'noredirect' => false,
@@ -213,7 +213,7 @@ class ApiMove extends ApiBase {
                        'fromid' => "Page ID of the page you want to move. Cannot be used together with {$p}from",
                        'to' => 'Title you want to rename the page to',
                        'token' => 'A move token previously retrieved through prop=info',
-                       'reason' => 'Reason for the move (optional)',
+                       'reason' => 'Reason for the move',
                        'movetalk' => 'Move the talk page, if it exists',
                        'movesubpages' => 'Move subpages, if applicable',
                        'noredirect' => 'Don\'t create a redirect',
index 0727cff..5a78354 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 13, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index b7db4f0..0f5be6b 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 24, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -52,7 +52,7 @@ class ApiPageSet extends ApiQueryBase {
 
        /**
         * Constructor
-        * @param $query ApiQueryBase
+        * @param $query ApiBase
         * @param $resolveRedirects bool Whether redirects should be resolved
         * @param $convertTitles bool
         */
@@ -266,8 +266,8 @@ class ApiPageSet extends ApiQueryBase {
        }
 
        /**
-        * Returns the number of revisions (requested with revids= parameter)\
-        * @return int
+        * Returns the number of revisions (requested with revids= parameter).
+        * @return int Number of revisions.
         */
        public function getRevisionCount() {
                return count( $this->getRevisionIDs() );
index dffce5b..343a262 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Dec 01, 2007
  *
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
index dbcd43c..875684e 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Created on Dec 01, 2007
  *
- * Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 45d19d0..cb5e081 100644 (file)
@@ -65,7 +65,10 @@ class ApiPatrol extends ApiBase {
 
        public function getAllowedParams() {
                return array(
-                       'token' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
                        'rcid' => array(
                                ApiBase::PARAM_TYPE => 'integer',
                                ApiBase::PARAM_REQUIRED => true
index 0fcaf42..d516504 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 1, 2007
  *
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -139,7 +139,10 @@ class ApiProtect extends ApiBase {
                        'pageid' => array(
                                ApiBase::PARAM_TYPE => 'integer',
                        ),
-                       'token' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
                        'protections' => array(
                                ApiBase::PARAM_ISMULTI => true,
                                ApiBase::PARAM_REQUIRED => true,
@@ -176,7 +179,7 @@ class ApiProtect extends ApiBase {
                        'protections' => 'Pipe-separated list of protection levels, formatted action=group (e.g. edit=sysop)',
                        'expiry' => array( 'Expiry timestamps. If only one timestamp is set, it\'ll be used for all protections.',
                                        'Use \'infinite\', \'indefinite\' or \'never\', for a neverexpiring protection.' ),
-                       'reason' => 'Reason for (un)protecting (optional)',
+                       'reason' => 'Reason for (un)protecting',
                        'cascade' => array( 'Enable cascading protection (i.e. protect pages included in this page)',
                                        'Ignored if not all protection levels are \'sysop\' or \'protect\'' ),
                        'watch' => 'If set, add the page being (un)protected to your watchlist',
index 866b71c..99e22d5 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 7, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -103,6 +103,10 @@ class ApiQuery extends ApiBase {
 
        protected $mAllowedGenerators = array();
 
+       /**
+        * @param $main ApiMain
+        * @param $action string
+        */
        public function __construct( $main, $action ) {
                parent::__construct( $main, $action );
 
@@ -202,6 +206,9 @@ class ApiQuery extends ApiBase {
                return null;
        }
 
+       /**
+        * @return ApiFormatRaw|null
+        */
        public function getCustomPrinter() {
                // If &exportnowrap is set, use the raw formatter
                if ( $this->getParameter( 'export' ) &&
@@ -258,6 +265,9 @@ class ApiQuery extends ApiBase {
                $this->outputGeneralPageInfo();
 
                // Execute all requested modules.
+               /**
+                * @var $module ApiQueryBase
+                */
                foreach ( $modules as $module ) {
                        $params = $module->extractRequestParams();
                        $cacheMode = $this->mergeCacheMode(
@@ -303,6 +313,9 @@ class ApiQuery extends ApiBase {
         */
        private function addCustomFldsToPageSet( $modules, $pageSet ) {
                // Query all requested modules.
+               /**
+                * @var $module ApiQueryBase
+                */
                foreach ( $modules as $module ) {
                        $module->requestExtraData( $pageSet );
                }
@@ -384,6 +397,9 @@ class ApiQuery extends ApiBase {
 
                // Show redirect information
                $redirValues = array();
+               /**
+                * @var $titleTo Title
+                */
                foreach ( $pageSet->getRedirectTitles() as $titleStrFrom => $titleTo ) {
                        $r = array(
                                'from' => strval( $titleStrFrom ),
index 60b57bf..09f6edb 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on December 12, 2007
  *
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -58,6 +58,17 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
                $this->addTables( 'category' );
                $this->addFields( 'cat_title' );
 
+               if ( !is_null( $params['continue'] ) ) {
+                       $cont = explode( '|', $params['continue'] );
+                       if ( count( $cont ) != 1 ) {
+                               $this->dieUsage( "Invalid continue param. You should pass the " .
+                                       "original value returned by the previous query", "_badcontinue" );
+                       }
+                       $op = $params['dir'] == 'descending' ? '<' : '>';
+                       $cont_from = $db->addQuotes( $cont[0] );
+                       $this->addWhere( "cat_title $op= $cont_from" );
+               }
+
                $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
                $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
                $to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) );
@@ -104,8 +115,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
                foreach ( $res as $row ) {
                        if ( ++ $count > $params['limit'] ) {
                                // We've reached the one extra which shows that there are additional cats to be had. Stop here...
-                               // TODO: Security issue - if the user has no right to view next title, it will still be shown
-                               $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->cat_title ) );
+                               $this->setContinueEnumParameter( 'continue', $row->cat_title );
                                break;
                        }
 
@@ -127,7 +137,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
                                }
                                $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $item );
                                if ( !$fit ) {
-                                       $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->cat_title ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->cat_title );
                                        break;
                                }
                        }
@@ -143,6 +153,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
        public function getAllowedParams() {
                return array(
                        'from' => null,
+                       'continue' => null,
                        'to' => null,
                        'prefix' => null,
                        'dir' => array(
@@ -178,6 +189,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
        public function getParamDescription() {
                return array(
                        'from' => 'The category to start enumerating from',
+                       'continue' => 'When more results are available, use this to continue',
                        'to' => 'The category to stop enumerating at',
                        'prefix' => 'Search for all category titles that begin with this value',
                        'dir' => 'Direction to sort in',
@@ -213,6 +225,12 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
                return 'Enumerate all categories';
        }
 
+       public function getPossibleErrors() {
+               return array_merge( parent::getPossibleErrors(), array(
+                       array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
+               ) );
+       }
+
        public function getExamples() {
                return array(
                        'api.php?action=query&list=allcategories&acprop=size',
index 4ab4d72..45cc404 100644 (file)
@@ -85,6 +85,17 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
 
                $params = $this->extractRequestParams();
 
+               if ( !is_null( $params['continue'] ) ) {
+                       $cont = explode( '|', $params['continue'] );
+                       if ( count( $cont ) != 1 ) {
+                               $this->dieUsage( "Invalid continue param. You should pass the " .
+                                       "original value returned by the previous query", "_badcontinue" );
+                       }
+                       $op = $params['dir'] == 'descending' ? '<' : '>';
+                       $cont_from = $db->addQuotes( $cont[0] );
+                       $this->addWhere( "img_name $op= $cont_from" );
+               }
+
                // Image filters
                $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
                $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
@@ -148,8 +159,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
                foreach ( $res as $row ) {
                        if ( ++ $count > $limit ) {
                                // We've reached the one extra which shows that there are additional pages to be had. Stop here...
-                               // TODO: Security issue - if the user has no right to view next title, it will still be shown
-                               $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->img_name ) );
+                               $this->setContinueEnumParameter( 'continue', $row->img_name );
                                break;
                        }
 
@@ -161,7 +171,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
 
                                $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $info );
                                if ( !$fit ) {
-                                       $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->img_name ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->img_name );
                                        break;
                                }
                        } else {
@@ -179,6 +189,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
        public function getAllowedParams() {
                return array (
                        'from' => null,
+                       'continue' => null,
                        'to' => null,
                        'prefix' => null,
                        'minsize' => array(
@@ -215,6 +226,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
        public function getParamDescription() {
                return array(
                        'from' => 'The image title to start enumerating from',
+                       'continue' => 'When more results are available, use this to continue',
                        'to' => 'The image title to stop enumerating at',
                        'prefix' => 'Search for all image titles that begin with this value',
                        'dir' => 'The direction in which to list',
@@ -228,7 +240,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
                );
        }
 
-       private $propertyFilter = array( 'archivename' );
+       private $propertyFilter = array( 'archivename', 'thumbmime' );
 
        public function getResultProperties() {
                return array_merge(
@@ -254,6 +266,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
                        array( 'code' => 'mimesearchdisabled', 'info' => 'MIME search disabled in Miser Mode' ),
                        array( 'code' => 'invalidsha1hash', 'info' => 'The SHA1 hash provided is not valid' ),
                        array( 'code' => 'invalidsha1base36hash', 'info' => 'The SHA1Base36 hash provided is not valid' ),
+                       array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
                ) );
        }
 
index 70b6656..da4840f 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on July 7, 2007
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -77,17 +77,25 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
                }
                if ( !is_null( $params['continue'] ) ) {
                        $continueArr = explode( '|', $params['continue'] );
-                       if ( count( $continueArr ) != 2 ) {
-                               $this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
-                       }
                        $op = $params['dir'] == 'descending' ? '<' : '>';
-                       $continueTitle = $db->addQuotes( $this->titleToKey( $continueArr[0] ) );
-                       $continueFrom = intval( $continueArr[1] );
-                       $this->addWhere(
-                               "pl_title $op $continueTitle OR " .
-                               "(pl_title = $continueTitle AND " .
-                               "pl_from $op= $continueFrom)"
-                       );
+                       if ( $params['unique'] ) {
+                               if ( count( $continueArr ) != 1 ) {
+                                       $this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
+                               }
+                               $continueTitle = $db->addQuotes( $continueArr[0] );
+                               $this->addWhere( "pl_title $op= $continueTitle" );
+                       } else {
+                               if ( count( $continueArr ) != 2 ) {
+                                       $this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
+                               }
+                               $continueTitle = $db->addQuotes( $continueArr[0] );
+                               $continueFrom = intval( $continueArr[1] );
+                               $this->addWhere(
+                                       "pl_title $op $continueTitle OR " .
+                                       "(pl_title = $continueTitle AND " .
+                                       "pl_from $op= $continueFrom)"
+                               );
+                       }
                }
 
                $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
@@ -121,11 +129,10 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
                foreach ( $res as $row ) {
                        if ( ++ $count > $limit ) {
                                // We've reached the one extra which shows that there are additional pages to be had. Stop here...
-                               // TODO: Security issue - if the user has no right to view next title, it will still be shown
                                if ( $params['unique'] ) {
-                                       $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->pl_title );
                                } else {
-                                       $this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from );
+                                       $this->setContinueEnumParameter( 'continue', $row->pl_title . "|" . $row->pl_from );
                                }
                                break;
                        }
@@ -142,9 +149,9 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
                                $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
                                if ( !$fit ) {
                                        if ( $params['unique'] ) {
-                                               $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) );
+                                               $this->setContinueEnumParameter( 'continue', $row->pl_title );
                                        } else {
-                                               $this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from );
+                                               $this->setContinueEnumParameter( 'continue', $row->pl_title . "|" . $row->pl_from );
                                        }
                                        break;
                                }
index ac000bf..f5e1146 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Dec 1, 2007
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index cfc22ff..16cc31d 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 25, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -67,6 +67,17 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
                // Page filters
                $this->addTables( 'page' );
 
+               if ( !is_null( $params['continue'] ) ) {
+                       $cont = explode( '|', $params['continue'] );
+                       if ( count( $cont ) != 1 ) {
+                               $this->dieUsage( "Invalid continue param. You should pass the " .
+                                       "original value returned by the previous query", "_badcontinue" );
+                       }
+                       $op = $params['dir'] == 'descending' ? '<' : '>';
+                       $cont_from = $db->addQuotes( $cont[0] );
+                       $this->addWhere( "page_title $op= $cont_from" );
+               }
+
                if ( $params['filterredir'] == 'redirects' ) {
                        $this->addWhereFld( 'page_is_redirect', 1 );
                } elseif ( $params['filterredir'] == 'nonredirects' ) {
@@ -165,13 +176,22 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
                $this->addOption( 'LIMIT', $limit + 1 );
                $res = $this->select( __METHOD__ );
 
+               //Get gender information
+               if( MWNamespace::hasGenderDistinction( $params['namespace'] ) ) {
+                       $users = array();
+                       foreach ( $res as $row ) {
+                               $users[] = $row->page_title;
+                       }
+                       GenderCache::singleton()->doQuery( $users, __METHOD__ );
+                       $res->rewind(); //reset
+               }
+
                $count = 0;
                $result = $this->getResult();
                foreach ( $res as $row ) {
                        if ( ++ $count > $limit ) {
                                // We've reached the one extra which shows that there are additional pages to be had. Stop here...
-                               // TODO: Security issue - if the user has no right to view next title, it will still be shown
-                               $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->page_title ) );
+                               $this->setContinueEnumParameter( 'continue', $row->page_title );
                                break;
                        }
 
@@ -184,7 +204,7 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
                                );
                                $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
                                if ( !$fit ) {
-                                       $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->page_title ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->page_title );
                                        break;
                                }
                        } else {
@@ -202,6 +222,7 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
 
                return array(
                        'from' => null,
+                       'continue' => null,
                        'to' => null,
                        'prefix' => null,
                        'namespace' => array(
@@ -275,6 +296,7 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
                $p = $this->getModulePrefix();
                return array(
                        'from' => 'The page title to start enumerating from',
+                       'continue' => 'When more results are available, use this to continue',
                        'to' => 'The page title to stop enumerating at',
                        'prefix' => 'Search for all page titles that begin with this value',
                        'namespace' => 'The namespace to enumerate',
@@ -314,6 +336,7 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
                return array_merge( parent::getPossibleErrors(), array(
                        array( 'code' => 'params', 'info' => 'Use "gapfilterredir=nonredirects" option instead of "redirects" when using allpages as a generator' ),
                        array( 'code' => 'params', 'info' => 'prlevel may not be used without prtype' ),
+                       array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
                ) );
        }
 
index dda8c72..1e29a64 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on July 7, 2007
  *
- * Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 7491bbb..06db87b 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 16, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 92fabdd..2c48aca 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 7, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -571,6 +571,11 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase {
 
        private $mIsGenerator;
 
+       /**
+        * @param $query ApiBase
+        * @param $moduleName string
+        * @param $paramPrefix string
+        */
        public function __construct( $query, $moduleName, $paramPrefix = '' ) {
                parent::__construct( $query, $moduleName, $paramPrefix );
                $this->mIsGenerator = false;
index 49cd590..097487e 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 10, 2007
  *
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
index 283eb13..309c2ce 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on May 13, 2007
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -91,7 +91,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
                        }
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $clfrom = intval( $cont[0] );
-                       $clto = $this->getDB()->addQuotes( $this->titleToKey( $cont[1] ) );
+                       $clto = $this->getDB()->addQuotes( $cont[1] );
                        $this->addWhere(
                                "cl_from $op $clfrom OR " .
                                "(cl_from = $clfrom AND " .
@@ -143,8 +143,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
                                if ( ++$count > $params['limit'] ) {
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
-                                       $this->setContinueEnumParameter( 'continue', $row->cl_from .
-                                                       '|' . $this->keyToTitle( $row->cl_to ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->cl_from . '|' . $row->cl_to );
                                        break;
                                }
 
@@ -164,8 +163,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
 
                                $fit = $this->addPageSubItem( $row->cl_from, $vals );
                                if ( !$fit ) {
-                                       $this->setContinueEnumParameter( 'continue', $row->cl_from .
-                                                       '|' . $this->keyToTitle( $row->cl_to ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->cl_from . '|' . $row->cl_to );
                                        break;
                                }
                        }
@@ -175,8 +173,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
                                if ( ++$count > $params['limit'] ) {
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
-                                       $this->setContinueEnumParameter( 'continue', $row->cl_from .
-                                                       '|' . $this->keyToTitle( $row->cl_to ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->cl_from . '|' . $row->cl_to );
                                        break;
                                }
 
index e5eca85..76246a2 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on May 13, 2007
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -25,7 +25,8 @@
  */
 
 /**
- * This query adds the <categories> subelement to all pages with the list of categories the page is in
+ * This query adds the "<categories>" subelement to all pages with the list of
+ * categories the page is in.
  *
  * @ingroup API
  */
index 9f66f22..55ce023 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on June 14, 2007
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index df5e303..e69ccbd 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Jul 2, 2007
  *
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -164,7 +164,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                                $this->dieUsage( 'Invalid continue param. You should pass the original value returned by the previous query', 'badcontinue' );
                        }
                        $ns = intval( $cont[0] );
-                       $title = $db->addQuotes( $this->titleToKey( $cont[1] ) );
+                       $title = $db->addQuotes( $cont[1] );
                        $ts = $db->addQuotes( $db->timestamp( $cont[2] ) );
                        $op = ( $dir == 'newer' ? '>' : '<' );
                        $this->addWhere( "ar_namespace $op $ns OR " .
@@ -203,7 +203,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                                // We've had enough
                                if ( $mode == 'all' || $mode == 'revs' ) {
                                        $this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' .
-                                               $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp );
+                                               $row->ar_title . '|' . $row->ar_timestamp );
                                } else {
                                        $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ar_timestamp ) );
                                }
@@ -269,7 +269,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                        if ( !$fit ) {
                                if ( $mode == 'all' || $mode == 'revs' ) {
                                        $this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' .
-                                               $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp );
+                                               $row->ar_title . '|' . $row->ar_timestamp );
                                } else {
                                        $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ar_timestamp ) );
                                }
index d68480c..6715969 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 25, 2008
  *
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
index 856d0fd..e05ffb6 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 27, 2008
  *
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -82,8 +82,8 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
                        }
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $db = $this->getDB();
-                       $orig = $db->addQuotes( $this->titleTokey( $cont[0] ) );
-                       $dup = $db->addQuotes( $this->titleToKey( $cont[1] ) );
+                       $orig = $db->addQuotes( $cont[0] );
+                       $dup = $db->addQuotes( $cont[1] );
                        $this->addWhere(
                                "i1.img_name $op $orig OR " .
                                "(i1.img_name = $orig AND " .
@@ -110,9 +110,7 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
                        if ( ++$count > $params['limit'] ) {
                                // We've reached the one extra which shows that
                                // there are additional pages to be had. Stop here...
-                               $this->setContinueEnumParameter( 'continue',
-                                       $this->keyToTitle( $row->orig_name ) . '|' .
-                                       $this->keyToTitle( $row->dup_name ) );
+                               $this->setContinueEnumParameter( 'continue', $row->orig_name . '|' . $row->dup_name );
                                break;
                        }
                        if ( !is_null( $resultPageSet ) ) {
@@ -125,9 +123,7 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
                                );
                                $fit = $this->addPageSubItem( $images[$row->orig_name], $r );
                                if ( !$fit ) {
-                                       $this->setContinueEnumParameter( 'continue',
-                                                       $this->keyToTitle( $row->orig_name ) . '|' .
-                                                       $this->keyToTitle( $row->dup_name ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->orig_name . '|' . $row->dup_name );
                                        break;
                                }
                        }
index 221ffac..42b398b 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on July 7, 2007
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 999ddc8..9365a9b 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on May 13, 2007
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 07d5b41..eed9d7c 100644 (file)
@@ -73,9 +73,23 @@ class ApiQueryFilearchive extends ApiQueryBase {
                $this->addFieldsIf( 'fa_metadata', $fld_metadata );
                $this->addFieldsIf( 'fa_bits', $fld_bitdepth );
 
+               if ( !is_null( $params['continue'] ) ) {
+                       $cont = explode( '|', $params['continue'] );
+                       if ( count( $cont ) != 1 ) {
+                               $this->dieUsage( "Invalid continue param. You should pass the " .
+                                       "original value returned by the previous query", "_badcontinue" );
+                       }
+                       $op = $params['dir'] == 'descending' ? '<' : '>';
+                       $cont_from = $db->addQuotes( $cont[0] );
+                       $this->addWhere( "fa_name $op= $cont_from" );
+               }
+
                // Image filters
                $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
                $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
+               if ( !is_null( $params['continue'] ) ) {
+                       $from = $params['continue'];
+               }
                $to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) );
                $this->addWhereRange( 'fa_name', $dir, $from, $to );
                if ( isset( $params['prefix'] ) ) {
@@ -129,8 +143,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
                foreach ( $res as $row ) {
                        if ( ++$count > $limit ) {
                                // We've reached the one extra which shows that there are additional pages to be had. Stop here...
-                               // TODO: Security issue - if the user has no right to view next title, it will still be shown
-                               $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->fa_name ) );
+                               $this->setContinueEnumParameter( 'continue', $row->fa_name );
                                break;
                        }
 
@@ -199,7 +212,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
 
                        $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $file );
                        if ( !$fit ) {
-                               $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->fa_name ) );
+                               $this->setContinueEnumParameter( 'continue', $row->fa_name );
                                break;
                        }
                }
@@ -210,6 +223,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
        public function getAllowedParams() {
                return array (
                        'from' => null,
+                       'continue' => null,
                        'to' => null,
                        'prefix' => null,
                        'limit' => array(
@@ -251,6 +265,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
        public function getParamDescription() {
                return array(
                        'from' => 'The image title to start enumerating from',
+                       'continue' => 'When more results are available, use this to continue',
                        'to' => 'The image title to stop enumerating at',
                        'prefix' => 'Search for all image titles that begin with this value',
                        'dir' => 'The direction in which to list',
@@ -345,6 +360,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
                        array( 'code' => 'hashsearchdisabled', 'info' => 'Search by hash disabled in Miser Mode' ),
                        array( 'code' => 'invalidsha1hash', 'info' => 'The SHA1 hash provided is not valid' ),
                        array( 'code' => 'invalidsha1base36hash', 'info' => 'The SHA1Base36 hash provided is not valid' ),
+                       array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
                ) );
        }
 
index 37f347d..c5012f0 100644 (file)
@@ -5,7 +5,7 @@
  * Created on May 14, 2010
  *
  * Copyright © 2010 Sam Reed
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -64,7 +64,7 @@ class ApiQueryIWBacklinks extends ApiQueryGeneratorBase {
                        $db = $this->getDB();
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $prefix = $db->addQuotes( $cont[0] );
-                       $title = $db->addQuotes( $this->titleToKey( $cont[1] ) );
+                       $title = $db->addQuotes( $cont[1] );
                        $from = intval( $cont[2] );
                        $this->addWhere(
                                "iwl_prefix $op $prefix OR " .
index 7e69513..30c7f5a 100644 (file)
@@ -5,7 +5,7 @@
  * Created on May 14, 2010
  *
  * Copyright © 2010 Sam Reed
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -66,7 +66,7 @@ class ApiQueryIWLinks extends ApiQueryBase {
                        $db = $this->getDB();
                        $iwlfrom = intval( $cont[0] );
                        $iwlprefix = $db->addQuotes( $cont[1] );
-                       $iwltitle = $db->addQuotes( $this->titleToKey( $cont[2] ) );
+                       $iwltitle = $db->addQuotes( $cont[2] );
                        $this->addWhere(
                                "iwl_from $op $iwlfrom OR " .
                                "(iwl_from = $iwlfrom AND " .
index 4d2d04b..7184c88 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on July 6, 2007
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -356,8 +356,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
 
                                        if ( isset( $prop['thumbmime'] ) && $file->getHandler() ) {
                                                list( $ext, $mime ) = $file->getHandler()->getThumbType(
-                                                       substr( $mto->getPath(), strrpos( $mto->getPath(), '.' ) + 1 ),
-                                                       $file->getMimeType(), $thumbParams );
+                                                       $mto->getExtension(), $file->getMimeType(), $thumbParams );
                                                $vals['thumbmime'] = $mime;
                                        }
                                } elseif ( $mto && $mto->isError() ) {
@@ -491,7 +490,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
         *
         * @return array
         */
-       private static function getProperties() {
+       private static function getProperties( $modulePrefix = '' ) {
                return array(
                        'timestamp' =>      ' timestamp     - Adds timestamp for the uploaded version',
                        'user' =>           ' user          - Adds the user who uploaded the image version',
@@ -503,7 +502,8 @@ class ApiQueryImageInfo extends ApiQueryBase {
                        'dimensions' =>     ' dimensions    - Alias for size', // For backwards compatibility with Allimages
                        'sha1' =>           ' sha1          - Adds SHA-1 hash for the image',
                        'mime' =>           ' mime          - Adds MIME type of the image',
-                       'thumbmime' =>      ' thumbmime     - Adds MIME type of the image thumbnail (requires url)',
+                       'thumbmime' =>      ' thumbmime     - Adds MIME type of the image thumbnail' .
+                               ' (requires url and param ' . $modulePrefix . 'urlwidth)',
                        'mediatype' =>      ' mediatype     - Adds the media type of the image',
                        'metadata' =>       ' metadata      - Lists EXIF metadata for the version of the image',
                        'archivename' =>    ' archivename   - Adds the file name of the archive version for non-latest versions',
@@ -518,10 +518,10 @@ class ApiQueryImageInfo extends ApiQueryBase {
         *
         * @return array
         */
-       public static function getPropertyDescriptions( $filter = array() ) {
+       public static function getPropertyDescriptions( $filter = array(), $modulePrefix = '' ) {
                return array_merge(
                        array( 'What image information to get:' ),
-                       array_values( array_diff_key( self::getProperties(), array_flip( $filter ) ) )
+                       array_values( array_diff_key( self::getProperties( $modulePrefix ), array_flip( $filter ) ) )
                );
        }
 
@@ -532,7 +532,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
        public function getParamDescription() {
                $p = $this->getModulePrefix();
                return array(
-                       'prop' => self::getPropertyDescriptions(),
+                       'prop' => self::getPropertyDescriptions( array(), $p ),
                        'urlwidth' => array( "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.",
                                            'Only the current version of the image can be scaled' ),
                        'urlheight' => "Similar to {$p}urlwidth. Cannot be used without {$p}urlwidth",
index 147ab67..6052a75 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on May 13, 2007
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -25,7 +25,8 @@
  */
 
 /**
- * This query adds an <images> subelement to all pages with the list of images embedded into those pages.
+ * This query adds an "<images>" subelement to all pages with the list of
+ * images embedded into those pages.
  *
  * @ingroup API
  */
@@ -67,7 +68,7 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
                        }
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $ilfrom = intval( $cont[0] );
-                       $ilto = $this->getDB()->addQuotes( $this->titleToKey( $cont[1] ) );
+                       $ilto = $this->getDB()->addQuotes( $cont[1] );
                        $this->addWhere(
                                "il_from $op $ilfrom OR " .
                                "(il_from = $ilfrom AND " .
@@ -108,16 +109,14 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
                                if ( ++$count > $params['limit'] ) {
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
-                                       $this->setContinueEnumParameter( 'continue', $row->il_from .
-                                                       '|' . $this->keyToTitle( $row->il_to ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->il_from . '|' . $row->il_to );
                                        break;
                                }
                                $vals = array();
                                ApiQueryBase::addTitleInfo( $vals, Title::makeTitle( NS_FILE, $row->il_to ) );
                                $fit = $this->addPageSubItem( $row->il_from, $vals );
                                if ( !$fit ) {
-                                       $this->setContinueEnumParameter( 'continue', $row->il_from .
-                                                       '|' . $this->keyToTitle( $row->il_to ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->il_from . '|' . $row->il_to );
                                        break;
                                }
                        }
@@ -128,8 +127,7 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
                                if ( ++$count > $params['limit'] ) {
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
-                                       $this->setContinueEnumParameter( 'continue', $row->il_from .
-                                                       '|' . $this->keyToTitle( $row->il_to ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->il_from . '|' . $row->il_to );
                                        break;
                                }
                                $titles[] = Title::makeTitle( NS_FILE, $row->il_to );
index 87fd58b..95c6bda 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 25, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -57,7 +57,10 @@ class ApiQueryInfo extends ApiQueryBase {
                global $wgDisableCounters;
 
                $pageSet->requestField( 'page_restrictions' );
-               $pageSet->requestField( 'page_is_redirect' );
+               // when resolving redirects, no page will have this field
+               if( !$pageSet->isResolvingRedirects() ) {
+                       $pageSet->requestField( 'page_is_redirect' );
+               }
                $pageSet->requestField( 'page_is_new' );
                if ( !$wgDisableCounters ) {
                        $pageSet->requestField( 'page_counter' );
@@ -280,7 +283,10 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                $this->pageRestrictions = $pageSet->getCustomField( 'page_restrictions' );
-               $this->pageIsRedir = $pageSet->getCustomField( 'page_is_redirect' );
+               // when resolving redirects, no page will have this field
+               $this->pageIsRedir = !$pageSet->isResolvingRedirects()
+                       ? $pageSet->getCustomField( 'page_is_redirect' )
+                       : array();
                $this->pageIsNew = $pageSet->getCustomField( 'page_is_new' );
 
                global $wgDisableCounters;
@@ -346,7 +352,7 @@ class ApiQueryInfo extends ApiQueryBase {
                                : intval( $this->pageCounter[$pageid] );
                        $pageInfo['length'] = intval( $this->pageLength[$pageid] );
 
-                       if ( $this->pageIsRedir[$pageid] ) {
+                       if ( isset( $this->pageIsRedir[$pageid] ) && $this->pageIsRedir[$pageid] ) {
                                $pageInfo['redirect'] = '';
                        }
                        if ( $this->pageIsNew[$pageid] ) {
index c7639ee..3920407 100644 (file)
@@ -5,7 +5,7 @@
  * Created on May 14, 2011
  *
  * Copyright © 2011 Sam Reed
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -64,7 +64,7 @@ class ApiQueryLangBacklinks extends ApiQueryGeneratorBase {
                        $db = $this->getDB();
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $prefix = $db->addQuotes( $cont[0] );
-                       $title = $db->addQuotes( $this->titleToKey( $cont[1] ) );
+                       $title = $db->addQuotes( $cont[1] );
                        $from = intval( $cont[2] );
                        $this->addWhere(
                                "ll_lang $op $prefix OR " .
index 7cab739..3109a09 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on May 13, 2007
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 5cf9068..e54e2e8 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on May 12, 2007
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -119,7 +119,7 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $plfrom = intval( $cont[0] );
                        $plns = intval( $cont[1] );
-                       $pltitle = $this->getDB()->addQuotes( $this->titleToKey( $cont[2] ) );
+                       $pltitle = $this->getDB()->addQuotes( $cont[2] );
                        $this->addWhere(
                                "{$this->prefix}_from $op $plfrom OR " .
                                "({$this->prefix}_from = $plfrom AND " .
@@ -157,8 +157,7 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
                                        $this->setContinueEnumParameter( 'continue',
-                                               "{$row->pl_from}|{$row->pl_namespace}|" .
-                                               $this->keyToTitle( $row->pl_title ) );
+                                               "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
                                        break;
                                }
                                $vals = array();
@@ -166,8 +165,7 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
                                $fit = $this->addPageSubItem( $row->pl_from, $vals );
                                if ( !$fit ) {
                                        $this->setContinueEnumParameter( 'continue',
-                                               "{$row->pl_from}|{$row->pl_namespace}|" .
-                                               $this->keyToTitle( $row->pl_title ) );
+                                               "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
                                        break;
                                }
                        }
@@ -179,8 +177,7 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
                                        $this->setContinueEnumParameter( 'continue',
-                                               "{$row->pl_from}|{$row->pl_namespace}|" .
-                                               $this->keyToTitle( $row->pl_title ) );
+                                               "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
                                        break;
                                }
                                $titles[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
index bb4b429..2f11570 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 16, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 360183c..5e270e0 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Feb 13, 2009
  *
- * Copyright © 2009 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2009 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
index 2c7ba12..a8be26d 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Dec 22, 2010
  *
- * Copyright © 2010 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2010 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
index 62c6678..91d371b 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 19, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 1a5ad17..7ef90fb 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 7, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index ed6c3cb..364433d 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on July 30, 2007
  *
- * Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 511cbe4..ec503d6 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 25, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -259,9 +259,11 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                $data = array();
                $aliases = $wgContLang->getSpecialPageAliases();
                foreach ( SpecialPageFactory::getList() as $specialpage => $stuff ) {
-                       $arr = array( 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] );
-                       $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
-                       $data[] = $arr;
+                       if ( isset( $aliases[$specialpage] ) ) {
+                               $arr = array( 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] );
+                               $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
+                               $data[] = $arr;
+                       }
                }
                $this->getResult()->setIndexedTagName( $data, 'specialpage' );
                return $this->getResult()->addValue( 'query', $property, $data );
index 02484ae..a310d10 100644 (file)
@@ -113,7 +113,7 @@ class ApiQueryStashImageInfo extends ApiQueryImageInfo {
        public function getParamDescription() {
                $p = $this->getModulePrefix();
                return array(
-                       'prop' => self::getPropertyDescriptions( $this->propertyFilter ),
+                       'prop' => self::getPropertyDescriptions( $this->propertyFilter, $p ),
                        'filekey' => 'Key that identifies a previous upload that was stashed temporarily.',
                        'sessionkey' => 'Alias for filekey, for backward compatibility.',
                        'urlwidth' => "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.",
index f531980..f30b132 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 16, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -256,7 +256,7 @@ class ApiQueryContributions extends ApiQueryBase {
                $this->addFieldsIf( 'page_latest', $this->fld_flags );
                // $this->addFieldsIf( 'rev_text_id', $this->fld_ids ); // Should this field be exposed?
                $this->addFieldsIf( 'rev_comment', $this->fld_comment || $this->fld_parsedcomment );
-               $this->addFieldsIf( 'rev_len', $this->fld_size );
+               $this->addFieldsIf( 'rev_len', $this->fld_size || $this->fld_sizediff );
                $this->addFieldsIf( 'rev_minor_edit', $this->fld_flags );
                $this->addFieldsIf( 'rev_parent_id', $this->fld_flags || $this->fld_sizediff );
                $this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
@@ -513,6 +513,12 @@ class ApiQueryContributions extends ApiQueryBase {
                                        ApiBase::PROP_TYPE => 'integer',
                                        ApiBase::PROP_NULLABLE => true
                                )
+                       ),
+                       'sizediff' => array(
+                               'sizediff' => array(
+                                       ApiBase::PROP_TYPE => 'integer',
+                                       ApiBase::PROP_NULLABLE => true
+                               )
                        )
                );
        }
index e4617d9..d211918 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on July 30, 2007
  *
- * Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 02fcf01..acc846b 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on July 30, 2007
  *
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
index a93f94f..36644a4 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 25, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
index 1b1eee0..6b24aef 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Oct 4, 2008
  *
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -76,7 +76,7 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
                                        "original value returned by the previous query", "_badcontinue" );
                        }
                        $ns = intval( $cont[0] );
-                       $title = $this->getDB()->addQuotes( $this->titleToKey( $cont[1] ) );
+                       $title = $this->getDB()->addQuotes( $cont[1] );
                        $op = $params['dir'] == 'ascending' ? '>' : '<';
                        $this->addWhere(
                                "wl_namespace $op $ns OR " .
@@ -103,8 +103,7 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
                foreach ( $res as $row ) {
                        if ( ++$count > $params['limit'] ) {
                                // We've reached the one extra which shows that there are additional pages to be had. Stop here...
-                               $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' .
-                                                                       $this->keyToTitle( $row->wl_title ) );
+                               $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' . $row->wl_title );
                                break;
                        }
                        $t = Title::makeTitle( $row->wl_namespace, $row->wl_title );
@@ -118,8 +117,7 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
                                }
                                $fit = $this->getResult()->addValue( $this->getModuleName(), null, $vals );
                                if ( !$fit ) {
-                                       $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' .
-                                                                       $this->keyToTitle( $row->wl_title ) );
+                                       $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' . $row->wl_title );
                                        break;
                                }
                        } else {
index 798b227..91e2081 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 4, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * 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
@@ -165,7 +165,7 @@ class ApiResult extends ApiBase {
         * @param $value Mixed
         * @param $subElemName string when present, content element is created
         *  as a sub item of $arr. Use this parameter to create elements in
-        *  format <elem>text</elem> without attributes
+        *  format "<elem>text</elem>" without attributes.
         */
        public static function setContent( &$arr, $value, $subElemName = null ) {
                if ( is_array( $value ) ) {
index 4de49ea..677df16 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Jun 20, 2007
  *
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -49,7 +49,7 @@ class ApiRollback extends ApiBase {
                // User and title already validated in call to getTokenSalt from Main
                $titleObj = $this->getRbTitle();
                $pageObj = WikiPage::factory( $titleObj );
-               $summary = ( isset( $params['summary'] ) ? $params['summary'] : '' );
+               $summary = $params['summary'];
                $details = array();
                $retval = $pageObj->doRollback( $this->getRbUser(), $summary, $params['token'], $params['markbot'], $details, $this->getUser() );
 
@@ -90,8 +90,11 @@ class ApiRollback extends ApiBase {
                                ApiBase::PARAM_TYPE => 'string',
                                ApiBase::PARAM_REQUIRED => true
                        ),
-                       'token' => null,
-                       'summary' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
+                       'summary' => '',
                        'markbot' => false,
                        'watchlist' => array(
                                ApiBase::PARAM_DFLT => 'preferences',
@@ -110,7 +113,7 @@ class ApiRollback extends ApiBase {
                        'title' => 'Title of the page you want to rollback.',
                        'user' => 'Name of the user whose edits are to be rolled back. If set incorrectly, you\'ll get a badtoken error.',
                        'token' => "A rollback token previously retrieved through {$this->getModulePrefix()}prop=revisions",
-                       'summary' => 'Custom edit summary. If not set, default summary will be used',
+                       'summary' => 'Custom edit summary. If empty, default summary will be used',
                        'markbot' => 'Mark the reverted edits and the revert as bot edits',
                        'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch',
                );
index 40a6d44..e34771f 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Sep 7, 2007
  *
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -69,7 +69,7 @@ class ApiUnblock extends ApiBase {
 
                $data = array(
                        'Target' => is_null( $params['id'] ) ? $params['user'] : "#{$params['id']}",
-                       'Reason' => is_null( $params['reason'] ) ? '' : $params['reason']
+                       'Reason' => $params['reason']
                );
                $block = Block::newFromTarget( $data['Target'] );
                $retval = SpecialUnblock::processUnblock( $data, $this->getContext() );
@@ -104,7 +104,7 @@ class ApiUnblock extends ApiBase {
                                ApiBase::PARAM_DFLT => false,
                                ApiBase::PARAM_DEPRECATED => true,
                        ),
-                       'reason' => null,
+                       'reason' => '',
                );
        }
 
@@ -115,7 +115,7 @@ class ApiUnblock extends ApiBase {
                        'user' => "Username, IP address or IP range you want to unblock. Cannot be used together with {$p}id",
                        'token' => "An unblock token previously obtained through prop=info",
                        'gettoken' => 'If set, an unblock token will be returned, and no other action will be taken',
-                       'reason' => 'Reason for unblock (optional)',
+                       'reason' => 'Reason for unblock',
                );
        }
 
index c89f49a..c996251 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Jul 3, 2007
  *
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -94,7 +94,10 @@ class ApiUndelete extends ApiBase {
                                ApiBase::PARAM_TYPE => 'string',
                                ApiBase::PARAM_REQUIRED => true
                        ),
-                       'token' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
                        'reason' => '',
                        'timestamps' => array(
                                ApiBase::PARAM_TYPE => 'timestamp',
@@ -116,7 +119,7 @@ class ApiUndelete extends ApiBase {
                return array(
                        'title' => 'Title of the page you want to restore',
                        'token' => 'An undelete token previously retrieved through list=deletedrevs',
-                       'reason' => 'Reason for restoring (optional)',
+                       'reason' => 'Reason for restoring',
                        'timestamps' => 'Timestamps of the revisions to restore. If not set, all revisions will be restored.',
                        'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch',
                );
index aaa4c85..31b4351 100644 (file)
@@ -560,7 +560,10 @@ class ApiUpload extends ApiBase {
                                ApiBase::PARAM_DFLT => ''
                        ),
                        'text' => null,
-                       'token' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
                        'watch' => array(
                                ApiBase::PARAM_DFLT => false,
                                ApiBase::PARAM_DEPRECATED => true,
index 399bc54..cbb66a4 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Created on Mar 24, 2009
  *
- * Copyright © 2009 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2009 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
  *
  * 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
@@ -100,7 +100,10 @@ class ApiUserrights extends ApiBase {
                                ApiBase::PARAM_TYPE => User::getAllGroups(),
                                ApiBase::PARAM_ISMULTI => true
                        ),
-                       'token' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
                        'reason' => array(
                                ApiBase::PARAM_DFLT => ''
                        )
index c923c6d..0509f1f 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Created on Jan 4, 2008
  *
- * Copyright © 2008 Yuri Astrakhan <Firstname><Lastname>@gmail.com,
+ * Copyright © 2008 Yuri Astrakhan "<Firstname><Lastname>@gmail.com",
  *
  * 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
@@ -88,7 +88,10 @@ class ApiWatch extends ApiBase {
                                ApiBase::PARAM_REQUIRED => true
                        ),
                        'unwatch' => false,
-                       'token' => null,
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
                );
        }
 
diff --git a/includes/cache/ProcessCacheLRU.php b/includes/cache/ProcessCacheLRU.php
new file mode 100644 (file)
index 0000000..f215ebd
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+/**
+ * Per-process memory cache for storing items.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Cache
+ */
+
+/**
+ * Handles per process caching of items
+ * @ingroup Cache
+ */
+class ProcessCacheLRU {
+       /** @var Array */
+       protected $cache = array(); // (key => prop => value)
+
+       protected $maxCacheKeys; // integer; max entries
+
+       /**
+        * @param $maxKeys integer Maximum number of entries allowed (min 1).
+        * @throws MWException When $maxCacheKeys is not an int or =< 0.
+        */
+       public function __construct( $maxKeys ) {
+               if ( !is_int( $maxKeys ) || $maxKeys < 1 ) {
+                       throw new MWException( __METHOD__ . " must be given an integer and >= 1" );
+               }
+               $this->maxCacheKeys = $maxKeys;
+       }
+
+       /**
+        * Set a property field for a cache entry.
+        * This will prune the cache if it gets too large.
+        * If the item is already set, it will be pushed to the top of the cache.
+        *
+        * @param $key string
+        * @param $prop string
+        * @param $value mixed
+        * @return void
+        */
+       public function set( $key, $prop, $value ) {
+               if ( isset( $this->cache[$key] ) ) {
+                       $this->ping( $key ); // push to top
+               } elseif ( count( $this->cache ) >= $this->maxCacheKeys ) {
+                       reset( $this->cache );
+                       unset( $this->cache[key( $this->cache )] );
+               }
+               $this->cache[$key][$prop] = $value;
+       }
+
+       /**
+        * Check if a property field exists for a cache entry.
+        *
+        * @param $key string
+        * @param $prop string
+        * @return bool
+        */
+       public function has( $key, $prop ) {
+               return isset( $this->cache[$key][$prop] );
+       }
+
+       /**
+        * Get a property field for a cache entry.
+        * This returns null if the property is not set.
+        * If the item is already set, it will be pushed to the top of the cache.
+        *
+        * @param $key string
+        * @param $prop string
+        * @return mixed
+        */
+       public function get( $key, $prop ) {
+               if ( isset( $this->cache[$key][$prop] ) ) {
+                       $this->ping( $key ); // push to top
+                       return $this->cache[$key][$prop];
+               } else {
+                       return null;
+               }
+       }
+
+       /**
+        * Clear one or several cache entries, or all cache entries
+        *
+        * @param $keys string|Array
+        * @return void
+        */
+       public function clear( $keys = null ) {
+               if ( $keys === null ) {
+                       $this->cache = array();
+               } else {
+                       foreach ( (array)$keys as $key ) {
+                               unset( $this->cache[$key] );
+                       }
+               }
+       }
+
+       /**
+        * Push an entry to the top of the cache
+        *
+        * @param $key string
+        */
+       protected function ping( $key ) {
+               $item = $this->cache[$key];
+               unset( $this->cache[$key] );
+               $this->cache[$key] = $item;
+       }
+}
index 0a1f988..cc4ffaf 100644 (file)
@@ -271,7 +271,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool|null previous value of the flag
         */
-       function debug( $debug = null ) {
+       public function debug( $debug = null ) {
                return wfSetBit( $this->mFlags, DBO_DEBUG, $debug );
        }
 
@@ -297,7 +297,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return null|bool The previous value of the flag
         */
-       function bufferResults( $buffer = null ) {
+       public function bufferResults( $buffer = null ) {
                if ( is_null( $buffer ) ) {
                        return !(bool)( $this->mFlags & DBO_NOBUFFER );
                } else {
@@ -316,7 +316,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool The previous value of the flag.
         */
-       function ignoreErrors( $ignoreErrors = null ) {
+       public function ignoreErrors( $ignoreErrors = null ) {
                return wfSetBit( $this->mFlags, DBO_IGNORE, $ignoreErrors );
        }
 
@@ -329,7 +329,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $level int An integer (0 or 1), or omitted to leave it unchanged.
         * @return int The previous value
         */
-       function trxLevel( $level = null ) {
+       public function trxLevel( $level = null ) {
                return wfSetVar( $this->mTrxLevel, $level );
        }
 
@@ -338,7 +338,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $count int The count to set, or omitted to leave it unchanged.
         * @return int The error count
         */
-       function errorCount( $count = null ) {
+       public function errorCount( $count = null ) {
                return wfSetVar( $this->mErrorCount, $count );
        }
 
@@ -347,7 +347,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $prefix string The table prefix to set, or omitted to leave it unchanged.
         * @return string The previous table prefix.
         */
-       function tablePrefix( $prefix = null ) {
+       public function tablePrefix( $prefix = null ) {
                return wfSetVar( $this->mTablePrefix, $prefix );
        }
 
@@ -360,7 +360,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return LoadBalancer|null
         */
-       function getLBInfo( $name = null ) {
+       public function getLBInfo( $name = null ) {
                if ( is_null( $name ) ) {
                        return $this->mLBInfo;
                } else {
@@ -380,7 +380,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $name
         * @param $value
         */
-       function setLBInfo( $name, $value = null ) {
+       public function setLBInfo( $name, $value = null ) {
                if ( is_null( $value ) ) {
                        $this->mLBInfo = $name;
                } else {
@@ -393,7 +393,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $lag int
         */
-       function setFakeSlaveLag( $lag ) {
+       public function setFakeSlaveLag( $lag ) {
                $this->mFakeSlaveLag = $lag;
        }
 
@@ -402,7 +402,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $enabled bool
         */
-       function setFakeMaster( $enabled = true ) {
+       public function setFakeMaster( $enabled = true ) {
                $this->mFakeMaster = $enabled;
        }
 
@@ -411,7 +411,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function cascadingDeletes() {
+       public function cascadingDeletes() {
                return false;
        }
 
@@ -420,7 +420,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function cleanupTriggers() {
+       public function cleanupTriggers() {
                return false;
        }
 
@@ -430,7 +430,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function strictIPs() {
+       public function strictIPs() {
                return false;
        }
 
@@ -439,7 +439,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
        */
-       function realTimestamps() {
+       public function realTimestamps() {
                return false;
        }
 
@@ -448,7 +448,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function implicitGroupby() {
+       public function implicitGroupby() {
                return true;
        }
 
@@ -458,7 +458,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function implicitOrderby() {
+       public function implicitOrderby() {
                return true;
        }
 
@@ -478,7 +478,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function searchableIPs() {
+       public function searchableIPs() {
                return false;
        }
 
@@ -487,7 +487,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function functionalIndexes() {
+       public function functionalIndexes() {
                return false;
        }
 
@@ -495,7 +495,7 @@ abstract class DatabaseBase implements DatabaseType {
         * Return the last query that went through DatabaseBase::query()
         * @return String
         */
-       function lastQuery() {
+       public function lastQuery() {
                return $this->mLastQuery;
        }
 
@@ -505,7 +505,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function doneWrites() {
+       public function doneWrites() {
                return $this->mDoneWrites;
        }
 
@@ -513,7 +513,7 @@ abstract class DatabaseBase implements DatabaseType {
         * Is a connection to the database open?
         * @return Boolean
         */
-       function isOpen() {
+       public function isOpen() {
                return $this->mOpened;
        }
 
@@ -529,7 +529,7 @@ abstract class DatabaseBase implements DatabaseType {
         *       and removes it in command line mode
         *   - DBO_PERSISTENT: use persistant database connection
         */
-       function setFlag( $flag ) {
+       public function setFlag( $flag ) {
                global $wgDebugDBTransactions;
                $this->mFlags |= $flag;
                if ( ( $flag & DBO_TRX) & $wgDebugDBTransactions ) {
@@ -542,7 +542,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $flag: same as setFlag()'s $flag param
         */
-       function clearFlag( $flag ) {
+       public function clearFlag( $flag ) {
                global $wgDebugDBTransactions;
                $this->mFlags &= ~$flag;
                if ( ( $flag & DBO_TRX ) && $wgDebugDBTransactions ) {
@@ -556,7 +556,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $flag: same as setFlag()'s $flag param
         * @return Boolean
         */
-       function getFlag( $flag ) {
+       public function getFlag( $flag ) {
                return !!( $this->mFlags & $flag );
        }
 
@@ -567,14 +567,14 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return string
         */
-       function getProperty( $name ) {
+       public function getProperty( $name ) {
                return $this->$name;
        }
 
        /**
         * @return string
         */
-       function getWikiID() {
+       public function getWikiID() {
                if ( $this->mTablePrefix ) {
                        return "{$this->mDBname}-{$this->mTablePrefix}";
                } else {
@@ -811,7 +811,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function isWriteQuery( $sql ) {
+       public function isWriteQuery( $sql ) {
                return !preg_match( '/^(?:SELECT|BEGIN|ROLLBACK|COMMIT|SET|SHOW|EXPLAIN|\(SELECT)\b/i', $sql );
        }
 
@@ -957,7 +957,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $fname String
         * @param $tempIgnore Boolean
         */
-       function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
+       public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
                # Ignore errors during error handling to avoid infinite recursion
                $ignore = $this->ignoreErrors( true );
                ++$this->mErrorCount;
@@ -1013,7 +1013,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return ResultWrapper
         */
-       function execute( $prepared, $args = null ) {
+       public function execute( $prepared, $args = null ) {
                if ( !is_array( $args ) ) {
                        # Pull the var args
                        $args = func_get_args();
@@ -1060,7 +1060,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $args Array of arguments to fill it with
         * @return string executable SQL
         */
-       function fillPrepared( $preparedQuery, $args ) {
+       public function fillPrepared( $preparedQuery, $args ) {
                reset( $args );
                $this->preparedArgs =& $args;
 
@@ -1147,7 +1147,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool|mixed The value from the field, or false on failure.
         */
-       function selectField( $table, $var, $cond = '', $fname = 'DatabaseBase::selectField',
+       public function selectField( $table, $var, $cond = '', $fname = 'DatabaseBase::selectField',
                $options = array() )
        {
                if ( !is_array( $options ) ) {
@@ -1180,7 +1180,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @return Array
         * @see DatabaseBase::select()
         */
-       function makeSelectOptions( $options ) {
+       public function makeSelectOptions( $options ) {
                $preLimitTail = $postLimitTail = '';
                $startOpts = '';
 
@@ -1405,7 +1405,7 @@ abstract class DatabaseBase implements DatabaseType {
         *   DBQueryError exception will be thrown, except if the "ignore errors"
         *   option was set, in which case false will be returned.
         */
-       function select( $table, $vars, $conds = '', $fname = 'DatabaseBase::select',
+       public function select( $table, $vars, $conds = '', $fname = 'DatabaseBase::select',
                $options = array(), $join_conds = array() ) {
                $sql = $this->selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
 
@@ -1426,7 +1426,9 @@ abstract class DatabaseBase implements DatabaseType {
         * @return string SQL query string.
         * @see DatabaseBase::select()
         */
-       function selectSQLText( $table, $vars, $conds = '', $fname = 'DatabaseBase::select', $options = array(), $join_conds = array() ) {
+       public function selectSQLText( $table, $vars, $conds = '', $fname = 'DatabaseBase::select',
+               $options = array(), $join_conds = array() )
+       {
                if ( is_array( $vars ) ) {
                        $vars = implode( ',', $vars );
                }
@@ -1491,7 +1493,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return object|bool
         */
-       function selectRow( $table, $vars, $conds, $fname = 'DatabaseBase::selectRow',
+       public function selectRow( $table, $vars, $conds, $fname = 'DatabaseBase::selectRow',
                $options = array(), $join_conds = array() )
        {
                $options = (array)$options;
@@ -1581,7 +1583,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $fname String: calling function name (optional)
         * @return Boolean: whether $table has filed $field
         */
-       function fieldExists( $table, $field, $fname = 'DatabaseBase::fieldExists' ) {
+       public function fieldExists( $table, $field, $fname = 'DatabaseBase::fieldExists' ) {
                $info = $this->fieldInfo( $table, $field );
 
                return (bool)$info;
@@ -1598,7 +1600,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool|null
         */
-       function indexExists( $table, $index, $fname = 'DatabaseBase::indexExists' ) {
+       public function indexExists( $table, $index, $fname = 'DatabaseBase::indexExists' ) {
                $info = $this->indexInfo( $table, $index, $fname );
                if ( is_null( $info ) ) {
                        return null;
@@ -1615,7 +1617,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function tableExists( $table, $fname = __METHOD__ ) {
+       public function tableExists( $table, $fname = __METHOD__ ) {
                $table = $this->tableName( $table );
                $old = $this->ignoreErrors( true );
                $res = $this->query( "SELECT 1 FROM $table LIMIT 1", $fname );
@@ -1630,7 +1632,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $index
         * @return string
         */
-       function fieldType( $res, $index ) {
+       public function fieldType( $res, $index ) {
                if ( $res instanceof ResultWrapper ) {
                        $res = $res->result;
                }
@@ -1646,7 +1648,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function indexUnique( $table, $index ) {
+       public function indexUnique( $table, $index ) {
                $indexInfo = $this->indexInfo( $table, $index );
 
                if ( !$indexInfo ) {
@@ -1699,7 +1701,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function insert( $table, $a, $fname = 'DatabaseBase::insert', $options = array() ) {
+       public function insert( $table, $a, $fname = 'DatabaseBase::insert', $options = array() ) {
                # No rows to insert, easy just return now
                if ( !count( $a ) ) {
                        return true;
@@ -1813,7 +1815,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return string
         */
-       function makeList( $a, $mode = LIST_COMMA ) {
+       public function makeList( $a, $mode = LIST_COMMA ) {
                if ( !is_array( $a ) ) {
                        throw new DBUnexpectedError( $this, 'DatabaseBase::makeList called with incorrect parameters' );
                }
@@ -1873,12 +1875,12 @@ abstract class DatabaseBase implements DatabaseType {
         * The keys on each level may be either integers or strings.
         *
         * @param $data Array: organized as 2-d
-        *              array(baseKeyVal => array(subKeyVal => <ignored>, ...), ...)
+        *              array(baseKeyVal => array(subKeyVal => [ignored], ...), ...)
         * @param $baseKey String: field name to match the base-level keys to (eg 'pl_namespace')
         * @param $subKey String: field name to match the sub-level keys to (eg 'pl_title')
         * @return Mixed: string SQL fragment, or false if no items in array.
         */
-       function makeWhereFrom2d( $data, $baseKey, $subKey ) {
+       public function makeWhereFrom2d( $data, $baseKey, $subKey ) {
                $conds = array();
 
                foreach ( $data as $base => $sub ) {
@@ -1905,7 +1907,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $field
         * @return string
         */
-       function bitNot( $field ) {
+       public function bitNot( $field ) {
                return "(~$field)";
        }
 
@@ -1914,7 +1916,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $fieldRight
         * @return string
         */
-       function bitAnd( $fieldLeft, $fieldRight ) {
+       public function bitAnd( $fieldLeft, $fieldRight ) {
                return "($fieldLeft & $fieldRight)";
        }
 
@@ -1923,7 +1925,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param  $fieldRight
         * @return string
         */
-       function bitOr( $fieldLeft, $fieldRight ) {
+       public function bitOr( $fieldLeft, $fieldRight ) {
                return "($fieldLeft | $fieldRight)";
        }
 
@@ -1936,7 +1938,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool Success or failure
         */
-       function selectDB( $db ) {
+       public function selectDB( $db ) {
                # Stub.  Shouldn't cause serious problems if it's not overridden, but
                # if your database engine supports a concept similar to MySQL's
                # databases you may as well.
@@ -1947,14 +1949,14 @@ abstract class DatabaseBase implements DatabaseType {
        /**
         * Get the current DB name
         */
-       function getDBname() {
+       public function getDBname() {
                return $this->mDBname;
        }
 
        /**
         * Get the server hostname or IP address
         */
-       function getServer() {
+       public function getServer() {
                return $this->mServer;
        }
 
@@ -1975,7 +1977,7 @@ abstract class DatabaseBase implements DatabaseType {
         *   raw - Do not add identifier quotes to the table name
         * @return String: full database name
         */
-       function tableName( $name, $format = 'quoted' ) {
+       public function tableName( $name, $format = 'quoted' ) {
                global $wgSharedDB, $wgSharedPrefix, $wgSharedTables;
                # Skip the entire process when we have a string quoted on both ends.
                # Note that we check the end so that we will still quote any use of
@@ -2209,7 +2211,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return string
         */
-       function addQuotes( $s ) {
+       public function addQuotes( $s ) {
                if ( $s === null ) {
                        return 'NULL';
                } else {
@@ -2301,7 +2303,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @since 1.16
         * @return String: fully built LIKE statement
         */
-       function buildLike() {
+       public function buildLike() {
                $params = func_get_args();
 
                if ( count( $params ) > 0 && is_array( $params[0] ) ) {
@@ -2326,7 +2328,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return LikeMatch
         */
-       function anyChar() {
+       public function anyChar() {
                return new LikeMatch( '_' );
        }
 
@@ -2335,7 +2337,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return LikeMatch
         */
-       function anyString() {
+       public function anyString() {
                return new LikeMatch( '%' );
        }
 
@@ -2350,7 +2352,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $seqName string
         * @return null
         */
-       function nextSequenceValue( $seqName ) {
+       public function nextSequenceValue( $seqName ) {
                return null;
        }
 
@@ -2364,7 +2366,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $index
         * @return string
         */
-       function useIndexClause( $index ) {
+       public function useIndexClause( $index ) {
                return '';
        }
 
@@ -2390,7 +2392,7 @@ abstract class DatabaseBase implements DatabaseType {
         *    a field name or an array of field names
         * @param $fname String: Calling function name (use __METHOD__) for logs/profiling
         */
-       function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabaseBase::replace' ) {
+       public function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabaseBase::replace' ) {
                $quotedTable = $this->tableName( $table );
 
                if ( count( $rows ) == 0 ) {
@@ -2491,7 +2493,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $fname      String: Calling function name (use __METHOD__) for
         *                    logs/profiling
         */
-       function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
+       public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
                $fname = 'DatabaseBase::deleteJoin' )
        {
                if ( !$conds ) {
@@ -2518,7 +2520,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return int
         */
-       function textFieldSize( $table, $field ) {
+       public function textFieldSize( $table, $field ) {
                $table = $this->tableName( $table );
                $sql = "SHOW COLUMNS FROM $table LIKE \"$field\";";
                $res = $this->query( $sql, 'DatabaseBase::textFieldSize' );
@@ -2543,7 +2545,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @return string Returns the text of the low priority option if it is
         *   supported, or a blank string otherwise
         */
-       function lowPriorityOption() {
+       public function lowPriorityOption() {
                return '';
        }
 
@@ -2557,7 +2559,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function delete( $table, $conds, $fname = 'DatabaseBase::delete' ) {
+       public function delete( $table, $conds, $fname = 'DatabaseBase::delete' ) {
                if ( !$conds ) {
                        throw new DBUnexpectedError( $this, 'DatabaseBase::delete() called with no conditions' );
                }
@@ -2598,7 +2600,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return ResultWrapper
         */
-       function insertSelect( $destTable, $srcTable, $varMap, $conds,
+       public function insertSelect( $destTable, $srcTable, $varMap, $conds,
                $fname = 'DatabaseBase::insertSelect',
                $insertOptions = array(), $selectOptions = array() )
        {
@@ -2656,7 +2658,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return string
         */
-       function limitResult( $sql, $limit, $offset = false ) {
+       public function limitResult( $sql, $limit, $offset = false ) {
                if ( !is_numeric( $limit ) ) {
                        throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" );
                }
@@ -2680,7 +2682,7 @@ abstract class DatabaseBase implements DatabaseType {
         * within the UNION construct.
         * @return Boolean
         */
-       function unionSupportsOrderAndLimit() {
+       public function unionSupportsOrderAndLimit() {
                return true; // True for almost every DB supported
        }
 
@@ -2692,7 +2694,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $all Boolean: use UNION ALL
         * @return String: SQL fragment
         */
-       function unionQueries( $sqls, $all ) {
+       public function unionQueries( $sqls, $all ) {
                $glue = $all ? ') UNION ALL (' : ') UNION (';
                return '(' . implode( $glue, $sqls ) . ')';
        }
@@ -2706,7 +2708,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $falseVal String: SQL expression to return if false
         * @return String: SQL fragment
         */
-       function conditional( $cond, $trueVal, $falseVal ) {
+       public function conditional( $cond, $trueVal, $falseVal ) {
                return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
        }
 
@@ -2720,7 +2722,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return string
         */
-       function strreplace( $orig, $old, $new ) {
+       public function strreplace( $orig, $old, $new ) {
                return "REPLACE({$orig}, {$old}, {$new})";
        }
 
@@ -2730,7 +2732,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return int
         */
-       function getServerUptime() {
+       public function getServerUptime() {
                return 0;
        }
 
@@ -2740,7 +2742,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function wasDeadlock() {
+       public function wasDeadlock() {
                return false;
        }
 
@@ -2750,7 +2752,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function wasLockTimeout() {
+       public function wasLockTimeout() {
                return false;
        }
 
@@ -2761,7 +2763,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function wasErrorReissuable() {
+       public function wasErrorReissuable() {
                return false;
        }
 
@@ -2771,7 +2773,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function wasReadOnlyError() {
+       public function wasReadOnlyError() {
                return false;
        }
 
@@ -2793,8 +2795,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool
         */
-       function deadlockLoop() {
-
+       public function deadlockLoop() {
                $this->begin( __METHOD__ );
                $args = func_get_args();
                $function = array_shift( $args );
@@ -2846,7 +2847,7 @@ abstract class DatabaseBase implements DatabaseType {
         *   greater than zero if we waited for some period of time, less than
         *   zero if we timed out.
         */
-       function masterPosWait( DBMasterPos $pos, $timeout ) {
+       public function masterPosWait( DBMasterPos $pos, $timeout ) {
                wfProfileIn( __METHOD__ );
 
                if ( !is_null( $this->mFakeSlaveLag ) ) {
@@ -2879,7 +2880,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return DBMasterPos, or false if this is not a slave.
         */
-       function getSlavePos() {
+       public function getSlavePos() {
                if ( !is_null( $this->mFakeSlaveLag ) ) {
                        $pos = new MySQLMasterPos( 'fake', microtime( true ) - $this->mFakeSlaveLag );
                        wfDebug( __METHOD__ . ": fake slave pos = $pos\n" );
@@ -2895,7 +2896,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return DBMasterPos, or false if this is not a master
         */
-       function getMasterPos() {
+       public function getMasterPos() {
                if ( $this->mFakeMaster ) {
                        return new MySQLMasterPos( 'fake', microtime( true ) );
                } else {
@@ -2908,7 +2909,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $fname string
         */
-       function begin( $fname = 'DatabaseBase::begin' ) {
+       public function begin( $fname = 'DatabaseBase::begin' ) {
                $this->query( 'BEGIN', $fname );
                $this->mTrxLevel = 1;
        }
@@ -2918,7 +2919,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $fname string
         */
-       function commit( $fname = 'DatabaseBase::commit' ) {
+       public function commit( $fname = 'DatabaseBase::commit' ) {
                if ( $this->mTrxLevel ) {
                        $this->query( 'COMMIT', $fname );
                        $this->mTrxLevel = 0;
@@ -2931,7 +2932,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $fname string
         */
-       function rollback( $fname = 'DatabaseBase::rollback' ) {
+       public function rollback( $fname = 'DatabaseBase::rollback' ) {
                if ( $this->mTrxLevel ) {
                        $this->query( 'ROLLBACK', $fname, true );
                        $this->mTrxLevel = 0;
@@ -2952,7 +2953,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $fname String: calling function name
         * @return Boolean: true if operation was successful
         */
-       function duplicateTableStructure( $oldName, $newName, $temporary = false,
+       public function duplicateTableStructure( $oldName, $newName, $temporary = false,
                $fname = 'DatabaseBase::duplicateTableStructure' )
        {
                throw new MWException(
@@ -2980,7 +2981,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return string
         */
-       function timestamp( $ts = 0 ) {
+       public function timestamp( $ts = 0 ) {
                return wfTimestamp( TS_MW, $ts );
        }
 
@@ -2997,7 +2998,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return string
         */
-       function timestampOrNull( $ts = null ) {
+       public function timestampOrNull( $ts = null ) {
                if ( is_null( $ts ) ) {
                        return null;
                } else {
@@ -3020,7 +3021,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool|ResultWrapper
         */
-       function resultObject( $result ) {
+       public function resultObject( $result ) {
                if ( empty( $result ) ) {
                        return false;
                } elseif ( $result instanceof ResultWrapper ) {
@@ -3050,7 +3051,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return bool Success or failure
         */
-       function ping() {
+       public function ping() {
                # Stub.  Not essential to override.
                return true;
        }
@@ -3064,7 +3065,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return int Database replication lag in seconds
         */
-       function getLag() {
+       public function getLag() {
                return intval( $this->mFakeSlaveLag );
        }
 
@@ -3085,7 +3086,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $b string
         * @return string
         */
-       function encodeBlob( $b ) {
+       public function encodeBlob( $b ) {
                return $b;
        }
 
@@ -3096,7 +3097,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $b string
         * @return string
         */
-       function decodeBlob( $b ) {
+       public function decodeBlob( $b ) {
                return $b;
        }
 
@@ -3137,7 +3138,9 @@ abstract class DatabaseBase implements DatabaseType {
         *      generated dynamically using $filename
         * @return bool|string
         */
-       function sourceFile( $filename, $lineCallback = false, $resultCallback = false, $fname = false ) {
+       public function sourceFile(
+               $filename, $lineCallback = false, $resultCallback = false, $fname = false
+       ) {
                wfSuppressWarnings();
                $fp = fopen( $filename, 'r' );
                wfRestoreWarnings();
@@ -3189,7 +3192,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $vars bool|array mapping variable name to value.
         */
-       function setSchemaVars( $vars ) {
+       public function setSchemaVars( $vars ) {
                $this->mSchemaVars = $vars;
        }
 
index 8ce6e70..e1b3a9a 100644 (file)
@@ -810,7 +810,7 @@ class DatabaseOracle extends DatabaseBase {
        /**
         * Return aggregated value function call
         */
-       function aggregateValue ( $valuedata, $valuename = 'value' ) {
+       public function aggregateValue( $valuedata, $valuename = 'value' ) {
                return $valuedata;
        }
 
index 763ca52..2b7521f 100644 (file)
@@ -1037,7 +1037,7 @@ __INDEXATTR__;
        /**
         * Return aggregated value function call
         */
-       function aggregateValue( $valuedata, $valuename = 'value' ) {
+       public function aggregateValue( $valuedata, $valuename = 'value' ) {
                return $valuedata;
        }
 
index d4eef87..3d2ccf4 100644 (file)
@@ -164,18 +164,6 @@ class FileRepo {
                return $status;
        }
 
-       /**
-        * Take all available measures to prevent web accessibility of new deleted
-        * directories, in case the user has not configured offline storage
-        *
-        * @param $dir string
-        * @return void
-        */
-       protected function initDeletedDir( $dir ) {
-               $this->backend->secure( // prevent web access & dir listings
-                       array( 'dir' => $dir, 'noAccess' => true, 'noListing' => true ) );
-       }
-
        /**
         * Determine if a string is an mwrepo:// URL
         *
@@ -726,14 +714,10 @@ class FileRepo {
                        $dstPath = "$root/$dstRel";
                        $dstDir  = dirname( $dstPath );
                        // Create destination directories for this triplet
-                       if ( !$backend->prepare( array( 'dir' => $dstDir ) )->isOK() ) {
+                       if ( !$this->initDirectory( $dstDir )->isOK() ) {
                                return $this->newFatal( 'directorycreateerror', $dstDir );
                        }
 
-                       if ( $dstZone == 'deleted' ) {
-                               $this->initDeletedDir( $dstDir );
-                       }
-
                        // Resolve source to a storage path if virtual
                        $srcPath = $this->resolveToStoragePath( $srcPath );
 
@@ -863,12 +847,13 @@ class FileRepo {
                $operations = array();
                foreach ( $pairs as $pair ) {
                        list ( $src, $dst ) = $pair;
+                       $dst = $this->resolveToStoragePath( $dst );
                        $operations[] = array(
                                'op'        => 'store',
                                'src'       => $src,
-                               'dst'       => $this->resolveToStoragePath( $dst )
+                               'dst'       => $dst
                        );
-                       $this->backend->prepare( array( 'dir' => dirname( $dst ) ) );
+                       $status->merge( $this->initDirectory( dirname( $dst ) ) );
                }
                $status->merge( $this->backend->doQuickOperations( $operations ) );
 
@@ -1058,10 +1043,10 @@ class FileRepo {
                        $dstDir = dirname( $dstPath );
                        $archiveDir = dirname( $archivePath );
                        // Abort immediately on directory creation errors since they're likely to be repetitive
-                       if ( !$backend->prepare( array( 'dir' => $dstDir ) )->isOK() ) {
+                       if ( !$this->initDirectory( $dstDir )->isOK() ) {
                                return $this->newFatal( 'directorycreateerror', $dstDir );
                        }
-                       if ( !$backend->prepare( array( 'dir' => $archiveDir ) )->isOK() ) {
+                       if ( !$this->initDirectory($archiveDir )->isOK() ) {
                                return $this->newFatal( 'directorycreateerror', $archiveDir );
                        }
 
@@ -1126,6 +1111,27 @@ class FileRepo {
                return $status;
        }
 
+       /**
+        * Creates a directory with the appropriate zone permissions.
+        * Callers are responsible for doing read-only and "writable repo" checks.
+        *
+        * @param $dir string Virtual URL (or storage path) of directory to clean
+        * @return Status
+        */
+       protected function initDirectory( $dir ) {
+               $path = $this->resolveToStoragePath( $dir );
+               list( $b, $container, $r ) = FileBackend::splitStoragePath( $path );
+
+               $params = array( 'dir' => $path );
+               if ( $container === $this->zones['deleted']['container'] ) {
+                       # Take all available measures to prevent web accessibility of new deleted
+                       # directories, in case the user has not configured offline storage
+                       $params = array( 'noAccess' => true, 'noListing' => true ) + $params;
+               }
+
+               return $this->backend->prepare( $params );
+       }
+
        /**
         * Deletes a directory if empty.
         *
@@ -1231,10 +1237,9 @@ class FileRepo {
                        $archiveDir = dirname( $archivePath ); // does not touch FS
 
                        // Create destination directories
-                       if ( !$backend->prepare( array( 'dir' => $archiveDir ) )->isOK() ) {
+                       if ( !$this->initDirectory( $archiveDir )->isOK() ) {
                                return $this->newFatal( 'directorycreateerror', $archiveDir );
                        }
-                       $this->initDeletedDir( $archiveDir );
 
                        $operations[] = array(
                                'op'            => 'move',
index c616e16..dd0c947 100644 (file)
@@ -235,7 +235,8 @@ class LocalRepo extends FileRepo {
                        'image',
                        LocalFile::selectFields(),
                        array( 'img_sha1' => $hash ),
-                       __METHOD__
+                       __METHOD__,
+                       array( 'ORDER BY' => 'img_name' )
                );
                
                $result = array();
index 709655a..6b31b7e 100644 (file)
@@ -259,6 +259,7 @@ class RepoGroup {
                foreach ( $this->foreignRepos as $repo ) {
                        $result = array_merge( $result, $repo->findBySha1( $hash ) );
                }
+               usort( $result, 'File::compare' );
                return $result;
        }
 
index 8157916..c953d77 100644 (file)
@@ -49,10 +49,10 @@ class FSFileBackend extends FileBackendStore {
        /**
         * @see FileBackendStore::__construct()
         * Additional $config params include:
-        *    basePath       : File system directory that holds containers.
-        *    containerPaths : Map of container names to custom file system directories.
-        *                     This should only be used for backwards-compatibility.
-        *    fileMode       : Octal UNIX file permissions to use on files stored.
+        *   - basePath       : File system directory that holds containers.
+        *   - containerPaths : Map of container names to custom file system directories.
+        *                      This should only be used for backwards-compatibility.
+        *   - fileMode       : Octal UNIX file permissions to use on files stored.
         */
        public function __construct( array $config ) {
                parent::__construct( $config );
@@ -474,13 +474,18 @@ class FSFileBackend extends FileBackendStore {
                list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
                $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
                $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
+               $existed = is_dir( $dir ); // already there?
                if ( !wfMkdirParents( $dir ) ) { // make directory and its parents
-                       $status->fatal( 'directorycreateerror', $params['dir'] );
+                       $status->fatal( 'directorycreateerror', $params['dir'] ); // fails on races
                } elseif ( !is_writable( $dir ) ) {
                        $status->fatal( 'directoryreadonlyerror', $params['dir'] );
                } elseif ( !is_readable( $dir ) ) {
                        $status->fatal( 'directorynotreadableerror', $params['dir'] );
                }
+               if ( is_dir( $dir ) && !$existed ) {
+                       // Respect any 'noAccess' or 'noListing' flags...
+                       $status->merge( $this->doSecureInternal( $fullCont, $dirRel, $params ) );
+               }
                return $status;
        }
 
@@ -495,21 +500,48 @@ class FSFileBackend extends FileBackendStore {
                $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
                // Seed new directories with a blank index.html, to prevent crawling...
                if ( !empty( $params['noListing'] ) && !file_exists( "{$dir}/index.html" ) ) {
-                       $bytes = file_put_contents( "{$dir}/index.html", '' );
-                       if ( !$bytes ) {
+                       $bytes = file_put_contents( "{$dir}/index.html", $this->indexHtmlPrivate() );
+                       if ( $bytes === false ) {
                                $status->fatal( 'backend-fail-create', $params['dir'] . '/index.html' );
                                return $status;
                        }
                }
                // Add a .htaccess file to the root of the container...
-               if ( !empty( $params['noAccess'] ) ) {
-                       if ( !file_exists( "{$contRoot}/.htaccess" ) ) {
-                               $bytes = file_put_contents( "{$contRoot}/.htaccess", "Deny from all\n" );
-                               if ( !$bytes ) {
-                                       $storeDir = "mwstore://{$this->name}/{$shortCont}";
-                                       $status->fatal( 'backend-fail-create', "{$storeDir}/.htaccess" );
-                                       return $status;
-                               }
+               if ( !empty( $params['noAccess'] ) && !file_exists( "{$contRoot}/.htaccess" ) ) {
+                       $bytes = file_put_contents( "{$contRoot}/.htaccess", $this->htaccessPrivate() );
+                       if ( $bytes === false ) {
+                               $storeDir = "mwstore://{$this->name}/{$shortCont}";
+                               $status->fatal( 'backend-fail-create', "{$storeDir}/.htaccess" );
+                               return $status;
+                       }
+               }
+               return $status;
+       }
+
+       /**
+        * @see FileBackendStore::doPublishInternal()
+        * @return Status
+        */
+       protected function doPublishInternal( $fullCont, $dirRel, array $params ) {
+               $status = Status::newGood();
+               list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
+               $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
+               $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
+               // Unseed new directories with a blank index.html, to allow crawling...
+               if ( !empty( $params['listing'] ) && is_file( "{$dir}/index.html" ) ) {
+                       $exists = ( file_get_contents( "{$dir}/index.html" ) === $this->indexHtmlPrivate() );
+                       if ( $exists && !unlink( "{$dir}/index.html" ) ) { // reverse secure()
+                               $status->fatal( 'backend-fail-delete', $params['dir'] . '/index.html' );
+                               return $status;
+                       }
+               }
+               // Remove the .htaccess file from the root of the container...
+               if ( !empty( $params['access'] ) && is_file( "{$contRoot}/.htaccess" ) ) {
+                       $exists = ( file_get_contents( "{$contRoot}/.htaccess" ) === $this->htaccessPrivate() );
+                       if ( $exists && !unlink( "{$contRoot}/.htaccess" ) ) { // reverse secure()
+                               $storeDir = "mwstore://{$this->name}/{$shortCont}";
+                               $status->fatal( 'backend-fail-delete', "{$storeDir}/.htaccess" );
+                               return $status;
                        }
                }
                return $status;
@@ -716,6 +748,24 @@ class FSFileBackend extends FileBackendStore {
                return $ok;
        }
 
+       /**
+        * Return the text of an index.html file to hide directory listings
+        *
+        * @return string
+        */
+       protected function indexHtmlPrivate() {
+               return '';
+       }
+
+       /**
+        * Return the text of a .htaccess file to make a directory private
+        *
+        * @return string
+        */
+       protected function htaccessPrivate() {
+               return "Deny from all\n";
+       }
+
        /**
         * Clean up directory separators for the given OS
         *
index 3cc9021..ff9329c 100644 (file)
@@ -37,8 +37,9 @@
  * Outside callers can assume that all backends will have these functions.
  *
  * All "storage paths" are of the format "mwstore://<backend>/<container>/<path>".
- * The <path> portion is a relative path that uses UNIX file system (FS) notation,
- * though any particular backend may not actually be using a local filesystem.
+ * The "<path>" portion is a relative path that uses UNIX file system (FS)
+ * notation, though any particular backend may not actually be using a local
+ * filesystem.
  * Therefore, the relative paths are only virtual.
  *
  * Backend contents are stored under wiki-specific container names by default.
@@ -73,19 +74,19 @@ abstract class FileBackend {
         * This should only be called from within FileBackendGroup.
         *
         * $config includes:
-        *     'name'        : The unique name of this backend.
-        *                     This should consist of alphanumberic, '-', and '_' characters.
-        *                     This name should not be changed after use.
-        *     'wikiId'      : Prefix to container names that is unique to this wiki.
-        *                     It should only consist of alphanumberic, '-', and '_' characters.
-        *     'lockManager' : Registered name of a file lock manager to use.
-        *     'fileJournal' : File journal configuration; see FileJournal::factory().
-        *                     Journals simply log changes to files stored in the backend.
-        *     'readOnly'    : Write operations are disallowed if this is a non-empty string.
-        *                     It should be an explanation for the backend being read-only.
-        *     'parallelize' : When to do file operations in parallel (when possible).
-        *                     Allowed values are "implicit", "explicit" and "off".
-        *     'concurrency' : How many file operations can be done in parallel.
+        *   - name        : The unique name of this backend.
+        *                   This should consist of alphanumberic, '-', and '_' characters.
+        *                   This name should not be changed after use.
+        *   - wikiId      : Prefix to container names that is unique to this wiki.
+        *                   It should only consist of alphanumberic, '-', and '_' characters.
+        *   - lockManager : Registered name of a file lock manager to use.
+        *   - fileJournal : File journal configuration; see FileJournal::factory().
+        *                   Journals simply log changes to files stored in the backend.
+        *   - readOnly    : Write operations are disallowed if this is a non-empty string.
+        *                   It should be an explanation for the backend being read-only.
+        *   - parallelize : When to do file operations in parallel (when possible).
+        *                   Allowed values are "implicit", "explicit" and "off".
+        *   - concurrency : How many file operations can be done in parallel.
         *
         * @param $config Array
         * @throws MWException
@@ -102,7 +103,9 @@ abstract class FileBackend {
                        ? $config['lockManager']
                        : LockManagerGroup::singleton()->get( $config['lockManager'] );
                $this->fileJournal = isset( $config['fileJournal'] )
-                       ? FileJournal::factory( $config['fileJournal'], $this->name )
+                       ? ( ( $config['fileJournal'] instanceof FileJournal )
+                               ? $config['fileJournal']
+                               : FileJournal::factory( $config['fileJournal'], $this->name ) )
                        : FileJournal::factory( array( 'class' => 'NullFileJournal' ), $this->name );
                $this->readOnly = isset( $config['readOnly'] )
                        ? (string)$config['readOnly']
@@ -153,16 +156,27 @@ abstract class FileBackend {
         * $ops is an array of arrays. The outer array holds a list of operations.
         * Each inner array is a set of key value pairs that specify an operation.
         *
-        * Supported operations and their parameters:
+        * Supported operations and their parameters. The supported actions are:
+        *  - create
+        *  - store
+        *  - copy
+        *  - move
+        *  - delete
+        *  - null
+        *
         * a) Create a new file in storage with the contents of a string
+        * @code
         *     array(
         *         'op'                  => 'create',
         *         'dst'                 => <storage path>,
         *         'content'             => <string of new file contents>,
         *         'overwrite'           => <boolean>,
         *         'overwriteSame'       => <boolean>
-        *     )
+        *     );
+        * @endcode
+        *
         * b) Copy a file system file into storage
+        * @code
         *     array(
         *         'op'                  => 'store',
         *         'src'                 => <file system path>,
@@ -170,7 +184,10 @@ abstract class FileBackend {
         *         'overwrite'           => <boolean>,
         *         'overwriteSame'       => <boolean>
         *     )
+        * @endcode
+        *
         * c) Copy a file within storage
+        * @code
         *     array(
         *         'op'                  => 'copy',
         *         'src'                 => <storage path>,
@@ -178,7 +195,10 @@ abstract class FileBackend {
         *         'overwrite'           => <boolean>,
         *         'overwriteSame'       => <boolean>
         *     )
+        * @endcode
+        *
         * d) Move a file within storage
+        * @code
         *     array(
         *         'op'                  => 'move',
         *         'src'                 => <storage path>,
@@ -186,57 +206,68 @@ abstract class FileBackend {
         *         'overwrite'           => <boolean>,
         *         'overwriteSame'       => <boolean>
         *     )
+        * @endcode
+        *
         * e) Delete a file within storage
+        * @code
         *     array(
         *         'op'                  => 'delete',
         *         'src'                 => <storage path>,
         *         'ignoreMissingSource' => <boolean>
         *     )
+        * @endcode
+        *
         * f) Do nothing (no-op)
+        * @code
         *     array(
         *         'op'                  => 'null',
         *     )
+        * @endcode
         *
         * Boolean flags for operations (operation-specific):
-        * 'ignoreMissingSource' : The operation will simply succeed and do
-        *                         nothing if the source file does not exist.
-        * 'overwrite'           : Any destination file will be overwritten.
-        * 'overwriteSame'       : An error will not be given if a file already
-        *                         exists at the destination that has the same
-        *                         contents as the new contents to be written there.
+        *   - ignoreMissingSource : The operation will simply succeed and do
+        *                           nothing if the source file does not exist.
+        *   - overwrite           : Any destination file will be overwritten.
+        *   - overwriteSame       : An error will not be given if a file already
+        *                           exists at the destination that has the same
+        *                           contents as the new contents to be written there.
         *
         * $opts is an associative of boolean flags, including:
-        * 'force'               : Operation precondition errors no longer trigger an abort.
-        *                         Any remaining operations are still attempted. Unexpected
-        *                         failures may still cause remaning operations to be aborted.
-        * 'nonLocking'          : No locks are acquired for the operations.
-        *                         This can increase performance for non-critical writes.
-        *                         This has no effect unless the 'force' flag is set.
-        * 'allowStale'          : Don't require the latest available data.
-        *                         This can increase performance for non-critical writes.
-        *                         This has no effect unless the 'force' flag is set.
-        * 'nonJournaled'        : Don't log this operation batch in the file journal.
-        *                         This limits the ability of recovery scripts.
-        * 'parallelize'         : Try to do operations in parallel when possible.
-        *
-        * Remarks on locking:
+        *   - force               : Operation precondition errors no longer trigger an abort.
+        *                           Any remaining operations are still attempted. Unexpected
+        *                           failures may still cause remaning operations to be aborted.
+        *   - nonLocking          : No locks are acquired for the operations.
+        *                           This can increase performance for non-critical writes.
+        *                           This has no effect unless the 'force' flag is set.
+        *   - allowStale          : Don't require the latest available data.
+        *                           This can increase performance for non-critical writes.
+        *                           This has no effect unless the 'force' flag is set.
+        *   - nonJournaled        : Don't log this operation batch in the file journal.
+        *                           This limits the ability of recovery scripts.
+        *   - parallelize         : Try to do operations in parallel when possible.
+        *   - bypassReadOnly      : Allow writes in read-only mode (since 1.20).
+        *
+        * @remarks Remarks on locking:
         * File system paths given to operations should refer to files that are
         * already locked or otherwise safe from modification from other processes.
         * Normally these files will be new temp files, which should be adequate.
         *
-        * Return value:
+        * @par Return value:
+        *
         * This returns a Status, which contains all warnings and fatals that occured
         * during the operation. The 'failCount', 'successCount', and 'success' members
-        * will reflect each operation attempted. The status will be "OK" unless:
-        *     a) unexpected operation errors occurred (network partitions, disk full...)
-        *     b) significant operation errors occured and 'force' was not set
+        * will reflect each operation attempted.
+        *
+        * The status will be "OK" unless:
+        *   - a) unexpected operation errors occurred (network partitions, disk full...)
+        *   - b) significant operation errors occured and 'force' was not set
         *
         * @param $ops Array List of operations to execute in order
         * @param $opts Array Batch operation options
         * @return Status
         */
        final public function doOperations( array $ops, array $opts = array() ) {
-               if ( $this->isReadOnly() ) {
+               if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
                if ( empty( $opts['force'] ) ) { // sanity
@@ -354,57 +385,80 @@ abstract class FileBackend {
         * This should *only* be used on non-original files, like cache files.
         *
         * Supported operations and their parameters:
+        *  - create
+        *  - store
+        *  - copy
+        *  - move
+        *  - delete
+        *  - null
+        *
         * a) Create a new file in storage with the contents of a string
+        * @code
         *     array(
         *         'op'                  => 'create',
         *         'dst'                 => <storage path>,
         *         'content'             => <string of new file contents>
         *     )
+        * @endcode
         * b) Copy a file system file into storage
+        * @code
         *     array(
         *         'op'                  => 'store',
         *         'src'                 => <file system path>,
         *         'dst'                 => <storage path>
         *     )
+        * @endcode
         * c) Copy a file within storage
+        * @code
         *     array(
         *         'op'                  => 'copy',
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>
         *     )
+        * @endcode
         * d) Move a file within storage
+        * @code
         *     array(
         *         'op'                  => 'move',
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>
         *     )
+        * @endcode
         * e) Delete a file within storage
+        * @code
         *     array(
         *         'op'                  => 'delete',
         *         'src'                 => <storage path>,
         *         'ignoreMissingSource' => <boolean>
         *     )
+        * @endcode
         * f) Do nothing (no-op)
+        * @code
         *     array(
         *         'op'                  => 'null',
         *     )
+        * @endcode
         *
-        * Boolean flags for operations (operation-specific):
-        * 'ignoreMissingSource' : The operation will simply succeed and do
-        *                         nothing if the source file does not exist.
+        * @par Boolean flags for operations (operation-specific):
+        *   - ignoreMissingSource : The operation will simply succeed and do
+        *                           nothing if the source file does not exist.
+        *
+        * $opts is an associative of boolean flags, including:
+        *   - bypassReadOnly      : Allow writes in read-only mode (since 1.20)
         *
-        * Return value:
+        * @par Return value:
         * This returns a Status, which contains all warnings and fatals that occured
         * during the operation. The 'failCount', 'successCount', and 'success' members
         * will reflect each operation attempted for the given files. The status will be
         * considered "OK" as long as no fatal errors occured.
         *
         * @param $ops Array Set of operations to execute
+        * @param $opts Array Batch operation options
         * @return Status
         * @since 1.20
         */
-       final public function doQuickOperations( array $ops ) {
-               if ( $this->isReadOnly() ) {
+       final public function doQuickOperations( array $ops, array $opts = array() ) {
+               if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
                foreach ( $ops as &$op ) {
@@ -508,11 +562,11 @@ abstract class FileBackend {
         * The target path should refer to a file that is already locked or
         * otherwise safe from modification from other processes. Normally,
         * the file will be a new temp file, which should be adequate.
-        * $params include:
-        *     srcs : ordered source storage paths (e.g. chunk1, chunk2, ...)
-        *     dst  : file system path to 0-byte temp file
         *
         * @param $params Array Operation parameters
+        * $params include:
+        *   - srcs : ordered source storage paths (e.g. chunk1, chunk2, ...)
+        *   - dst  : file system path to 0-byte temp file
         * @return Status
         */
        abstract public function concatenate( array $params );
@@ -522,14 +576,20 @@ abstract class FileBackend {
         * This will create any required containers and parent directories.
         * Backends using key/value stores only need to create the container.
         *
-        * $params include:
-        *     dir : storage directory
+        * The 'noAccess' and 'noListing' parameters works the same as in secure(),
+        * except they are only applied *if* the directory/container had to be created.
+        * These flags should always be set for directories that have private files.
         *
         * @param $params Array
+        * $params include:
+        *   - dir            : storage directory
+        *   - noAccess       : try to deny file access (since 1.20)
+        *   - noListing      : try to deny file listing (since 1.20)
+        *   - bypassReadOnly : allow writes in read-only mode (since 1.20)
         * @return Status
         */
        final public function prepare( array $params ) {
-               if ( $this->isReadOnly() ) {
+               if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
                return $this->doPrepare( $params );
@@ -543,27 +603,23 @@ abstract class FileBackend {
        /**
         * Take measures to block web access to a storage directory and
         * the container it belongs to. FS backends might add .htaccess
-        * files whereas key/value store backends might restrict container
-        * access to the auth user that represents end-users in web request.
+        * files whereas key/value store backends might revoke container
+        * access to the storage user representing end-users in web requests.
         * This is not guaranteed to actually do anything.
         *
-        * $params include:
-        *     dir       : storage directory
-        *     noAccess  : try to deny file access
-        *     noListing : try to deny file listing
-        *
         * @param $params Array
+        * $params include:
+        *   - dir            : storage directory
+        *   - noAccess       : try to deny file access
+        *   - noListing      : try to deny file listing
+        *   - bypassReadOnly : allow writes in read-only mode (since 1.20)
         * @return Status
         */
        final public function secure( array $params ) {
-               if ( $this->isReadOnly() ) {
+               if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
-               $status = $this->doPrepare( $params ); // dir must exist to restrict it
-               if ( $status->isOK() ) {
-                       $status->merge( $this->doSecure( $params ) );
-               }
-               return $status;
+               return $this->doSecure( $params );
        }
 
        /**
@@ -571,20 +627,48 @@ abstract class FileBackend {
         */
        abstract protected function doSecure( array $params );
 
+       /**
+        * Remove measures to block web access to a storage directory and
+        * the container it belongs to. FS backends might remove .htaccess
+        * files whereas key/value store backends might grant container
+        * access to the storage user representing end-users in web requests.
+        * This essentially can undo the result of secure() calls.
+        *
+        * @param $params Array
+        * $params include:
+        *   - dir            : storage directory
+        *   - access         : try to allow file access
+        *   - listing        : try to allow file listing
+        *   - bypassReadOnly : allow writes in read-only mode (since 1.20)
+        * @return Status
+        * @since 1.20
+        */
+       final public function publish( array $params ) {
+               if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
+                       return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
+               }
+               return $this->doPublish( $params );
+       }
+
+       /**
+        * @see FileBackend::publish()
+        */
+       abstract protected function doPublish( array $params );
+
        /**
         * Delete a storage directory if it is empty.
         * Backends using key/value stores may do nothing unless the directory
         * is that of an empty container, in which case it should be deleted.
         *
-        * $params include:
-        *     dir       : storage directory
-        *     recursive : recursively delete empty subdirectories first (@since 1.20)
-        *
         * @param $params Array
+        * $params include:
+        *   - dir            : storage directory
+        *   - recursive      : recursively delete empty subdirectories first (since 1.20)
+        *   - bypassReadOnly : allow writes in read-only mode (since 1.20)
         * @return Status
         */
        final public function clean( array $params ) {
-               if ( $this->isReadOnly() ) {
+               if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
                return $this->doClean( $params );
@@ -599,11 +683,10 @@ abstract class FileBackend {
         * Check if a file exists at a storage path in the backend.
         * This returns false if only a directory exists at the path.
         *
-        * $params include:
-        *     src    : source storage path
-        *     latest : use the latest available data
-        *
         * @param $params Array
+        * $params include:
+        *   - src    : source storage path
+        *   - latest : use the latest available data
         * @return bool|null Returns null on failure
         */
        abstract public function fileExists( array $params );
@@ -611,11 +694,10 @@ abstract class FileBackend {
        /**
         * Get the last-modified timestamp of the file at a storage path.
         *
-        * $params include:
-        *     src    : source storage path
-        *     latest : use the latest available data
-        *
         * @param $params Array
+        * $params include:
+        *   - src    : source storage path
+        *   - latest : use the latest available data
         * @return string|bool TS_MW timestamp or false on failure
         */
        abstract public function getFileTimestamp( array $params );
@@ -624,11 +706,10 @@ abstract class FileBackend {
         * Get the contents of a file at a storage path in the backend.
         * This should be avoided for potentially large files.
         *
-        * $params include:
-        *     src    : source storage path
-        *     latest : use the latest available data
-        *
         * @param $params Array
+        * $params include:
+        *   - src    : source storage path
+        *   - latest : use the latest available data
         * @return string|bool Returns false on failure
         */
        abstract public function getFileContents( array $params );
@@ -636,11 +717,10 @@ abstract class FileBackend {
        /**
         * Get the size (bytes) of a file at a storage path in the backend.
         *
-        * $params include:
-        *     src    : source storage path
-        *     latest : use the latest available data
-        *
         * @param $params Array
+        * $params include:
+        *   - src    : source storage path
+        *   - latest : use the latest available data
         * @return integer|bool Returns false on failure
         */
        abstract public function getFileSize( array $params );
@@ -649,15 +729,14 @@ abstract class FileBackend {
         * Get quick information about a file at a storage path in the backend.
         * If the file does not exist, then this returns false.
         * Otherwise, the result is an associative array that includes:
-        *     mtime  : the last-modified timestamp (TS_MW)
-        *     size   : the file size (bytes)
+        *   - mtime  : the last-modified timestamp (TS_MW)
+        *   - size   : the file size (bytes)
         * Additional values may be included for internal use only.
         *
-        * $params include:
-        *     src    : source storage path
-        *     latest : use the latest available data
-        *
         * @param $params Array
+        * $params include:
+        *   - src    : source storage path
+        *   - latest : use the latest available data
         * @return Array|bool|null Returns null on failure
         */
        abstract public function getFileStat( array $params );
@@ -665,11 +744,10 @@ abstract class FileBackend {
        /**
         * Get a SHA-1 hash of the file at a storage path in the backend.
         *
-        * $params include:
-        *     src    : source storage path
-        *     latest : use the latest available data
-        *
         * @param $params Array
+        * $params include:
+        *   - src    : source storage path
+        *   - latest : use the latest available data
         * @return string|bool Hash string or false on failure
         */
        abstract public function getFileSha1Base36( array $params );
@@ -678,11 +756,10 @@ abstract class FileBackend {
         * Get the properties of the file at a storage path in the backend.
         * Returns FSFile::placeholderProps() on failure.
         *
-        * $params include:
-        *     src    : source storage path
-        *     latest : use the latest available data
-        *
         * @param $params Array
+        * $params include:
+        *   - src    : source storage path
+        *   - latest : use the latest available data
         * @return Array
         */
        abstract public function getFileProps( array $params );
@@ -694,12 +771,11 @@ abstract class FileBackend {
         * must be sent if streaming began, while none should be sent otherwise.
         * Implementations should flush the output buffer before sending data.
         *
-        * $params include:
-        *     src     : source storage path
-        *     headers : additional HTTP headers to send on success
-        *     latest  : use the latest available data
-        *
         * @param $params Array
+        * $params include:
+        *   - src     : source storage path
+        *   - headers : additional HTTP headers to send on success
+        *   - latest  : use the latest available data
         * @return Status
         */
        abstract public function streamFile( array $params );
@@ -707,9 +783,9 @@ abstract class FileBackend {
        /**
         * Returns a file system file, identical to the file at a storage path.
         * The file returned is either:
-        * a) A local copy of the file at a storage path in the backend.
-        *    The temporary copy will have the same extension as the source.
-        * b) An original of the file at a storage path in the backend.
+        *   - a) A local copy of the file at a storage path in the backend.
+        *        The temporary copy will have the same extension as the source.
+        *   - b) An original of the file at a storage path in the backend.
         * Temporary files may be purged when the file object falls out of scope.
         *
         * Write operations should *never* be done on this file as some backends
@@ -717,11 +793,10 @@ abstract class FileBackend {
         * In that later case, there are copies of the file that must stay in sync.
         * Additionally, further calls to this function may return the same file.
         *
-        * $params include:
-        *     src    : source storage path
-        *     latest : use the latest available data
-        *
         * @param $params Array
+        * $params include:
+        *   - src    : source storage path
+        *   - latest : use the latest available data
         * @return FSFile|null Returns null on failure
         */
        abstract public function getLocalReference( array $params );
@@ -731,11 +806,10 @@ abstract class FileBackend {
         * The temporary copy will have the same file extension as the source.
         * Temporary files may be purged when the file object falls out of scope.
         *
-        * $params include:
-        *     src    : source storage path
-        *     latest : use the latest available data
-        *
         * @param $params Array
+        * $params include:
+        *   - src    : source storage path
+        *   - latest : use the latest available data
         * @return TempFSFile|null Returns null on failure
         */
        abstract public function getLocalCopy( array $params );
@@ -747,10 +821,9 @@ abstract class FileBackend {
         *
         * Storage backends with eventual consistency might return stale data.
         *
-        * $params include:
-        *     dir : storage directory
-        *
         * @param $params array
+        * $params include:
+        *   - dir : storage directory
         * @return bool|null Returns null on failure
         * @since 1.20
         */
@@ -766,11 +839,10 @@ abstract class FileBackend {
         *
         * Storage backends with eventual consistency might return stale data.
         *
-        * $params include:
-        *     dir     : storage directory
-        *     topOnly : only return direct child dirs of the directory
-        *
         * @param $params array
+        * $params include:
+        *   - dir     : storage directory
+        *   - topOnly : only return direct child dirs of the directory
         * @return Traversable|Array|null Returns null on failure
         * @since 1.20
         */
@@ -782,10 +854,9 @@ abstract class FileBackend {
         *
         * Storage backends with eventual consistency might return stale data.
         *
-        * $params include:
-        *     dir : storage directory
-        *
         * @param $params array
+        * $params include:
+        *   - dir : storage directory
         * @return Traversable|Array|null Returns null on failure
         * @since 1.20
         */
@@ -803,11 +874,10 @@ abstract class FileBackend {
         *
         * Storage backends with eventual consistency might return stale data.
         *
-        * $params include:
-        *     dir     : storage directory
-        *     topOnly : only return direct child files of the directory (@since 1.20)
-        *
         * @param $params array
+        * $params include:
+        *   - dir     : storage directory
+        *   - topOnly : only return direct child files of the directory (since 1.20)
         * @return Traversable|Array|null Returns null on failure
         */
        abstract public function getFileList( array $params );
@@ -818,10 +888,9 @@ abstract class FileBackend {
         *
         * Storage backends with eventual consistency might return stale data.
         *
-        * $params include:
-        *     dir : storage directory
-        *
         * @param $params array
+        * $params include:
+        *   - dir : storage directory
         * @return Traversable|Array|null Returns null on failure
         * @since 1.20
         */
index efc6053..e0873d2 100644 (file)
@@ -48,22 +48,27 @@ class FileBackendMultiWrite extends FileBackend {
        /* Possible internal backend consistency checks */
        const CHECK_SIZE = 1;
        const CHECK_TIME = 2;
+       const CHECK_SHA1 = 4;
 
        /**
         * Construct a proxy backend that consists of several internal backends.
+        * Locking, journaling, and read-only checks are handled by the proxy backend.
+        *
         * Additional $config params include:
-        *     'backends'    : Array of backend config and multi-backend settings.
-        *                     Each value is the config used in the constructor of a
-        *                     FileBackendStore class, but with these additional settings:
-        *                         'class'         : The name of the backend class
-        *                         'isMultiMaster' : This must be set for one backend.
-        *                         'template:      : If given a backend name, this will use
-        *                                           the config of that backend as a template.
-        *                                           Values specified here take precedence.
-        *     'syncChecks'  : Integer bitfield of internal backend sync checks to perform.
-        *                     Possible bits include self::CHECK_SIZE and self::CHECK_TIME.
-        *                     The checks are done before allowing any file operations.
+        *   - backends   : Array of backend config and multi-backend settings.
+        *                  Each value is the config used in the constructor of a
+        *                  FileBackendStore class, but with these additional settings:
+        *                    - class         : The name of the backend class
+        *                    - isMultiMaster : This must be set for one backend.
+        *                    - template:     : If given a backend name, this will use
+        *                                      the config of that backend as a template.
+        *                                      Values specified here take precedence.
+        *   - syncChecks : Integer bitfield of internal backend sync checks to perform.
+        *                  Possible bits include the FileBackendMultiWrite::CHECK_* constants.
+        *                  There are constants for SIZE, TIME, and SHA1.
+        *                  The checks are done before allowing any file operations.
         * @param $config Array
+        * @throws MWException
         */
        public function __construct( array $config ) {
                parent::__construct( $config );
@@ -73,7 +78,7 @@ class FileBackendMultiWrite extends FileBackend {
                foreach ( $config['backends'] as $index => $config ) {
                        if ( isset( $config['template'] ) ) {
                                // Config is just a modified version of a registered backend's.
-                               // This should only be used when that config is used only be this backend.
+                               // This should only be used when that config is used only by this backend.
                                $config = $config + FileBackendGroup::singleton()->config( $config['template'] );
                        }
                        $name = $config['name'];
@@ -81,17 +86,24 @@ class FileBackendMultiWrite extends FileBackend {
                                throw new MWException( "Two or more backends defined with the name $name." );
                        }
                        $namesUsed[$name] = 1;
-                       if ( !isset( $config['class'] ) ) {
-                               throw new MWException( 'No class given for a backend config.' );
-                       }
-                       $class = $config['class'];
-                       $this->backends[$index] = new $class( $config );
+                       // Alter certain sub-backend settings for sanity
+                       unset( $config['readOnly'] ); // use proxy backend setting
+                       unset( $config['fileJournal'] ); // use proxy backend journal
+                       $config['wikiId'] = $this->wikiId; // use the proxy backend wiki ID
+                       $config['lockManager'] = 'nullLockManager'; // lock under proxy backend
                        if ( !empty( $config['isMultiMaster'] ) ) {
                                if ( $this->masterIndex >= 0 ) {
                                        throw new MWException( 'More than one master backend defined.' );
                                }
-                               $this->masterIndex = $index;
+                               $this->masterIndex = $index; // this is the "master"
+                               $config['fileJournal'] = $this->fileJournal; // log under proxy backend
+                       }
+                       // Create sub-backend object
+                       if ( !isset( $config['class'] ) ) {
+                               throw new MWException( 'No class given for a backend config.' );
                        }
+                       $class = $config['class'];
+                       $this->backends[$index] = new $class( $config );
                }
                if ( $this->masterIndex < 0 ) { // need backends and must have a master
                        throw new MWException( 'No master backend defined.' );
@@ -108,27 +120,14 @@ class FileBackendMultiWrite extends FileBackend {
        final protected function doOperationsInternal( array $ops, array $opts ) {
                $status = Status::newGood();
 
-               $performOps = array(); // list of FileOp objects
-               $paths = array(); // storage paths read from or written to
-               // Build up a list of FileOps. The list will have all the ops
-               // for one backend, then all the ops for the next, and so on.
-               // These batches of ops are all part of a continuous array.
-               // Also build up a list of files read/changed...
-               foreach ( $this->backends as $index => $backend ) {
-                       $backendOps = $this->substOpBatchPaths( $ops, $backend );
-                       // Add on the operation batch for this backend
-                       $performOps = array_merge( $performOps,
-                               $backend->getOperationsInternal( $backendOps ) );
-                       if ( $index == 0 ) { // first batch
-                               // Get the files used for these operations. Each backend has a batch of
-                               // the same operations, so we only need to get them from the first batch.
-                               $paths = $backend->getPathsToLockForOpsInternal( $performOps );
-                               // Get the paths under the proxy backend's name
-                               $paths['sh'] = $this->unsubstPaths( $paths['sh'] );
-                               $paths['ex'] = $this->unsubstPaths( $paths['ex'] );
-                       }
-               }
+               $mbe = $this->backends[$this->masterIndex]; // convenience
 
+               // Get the paths to lock from the master backend
+               $realOps = $this->substOpBatchPaths( $ops, $mbe );
+               $paths = $mbe->getPathsToLockForOpsInternal( $mbe->getOperationsInternal( $realOps ) );
+               // Get the paths under the proxy backend's name
+               $paths['sh'] = $this->unsubstPaths( $paths['sh'] );
+               $paths['ex'] = $this->unsubstPaths( $paths['ex'] );
                // Try to lock those files for the scope of this function...
                if ( empty( $opts['nonLocking'] ) ) {
                        // Try to lock those files for the scope of this function...
@@ -138,46 +137,29 @@ class FileBackendMultiWrite extends FileBackend {
                                return $status; // abort
                        }
                }
-
                // Clear any cache entries (after locks acquired)
                $this->clearCache();
-
                // Do a consistency check to see if the backends agree
-               if ( count( $this->backends ) > 1 ) {
-                       $status->merge( $this->consistencyCheck( array_merge( $paths['sh'], $paths['ex'] ) ) );
-                       if ( !$status->isOK() ) {
-                               return $status; // abort
+               $status->merge( $this->consistencyCheck( array_merge( $paths['sh'], $paths['ex'] ) ) );
+               if ( !$status->isOK() ) {
+                       return $status; // abort
+               }
+               // Actually attempt the operation batch on the master backend...
+               $masterStatus = $mbe->doOperations( $realOps, $opts );
+               $status->merge( $masterStatus );
+               // Propagate the operations to the clone backends...
+               foreach ( $this->backends as $index => $backend ) {
+                       if ( $index !== $this->masterIndex ) { // not done already
+                               $realOps = $this->substOpBatchPaths( $ops, $backend );
+                               $status->merge( $backend->doOperations( $realOps, $opts ) );
                        }
                }
-
-               // Actually attempt the operation batch...
-               $subStatus = FileOpBatch::attempt( $performOps, $opts, $this->fileJournal );
-
-               $success = array();
-               $failCount = 0;
-               $successCount = 0;
                // Make 'success', 'successCount', and 'failCount' fields reflect
                // the overall operation, rather than all the batches for each backend.
                // Do this by only using success values from the master backend's batch.
-               $batchStart = $this->masterIndex * count( $ops );
-               $batchEnd = $batchStart + count( $ops ) - 1;
-               for ( $i = $batchStart; $i <= $batchEnd; $i++ ) {
-                       if ( !isset( $subStatus->success[$i] ) ) {
-                               break; // failed out before trying this op
-                       } elseif ( $subStatus->success[$i] ) {
-                               ++$successCount;
-                       } else {
-                               ++$failCount;
-                       }
-                       $success[] = $subStatus->success[$i];
-               }
-               $subStatus->success = $success;
-               $subStatus->successCount = $successCount;
-               $subStatus->failCount = $failCount;
-
-               // Merge errors into status fields
-               $status->merge( $subStatus );
-               $status->success = $subStatus->success; // not done in merge()
+               $status->success = $masterStatus->success;
+               $status->successCount = $masterStatus->successCount;
+               $status->failCount = $masterStatus->failCount;
 
                return $status;
        }
@@ -190,21 +172,29 @@ class FileBackendMultiWrite extends FileBackend {
         */
        public function consistencyCheck( array $paths ) {
                $status = Status::newGood();
-               if ( $this->syncChecks == 0 ) {
+               if ( $this->syncChecks == 0 || count( $this->backends ) <= 1 ) {
                        return $status; // skip checks
                }
 
                $mBackend = $this->backends[$this->masterIndex];
                foreach ( array_unique( $paths ) as $path ) {
                        $params = array( 'src' => $path, 'latest' => true );
+                       $mParams = $this->substOpPaths( $params, $mBackend );
                        // Stat the file on the 'master' backend
-                       $mStat = $mBackend->getFileStat( $this->substOpPaths( $params, $mBackend ) );
+                       $mStat = $mBackend->getFileStat( $mParams );
+                       if ( $this->syncChecks & self::CHECK_SHA1 ) {
+                               $mSha1 = $mBackend->getFileSha1( $mParams );
+                       } else {
+                               $mSha1 = false;
+                       }
+                       $mUsable = $mBackend->isPathUsableInternal( $mParams['src'] );
                        // Check of all clone backends agree with the master...
                        foreach ( $this->backends as $index => $cBackend ) {
                                if ( $index === $this->masterIndex ) {
                                        continue; // master
                                }
-                               $cStat = $cBackend->getFileStat( $this->substOpPaths( $params, $cBackend ) );
+                               $cParams = $this->substOpPaths( $params, $cBackend );
+                               $cStat = $cBackend->getFileStat( $cParams );
                                if ( $mStat ) { // file is in master
                                        if ( !$cStat ) { // file should exist
                                                $status->fatal( 'backend-fail-synced', $path );
@@ -224,11 +214,20 @@ class FileBackendMultiWrite extends FileBackend {
                                                        continue;
                                                }
                                        }
+                                       if ( $this->syncChecks & self::CHECK_SHA1 ) {
+                                               if ( $cBackend->getFileSha1( $cParams ) !== $mSha1 ) { // wrong SHA1
+                                                       $status->fatal( 'backend-fail-synced', $path );
+                                                       continue;
+                                               }
+                                       }
                                } else { // file is not in master
                                        if ( $cStat ) { // file should not exist
                                                $status->fatal( 'backend-fail-synced', $path );
                                        }
                                }
+                               if ( $mUsable !== $cBackend->isPathUsableInternal( $cParams['src'] ) ) {
+                                       $status->fatal( 'backend-fail-synced', $path );
+                               }
                        }
                }
 
@@ -302,10 +301,12 @@ class FileBackendMultiWrite extends FileBackend {
         * @see FileBackend::doQuickOperationsInternal()
         * @return Status
         */
-       public function doQuickOperationsInternal( array $ops ) {
+       protected function doQuickOperationsInternal( array $ops ) {
+               $status = Status::newGood();
                // Do the operations on the master backend; setting Status fields...
                $realOps = $this->substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
-               $status = $this->backends[$this->masterIndex]->doQuickOperations( $realOps );
+               $masterStatus = $this->backends[$this->masterIndex]->doQuickOperations( $realOps );
+               $status->merge( $masterStatus );
                // Propagate the operations to the clone backends...
                foreach ( $this->backends as $index => $backend ) {
                        if ( $index !== $this->masterIndex ) { // not done already
@@ -313,6 +314,12 @@ class FileBackendMultiWrite extends FileBackend {
                                $status->merge( $backend->doQuickOperations( $realOps ) );
                        }
                }
+               // Make 'success', 'successCount', and 'failCount' fields reflect
+               // the overall operation, rather than all the batches for each backend.
+               // Do this by only using success values from the master backend's batch.
+               $status->success = $masterStatus->success;
+               $status->successCount = $masterStatus->successCount;
+               $status->failCount = $masterStatus->failCount;
                return $status;
        }
 
@@ -343,6 +350,20 @@ class FileBackendMultiWrite extends FileBackend {
                return $status;
        }
 
+       /**
+        * @see FileBackend::doPublish()
+        * @param $params array
+        * @return Status
+        */
+       protected function doPublish( array $params ) {
+               $status = Status::newGood();
+               foreach ( $this->backends as $backend ) {
+                       $realParams = $this->substOpPaths( $params, $backend );
+                       $status->merge( $backend->doPublish( $realParams ) );
+               }
+               return $status;
+       }
+
        /**
         * @see FileBackend::doClean()
         * @param $params array
index 5fe33ba..852a653 100644 (file)
 abstract class FileBackendStore extends FileBackend {
        /** @var BagOStuff */
        protected $memCache;
-
-       /** @var Array Map of paths to small (RAM/disk) cache items */
-       protected $cache = array(); // (storage path => key => value)
-       protected $maxCacheSize = 300; // integer; max paths with entries
-       /** @var Array Map of paths to large (RAM/disk) cache items */
-       protected $expensiveCache = array(); // (storage path => key => value)
-       protected $maxExpensiveCacheSize = 5; // integer; max paths with entries
+       /** @var ProcessCacheLRU */
+       protected $cheapCache; // Map of paths to small (RAM/disk) cache items
+       /** @var ProcessCacheLRU */
+       protected $expensiveCache; // Map of paths to large (RAM/disk) cache items
 
        /** @var Array Map of container names to sharding settings */
        protected $shardViaHashLevels = array(); // (container name => config array)
@@ -58,7 +55,9 @@ abstract class FileBackendStore extends FileBackend {
         */
        public function __construct( array $config ) {
                parent::__construct( $config );
-               $this->memCache = new EmptyBagOStuff(); // disabled by default
+               $this->memCache       = new EmptyBagOStuff(); // disabled by default
+               $this->cheapCache     = new ProcessCacheLRU( 300 );
+               $this->expensiveCache = new ProcessCacheLRU( 5 );
        }
 
        /**
@@ -87,10 +86,10 @@ abstract class FileBackendStore extends FileBackend {
         * Do not call this function from places outside FileBackend and FileOp.
         *
         * $params include:
-        *     content       : the raw file contents
-        *     dst           : destination storage path
-        *     overwrite     : overwrite any file that exists at the destination
-        *     async         : Status will be returned immediately if supported.
+        *   - content       : the raw file contents
+        *   - dst           : destination storage path
+        *   - overwrite     : overwrite any file that exists at the destination
+        *   - async         : Status will be returned immediately if supported.
         *                     If the status is OK, then its value field will be
         *                     set to a FileBackendStoreOpHandle object.
         *
@@ -123,10 +122,10 @@ abstract class FileBackendStore extends FileBackend {
         * Do not call this function from places outside FileBackend and FileOp.
         *
         * $params include:
-        *     src           : source path on disk
-        *     dst           : destination storage path
-        *     overwrite     : overwrite any file that exists at the destination
-        *     async         : Status will be returned immediately if supported.
+        *   - src           : source path on disk
+        *   - dst           : destination storage path
+        *   - overwrite     : overwrite any file that exists at the destination
+        *   - async         : Status will be returned immediately if supported.
         *                     If the status is OK, then its value field will be
         *                     set to a FileBackendStoreOpHandle object.
         *
@@ -159,10 +158,10 @@ abstract class FileBackendStore extends FileBackend {
         * Do not call this function from places outside FileBackend and FileOp.
         *
         * $params include:
-        *     src           : source storage path
-        *     dst           : destination storage path
-        *     overwrite     : overwrite any file that exists at the destination
-        *     async         : Status will be returned immediately if supported.
+        *   - src           : source storage path
+        *   - dst           : destination storage path
+        *   - overwrite     : overwrite any file that exists at the destination
+        *   - async         : Status will be returned immediately if supported.
         *                     If the status is OK, then its value field will be
         *                     set to a FileBackendStoreOpHandle object.
         *
@@ -190,9 +189,9 @@ abstract class FileBackendStore extends FileBackend {
         * Do not call this function from places outside FileBackend and FileOp.
         *
         * $params include:
-        *     src                 : source storage path
-        *     ignoreMissingSource : do nothing if the source file does not exist
-        *     async               : Status will be returned immediately if supported.
+        *   - src                 : source storage path
+        *   - ignoreMissingSource : do nothing if the source file does not exist
+        *   - async               : Status will be returned immediately if supported.
         *                           If the status is OK, then its value field will be
         *                           set to a FileBackendStoreOpHandle object.
         *
@@ -220,10 +219,10 @@ abstract class FileBackendStore extends FileBackend {
         * Do not call this function from places outside FileBackend and FileOp.
         *
         * $params include:
-        *     src           : source storage path
-        *     dst           : destination storage path
-        *     overwrite     : overwrite any file that exists at the destination
-        *     async         : Status will be returned immediately if supported.
+        *   - src           : source storage path
+        *   - dst           : destination storage path
+        *   - overwrite     : overwrite any file that exists at the destination
+        *   - async         : Status will be returned immediately if supported.
         *                     If the status is OK, then its value field will be
         *                     set to a FileBackendStoreOpHandle object.
         *
@@ -426,6 +425,46 @@ abstract class FileBackendStore extends FileBackend {
                return Status::newGood();
        }
 
+       /**
+        * @see FileBackend::doPublish()
+        * @return Status
+        */
+       final protected function doPublish( array $params ) {
+               wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
+               $status = Status::newGood();
+
+               list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
+               if ( $dir === null ) {
+                       $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
+                       wfProfileOut( __METHOD__ . '-' . $this->name );
+                       wfProfileOut( __METHOD__ );
+                       return $status; // invalid storage path
+               }
+
+               if ( $shard !== null ) { // confined to a single container/shard
+                       $status->merge( $this->doPublishInternal( $fullCont, $dir, $params ) );
+               } else { // directory is on several shards
+                       wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
+                       list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
+                       foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
+                               $status->merge( $this->doPublishInternal( "{$fullCont}{$suffix}", $dir, $params ) );
+                       }
+               }
+
+               wfProfileOut( __METHOD__ . '-' . $this->name );
+               wfProfileOut( __METHOD__ );
+               return $status;
+       }
+
+       /**
+        * @see FileBackendStore::doPublish()
+        * @return Status
+        */
+       protected function doPublishInternal( $container, $dir, array $params ) {
+               return Status::newGood();
+       }
+
        /**
         * @see FileBackend::doClean()
         * @return Status
@@ -539,17 +578,17 @@ abstract class FileBackendStore extends FileBackend {
                wfProfileIn( __METHOD__ );
                wfProfileIn( __METHOD__ . '-' . $this->name );
                $latest = !empty( $params['latest'] ); // use latest data?
-               if ( !isset( $this->cache[$path]['stat'] ) ) {
+               if ( !$this->cheapCache->has( $path, 'stat' ) ) {
                        $this->primeFileCache( array( $path ) ); // check persistent cache
                }
-               if ( isset( $this->cache[$path]['stat'] ) ) {
+               if ( $this->cheapCache->has( $path, 'stat' ) ) {
+                       $stat = $this->cheapCache->get( $path, 'stat' );
                        // If we want the latest data, check that this cached
                        // value was in fact fetched with the latest available data.
-                       if ( !$latest || $this->cache[$path]['stat']['latest'] ) {
-                               $this->pingCache( $path ); // LRU
+                       if ( !$latest || $stat['latest'] ) {
                                wfProfileOut( __METHOD__ . '-' . $this->name );
                                wfProfileOut( __METHOD__ );
-                               return $this->cache[$path]['stat'];
+                               return $stat;
                        }
                }
                wfProfileIn( __METHOD__ . '-miss' );
@@ -559,13 +598,11 @@ abstract class FileBackendStore extends FileBackend {
                wfProfileOut( __METHOD__ . '-miss' );
                if ( is_array( $stat ) ) { // don't cache negatives
                        $stat['latest'] = $latest;
-                       $this->trimCache(); // limit memory
-                       $this->cache[$path]['stat'] = $stat;
+                       $this->cheapCache->set( $path, 'stat', $stat );
                        $this->setFileCache( $path, $stat ); // update persistent cache
                        if ( isset( $stat['sha1'] ) ) { // some backends store SHA-1 as metadata
-                               $this->trimCache(); // limit memory
-                               $this->cache[$path]['sha1'] =
-                                       array( 'hash' => $stat['sha1'], 'latest' => $latest );
+                               $this->cheapCache->set( $path, 'sha1',
+                                       array( 'hash' => $stat['sha1'], 'latest' => $latest ) );
                        }
                } else {
                        wfDebug( __METHOD__ . ": File $path does not exist.\n" );
@@ -613,14 +650,14 @@ abstract class FileBackendStore extends FileBackend {
                wfProfileIn( __METHOD__ );
                wfProfileIn( __METHOD__ . '-' . $this->name );
                $latest = !empty( $params['latest'] ); // use latest data?
-               if ( isset( $this->cache[$path]['sha1'] ) ) {
+               if ( $this->cheapCache->has( $path, 'sha1' ) ) {
+                       $stat = $this->cheapCache->get( $path, 'sha1' );
                        // If we want the latest data, check that this cached
                        // value was in fact fetched with the latest available data.
-                       if ( !$latest || $this->cache[$path]['sha1']['latest'] ) {
-                               $this->pingCache( $path ); // LRU
+                       if ( !$latest || $stat['latest'] ) {
                                wfProfileOut( __METHOD__ . '-' . $this->name );
                                wfProfileOut( __METHOD__ );
-                               return $this->cache[$path]['sha1']['hash'];
+                               return $stat['hash'];
                        }
                }
                wfProfileIn( __METHOD__ . '-miss' );
@@ -629,8 +666,8 @@ abstract class FileBackendStore extends FileBackend {
                wfProfileOut( __METHOD__ . '-miss-' . $this->name );
                wfProfileOut( __METHOD__ . '-miss' );
                if ( $hash ) { // don't cache negatives
-                       $this->trimCache(); // limit memory
-                       $this->cache[$path]['sha1'] = array( 'hash' => $hash, 'latest' => $latest );
+                       $this->cheapCache->set( $path, 'sha1',
+                               array( 'hash' => $hash, 'latest' => $latest ) );
                }
                wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
@@ -676,21 +713,20 @@ abstract class FileBackendStore extends FileBackend {
                wfProfileIn( __METHOD__ );
                wfProfileIn( __METHOD__ . '-' . $this->name );
                $latest = !empty( $params['latest'] ); // use latest data?
-               if ( isset( $this->expensiveCache[$path]['localRef'] ) ) {
+               if ( $this->expensiveCache->has( $path, 'localRef' ) ) {
+                       $val = $this->expensiveCache->get( $path, 'localRef' );
                        // If we want the latest data, check that this cached
                        // value was in fact fetched with the latest available data.
-                       if ( !$latest || $this->expensiveCache[$path]['localRef']['latest'] ) {
-                               $this->pingExpensiveCache( $path );
+                       if ( !$latest || $val['latest'] ) {
                                wfProfileOut( __METHOD__ . '-' . $this->name );
                                wfProfileOut( __METHOD__ );
-                               return $this->expensiveCache[$path]['localRef']['object'];
+                               return $val['object'];
                        }
                }
                $tmpFile = $this->getLocalCopy( $params );
                if ( $tmpFile ) { // don't cache negatives
-                       $this->trimExpensiveCache(); // limit memory
-                       $this->expensiveCache[$path]['localRef'] =
-                               array( 'object' => $tmpFile, 'latest' => $latest );
+                       $this->expensiveCache->set( $path, 'localRef',
+                               array( 'object' => $tmpFile, 'latest' => $latest ) );
                }
                wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
@@ -930,7 +966,7 @@ abstract class FileBackendStore extends FileBackend {
         * @see FileBackend::doOperationsInternal()
         * @return Status
         */
-       protected function doOperationsInternal( array $ops, array $opts ) {
+       final protected function doOperationsInternal( array $ops, array $opts ) {
                wfProfileIn( __METHOD__ );
                wfProfileIn( __METHOD__ . '-' . $this->name );
                $status = Status::newGood();
@@ -1079,12 +1115,12 @@ abstract class FileBackendStore extends FileBackend {
                        $paths = array_filter( $paths, 'strlen' ); // remove nulls
                }
                if ( $paths === null ) {
-                       $this->cache = array();
-                       $this->expensiveCache = array();
+                       $this->cheapCache->clear();
+                       $this->expensiveCache->clear();
                } else {
                        foreach ( $paths as $path ) {
-                               unset( $this->cache[$path] );
-                               unset( $this->expensiveCache[$path] );
+                               $this->cheapCache->clear( $path );
+                               $this->expensiveCache->clear( $path );
                        }
                }
                $this->doClearCache( $paths );
@@ -1109,58 +1145,6 @@ abstract class FileBackendStore extends FileBackend {
         */
        abstract protected function directoriesAreVirtual();
 
-       /**
-        * Move a cache entry to the top (such as when accessed)
-        *
-        * @param $path string Storage path
-        * @return void
-        */
-       protected function pingCache( $path ) {
-               if ( isset( $this->cache[$path] ) ) {
-                       $tmp = $this->cache[$path];
-                       unset( $this->cache[$path] );
-                       $this->cache[$path] = $tmp;
-               }
-       }
-
-       /**
-        * Prune the inexpensive cache if it is too big to add an item
-        *
-        * @return void
-        */
-       protected function trimCache() {
-               if ( count( $this->cache ) >= $this->maxCacheSize ) {
-                       reset( $this->cache );
-                       unset( $this->cache[key( $this->cache )] );
-               }
-       }
-
-       /**
-        * Move a cache entry to the top (such as when accessed)
-        *
-        * @param $path string Storage path
-        * @return void
-        */
-       protected function pingExpensiveCache( $path ) {
-               if ( isset( $this->expensiveCache[$path] ) ) {
-                       $tmp = $this->expensiveCache[$path];
-                       unset( $this->expensiveCache[$path] );
-                       $this->expensiveCache[$path] = $tmp;
-               }
-       }
-
-       /**
-        * Prune the expensive cache if it is too big to add an item
-        *
-        * @return void
-        */
-       protected function trimExpensiveCache() {
-               if ( count( $this->expensiveCache ) >= $this->maxExpensiveCacheSize ) {
-                       reset( $this->expensiveCache );
-                       unset( $this->expensiveCache[key( $this->expensiveCache )] );
-               }
-       }
-
        /**
         * Check if a container name is valid.
         * This checks for for length and illegal characters.
@@ -1515,12 +1499,10 @@ abstract class FileBackendStore extends FileBackend {
                foreach ( $values as $cacheKey => $val ) {
                        if ( is_array( $val ) ) {
                                $path = $pathNames[$cacheKey];
-                               $this->trimCache(); // limit memory
-                               $this->cache[$path]['stat'] = $val;
+                               $this->cheapCache->set( $path, 'stat', $val );
                                if ( isset( $val['sha1'] ) ) { // some backends store SHA-1 as metadata
-                                       $this->trimCache(); // limit memory
-                                       $this->cache[$path]['sha1'] =
-                                               array( 'hash' => $val['sha1'], 'latest' => $val['latest'] );
+                                       $this->cheapCache->set( $path, 'sha1',
+                                               array( 'hash' => $val['sha1'], 'latest' => $val['latest'] ) );
                                }
                        }
                }
index ac2496d..fa87c3a 100644 (file)
@@ -432,10 +432,10 @@ abstract class FileOp {
 /**
  * Store a file into the backend from a file on the file system.
  * Parameters similar to FileBackendStore::storeInternal(), which include:
- *     src           : source path on file system
- *     dst           : destination storage path
- *     overwrite     : do nothing and pass if an identical file exists at destination
- *     overwriteSame : override any existing file at destination
+ *   - src           : source path on file system
+ *   - dst           : destination storage path
+ *   - overwrite     : do nothing and pass if an identical file exists at destination
+ *   - overwriteSame : override any existing file at destination
  */
 class StoreFileOp extends FileOp {
        /**
@@ -509,10 +509,10 @@ class StoreFileOp extends FileOp {
 /**
  * Create a file in the backend with the given content.
  * Parameters similar to FileBackendStore::createInternal(), which include:
- *     content       : the raw file contents
- *     dst           : destination storage path
- *     overwrite     : do nothing and pass if an identical file exists at destination
- *     overwriteSame : override any existing file at destination
+ *   - content       : the raw file contents
+ *   - dst           : destination storage path
+ *   - overwrite     : do nothing and pass if an identical file exists at destination
+ *   - overwriteSame : override any existing file at destination
  */
 class CreateFileOp extends FileOp {
        protected function allowedParams() {
@@ -572,10 +572,10 @@ class CreateFileOp extends FileOp {
 /**
  * Copy a file from one storage path to another in the backend.
  * Parameters similar to FileBackendStore::copyInternal(), which include:
- *     src           : source storage path
- *     dst           : destination storage path
- *     overwrite     : do nothing and pass if an identical file exists at destination
- *     overwriteSame : override any existing file at destination
+ *   - src           : source storage path
+ *   - dst           : destination storage path
+ *   - overwrite     : do nothing and pass if an identical file exists at destination
+ *   - overwriteSame : override any existing file at destination
  */
 class CopyFileOp extends FileOp {
        /**
@@ -643,10 +643,10 @@ class CopyFileOp extends FileOp {
 /**
  * Move a file from one storage path to another in the backend.
  * Parameters similar to FileBackendStore::moveInternal(), which include:
- *     src           : source storage path
- *     dst           : destination storage path
- *     overwrite     : do nothing and pass if an identical file exists at destination
- *     overwriteSame : override any existing file at destination
+ *   - src           : source storage path
+ *   - dst           : destination storage path
+ *   - overwrite     : do nothing and pass if an identical file exists at destination
+ *   - overwriteSame : override any existing file at destination
  */
 class MoveFileOp extends FileOp {
        /**
@@ -720,8 +720,8 @@ class MoveFileOp extends FileOp {
 /**
  * Delete a file at the given storage path from the backend.
  * Parameters similar to FileBackendStore::deleteInternal(), which include:
- *     src                 : source storage path
- *     ignoreMissingSource : don't return an error if the file does not exist
+ *   - src                 : source storage path
+ *   - ignoreMissingSource : don't return an error if the file does not exist
  */
 class DeleteFileOp extends FileOp {
        /**
index d22a2ec..fd83e2c 100644 (file)
@@ -40,17 +40,17 @@ class FileOpBatch {
         * Callers are responsible for handling file locking.
         *
         * $opts is an array of options, including:
-        * 'force'        : Errors that would normally cause a rollback do not.
-        *                  The remaining operations are still attempted if any fail.
-        * 'allowStale'   : Don't require the latest available data.
-        *                  This can increase performance for non-critical writes.
-        *                  This has no effect unless the 'force' flag is set.
-        * 'nonJournaled' : Don't log this operation batch in the file journal.
-        * 'concurrency'  : Try to do this many operations in parallel when possible.
+        *   - force        : Errors that would normally cause a rollback do not.
+        *                    The remaining operations are still attempted if any fail.
+        *   - allowStale   : Don't require the latest available data.
+        *                    This can increase performance for non-critical writes.
+        *                    This has no effect unless the 'force' flag is set.
+        *   - nonJournaled : Don't log this operation batch in the file journal.
+        *   - concurrency  : Try to do this many operations in parallel when possible.
         *
         * The resulting Status will be "OK" unless:
-        *     a) unexpected operation errors occurred (network partitions, disk full...)
-        *     b) significant operation errors occured and 'force' was not set
+        *   - a) unexpected operation errors occurred (network partitions, disk full...)
+        *   - b) significant operation errors occured and 'force' was not set
         *
         * @param $performOps Array List of FileOp operations
         * @param $opts Array Batch operation options
index 2b10917..94ddb44 100644 (file)
@@ -56,22 +56,25 @@ class SwiftFileBackend extends FileBackendStore {
        /**
         * @see FileBackendStore::__construct()
         * Additional $config params include:
-        *    swiftAuthUrl       : Swift authentication server URL
-        *    swiftUser          : Swift user used by MediaWiki (account:username)
-        *    swiftKey           : Swift authentication key for the above user
-        *    swiftAuthTTL       : Swift authentication TTL (seconds)
-        *    swiftAnonUser      : Swift user used for end-user requests (account:username)
-        *    swiftUseCDN        : Whether a Cloud Files Content Delivery Network is set up
-        *    swiftCDNExpiry     : How long (in seconds) to store content in the CDN.
-        *                         If files may likely change, this should probably not exceed
-        *                         a few days. For example, deletions may take this long to apply.
-        *                         If object purging is enabled, however, this is not an issue.
-        *    swiftCDNPurgable   : Whether object purge requests are allowed by the CDN.
-        *    shardViaHashLevels : Map of container names to sharding config with:
-        *                         'base'   : base of hash characters, 16 or 36
-        *                         'levels' : the number of hash levels (and digits)
-        *                         'repeat' : hash subdirectories are prefixed with all the
-        *                                    parent hash directory names (e.g. "a/ab/abc")
+        *   - swiftAuthUrl       : Swift authentication server URL
+        *   - swiftUser          : Swift user used by MediaWiki (account:username)
+        *   - swiftKey           : Swift authentication key for the above user
+        *   - swiftAuthTTL       : Swift authentication TTL (seconds)
+        *   - swiftAnonUser      : Swift user used for end-user requests (account:username).
+        *                          If set, then views of public containers are assumed to go
+        *                          through this user. If not set, then public containers are
+        *                          accessible to unauthenticated requests via ".r:*" in the ACL.
+        *   - swiftUseCDN        : Whether a Cloud Files Content Delivery Network is set up
+        *   - swiftCDNExpiry     : How long (in seconds) to store content in the CDN.
+        *                          If files may likely change, this should probably not exceed
+        *                          a few days. For example, deletions may take this long to apply.
+        *                          If object purging is enabled, however, this is not an issue.
+        *   - swiftCDNPurgable   : Whether object purge requests are allowed by the CDN.
+        *   - shardViaHashLevels : Map of container names to sharding config with:
+        *                             - base   : base of hash characters, 16 or 36
+        *                             - levels : the number of hash levels (and digits)
+        *                             - repeat : hash subdirectories are prefixed with all the
+        *                                        parent hash directory names (e.g. "a/ab/abc")
         */
        public function __construct( array $config ) {
                parent::__construct( $config );
@@ -524,13 +527,12 @@ class SwiftFileBackend extends FileBackendStore {
                // (b) Create container as needed
                try {
                        $contObj = $this->createContainer( $fullCont );
-                       // Make container public to end-users...
-                       if ( $this->swiftAnonUser != '' ) {
-                               $status->merge( $this->setContainerAccess(
-                                       $contObj,
-                                       array( $this->auth->username, $this->swiftAnonUser ), // read
-                                       array( $this->auth->username ) // write
-                               ) );
+                       if ( !empty( $params['noAccess'] ) ) {
+                               // Make container private to end-users...
+                               $status->merge( $this->doSecureInternal( $fullCont, $dir, $params ) );
+                       } else {
+                               // Make container public to end-users...
+                               $status->merge( $this->doPublishInternal( $fullCont, $dir, $params ) );
                        }
                        if ( $this->swiftUseCDN ) { // Rackspace style CDN
                                $contObj->make_public( $this->swiftCDNExpiry );
@@ -551,6 +553,9 @@ class SwiftFileBackend extends FileBackendStore {
         */
        protected function doSecureInternal( $fullCont, $dir, array $params ) {
                $status = Status::newGood();
+               if ( empty( $params['noAccess'] ) ) {
+                       return $status; // nothing to do
+               }
 
                // Restrict container from end-users...
                try {
@@ -560,18 +565,53 @@ class SwiftFileBackend extends FileBackendStore {
                        // NoSuchContainerException not thrown: container must exist
 
                        // Make container private to end-users...
-                       if ( $this->swiftAnonUser != '' && !isset( $contObj->mw_wasSecured ) ) {
+                       $status->merge( $this->setContainerAccess(
+                               $contObj,
+                               array( $this->auth->username ), // read
+                               array( $this->auth->username ) // write
+                       ) );
+                       if ( $this->swiftUseCDN && $contObj->is_public() ) { // Rackspace style CDN
+                               $contObj->make_private();
+                       }
+               } catch ( CDNNotEnabledException $e ) {
+                       // CDN not enabled; nothing to see here
+               } catch ( CloudFilesException $e ) { // some other exception?
+                       $this->handleException( $e, $status, __METHOD__, $params );
+               }
+
+               return $status;
+       }
+
+       /**
+        * @see FileBackendStore::doPublishInternal()
+        * @return Status
+        */
+       protected function doPublishInternal( $fullCont, $dir, array $params ) {
+               $status = Status::newGood();
+
+               // Unrestrict container from end-users...
+               try {
+                       // doPrepareInternal() should have been called,
+                       // so the Swift container should already exist...
+                       $contObj = $this->getContainer( $fullCont ); // normally a cache hit
+                       // NoSuchContainerException not thrown: container must exist
+
+                       // Make container public to end-users...
+                       if ( $this->swiftAnonUser != '' ) {
+                               $status->merge( $this->setContainerAccess(
+                                       $contObj,
+                                       array( $this->auth->username, $this->swiftAnonUser ), // read
+                                       array( $this->auth->username, $this->swiftAnonUser ) // write
+                               ) );
+                       } else {
                                $status->merge( $this->setContainerAccess(
                                        $contObj,
-                                       array( $this->auth->username ), // read
+                                       array( $this->auth->username, '.r:*' ), // read
                                        array( $this->auth->username ) // write
                                ) );
-                               // @TODO: when php-cloudfiles supports container
-                               // metadata, we can make use of that to avoid RTTs
-                               $contObj->mw_wasSecured = true; // avoid useless RTTs
                        }
-                       if ( $this->swiftUseCDN && $contObj->is_public() ) { // Rackspace style CDN
-                               $contObj->make_private();
+                       if ( $this->swiftUseCDN && !$contObj->is_public() ) { // Rackspace style CDN
+                               $contObj->make_public();
                        }
                } catch ( CDNNotEnabledException $e ) {
                        // CDN not enabled; nothing to see here
@@ -1008,11 +1048,29 @@ class SwiftFileBackend extends FileBackendStore {
        }
 
        /**
-        * Set read/write permissions for a Swift container
+        * Set read/write permissions for a Swift container.
+        *
+        * $readGrps is a list of the possible criteria for a request to have
+        * access to read a container. Each item is one of the following formats:
+        *   - account:user       : Grants access if the request is by the given user
+        *   - .r:<regex>         : Grants access if the request is from a referrer host that
+        *                          matches the expression and the request is not for a listing.
+        *                          Setting this to '*' effectively makes a container public.
+        *   - .rlistings:<regex> : Grants access if the request is from a referrer host that
+        *                          matches the expression and the request for a listing.
+        * 
+        * $writeGrps is a list of the possible criteria for a request to have
+        * access to write to a container. Each item is of the following format:
+        *   - account:user       : Grants access if the request is by the given user
+        *
+        * @see http://swift.openstack.org/misc.html#acls
+        *
+        * In general, we don't allow listings to end-users. It's not useful, isn't well-defined
+        * (lists are truncated to 10000 item with no way to page), and is just a performance risk.
         *
         * @param $contObj CF_Container Swift container
-        * @param $readGrps Array Swift users who can read (account:user)
-        * @param $writeGrps Array Swift users who can write (account:user)
+        * @param $readGrps Array List of read access routes
+        * @param $writeGrps Array List of write access routes
         * @return Status
         */
        protected function setContainerAccess(
index 024e11b..8942818 100644 (file)
@@ -57,14 +57,14 @@ class LSLockManager extends QuorumLockManager {
         * Construct a new instance from configuration.
         *
         * $config paramaters include:
-        *     'lockServers'  : Associative array of server names to configuration.
-        *                      Configuration is an associative array that includes:
-        *                      'host'    - IP address/hostname
-        *                      'port'    - TCP port
-        *                      'authKey' - Secret string the lock server uses
-        *     'srvsByBucket' : Array of 1-16 consecutive integer keys, starting from 0,
-        *                      each having an odd-numbered list of server names (peers) as values.
-        *     'connTimeout'  : Lock server connection attempt timeout. [optional]
+        *   - lockServers  : Associative array of server names to configuration.
+        *                    Configuration is an associative array that includes:
+        *                      - host    : IP address/hostname
+        *                      - port    : TCP port
+        *                      - authKey : Secret string the lock server uses
+        *   - srvsByBucket : Array of 1-16 consecutive integer keys, starting from 0,
+        *                    each having an odd-numbered list of server names (peers) as values.
+        *   - connTimeout  : Lock server connection attempt timeout. [optional]
         *
         * @param Array $config
         */
@@ -82,7 +82,7 @@ class LSLockManager extends QuorumLockManager {
                        $this->connTimeout = 3; // use some sane amount
                }
 
-               $this->session = wfRandomString( 31 );
+               $this->session = wfRandomString( 32 ); // 128 bits
        }
 
        /**
index add1f2c..9e81dbf 100644 (file)
@@ -56,12 +56,12 @@ class MemcLockManager extends QuorumLockManager {
         * Construct a new instance from configuration.
         *
         * $config paramaters include:
-        *     'lockServers'  : Associative array of server names to <IP>:<port> strings.
-        *     'srvsByBucket' : Array of 1-16 consecutive integer keys, starting from 0,
+        *   - 'lockServers'  : Associative array of server names to "<IP>:<port>" strings.
+        *   - 'srvsByBucket' : Array of 1-16 consecutive integer keys, starting from 0,
         *                      each having an odd-numbered list of server names (peers) as values.
-        *     'memcConfig'   : Configuration array for ObjectCache::newFromParams. [optional]
+        *   - 'memcConfig'   : Configuration array for ObjectCache::newFromParams. [optional]
         *                      If set, this must use one of the memcached classes.
-        *     'wikiId'       : Wiki ID string that all resources are relative to. [optional]
+        *   - 'wikiId'       : Wiki ID string that all resources are relative to. [optional]
         *
         * @param Array $config
         */
@@ -92,7 +92,7 @@ class MemcLockManager extends QuorumLockManager {
                $met = ini_get( 'max_execution_time' ); // this is 0 in CLI mode
                $this->lockExpiry = $met ? 2*(int)$met : 2*3600;
 
-               $this->session = wfRandomString( 31 );
+               $this->session = wfRandomString( 32 );
        }
 
        /**
@@ -264,11 +264,24 @@ class MemcLockManager extends QuorumLockManager {
        protected function acquireMutexes( MemcachedBagOStuff $memc, array $keys ) {
                $lockedKeys = array();
 
+               // Acquire the keys in lexicographical order, to avoid deadlock problems.
+               // If P1 is waiting to acquire a key P2 has, P2 can't also be waiting for a key P1 has.
+               sort( $keys );
+
+               // Try to quickly loop to acquire the keys, but back off after a few rounds.
+               // This reduces memcached spam, especially in the rare case where a server acquires
+               // some lock keys and dies without releasing them. Lock keys expire after a few minutes.
+               $rounds = 0;
                $start = microtime( true );
                do {
+                       if ( ( ++$rounds % 4 ) == 0 ) {
+                               usleep( 1000*50 ); // 50 ms
+                       }
                        foreach ( array_diff( $keys, $lockedKeys ) as $key ) {
                                if ( $memc->add( "$key:mutex", 1, 180 ) ) { // lock record
                                        $lockedKeys[] = $key;
+                               } else {
+                                       continue; // acquire in order
                                }
                        }
                } while ( count( $lockedKeys ) < count( $keys ) && ( microtime( true ) - $start ) <= 6 );
index 065679a..3fa8166 100644 (file)
@@ -245,6 +245,18 @@ abstract class File {
                }
        }
 
+       /**
+        * Callback for usort() to do file sorts by title
+        *
+        * @param $a File
+        * @param $b File
+        *
+        * @return Integer: result of title comparison
+        */
+       public static function compare( File $a, File $b ) {
+               return Title::compare( $a->getTitle(), $b->getTitle() );
+       }
+
        /**
         * Return the name of this file
         *
@@ -1606,7 +1618,7 @@ abstract class File {
        }
 
        /**
-        * Get the deletion archive key, <sha1>.<ext>
+        * Get the deletion archive key, "<sha1>.<ext>"
         *
         * @return string
         */
index 67768c4..4db1f5f 100644 (file)
@@ -633,7 +633,7 @@ class LocalFile extends File {
 
        /**
         * Fix thumbnail files from 1.4 or before, with extreme prejudice
-        * @TODO: do we still care about this? Perhaps a maintenance script
+        * @todo : do we still care about this? Perhaps a maintenance script
         *        can be made instead. Enabling this code results in a serious
         *        RTT regression for wikis without 404 handling.
         */
index 12a84a1..d87f294 100644 (file)
@@ -558,7 +558,7 @@ abstract class Installer {
         * write your messages. This appears to work well enough. Basic formatting and
         * external links work just fine.
         *
-        * But in case a translator decides to throw in a #ifexist or internal link or
+        * But in case a translator decides to throw in a "#ifexist" or internal link or
         * whatever, this function is guarded to catch the attempted DB access and to present
         * some fallback text.
         *
index aabc1f3..84d115b 100644 (file)
@@ -153,7 +153,7 @@ class WebInstallerOutput {
        }
 
        /**
-        * <link> to index.php?css=foobar for the <head>
+        * "<link>" to index.php?css=foobar for the "<head>"
         * @return String
         */
        private function getCssUrl( ) {
index b6e7717..c10fe88 100644 (file)
@@ -232,7 +232,7 @@ class WebInstaller_Language extends WebInstallerPage {
        }
 
        /**
-        * Get a <select> for selecting languages.
+        * Get a "<select>" for selecting languages.
         *
         * @param $name
         * @param $label
index 4e02258..b6f54ea 100644 (file)
@@ -36,7 +36,7 @@ class DoubleRedirectJob extends Job {
 
        /**
         * Insert jobs into the job queue to fix redirects to the given title
-        * @param $reason String: the reason for the fix, see message double-redirect-fixed-<reason>
+        * @param $reason String: the reason for the fix, see message "double-redirect-fixed-<reason>"
         * @param $redirTitle Title: the title which has changed, redirects pointing to this title are fixed
         * @param $destTitle bool Not used
         */
index cae3f12..d3cc550 100644 (file)
@@ -60,7 +60,7 @@ class FormatJson {
         *
         * @return Mixed: the value encoded in json in appropriate PHP type.
         * Values true, false and null (case-insensitive) are returned as true, false
-        * and &null; respectively. &null; is returned if the json cannot be
+        * and "&null;" respectively. "&null;" is returned if the json cannot be
         * decoded or if the encoded data is deeper than the recursion limit.
         */
        public static function decode( $value, $assoc = false ) {
index ff5fc8a..d13591c 100644 (file)
@@ -359,12 +359,12 @@ class LogEventsList extends ContextSource {
        }
 
        /**
-        * @TODO: split up!
+        * @todo split up!
         *
         * @param  $row
         * @param Title $title
         * @param Array $paramArray
-        * @param  $comment
+        * @param String $comment Passed by reference
         * @return String
         */
        private function logActionLinks( $row, $title, $paramArray, &$comment ) {
@@ -493,9 +493,9 @@ class LogEventsList extends ContextSource {
                }
                $del = '';
                $user = $this->getUser();
-               // Don't show useless checkbox to people who cannot hide revisions
+               // Don't show useless checkbox to people who cannot hide log entries
                if( $user->isAllowed( 'deletedhistory' ) ) {
-                       if( $row->log_deleted || $user->isAllowed( 'deleterevision' ) ) {
+                       if( $row->log_deleted || $user->isAllowed( 'deletelogentry' ) ) {
                                $canHide = $user->isAllowed( 'deleterevision' );
                                if ( $this->flags & self::USE_REVDEL_CHECKBOXES ) { // Show checkboxes instead of links.
                                        if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $user ) ) { // If event was hidden from sysops
index 93f3f83..a7c803d 100644 (file)
@@ -509,11 +509,9 @@ class LogFormatter {
                        );
 
                        if ( $this->linkFlood ) {
-                               $element .= Linker::userToolLinks(
+                               $element .= Linker::userToolLinksRedContribs(
                                        $user->getId(),
                                        $user->getName(),
-                                       true, // Red if no edits
-                                       0, // Flags
                                        $user->getEditCount()
                                );
                        }
index 3891f34..cf921b0 100644 (file)
@@ -255,9 +255,12 @@ class LogPage {
                                        $rightsnone = wfMsgExt( 'rightsnone', array( 'parsemag', 'language' => $langObj ) );
 
                                        if( $skin ) {
+                                               $username = $title->getText();
                                                foreach ( $params as &$param ) {
                                                        $groupArray = array_map( 'trim', explode( ',', $param ) );
-                                                       $groupArray = array_map( array( 'User', 'getGroupMember' ), $groupArray );
+                                                       foreach( $groupArray as &$group ) {
+                                                               $group = User::getGroupMember( $group, $username );
+                                                       }
                                                        $param = $wgLang->listToText( $groupArray );
                                                }
                                        }
@@ -417,7 +420,8 @@ class LogPage {
                                        # Use the language name for log titles, rather than Log/X
                                        if( $name == 'Log' ) {
                                                $titleLink = Linker::link( $title, LogPage::logName( $par ) );
-                                               $titleLink = wfMessage( 'parentheses' )->rawParams( $titleLink )->escaped();
+                                               $titleLink = wfMessage( 'parentheses' )->inLanguage( $lang )
+                                                       ->rawParams( $titleLink )->escaped();
                                        } else {
                                                $titleLink = Linker::link( $title );
                                        }
@@ -551,7 +555,8 @@ class LogPage {
                        for( $i = 0; $i < count( $flags ); $i++ ) {
                                $flags[$i] = self::formatBlockFlag( $flags[$i], $lang );
                        }
-                       return wfMessage( 'parentheses' )->rawParams( $lang->commaList( $flags ) )->escaped();
+                       return wfMessage( 'parentheses' )->inLanguage( $lang )
+                               ->rawParams( $lang->commaList( $flags ) )->escaped();
                } else {
                        return '';
                }
index 4fc7637..911fffc 100644 (file)
@@ -33,7 +33,7 @@ class PatrolLog {
         *
         * @param $rc Mixed: change identifier or RecentChange object
         * @param $auto Boolean: was this patrol event automatic?
-        * @param $performer User: user performing the action or null to use $wgUser
+        * @param $user User: user performing the action or null to use $wgUser
         *
         * @return bool
         */
index 8cf30ab..56c5842 100644 (file)
@@ -261,7 +261,7 @@ class BitmapHandler extends ImageHandler {
         * @param $params array Array with scaler params
         * @return ThumbnailImage
         *
-        * @fixme no rotation support
+        * @todo fixme: no rotation support
         */
        protected function getClientScalingThumbnailImage( $image, $params ) {
                return new ThumbnailImage( $image, $image->getURL(),
index 65575ec..34a1f51 100644 (file)
@@ -199,7 +199,8 @@ class ExifBitmapHandler extends BitmapHandler {
         *
         * @param string $data
         * @return int 0, 90, 180 or 270
-        * @fixme orientation can include flipping as well; see if this is an issue!
+        * @todo FIXME orientation can include flipping as well; see if this is an
+        * issue!
         */
        protected function getRotationForExif( $data ) {
                if ( !$data ) {
index c22ea08..0ae9a05 100644 (file)
@@ -309,7 +309,7 @@ class FormatMetadata {
                                                'redeye'   => ( $val & bindec( '01000000' ) ) >> 6,
 //                                             'reserved' => ($val & bindec( '10000000' )) >> 7,
                                        );
-       
+                                       $flashMsgs = array();
                                        # We do not need to handle unknown values since all are used.
                                        foreach ( $flashDecode as $subTag => $subValue ) {
                                                # We do not need any message for zeroed values.
@@ -640,7 +640,7 @@ class FormatMetadata {
                                                }
                                        }
                                        break;
-                                       
+
                                case 'iimCategory':
                                        switch( strtolower( $val ) ) {
                                                // See pg 29 of IPTC photo
@@ -802,7 +802,7 @@ class FormatMetadata {
                                        break;
 
                                case 'LanguageCode':
-                                       $lang = Language::fetchLanguageName( strtolower( $val ), $wgLang );
+                                       $lang = Language::fetchLanguageName( strtolower( $val ), $wgLang->getCode() );
                                        if ($lang) {
                                                $val = htmlspecialchars( $lang );
                                        } else {
@@ -827,14 +827,14 @@ class FormatMetadata {
        * This turns an array of (for example) authors into a bulleted list.
        *
        * This is public on the basis it might be useful outside of this class.
-       * 
+       *
        * @param $vals Array array of values
        * @param $type String Type of array (either lang, ul, ol).
        * lang = language assoc array with keys being the lang code
        * ul = unordered list, ol = ordered list
        * type can also come from the '_type' member of $vals.
        * @param $noHtml Boolean If to avoid returning anything resembling
-       * html. (Ugly hack for backwards compatibility with old mediawiki). 
+       * html. (Ugly hack for backwards compatibility with old mediawiki).
        * @return String single value (in wiki-syntax).
        */
        public static function flattenArray( $vals, $type = 'ul', $noHtml = false ) {
@@ -876,7 +876,7 @@ class FormatMetadata {
                                // If default is set, save it for later,
                                // as we don't know if it's equal to
                                // one of the lang codes. (In xmp
-                               // you specify the language for a 
+                               // you specify the language for a
                                // default property by having both
                                // a default prop, and one in the language
                                // that are identical)
@@ -939,8 +939,9 @@ class FormatMetadata {
         * @param $lang String lang code of item or false
         * @param $default Boolean if it is default value.
         * @param $noHtml Boolean If to avoid html (for back-compat)
-        * @return language item (Note: despite how this looks,
-        *      this is treated as wikitext not html).
+        * @throws MWException
+        * @return string language item (Note: despite how this looks,
+        * this is treated as wikitext not html).
         */
        private static function langItem( $value, $lang, $default = false, $noHtml = false ) {
                if ( $lang === false && $default === false) {
@@ -1019,10 +1020,8 @@ class FormatMetadata {
         * Format a number, convert numbers from fractions into floating point
         * numbers, joins arrays of numbers with commas.
         *
-        * @private
-        *
         * @param $num Mixed: the value to format
-        * @param $round float|int digits to round to or false.
+        * @param $round float|int|bool digits to round to or false.
         * @return mixed A floating point number or whatever we were fed
         */
        static function formatNum( $num, $round = false ) {
@@ -1105,7 +1104,7 @@ class FormatMetadata {
 
        /**
         * Fetch the human readable version of a news code.
-        * A news code is an 8 digit code. The first two 
+        * A news code is an 8 digit code. The first two
         * digits are a general classification, so we just
         * translate that.
         *
@@ -1113,7 +1112,7 @@ class FormatMetadata {
         * a string, not an int.
         *
         * @param $val String: The 8 digit news code.
-        * @return srting The human readable form
+        * @return string The human readable form
         */
        static private function convertNewsCode( $val ) {
                if ( !preg_match( '/^\d{8}$/D', $val ) ) {
@@ -1185,7 +1184,7 @@ class FormatMetadata {
         * Format a coordinate value, convert numbers from floating point
         * into degree minute second representation.
         *
-        * @param $coord Array: degrees, minutes and seconds
+        * @param $coord int degrees, minutes and seconds
         * @param $type String: latitude or longitude (for if its a NWS or E)
         * @return mixed A floating point number or whatever we were fed
         */
@@ -1195,17 +1194,14 @@ class FormatMetadata {
                        $nCoord = -$coord;
                        if ( $type === 'latitude' ) {
                                $ref = 'S';
-                       }
-                       elseif ( $type === 'longitude' ) {
+                       } elseif ( $type === 'longitude' ) {
                                $ref = 'W';
                        }
-               }
-               else {
+               } else {
                        $nCoord = $coord;
                        if ( $type === 'latitude' ) {
                                $ref = 'N';
-                       }
-                       elseif ( $type === 'longitude' ) {
+                       } elseif ( $type === 'longitude' ) {
                                $ref = 'E';
                        }
                }
@@ -1276,7 +1272,7 @@ class FormatMetadata {
                                // Todo: This can potentially be multi-line.
                                // Need to check how that works in XMP.
                                $street = '<span class="extended-address">'
-                                       . htmlspecialchars( 
+                                       . htmlspecialchars(
                                                $vals['CiAdrExtadr'] )
                                        . '</span>';
                        }
@@ -1323,7 +1319,7 @@ class FormatMetadata {
                        }
                        if ( isset( $vals['CiAdrPcode'] ) ) {
                                $postal = '<span class="postal-code">'
-                                       . htmlspecialchars( 
+                                       . htmlspecialchars(
                                                $vals['CiAdrPcode'] )
                                        . '</span>';
                        }
@@ -1354,12 +1350,19 @@ class FormatMetadata {
 **/
 class FormatExif {
        var $meta;
-       function FormatExif ( $meta ) {
+
+       /**
+        * @param $meta array
+        */
+       function FormatExif( $meta ) {
                wfDeprecated(__METHOD__);
                $this->meta = $meta;
        }
 
-       function getFormattedData ( ) {
+       /**
+        * @return array
+        */
+       function getFormattedData() {
                return FormatMetadata::getFormattedData( $this->meta );
        }
 }
index b41ac32..bd4d865 100644 (file)
@@ -282,6 +282,7 @@ abstract class MediaHandler {
         * Returns false if unknown or if the document is not multi-page.
         *
         * @param $image File
+        * @param $page Unused, left for backcompatibility?
         * @return array
         */
        function getPageDimensions( $image, $page ) {
@@ -536,7 +537,7 @@ abstract class MediaHandler {
 
        /**
         * Remove files from the purge list
-        * 
+        *
         * @param array $files
         * @param array $options
         */
@@ -716,7 +717,8 @@ abstract class ImageHandler extends MediaHandler {
                $page = isset( $params['page'] ) ? $params['page'] : false;
 
                if( $image->mustRender() || $params['width'] < $image->getWidth() ) {
-                       return new ThumbnailImage( $image, $url, $params['width'], $params['height'], $page );
+                       return new ThumbnailImage( $image,
+                               $url, $params['width'], $params['height'], false, $page );
                }
        }
 
index bf08de6..d0a7339 100644 (file)
@@ -36,21 +36,30 @@ abstract class MediaTransformOutput {
        protected $storagePath = false;
 
        /**
-        * Get the width of the output box
+        * @return integer Width of the output box
         */
        public function getWidth() {
                return $this->width;
        }
 
        /**
-        * Get the height of the output box
+        * @return integer Height of the output box
         */
        public function getHeight() {
                return $this->height;
        }
 
        /**
-        * @return string The thumbnail URL
+        * Get the final extension of the thumbnail.
+        * Returns false for scripted transformations.
+        * @return string|false
+        */
+       public function getExtension() {
+               return $this->path ? FileBackend::extensionFromPath( $this->path ) : false;
+       }
+
+       /**
+        * @return string|false The thumbnail URL
         */
        public function getUrl() {
                return $this->url;
@@ -84,7 +93,7 @@ abstract class MediaTransformOutput {
         *     custom-url-link    Custom URL to link to
         *     custom-title-link  Custom Title object to link to
         *     valign       vertical-align property, if the output is an inline element
-        *     img-class    Class applied to the <img> tag, if there is such a tag
+        *     img-class    Class applied to the "<img>" tag, if there is such a tag
         *
         * For images, desc-link and file-link are implemented as a click-through. For
         * sounds and videos, they may be displayed in other ways.
@@ -106,7 +115,7 @@ abstract class MediaTransformOutput {
         * This will return false if there was an error, the
         * thumbnail is to be handled client-side only, or if
         * transformation was deferred via TRANSFORM_LATER.
-        * 
+        *
         * @return Bool
         */
        public function hasFile() {
@@ -198,7 +207,7 @@ class ThumbnailImage extends MediaTransformOutput {
         * Get a thumbnail object from a file and parameters.
         * If $path is set to null, the output file is treated as a source copy.
         * If $path is set to false, no output file will be created.
-        * 
+        *
         * @param $file File object
         * @param $url String: URL path to the thumb
         * @param $width Integer: file's width
index da7d428..83f531c 100644 (file)
@@ -19,7 +19,7 @@
  *
  * @file
  * @ingroup Media
- * @author Derk-Jan Hartman <hartman _at_ videolan d0t org>
+ * @author "Derk-Jan Hartman <hartman _at_ videolan d0t org>"
  * @author Brion Vibber
  * @copyright Copyright © 2010-2010 Brion Vibber, Derk-Jan Hartman
  * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
@@ -255,7 +255,7 @@ class SVGReader {
        /**
         * Parse the attributes of an SVG element
         *
-        * The parser has to be in the start element of <svg>
+        * The parser has to be in the start element of "<svg>"
         */
        private function handleSVGAttribs( ) {
                $defaultWidth = self::DEFAULT_WIDTH;
index adb85df..75fdd96 100644 (file)
@@ -461,13 +461,15 @@ class XMPReader {
        * generally means we've finished processing a nested structure.
        * resets some internal variables to indicate that.
        *
-       * Note this means we hit the </closing element> not the </rdf:Seq>.
+       * Note this means we hit the closing element not the "</rdf:Seq>".
        *
-       * For example, when processing:
+       * @par For example, when processing:
+       * @code{,xml}
        * <exif:ISOSpeedRatings> <rdf:Seq> <rdf:li>64</rdf:li>
        *   </rdf:Seq> </exif:ISOSpeedRatings>
+       * @endcode
        *
-       * This method is called when we hit the </exif:ISOSpeedRatings> tag.
+       * This method is called when we hit the "</exif:ISOSpeedRatings>" tag.
        *
        * @param $elm String namespace . space . tag name.
        */
@@ -523,15 +525,17 @@ class XMPReader {
        * Hit a closing element in MODE_LI (either rdf:Seq, or rdf:Bag )
        * Add information about what type of element this is.
        *
-       * Note we still have to hit the outer </property>
+       * Note we still have to hit the outer "</property>"
        *
-       * For example, when processing:
+       * @par For example, when processing:
+       * @code{,xml}
        * <exif:ISOSpeedRatings> <rdf:Seq> <rdf:li>64</rdf:li>
        *   </rdf:Seq> </exif:ISOSpeedRatings>
+       * @endcode
        *
-       * This method is called when we hit the </rdf:Seq>.
+       * This method is called when we hit the "</rdf:Seq>".
        * (For comparison, we call endElementModeSimple when we
-       * hit the </rdf:li>)
+       * hit the "</rdf:li>")
        *
        * @param $elm String namespace . ' ' . element name
        */
@@ -1010,7 +1014,7 @@ class XMPReader {
        * Also does some initial set up for the wrapper element
        *
        * @param $parser XMLParser
-       * @param $elm String namespace <space> element
+       * @param $elm String namespace "<space>" element
        * @param $attribs Array attribute name => value
        */
        function startElement( $parser, $elm, $attribs ) {
@@ -1093,11 +1097,13 @@ class XMPReader {
        * Process attributes.
        * Simple values can be stored as either a tag or attribute
        *
-       * Often the initial <rdf:Description> tag just has all the simple
+       * Often the initial "<rdf:Description>" tag just has all the simple
        * properties as attributes.
        *
-       * Example:
+       * @par Example:
+       * @code
        * <rdf:Description rdf:about="" xmlns:exif="http://ns.adobe.com/exif/1.0/" exif:DigitalZoomRatio="0/10">
+       * @endcode
        *
        * @param $attribs Array attribute=>value array.
        */
index b9a6a69..5a7729b 100644 (file)
  * @ingroup Cache
  */
 class APCBagOStuff extends BagOStuff {
+
+       /**
+        * @param $key string
+        * @return mixed
+        */
        public function get( $key ) {
                $val = apc_fetch( $key );
 
@@ -37,18 +42,32 @@ class APCBagOStuff extends BagOStuff {
                return $val;
        }
 
+       /**
+        * @param $key string
+        * @param $value mixed
+        * @param $exptime int
+        * @return bool
+        */
        public function set( $key, $value, $exptime = 0 ) {
                apc_store( $key, serialize( $value ), $exptime );
 
                return true;
        }
 
+       /**
+        * @param $key string
+        * @param $time int
+        * @return bool
+        */
        public function delete( $key, $time = 0 ) {
                apc_delete( $key );
 
                return true;
        }
 
+       /**
+        * @return Array
+        */
        public function keys() {
                $info = apc_cache_info( 'user' );
                $list = $info['cache_list'];
index e6ba042..0aebfa3 100644 (file)
@@ -153,6 +153,7 @@ abstract class BagOStuff {
        /**
         * @param $key string
         * @param $value mixed
+        * @param $exptime int
         * @return bool success
         */
        public function replace( $key, $value, $exptime = 0 ) {
@@ -166,7 +167,7 @@ abstract class BagOStuff {
         * @param $key String: Key to increase
         * @param $value Integer: Value to add to $key (Default 1)
         * @return null if lock is not possible else $key value increased by $value
-        * @return success
+        * @return bool success
         */
        public function incr( $key, $value = 1 ) {
                if ( !$this->lock( $key ) ) {
index 63ad4de..8483d7e 100644 (file)
@@ -35,6 +35,9 @@
 class DBABagOStuff extends BagOStuff {
        var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
 
+       /**
+        * @param $params array
+        */
        public function __construct( $params ) {
                global $wgDBAhandler;
 
@@ -63,6 +66,7 @@ class DBABagOStuff extends BagOStuff {
        }
 
        /**
+        * @param $blob string
         * @return array list containing value first and expiry second
         */
        function decode( $blob ) {
@@ -76,6 +80,9 @@ class DBABagOStuff extends BagOStuff {
                }
        }
 
+       /**
+        * @return resource
+        */
        function getReader() {
                if ( file_exists( $this->mFile ) ) {
                        $handle = dba_open( $this->mFile, 'rl', $this->mHandler );
@@ -90,6 +97,9 @@ class DBABagOStuff extends BagOStuff {
                return $handle;
        }
 
+       /**
+        * @return resource
+        */
        function getWriter() {
                $handle = dba_open( $this->mFile, 'cl', $this->mHandler );
 
@@ -100,6 +110,10 @@ class DBABagOStuff extends BagOStuff {
                return $handle;
        }
 
+       /**
+        * @param $key string
+        * @return mixed|null|string
+        */
        function get( $key ) {
                wfProfileIn( __METHOD__ );
                wfDebug( __METHOD__ . "($key)\n" );
@@ -129,6 +143,12 @@ class DBABagOStuff extends BagOStuff {
                return $val;
        }
 
+       /**
+        * @param $key string
+        * @param $value mixed
+        * @param $exptime int
+        * @return bool
+        */
        function set( $key, $value, $exptime = 0 ) {
                wfProfileIn( __METHOD__ );
                wfDebug( __METHOD__ . "($key)\n" );
@@ -148,6 +168,11 @@ class DBABagOStuff extends BagOStuff {
                return $ret;
        }
 
+       /**
+        * @param $key string
+        * @param $time int
+        * @return bool
+        */
        function delete( $key, $time = 0 ) {
                wfProfileIn( __METHOD__ );
                wfDebug( __METHOD__ . "($key)\n" );
@@ -165,6 +190,12 @@ class DBABagOStuff extends BagOStuff {
                return $ret;
        }
 
+       /**
+        * @param $key string
+        * @param $value mixed
+        * @param $exptime int
+        * @return bool
+        */
        function add( $key, $value, $exptime = 0 ) {
                wfProfileIn( __METHOD__ );
 
@@ -197,6 +228,9 @@ class DBABagOStuff extends BagOStuff {
                return $ret;
        }
 
+       /**
+        * @return Array
+        */
        function keys() {
                $reader = $this->getReader();
                $k1 = dba_firstkey( $reader );
index fb31d5c..f86cf15 100644 (file)
@@ -33,6 +33,9 @@ class EhcacheBagOStuff extends BagOStuff {
        
        var $curls = array();
 
+       /**
+        * @param $params array
+        */
        function __construct( $params ) {
                if ( !defined( 'CURLOPT_TIMEOUT_MS' ) ) {
                        throw new MWException( __CLASS__.' requires curl version 7.16.2 or later.' );
@@ -59,6 +62,10 @@ class EhcacheBagOStuff extends BagOStuff {
                );
        }
 
+       /**
+        * @param $key string
+        * @return bool|mixed
+        */
        public function get( $key ) {
                wfProfileIn( __METHOD__ );
                $response = $this->doItemRequest( $key );
@@ -93,6 +100,12 @@ class EhcacheBagOStuff extends BagOStuff {
                return $data;
        }
 
+       /**
+        * @param $key string
+        * @param $value mixed
+        * @param $expiry int
+        * @return bool
+        */
        public function set( $key, $value, $expiry = 0 ) {
                wfProfileIn( __METHOD__ );
                $expiry = $this->convertExpiry( $expiry );
@@ -130,6 +143,11 @@ class EhcacheBagOStuff extends BagOStuff {
                return $result;
        }
 
+       /**
+        * @param $key string
+        * @param $time int
+        * @return bool
+        */
        public function delete( $key, $time = 0 ) {
                wfProfileIn( __METHOD__ );
                $response = $this->doItemRequest( $key,
@@ -145,6 +163,10 @@ class EhcacheBagOStuff extends BagOStuff {
                return $result;
        }
 
+       /**
+        * @param $key string
+        * @return string
+        */
        protected function getCacheUrl( $key ) {
                if ( count( $this->servers ) == 1 ) {
                        $server = reset( $this->servers );
@@ -172,6 +194,13 @@ class EhcacheBagOStuff extends BagOStuff {
                return $this->curls[$cacheUrl];
        }
 
+       /**
+        * @param $key string
+        * @param $data
+        * @param $type
+        * @param $ttl
+        * @return int
+        */
        protected function attemptPut( $key, $data, $type, $ttl ) {
                // In initial benchmarking, it was 30 times faster to use CURLOPT_POST 
                // than CURLOPT_UPLOAD with CURLOPT_READFUNCTION. This was because
@@ -196,6 +225,10 @@ class EhcacheBagOStuff extends BagOStuff {
                }
        }
 
+       /**
+        * @param $key string
+        * @return bool
+        */
        protected function createCache( $key ) {
                wfDebug( __METHOD__.": creating cache for $key\n" );
                $response = $this->doCacheRequest( $key, 
@@ -208,21 +241,26 @@ class EhcacheBagOStuff extends BagOStuff {
                        wfDebug( __CLASS__.": failed to create cache for $key\n" );
                        return false;
                }
-               if ( $response['http_code'] == 201 /* created */ 
-                       || $response['http_code'] == 409 /* already there */ ) 
-               {
-                       return true;
-               } else {
-                       return false;
-               }                       
+               return ( $response['http_code'] == 201 /* created */
+                       || $response['http_code'] == 409 /* already there */ );
        }
 
+       /**
+        * @param $key string
+        * @param $curlOptions array
+        * @return array|bool|mixed
+        */
        protected function doCacheRequest( $key, $curlOptions = array() ) {
                $cacheUrl = $this->getCacheUrl( $key );
                $curl = $this->getCurl( $cacheUrl );
                return $this->doRequest( $curl, $cacheUrl, $curlOptions );
        }
 
+       /**
+        * @param $key string
+        * @param $curlOptions array
+        * @return array|bool|mixed
+        */
        protected function doItemRequest( $key, $curlOptions = array() ) {
                $cacheUrl = $this->getCacheUrl( $key );
                $curl = $this->getCurl( $cacheUrl );
@@ -230,6 +268,13 @@ class EhcacheBagOStuff extends BagOStuff {
                return $this->doRequest( $curl, $url, $curlOptions );
        }
 
+       /**
+        * @param $curl
+        * @param $url string
+        * @param $curlOptions array
+        * @return array|bool|mixed
+        * @throws MWException
+        */
        protected function doRequest( $curl, $url, $curlOptions = array() ) {
                if ( array_diff_key( $curlOptions, $this->curlOptions ) ) {
                        // var_dump( array_diff_key( $curlOptions, $this->curlOptions ) );
index abc6bcf..bd28b24 100644 (file)
  * @ingroup Cache
  */
 class EmptyBagOStuff extends BagOStuff {
+
+       /**
+        * @param $key string
+        * @return bool
+        */
        function get( $key ) {
                return false;
        }
 
+       /**
+        * @param $key string
+        * @param $value mixed
+        * @param $exp int
+        * @return bool
+        */
        function set( $key, $value, $exp = 0 ) {
                return true;
        }
 
+       /**
+        * @param $key string
+        * @param $time int
+        * @return bool
+        */
        function delete( $key, $time = 0 ) {
                return true;
        }
index 26b949f..799f26a 100644 (file)
@@ -34,6 +34,10 @@ class HashBagOStuff extends BagOStuff {
                $this->bag = array();
        }
 
+       /**
+        * @param $key string
+        * @return bool
+        */
        protected function expire( $key ) {
                $et = $this->bag[$key][1];
 
@@ -46,6 +50,10 @@ class HashBagOStuff extends BagOStuff {
                return true;
        }
 
+       /**
+        * @param $key string
+        * @return bool|mixed
+        */
        function get( $key ) {
                if ( !isset( $this->bag[$key] ) ) {
                        return false;
@@ -58,10 +66,22 @@ class HashBagOStuff extends BagOStuff {
                return $this->bag[$key][0];
        }
 
+       /**
+        * @param $key string
+        * @param $value mixed
+        * @param $exptime int
+        * @return bool
+        */
        function set( $key, $value, $exptime = 0 ) {
                $this->bag[$key] = array( $value, $this->convertExpiry( $exptime ) );
+               return true;
        }
 
+       /**
+        * @param $key string
+        * @param $time int
+        * @return bool
+        */
        function delete( $key, $time = 0 ) {
                if ( !isset( $this->bag[$key] ) ) {
                        return false;
@@ -72,6 +92,9 @@ class HashBagOStuff extends BagOStuff {
                return true;
        }
 
+       /**
+        * @return array
+        */
        function keys() {
                return array_keys( $this->bag );
        }
index 1312866..464e507 100644 (file)
@@ -86,6 +86,7 @@ class MemcachedBagOStuff extends BagOStuff {
        /**
         * @param $key string
         * @param $value int
+        * @param $exptime int (default 0)
         * @return Mixed
         */
        public function add( $key, $value, $exptime = 0 ) {
@@ -119,6 +120,7 @@ class MemcachedBagOStuff extends BagOStuff {
         * the other control characters for compatibility with libmemcached
         * verify_key. We leave other punctuation alone, to maximise backwards
         * compatibility.
+        * @param $key string
         * @return string
         */
        public function encodeKey( $key ) {
@@ -126,6 +128,10 @@ class MemcachedBagOStuff extends BagOStuff {
                        array( $this, 'encodeKeyCallback' ), $key );
        }
 
+       /**
+        * @param $m array
+        * @return string
+        */
        protected function encodeKeyCallback( $m ) {
                return rawurlencode( $m[0] );
        }
index 9602ffe..eda57c0 100644 (file)
@@ -573,10 +573,10 @@ class MWMemcached {
         * output as an array (null array if no output)
         *
         * NOTE: due to a possible bug in how PHP reads while using fgets(), each
-        *       line may not be terminated by a \r\n.  More specifically, my testing
+        *       line may not be terminated by a "\r\n".  More specifically, my testing
         *       has shown that, on FreeBSD at least, each line is terminated only
-        *       with a \n.  This is with the PHP flag auto_detect_line_endings set
-        *       to falase (the default).
+        *       with a "\n".  This is with the PHP flag auto_detect_line_endings set
+        *       to false (the default).
         *
         * @param $sock Resource: socket to send command on
         * @param $cmd String: command to run
@@ -895,7 +895,10 @@ class MWMemcached {
        function _load_items( $sock, &$ret ) {
                while ( 1 ) {
                        $decl = fgets( $sock );
-                       if ( $decl == "END\r\n" ) {
+                       if( $decl === false ) {
+                               $this->_debugprint( "Error reading socket for a memcached response\n" );
+                               return 0;
+                       } elseif ( $decl == "END\r\n" ) {
                                return true;
                        } elseif ( preg_match( '/^VALUE (\S+) (\d+) (\d+)\r\n$/', $decl, $match ) ) {
                                list( $rkey, $flags, $len ) = array( $match[1], $match[2], $match[3] );
@@ -939,7 +942,12 @@ class MWMemcached {
                                }
 
                        } else {
-                               $this->_debugprint( "Error parsing memcached response\n" );
+                               $peer = $peerAddress = $peerPort = '';
+                               $gotPeer = socket_getpeername( $sock, $peerAddress, $peerPort );
+                               if( $gotPeer ) {
+                                       $peer = " from [$peerAddress:$peerPort";
+                               }
+                               $this->_debugprint( "Error parsing memcached response{$peer}\n" );
                                return 0;
                        }
                }
index 65d736a..76886eb 100644 (file)
@@ -141,6 +141,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
        /**
         * @param $key string
         * @param $value int
+        * @param $exptime int
         * @return Mixed
         */
        public function add( $key, $value, $exptime = 0 ) {
@@ -188,8 +189,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
         * the client, but some day we might find a case where it should be
         * different.
         *
-        * @param $key The key used by the caller, or false if there wasn't one.
-        * @param $result The return value
+        * @param $key string The key used by the caller, or false if there wasn't one.
+        * @param $result Mixed The return value
+        * @return Mixed
         */
        protected function checkResult( $key, $result ) {
                if ( $result !== false ) {
index c562134..a46dc71 100644 (file)
@@ -68,7 +68,7 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
        /**
         * @param $key
         * @param $timeout int
-        * @return
+        * @return bool
         */
        public function lock( $key, $timeout = 0 ) {
                return $this->client->lock( $this->encodeKey( $key ), $timeout );
index fd60e72..e496ddd 100644 (file)
@@ -34,11 +34,12 @@ class MultiWriteBagOStuff extends BagOStuff {
        /**
         * Constructor. Parameters are:
         *
-        *   - caches:   This should have a numbered array of cache parameter 
+        *   - caches:   This should have a numbered array of cache parameter
         *               structures, in the style required by $wgObjectCaches. See
         *               the documentation of $wgObjectCaches for more detail.
         *
         * @param $params array
+        * @throws MWException
         */
        public function __construct( $params ) {
                if ( !isset( $params['caches'] ) ) {
@@ -51,10 +52,17 @@ class MultiWriteBagOStuff extends BagOStuff {
                }
        }
 
+       /**
+        * @param $debug bool
+        */
        public function setDebug( $debug ) {
                $this->doWrite( 'setDebug', $debug );
        }
 
+       /**
+        * @param $key string
+        * @return bool|mixed
+        */
        public function get( $key ) {
                foreach ( $this->caches as $cache ) {
                        $value = $cache->get( $key );
@@ -65,30 +73,68 @@ class MultiWriteBagOStuff extends BagOStuff {
                return false;
        }
 
+       /**
+        * @param $key string
+        * @param $value mixed
+        * @param $exptime int
+        * @return bool
+        */
        public function set( $key, $value, $exptime = 0 ) {
                return $this->doWrite( 'set', $key, $value, $exptime );
        }
 
+       /**
+        * @param $key string
+        * @param $time int
+        * @return bool
+        */
        public function delete( $key, $time = 0 ) {
                return $this->doWrite( 'delete', $key, $time );
        }
 
+       /**
+        * @param $key string
+        * @param $value mixed
+        * @param $exptime int
+        * @return bool
+        */
        public function add( $key, $value, $exptime = 0 ) {
                return $this->doWrite( 'add', $key, $value, $exptime );
        }
 
+       /**
+        * @param $key string
+        * @param $value mixed
+        * @param $exptime int
+        * @return bool
+        */
        public function replace( $key, $value, $exptime = 0 ) {
                return $this->doWrite( 'replace', $key, $value, $exptime );
        }
 
+       /**
+        * @param $key string
+        * @param $value int
+        * @return bool|null
+        */
        public function incr( $key, $value = 1 ) {
                return $this->doWrite( 'incr', $key, $value );
        }
 
+       /**
+        * @param $key string
+        * @param $value int
+        * @return bool
+        */
        public function decr( $key, $value = 1 ) {
                return $this->doWrite( 'decr', $key, $value );
-       }       
+       }
 
+       /**
+        * @param $key string
+        * @param $timeout int
+        * @return bool
+        */
        public function lock( $key, $timeout = 0 ) {
                // Lock only the first cache, to avoid deadlocks
                if ( isset( $this->caches[0] ) ) {
@@ -98,6 +144,10 @@ class MultiWriteBagOStuff extends BagOStuff {
                }
        }
 
+       /**
+        * @param $key string
+        * @return bool
+        */
        public function unlock( $key ) {
                if ( isset( $this->caches[0] ) ) {
                        return $this->caches[0]->unlock( $key );
@@ -106,6 +156,10 @@ class MultiWriteBagOStuff extends BagOStuff {
                }
        }
 
+       /**
+        * @param $method string
+        * @return bool
+        */
        protected function doWrite( $method /*, ... */ ) {
                $ret = true;
                $args = func_get_args();
@@ -120,9 +174,11 @@ class MultiWriteBagOStuff extends BagOStuff {
        }
 
        /**
-        * Delete objects expiring before a certain date. 
+        * Delete objects expiring before a certain date.
         *
         * Succeed if any of the child caches succeed.
+        * @param $date string
+        * @param $progressCallback bool|callback
         * @return bool
         */
        public function deleteObjectsExpiringBefore( $date, $progressCallback = false ) {
index 2e00e16..9b360f3 100644 (file)
@@ -32,9 +32,9 @@ class ObjectCache {
        /**
         * Get a cached instance of the specified type of cache object.
         *
-        * @param $id
+        * @param $id string
         *
-        * @return object
+        * @return ObjectCache
         */
        static function getInstance( $id ) {
                if ( isset( self::$instances[$id] ) ) {
@@ -56,8 +56,9 @@ class ObjectCache {
        /**
         * Create a new cache object of the specified type.
         *
-        * @param $id
+        * @param $id string
         *
+        * @throws MWException
         * @return ObjectCache
         */
        static function newFromId( $id ) {
@@ -76,6 +77,7 @@ class ObjectCache {
         *
         * @param $params array
         *
+        * @throws MWException
         * @return ObjectCache
         */
        static function newFromParams( $params ) {
@@ -99,6 +101,8 @@ class ObjectCache {
         * be an alias to the configured cache choice for that.
         * If no cache choice is configured (by default $wgMainCacheType is CACHE_NONE),
         * then CACHE_ANYTHING will forward to CACHE_DB.
+        * @param $params array
+        * @return ObjectCache
         */
        static function newAnything( $params ) {
                global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType;
@@ -114,6 +118,8 @@ class ObjectCache {
        /**
         * Factory function referenced from DefaultSettings.php for CACHE_ACCEL.
         *
+        * @param $params array
+        * @throws MWException
         * @return ObjectCache
         */
        static function newAccelerator( $params ) {
index 209975b..5ad7020 100644 (file)
@@ -122,6 +122,7 @@ class SqlBagOStuff extends BagOStuff {
 
        /**
         * Get the table name for a given key
+        * @param $key string
         * @return string
         */
        protected function getTableByKey( $key ) {
@@ -135,6 +136,7 @@ class SqlBagOStuff extends BagOStuff {
 
        /**
         * Get the table name for a given shard index
+        * @param $index int
         * @return string
         */
        protected function getTableByShard( $index ) {
@@ -147,11 +149,19 @@ class SqlBagOStuff extends BagOStuff {
                }
        }
 
+       /**
+        * @param $key string
+        * @return mixed
+        */
        public function get( $key ) {
                $values = $this->getMulti( array( $key ) );
                return $values[$key];
        }
 
+       /**
+        * @param $keys array
+        * @return Array
+        */
        public function getMulti( array $keys ) {
                $values = array(); // array of (key => value)
 
@@ -208,6 +218,12 @@ class SqlBagOStuff extends BagOStuff {
                return $values;
        }
 
+       /**
+        * @param $key string
+        * @param $value mixed
+        * @param $exptime int
+        * @return bool
+        */
        public function set( $key, $value, $exptime = 0 ) {
                $db = $this->getDB();
                $exptime = intval( $exptime );
@@ -247,6 +263,11 @@ class SqlBagOStuff extends BagOStuff {
                return true;
        }
 
+       /**
+        * @param $key string
+        * @param $time int
+        * @return bool
+        */
        public function delete( $key, $time = 0 ) {
                $db = $this->getDB();
 
@@ -266,6 +287,11 @@ class SqlBagOStuff extends BagOStuff {
                return true;
        }
 
+       /**
+        * @param $key string
+        * @param $step int
+        * @return int|null
+        */
        public function incr( $key, $step = 1 ) {
                $db = $this->getDB();
                $tableName = $this->getTableByKey( $key );
@@ -316,6 +342,9 @@ class SqlBagOStuff extends BagOStuff {
                return $newValue;
        }
 
+       /**
+        * @return Array
+        */
        public function keys() {
                $db = $this->getDB();
                $result = array();
@@ -331,10 +360,17 @@ class SqlBagOStuff extends BagOStuff {
                return $result;
        }
 
+       /**
+        * @param $exptime string
+        * @return bool
+        */
        protected function isExpired( $exptime ) {
                return $exptime != $this->getMaxDateTime() && wfTimestamp( TS_UNIX, $exptime ) < time();
        }
 
+       /**
+        * @return string
+        */
        protected function getMaxDateTime() {
                if ( time() > 0x7fffffff ) {
                        return $this->getDB()->timestamp( 1 << 62 );
@@ -366,6 +402,8 @@ class SqlBagOStuff extends BagOStuff {
 
        /**
         * Delete objects from the database which expire before a certain date.
+        * @param $timestamp string
+        * @param $progressCallback bool|callback
         * @return bool
         */
        public function deleteObjectsExpiringBefore( $timestamp, $progressCallback = false ) {
index 4fd3cf4..21aa39e 100644 (file)
@@ -74,6 +74,9 @@ class WinCacheBagOStuff extends BagOStuff {
                return true;
        }
 
+       /**
+        * @return Array
+        */
        public function keys() {
                $info = wincache_ucache_info();
                $list = $info['ucache_entries'];
index def1aee..d9356b4 100644 (file)
@@ -209,6 +209,10 @@ class LinkHolderArray {
         * article length checks (for stub links) to be bundled into a single query.
         *
         * @param $nt Title
+        * @param $text String
+        * @param $query Array [optional]
+        * @param $trail String [optional]
+        * @param $prefix String [optional]
         * @return string
         */
        function makeHolder( $nt, $text = '', $query = array(), $trail = '', $prefix = ''  ) {
@@ -453,7 +457,7 @@ class LinkHolderArray {
                        foreach ( $entries as $index => $entry ) {
                                $pdbk = $entry['pdbk'];
                                // we only deal with new links (in its first query)
-                               if ( !isset( $colours[$pdbk] ) ) {
+                               if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] === 'new' ) {
                                        $title = $entry['title'];
                                        $titleText = $title->getText();
                                        $titlesAttrs[] = array(
@@ -469,7 +473,7 @@ class LinkHolderArray {
                }
 
                // Now do the conversion and explode string to text of titles
-               $titlesAllVariants = $wgContLang->autoConvertToAllVariants( $titlesToBeConverted );
+               $titlesAllVariants = $wgContLang->autoConvertToAllVariants( rtrim( $titlesToBeConverted, "\0" ) );
                $allVariantsName = array_keys( $titlesAllVariants );
                foreach ( $titlesAllVariants as &$titlesVariant ) {
                        $titlesVariant = explode( "\0", $titlesVariant );
@@ -537,7 +541,7 @@ class LinkHolderArray {
                                        $entry =& $this->internals[$ns][$index];
                                        $pdbk = $entry['pdbk'];
 
-                                       if(!isset($colours[$pdbk])){
+                                       if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] === 'new' ) {
                                                // found link in some of the variants, replace the link holder data
                                                $entry['title'] = $variantTitle;
                                                $entry['pdbk'] = $varPdbk;
index 7991ca6..94af6a8 100644 (file)
  * transformation of that wiki markup it into XHTML output / markup
  * (which in turn the browser understands, and can display).
  *
- * <pre>
- * There are five main entry points into the Parser class:
- * parse()
+ * There are seven main entry points into the Parser class:
+ *
+ * - Parser::parse()
  *     produces HTML output
- * preSaveTransform().
+ * - Parser::preSaveTransform().
  *     produces altered wiki markup.
- * preprocess()
+ * - Parser::preprocess()
  *     removes HTML comments and expands templates
- * cleanSig() / cleanSigInSig()
+ * - Parser::cleanSig() and Parser::cleanSigInSig()
  *     Cleans a signature before saving it to preferences
- * getSection()
+ * - Parser::getSection()
  *     Return the content of a section from an article for section editing
- * replaceSection()
+ * - Parser::replaceSection()
  *     Replaces a section by number inside an article
- * getPreloadText()
+ * - Parser::getPreloadText()
  *     Removes <noinclude> sections, and <includeonly> tags.
  *
  * Globals used:
  *    object: $wgContLang
  *
- * NOT $wgUser or $wgTitle or $wgRequest or $wgLang. Keep them away!
+ * @warning $wgUser or $wgTitle or $wgRequest or $wgLang. Keep them away!
  *
- * settings:
- *  $wgUseDynamicDates*, $wgInterwikiMagic*,
- *  $wgNamespacesWithSubpages, $wgAllowExternalImages*,
- *  $wgLocaltimezone, $wgAllowSpecialInclusion*,
- *  $wgMaxArticleSize*
+ * @par Settings:
+ * $wgLocaltimezone
+ * $wgNamespacesWithSubpages
  *
- *  * only within ParserOptions
- * </pre>
+ * @par Settings only within ParserOptions:
+ * $wgAllowExternalImages
+ * $wgAllowSpecialInclusion
+ * $wgInterwikiMagic
+ * $wgMaxArticleSize
+ * $wgUseDynamicDates
  *
  * @ingroup Parser
  */
@@ -557,10 +559,11 @@ class Parser {
        }
 
        /**
-        * Process the wikitext for the ?preload= feature. (bug 5210)
+        * Process the wikitext for the "?preload=" feature. (bug 5210)
         *
-        * <noinclude>, <includeonly> etc. are parsed as for template transclusion,
-        * comments, templates, arguments, tags hooks and parser functions are untouched.
+        * "<noinclude>", "<includeonly>" etc. are parsed as for template
+        * transclusion, comments, templates, arguments, tags hooks and parser
+        * functions are untouched.
         *
         * @param $text String
         * @param $title Title
@@ -788,11 +791,14 @@ class Parser {
         * in the text with a random marker and returns the next text. The output
         * parameter $matches will be an associative array filled with data in
         * the form:
+        *
+        * @code
         *   'UNIQ-xxxxx' => array(
         *     'element',
         *     'tag content',
         *     array( 'param' => 'x' ),
         *     '<element param="x">tag content</element>' ) )
+        * @endcode
         *
         * @param $elements array list of element names. Comments are always extracted.
         * @param $text string Source text string.
@@ -2940,7 +2946,7 @@ class Parser {
         *
         * @param $text String: The text to parse
         * @param $flags Integer: bitwise combination of:
-        *          self::PTD_FOR_INCLUSION    Handle <noinclude>/<includeonly> as if the text is being
+        *          self::PTD_FOR_INCLUSION    Handle "<noinclude>" and "<includeonly>" as if the text is being
         *                                     included. Default is to assume a direct page view.
         *
         * The generated DOM tree must depend only on the input text and the flags.
@@ -3517,7 +3523,7 @@ class Parser {
         * Static function to get a template
         * Can be overridden via ParserOptions::setTemplateCallback().
         *
-        * @parma $title Title
+        * @param $title  Title
         * @param $parser Parser
         *
         * @return array
@@ -3691,7 +3697,7 @@ class Parser {
         * Triple brace replacement -- used for template arguments
         * @private
         *
-        * @param $peice array
+        * @param $piece array
         * @param $frame PPFrame
         *
         * @return array
@@ -4324,7 +4330,7 @@ class Parser {
        }
 
        /**
-        * Transform wiki markup when saving a page by doing \r\n -> \n
+        * Transform wiki markup when saving a page by doing "\r\n" -> "\n"
         * conversion, substitting signatures, {{subst:}} templates, etc.
         *
         * @param $text String: the text to transform
@@ -4620,7 +4626,7 @@ class Parser {
        }
 
        /**
-        * Create an HTML-style tag, e.g. <yourtag>special text</yourtag>
+        * Create an HTML-style tag, e.g. "<yourtag>special text</yourtag>"
         * The callback should have the following form:
         *    function myParserHook( $text, $params, $parser, $frame ) { ... }
         *
@@ -4638,7 +4644,7 @@ class Parser {
         * this interface, as it is not documented and injudicious use could smash
         * private variables.**
         *
-        * @param $tag Mixed: the tag to use, e.g. 'hook' for <hook>
+        * @param $tag Mixed: the tag to use, e.g. 'hook' for "<hook>"
         * @param $callback Mixed: the callback function (and object) to use for the tag
         * @return Mixed|null The old value of the mTagHooks array associated with the hook
         */
@@ -4668,7 +4674,7 @@ class Parser {
         * @since 1.10
         * @todo better document or deprecate this
         *
-        * @param $tag Mixed: the tag to use, e.g. 'hook' for <hook>
+        * @param $tag Mixed: the tag to use, e.g. 'hook' for "<hook>"
         * @param $callback Mixed: the callback function (and object) to use for the tag
         * @return Mixed|null The old value of the mTagHooks array associated with the hook
         */
@@ -4776,7 +4782,7 @@ class Parser {
        }
 
        /**
-        * Create a tag function, e.g. <test>some stuff</test>.
+        * Create a tag function, e.g. "<test>some stuff</test>".
         * Unlike tag hooks, tag functions are parsed at preprocessor level.
         * Unlike parser functions, their content is not preprocessed.
         * @return null
@@ -4797,7 +4803,7 @@ class Parser {
 
        /**
         * @todo FIXME: Update documentation. makeLinkObj() is deprecated.
-        * Replace <!--LINK--> link placeholders with actual links, in the buffer
+        * Replace "<!--LINK-->" link placeholders with actual links, in the buffer
         * Placeholders created in Skin::makeLinkObj()
         *
         * @param $text string
@@ -4810,7 +4816,7 @@ class Parser {
        }
 
        /**
-        * Replace <!--LINK--> link placeholders with plain text of links
+        * Replace "<!--LINK-->" link placeholders with plain text of links
         * (not HTML-formatted).
         *
         * @param $text String
@@ -5282,13 +5288,13 @@ class Parser {
         *
         * @param $text String: Page wikitext
         * @param $section String: a section identifier string of the form:
-        *   <flag1> - <flag2> - ... - <section number>
+        *   "<flag1> - <flag2> - ... - <section number>"
         *
         * Currently the only recognised flag is "T", which means the target section number
         * was derived during a template inclusion parse, in other words this is a template
         * section edit link. If no flags are given, it was an ordinary section edit link.
         * This flag is required to avoid a section numbering mismatch when a section is
-        * enclosed by <includeonly> (bug 6563).
+        * enclosed by "<includeonly>" (bug 6563).
         *
         * The section number 0 pulls the text before the first heading; other numbers will
         * pull the given section along with its lower-level subsections. If the section is
index bb99039..6a4ef0c 100644 (file)
@@ -119,8 +119,9 @@ class ParserCache {
         *
         * @todo Document parameter $useOutdated
         *
-        * @param $article Article
-        * @param $popts ParserOptions
+        * @param $article     Article
+        * @param $popts       ParserOptions
+        * @param $useOutdated Boolean (default true)
         * @return bool|mixed|string
         */
        public function getKey( $article, $popts, $useOutdated = true ) {
@@ -157,9 +158,9 @@ class ParserCache {
         * Retrieve the ParserOutput from ParserCache.
         * false if not found or outdated.
         *
-        * @param $article Article
-        * @param $popts ParserOptions
-        * @param $useOutdated
+        * @param $article     Article
+        * @param $popts       ParserOptions
+        * @param $useOutdated Boolean (default false)
         *
         * @return ParserOutput|bool False on failure
         */
index d929f1a..62d3bfd 100644 (file)
@@ -348,7 +348,7 @@ class ParserOutput extends CacheTime {
        }
 
        /**
-        * Add some text to the <head>.
+        * Add some text to the "<head>".
         * If $tag is set, the section with that tag will only be included once
         * in a given page.
         */
index 19bcbf2..bd13f9a 100644 (file)
@@ -231,7 +231,7 @@ interface PPNode {
        function getName();
 
        /**
-        * Split a <part> node into an associative array containing:
+        * Split a "<part>" node into an associative array containing:
         *    name          PPNode name
         *    index         String index
         *    value         PPNode value
@@ -239,13 +239,13 @@ interface PPNode {
        function splitArg();
 
        /**
-        * Split an <ext> node into an associative array containing name, attr, inner and close
+        * Split an "<ext>" node into an associative array containing name, attr, inner and close
         * All values in the resulting array are PPNodes. Inner and close are optional.
         */
        function splitExt();
 
        /**
-        * Split an <h> node
+        * Split an "<h>" node
         */
        function splitHeading();
 }
index f991df2..7fe420d 100644 (file)
@@ -112,7 +112,7 @@ class Preprocessor_DOM implements Preprocessor {
         *
         * @param $text String: the text to parse
         * @param $flags Integer: bitwise combination of:
-        *          Parser::PTD_FOR_INCLUSION    Handle <noinclude>/<includeonly> as if the text is being
+        *          Parser::PTD_FOR_INCLUSION    Handle "<noinclude>" and "<includeonly>" as if the text is being
         *                                     included. Default is to assume a direct page view.
         *
         * The generated DOM tree must depend only on the input text and the flags.
@@ -1658,10 +1658,10 @@ class PPNode_DOM implements PPNode {
        }
 
        /**
-        * Split a <part> node into an associative array containing:
-        *    name          PPNode name
-        *    index         String index
-        *    value         PPNode value
+        * Split a "<part>" node into an associative array containing:
+        *  - name          PPNode name
+        *  - index         String index
+        *  - value         PPNode value
         *
         * @return array
         */
@@ -1681,7 +1681,7 @@ class PPNode_DOM implements PPNode {
        }
 
        /**
-        * Split an <ext> node into an associative array containing name, attr, inner and close
+        * Split an "<ext>" node into an associative array containing name, attr, inner and close
         * All values in the resulting array are PPNodes. Inner and close are optional.
         *
         * @return array
@@ -1708,7 +1708,7 @@ class PPNode_DOM implements PPNode {
        }
 
        /**
-        * Split a <h> node
+        * Split a "<h>" node
         * @return array
         */
        function splitHeading() {
index f455a1d..0e202fd 100644 (file)
@@ -24,7 +24,7 @@
 /**
  * Differences from DOM schema:
  *   * attribute nodes are children
- *   * <h> nodes that aren't at the top are replaced with <possible-h>
+ *   * "<h>" nodes that aren't at the top are replaced with <possible-h>
  * @ingroup Parser
  */
 class Preprocessor_Hash implements Preprocessor {
@@ -91,7 +91,7 @@ class Preprocessor_Hash implements Preprocessor {
         *
         * @param $text String: the text to parse
         * @param $flags Integer: bitwise combination of:
-        *          Parser::PTD_FOR_INCLUSION    Handle <noinclude>/<includeonly> as if the text is being
+        *          Parser::PTD_FOR_INCLUSION    Handle "<noinclude>" and "<includeonly>" as if the text is being
         *                                     included. Default is to assume a direct page view.
         *
         * The generated DOM tree must depend only on the input text and the flags.
@@ -1603,10 +1603,10 @@ class PPNode_Hash_Tree implements PPNode {
        }
 
        /**
-        * Split a <part> node into an associative array containing:
-        *    name          PPNode name
-        *    index         String index
-        *    value         PPNode value
+        * Split a "<part>" node into an associative array containing:
+        *  - name          PPNode name
+        *  - index         String index
+        *  - value         PPNode value
         *
         * @return array
         */
@@ -1638,7 +1638,7 @@ class PPNode_Hash_Tree implements PPNode {
        }
 
        /**
-        * Split an <ext> node into an associative array containing name, attr, inner and close
+        * Split an "<ext>" node into an associative array containing name, attr, inner and close
         * All values in the resulting array are PPNodes. Inner and close are optional.
         *
         * @return array
@@ -1666,7 +1666,7 @@ class PPNode_Hash_Tree implements PPNode {
        }
 
        /**
-        * Split an <h> node
+        * Split an "<h>" node
         *
         * @return array
         */
@@ -1692,7 +1692,7 @@ class PPNode_Hash_Tree implements PPNode {
        }
 
        /**
-        * Split a <template> or <tplarg> node
+        * Split a "<template>" or "<tplarg>" node
         *
         * @return array
         */
index 8b1452e..99fe7ed 100644 (file)
@@ -858,7 +858,7 @@ class ResourceLoader {
 
        /**
         * Combines an associative array mapping media type to CSS into a
-        * single stylesheet with @media blocks.
+        * single stylesheet with "@media" blocks.
         *
         * @param $styles Array: List of CSS strings keyed by media type
         *
index 035ff09..1e19d0f 100644 (file)
@@ -237,8 +237,8 @@ abstract class ResourceLoaderModule {
 
        /**
         * Where on the HTML page should this module's JS be loaded?
-        * 'top': in the <head>
-        * 'bottom': at the bottom of the <body>
+        *  - 'top': in the "<head>"
+        *  - 'bottom': at the bottom of the "<body>"
         *
         * @return string
         */
index 467a1ac..c86ed1d 100644 (file)
@@ -79,9 +79,6 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        'wgVersion' => $wgVersion,
                        'wgEnableAPI' => $wgEnableAPI,
                        'wgEnableWriteAPI' => $wgEnableWriteAPI,
-                       'wgDefaultDateFormat' => $wgContLang->getDefaultDateFormat(),
-                       'wgMonthNames' => $wgContLang->getMonthNamesArray(),
-                       'wgMonthNamesShort' => $wgContLang->getMonthAbbreviationsArray(),
                        'wgMainPageTitle' => $mainPage->getPrefixedText(),
                        'wgFormattedNamespaces' => $wgContLang->getFormattedNamespaces(),
                        'wgNamespaceIds' => $namespaceIds,
index f35e774..9dd6939 100644 (file)
@@ -42,7 +42,6 @@ abstract class ResourceLoaderWikiModule extends ResourceLoaderModule {
        /* Abstract Protected Methods */
 
        /**
-        * @abstract
         * @param $context ResourceLoaderContext
         */
        abstract protected function getPages( ResourceLoaderContext $context );
index 1c45dc4..a2db52f 100644 (file)
@@ -124,7 +124,7 @@ class SearchOracle extends SearchEngine {
        /**
         * Return a LIMIT clause to limit results on the query.
         *
-        * @param string
+        * @param $sql string
         *
         * @return String
         */
index 0c03871..fe9d41e 100644 (file)
@@ -146,8 +146,9 @@ class AllmessagesTablePager extends TablePager {
        function buildForm() {
                global $wgScript;
 
-               $languages = Language::fetchLanguageNames( null, 'mw' );
-               ksort( $languages );
+               $attrs = array( 'id' => 'mw-allmessages-form-lang', 'name' => 'lang' );
+               $msg = wfMessage( 'allmessages-language' );
+               $langSelect = Xml::languageSelector( $this->langcode, false, null, $attrs, $msg );
 
                $out  = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-allmessages-form' ) ) .
                        Xml::fieldset( $this->msg( 'allmessages-filter-legend' )->text() ) .
@@ -187,18 +188,8 @@ class AllmessagesTablePager extends TablePager {
                                "</td>\n
                        </tr>
                        <tr>\n
-                               <td class=\"mw-label\">" .
-                                       Xml::label( $this->msg( 'allmessages-language' )->text(), 'mw-allmessages-form-lang' ) .
-                               "</td>\n
-                               <td class=\"mw-input\">" .
-                                       Xml::openElement( 'select', array( 'id' => 'mw-allmessages-form-lang', 'name' => 'lang' ) );
-
-               foreach( $languages as $lang => $name ) {
-                       $selected = $lang == $this->langcode;
-                       $out .= Xml::option( $lang . ' - ' . $name, $lang, $selected ) . "\n";
-               }
-               $out .= Xml::closeElement( 'select' ) .
-                               "</td>\n
+                               <td class=\"mw-label\">" . $langSelect[0] . "</td>\n
+                               <td class=\"mw-input\">" . $langSelect[1] . "</td>\n
                        </tr>" .
 
                        '<tr>
index e8223e8..fb94ff7 100644 (file)
@@ -704,13 +704,22 @@ class ContribsPager extends ReverseChronologicalPager {
                $join_conds = array();
                $tables = array( 'revision', 'page', 'user' );
                if ( $this->contribs == 'newbie' ) {
-                       $tables[] = 'user_groups';
                        $max = $this->mDb->selectField( 'user', 'max(user_id)', false, __METHOD__ );
                        $condition[] = 'rev_user >' . (int)( $max - $max / 100 );
-                       $condition[] = 'ug_group IS NULL';
                        $index = 'user_timestamp';
-                       # @todo FIXME: Other groups may have 'bot' rights
-                       $join_conds['user_groups'] = array( 'LEFT JOIN', "ug_user = rev_user AND ug_group = 'bot'" );
+                       # ignore local groups with the bot right
+                       # @todo FIXME: Global groups may have 'bot' rights
+                       $groupsWithBotPermission = User::getGroupsWithPermission( 'bot' );
+                       if( count( $groupsWithBotPermission ) ) {
+                               $tables[] = 'user_groups';
+                               $condition[] = 'ug_group IS NULL';
+                               $join_conds['user_groups'] = array(
+                                       'LEFT JOIN', array(
+                                               'ug_user = rev_user',
+                                               'ug_group' => $groupsWithBotPermission
+                                       )
+                               );
+                       }
                } else {
                        $uid = User::idFromName( $this->target );
                        if ( $uid ) {
@@ -759,21 +768,15 @@ class ContribsPager extends ReverseChronologicalPager {
        }
 
        function doBatchLookups() {
-               $this->mResult->rewind();
-               $revIds = array();
-               foreach ( $this->mResult as $row ) {
-                       if( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
-                               $revIds[] = $row->rev_parent_id;
-                       }
-               }
-               $this->mParentLens = Revision::getParentLengths( $this->getDatabase(), $revIds );
-               $this->mResult->rewind(); // reset
-
                # Do a link batch query
                $this->mResult->seek( 0 );
+               $revIds = array();
                $batch = new LinkBatch();
                # Give some pointers to make (last) links
                foreach ( $this->mResult as $row ) {
+                       if( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
+                               $revIds[] = $row->rev_parent_id;
+                       }
                        if ( isset( $row->rev_id ) ) {
                                if ( $this->contribs === 'newbie' ) { // multiple users
                                        $batch->add( NS_USER, $row->user_name );
@@ -782,6 +785,7 @@ class ContribsPager extends ReverseChronologicalPager {
                                $batch->add( $row->page_namespace, $row->page_title );
                        }
                }
+               $this->mParentLens = Revision::getParentLengths( $this->getDatabase(), $revIds );
                $batch->execute();
                $this->mResult->seek( 0 );
        }
@@ -953,15 +957,6 @@ class ContribsPager extends ReverseChronologicalPager {
                return $ret;
        }
 
-       /**
-        * Get the Database object in use
-        *
-        * @return DatabaseBase
-        */
-       public function getDatabase() {
-               return $this->mDb;
-       }
-
        /**
         * Overwrite Pager function and return a helpful comment
         * @return string
index 18d19db..f8e40e0 100644 (file)
@@ -157,10 +157,24 @@ class FileDuplicateSearchPage extends QueryPage {
                                );
                        }
 
+                       $this->doBatchLookups( $dupes );
                        $this->showList( $dupes );
                }
        }
 
+       function doBatchLookups( $list ) {
+               $batch = new LinkBatch();
+               foreach( $list as $file ) {
+                       $batch->addObj( $file->getTitle() );
+                       if( $file->isLocal() ) {
+                               $userName = $file->getUser( 'text' );
+                               $batch->add( NS_USER, $userName );
+                               $batch->add( NS_USER_TALK, $userName );
+                       }
+               }
+               $batch->execute();
+       }
+
        /**
         *
         * @param Skin $skin
@@ -178,7 +192,17 @@ class FileDuplicateSearchPage extends QueryPage {
                );
 
                $userText = $result->getUser( 'text' );
-               $user = Linker::link( Title::makeTitle( NS_USER, $userText ), $userText );
+               if ( $result->isLocal() ) {
+                       $userId = $result->getUser( 'id' );
+                       $user = Linker::userLink( $userId, $userText );
+                       $user .= $this->getContext()->msg( 'word-separator' )->plain();
+                       $user .= '<span style="white-space: nowrap;">';
+                       $user .= Linker::userToolLinks( $userId, $userText );
+                       $user .= '</span>';
+               } else {
+                       $user = htmlspecialchars( $userText );
+               }
+
                $time = $this->getLanguage()->userTimeAndDate( $result->getTimestamp(), $this->getUser() );
 
                return "$plink . . $user . . $time";
index 75be397..ebcca73 100644 (file)
  */
 class UsersPager extends AlphabeticPager {
 
+       /**
+        * @param $context IContextSource
+        * @param $par null|array
+        */
        function __construct( IContextSource $context = null, $par = null ) {
                if ( $context ) {
                        $this->setContext( $context );
@@ -69,10 +73,16 @@ class UsersPager extends AlphabeticPager {
                parent::__construct();
        }
 
+       /**
+        * @return string
+        */
        function getIndexField() {
                return $this->creationSort ? 'user_id' : 'user_name';
        }
 
+       /**
+        * @return Array
+        */
        function getQueryInfo() {
                $dbr = wfGetDB( DB_SLAVE );
                $conds = array();
@@ -125,18 +135,20 @@ class UsersPager extends AlphabeticPager {
                return $query;
        }
 
+       /**
+        * @param $row Object
+        * @return String
+        */
        function formatRow( $row ) {
-               if ($row->user_id == 0) #Bug 16487
+               if ( $row->user_id == 0 ) { #Bug 16487
                        return '';
+               }
 
                $userName = $row->user_name;
 
                $ulinks = Linker::userLink( $row->user_id, $userName );
                $ulinks .= Linker::userToolLinks( $row->user_id, $userName );
 
-               $userPage = Title::makeTitle( NS_USER, $row->user_name );
-               $name = Linker::link( $userPage, htmlspecialchars( $userPage->getText() ) );
-
                $lang = $this->getLanguage();
 
                $groups_list = self::getGroups( $row->user_id );
@@ -186,9 +198,12 @@ class UsersPager extends AlphabeticPager {
                $this->mResult->rewind();
        }
 
+       /**
+        * @return string
+        */
        function getPageHeader( ) {
                global $wgScript;
-               // @todo Add a PrefixedBaseDBKey
+
                list( $self ) = explode( '/', $this->getTitle()->getPrefixedDBkey() );
 
                # Form tag
@@ -243,10 +258,12 @@ class UsersPager extends AlphabeticPager {
         */
        function getDefaultQuery() {
                $query = parent::getDefaultQuery();
-               if( $this->requestedGroup != '' )
+               if( $this->requestedGroup != '' ) {
                        $query['group'] = $this->requestedGroup;
-               if( $this->requestedUser != '' )
+               }
+               if( $this->requestedUser != '' ) {
                        $query['username'] = $this->requestedUser;
+               }
                wfRunHooks( 'SpecialListusersDefaultQuery', array( $this, &$query ) );
                return $query;
        }
index 2c96e83..8e4205c 100644 (file)
@@ -161,8 +161,8 @@ class SpecialLog extends SpecialPage {
        }
 
        private function getRevisionButton( $formcontents ) {
-               # If the user doesn't have the ability to delete revisions, don't bother showing him/her the button.
-               if ( !$this->getUser()->isAllowed( 'deleterevision' ) ) {
+               # If the user doesn't have the ability to delete log entries, don't bother showing him/her the button.
+               if ( !$this->getUser()->isAllowed( 'deletelogentry' ) ) {
                        return $formcontents;
                }
 
index 45dbd36..35f39ce 100644 (file)
@@ -68,15 +68,18 @@ class NewFilesPager extends ReverseChronologicalPager {
                $tables = array( 'image' );
 
                if( !$this->showbots ) {
-                       $tables[] = 'user_groups';
-                       $conds[] = 'ug_group IS NULL';
-                       $jconds['user_groups'] = array(
-                               'LEFT JOIN',
-                               array(
-                                       'ug_group' => User::getGroupsWithPermission( 'bot' ),
-                                       'ug_user = img_user'
-                               )
-                       );
+                       $groupsWithBotPermission = User::getGroupsWithPermission( 'bot' );
+                       if( count( $groupsWithBotPermission ) ) {
+                               $tables[] = 'user_groups';
+                               $conds[] = 'ug_group IS NULL';
+                               $jconds['user_groups'] = array(
+                                       'LEFT JOIN',
+                                       array(
+                                               'ug_group' => $groupsWithBotPermission,
+                                               'ug_user = img_user'
+                                       )
+                               );
+                       }
                }
 
                if( !$wgMiserMode && $this->like !== null ){
index d8fdbe7..74ed537 100644 (file)
@@ -70,8 +70,8 @@ class SpecialProtectedpages extends SpecialPage {
 
        /**
         * Callback function to output a restriction
-        * @param $row object Protected title
-        * @return string Formatted <li> element
+        * @param Title $row Protected title
+        * @return string Formatted "<li>" element
         */
        public function formatRow( $row ) {
                wfProfileIn( __METHOD__ );
index 83c7235..be432ac 100644 (file)
@@ -66,6 +66,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        'success'               => 'revdelete-success',
                        'failure'               => 'revdelete-failure',
                        'list-class'    => 'RevDel_RevisionList',
+                       'permission'    => 'deleterevision',
                ),
                'archive' => array(
                        'check-label'   => 'revdelete-hide-text',
@@ -73,6 +74,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        'success'               => 'revdelete-success',
                        'failure'               => 'revdelete-failure',
                        'list-class'    => 'RevDel_ArchiveList',
+                       'permission'    => 'deleterevision',
                ),
                'oldimage'=> array(
                        'check-label'   => 'revdelete-hide-image',
@@ -80,6 +82,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        'success'               => 'revdelete-success',
                        'failure'               => 'revdelete-failure',
                        'list-class'    => 'RevDel_FileList',
+                       'permission'    => 'deleterevision',
                ),
                'filearchive' => array(
                        'check-label'   => 'revdelete-hide-image',
@@ -87,6 +90,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        'success'               => 'revdelete-success',
                        'failure'               => 'revdelete-failure',
                        'list-class'    => 'RevDel_ArchivedFileList',
+                       'permission'    => 'deleterevision',
                ),
                'logging' => array(
                        'check-label'   => 'revdelete-hide-name',
@@ -94,6 +98,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        'success'               => 'logdelete-success',
                        'failure'               => 'logdelete-failure',
                        'list-class'    => 'RevDel_LogList',
+                       'permission'    => 'deletelogentry',
                ),
        );
 
@@ -117,7 +122,6 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                $output = $this->getOutput();
                $user = $this->getUser();
 
-               $this->mIsAllowed = $user->isAllowed('deleterevision'); // for changes
                $this->setHeaders();
                $this->outputHeader();
                $request = $this->getRequest();
@@ -163,6 +167,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        return;
                }
                $this->typeInfo = self::$allowedTypes[$this->typeName];
+               $this->mIsAllowed = $user->isAllowed( $this->typeInfo['permission'] );
 
                # If we have revisions, get the title from the first one
                # since they should all be from the same page. This allows
@@ -597,6 +602,9 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
 
        /**
         * Do the write operations. Simple wrapper for RevDel_*List::setVisibility().
+        * @param $bitfield
+        * @param $reason
+        * @param $title
         * @return
         */
        protected function save( $bitfield, $reason, $title ) {
index 6f59135..611b3b9 100644 (file)
@@ -921,7 +921,8 @@ class SpecialUndelete extends SpecialPage {
                                "</td>\n" .
                        "</tr>" .
                        $diffEngine->generateDiffBody(
-                               $previousRev->getText(), $currentRev->getText() ) .
+                               $previousRev->getText( Revision::FOR_THIS_USER, $this->getUser() ),
+                               $currentRev->getText( Revision::FOR_THIS_USER, $this->getUser() ) ) .
                        "</table>" .
                        "</div>\n"
                );
index 6052d09..1a00d73 100644 (file)
@@ -273,8 +273,8 @@ class SpecialUploadStash extends UnlistedSpecialPage {
        /**
         * Output HTTP response of raw content
         * Side effect: writes HTTP response to STDOUT.
-        * @param String $content: content
-        * @param String $mimeType: mime type
+        * @param $content String content
+        * @param $contentType String mime type
         * @return bool
         */
        private function outputContents( $content, $contentType ) {
@@ -322,7 +322,7 @@ class SpecialUploadStash extends UnlistedSpecialPage {
        /**
         * Default action when we don't have a subpage -- just show links to the uploads we have,
         * Also show a button to clear stashed files
-        * @param Status : $status - the result of processRequest
+        * @param $status [optional] Status: the result of processRequest
         * @return bool
         */
        private function showUploads( $status = null ) {
index 7d91096..613e3b9 100644 (file)
@@ -274,7 +274,7 @@ class LoginForm extends SpecialPage {
 
        /**
         * @private
-        * @return bool|\User
+        * @return bool|User
         */
        function addNewAccountInternal() {
                global $wgAuth, $wgMemc, $wgAccountCreationThrottle,
index 5a71ccb..51c2d0f 100644 (file)
@@ -108,6 +108,7 @@ class SpecialVersion extends SpecialPage {
                        'Alexandre Emsenhuber', 'Siebrand Mazeland', 'Chad Horohoe',
                        'Roan Kattouw', 'Trevor Parscal', 'Bryan Tong Minh', 'Sam Reed',
                        'Victor Vasiliev', 'Rotem Liss', 'Platonides', 'Antoine Musso',
+                       'Timo Tijhof',
                        wfMsg( 'version-poweredby-others' )
                );
 
index 8eb2781..0e647a9 100644 (file)
@@ -22,7 +22,7 @@
  */
 
 /**
- * @defgroup Upload
+ * @defgroup Upload Upload related
  */
 
 /**
@@ -614,6 +614,9 @@ abstract class UploadBase {
         * Really perform the upload. Stores the file in the local repo, watches
         * if necessary and runs the UploadComplete hook.
         *
+        * @param $comment
+        * @param $pageText
+        * @param $watch
         * @param $user User
         *
         * @return Status indicating the whether the upload succeeded.
index 12531c2..bbee0f5 100644 (file)
@@ -68,9 +68,11 @@ class UploadStash {
 
        /**
         * Represents a temporary filestore, with metadata in the database.
-        * Designed to be compatible with the session stashing code in UploadBase (should replace it eventually)
+        * Designed to be compatible with the session stashing code in UploadBase
+        * (should replace it eventually).
         *
         * @param $repo FileRepo
+        * @param $user User (default null)
         */
        public function __construct( FileRepo $repo, $user = null ) {
                // this might change based on wiki's configuration.
@@ -241,6 +243,9 @@ class UploadStash {
                }
                $stashPath = $storeStatus->value;
 
+               // we have renamed the file so we have to cleanup once done
+               unlink($path);
+
                // fetch the current user ID
                if ( !$this->isLoggedIn ) {
                        throw new UploadStashNotLoggedInException( __METHOD__ . ' No user is logged in, files must belong to users' );
@@ -442,6 +447,7 @@ class UploadStash {
         * Helper function: do the actual database query to fetch file metadata.
         *
         * @param $key String: key
+        * @param $readFromDB: constant (default: DB_SLAVE)
         * @return boolean
         */
        protected function fetchFileMetadata( $key, $readFromDB = DB_SLAVE ) {
@@ -474,7 +480,6 @@ class UploadStash {
        /**
         * Helper function: Initialize the UploadStashFile for a given file.
         *
-        * @param $path String: path to file
         * @param $key String: key under which to store the object
         * @throws UploadStashZeroLengthFileException
         * @return bool
@@ -574,8 +579,8 @@ class UploadStashFile extends UnregisteredLocalFile {
 
        /**
         * Helper function -- given a 'subpage', return the local URL e.g. /wiki/Special:UploadStash/subpage
-        * @param {String} $subPage
-        * @return {String} local URL for this subpage in the Special:UploadStash space.
+        * @param $subPage String
+        * @return String: local URL for this subpage in the Special:UploadStash space.
         */
        private function getSpecialUrl( $subPage ) {
                return SpecialPage::getTitleFor( 'UploadStash', $subPage )->getLocalURL();
index eb5fa39..1b84f8e 100644 (file)
@@ -239,7 +239,6 @@ U+09E21鸡|U+096DE雞|U+09DC4鷄|
 U+09E5A鹚|U+09DBF鶿|U+09DC0鷀|
 U+09E6E鹮|U+04D09䴉|
 U+09F44齄|U+09F47齇|
-U+0E82D|U+068E1棡|
 U+20BB6𠮶|U+055F0嗰|
 U+26216𦈖|U+04308䌈|
 U+28C3E𨰾|U+093B7鎷|
index 692c74b..7c3ce10 100644 (file)
@@ -43,7 +43,6 @@ U+065E3旣|U+065E2既|
 U+06607昇|U+05347升|
 U+0672E朮|U+0672F术|
 U+068CA棊|U+068CB棋|
-U+068E1棡|U+0E82D|
 U+069A6榦|U+05E72干|
 U+069D3槓|U+06760杠|
 U+06A11樑|U+06881梁|
index b531f2a..430209d 100644 (file)
@@ -246,7 +246,11 @@ class Language {
         */
        public static function isValidCode( $code ) {
                return
-                       strcspn( $code, ":/\\\000" ) === strlen( $code )
+                       // People think language codes are html safe, so enforce it.
+                       // Ideally we should only allow a-zA-Z0-9-
+                       // but, .+ and other chars are often used for {{int:}} hacks
+                       // see bugs 37564, 37587, 36938
+                       strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code )
                        && !preg_match( Title::getTitleInvalidRegex(), $code );
        }
 
@@ -3907,7 +3911,7 @@ class Language {
        /**
         * Decode an expiry (block, protection, etc) which has come from the DB
         *
-        * @FIXME: why are we returnings DBMS-dependent strings???
+        * @todo FIXME: why are we returnings DBMS-dependent strings???
         *
         * @param $expiry String: Database expiry String
         * @param $format Bool|Int true to process using language functions, or TS_ constant
index e5a45fe..bc1809f 100644 (file)
        'arz' => 'مصرى',    # Egyptian Spoken Arabic
        'as' => 'অসমীয়া',   # Assamese
        'ast' => 'asturianu',   # Asturian
-       'av' => 'Ð\90вар',     # Avar
+       'av' => 'авар',     # Avar
        'avk' => 'Kotava', # Kotava
        'ay' => 'Aymar aru',    # Aymara
        'az' => 'azərbaycanca',        # Azerbaijani
-       'ba' => 'Ð\91ашҡортса',   # Bashkir
+       'ba' => 'башҡортса',   # Bashkir
        'bar' => 'Boarisch',    # Bavarian (Austro-Bavarian and South Tyrolean)
-       'bat-smg' => 'Žemaitėška', # Samogitian (deprecated code, 'sgs' in ISO 693-3 since 2010-06-30 )
+       'bat-smg' => 'žemaitėška', # Samogitian (deprecated code, 'sgs' in ISO 693-3 since 2010-06-30 )
        'bcc' => 'بلوچی مکرانی', # Southern Balochi
        'bcl' => 'Bikol Central', # Bikol: Central Bicolano language
        'be' => 'беларуская', #  Belarusian normative
        'brh' => 'Bráhuí',    # Brahui
        'bs' => 'bosanski',             # Bosnian
        'bug' => 'ᨅᨔ ᨕᨘᨁᨗ', # Buginese
-       'bxr' => 'Ð\91уряад',        # Buryat (Russia)
+       'bxr' => 'буряад',        # Buryat (Russia)
        'ca' => 'català',      # Catalan
        'cbk-zam' => 'Chavacano de Zamboanga',  # Zamboanga Chavacano
        'cdo' => 'Mìng-dĕ̤ng-ngṳ̄',       # Min Dong
-       'ce' => 'Ð\9dохчийн',       # Chechen
+       'ce' => 'нохчийн',       # Chechen
        'ceb' => 'Cebuano',     # Cebuano
        'ch' => 'Chamoru',              # Chamorro
        'cho' => 'Choctaw',             # Choctaw
@@ -86,9 +86,9 @@
        'co' => 'corsu',                # Corsican
        'cps' => 'Capiceño', # Capiznon
        'cr' => 'Nēhiyawēwin / ᓀᐦᐃᔭᐍᐏᐣ',                # Cree
-       'crh' => 'Qırımtatarca',   # Crimean Tatar (multiple scripts - defaults to Latin)
-       'crh-latn' => "\xE2\x80\xAAQırımtatarca (Latin)\xE2\x80\xAC",       # Crimean Tatar (Latin)
-       'crh-cyrl' => "\xE2\x80\xAAÐ\9aъырымтатарджа (Кирилл)\xE2\x80\xAC",       # Crimean Tatar (Cyrillic)
+       'crh' => 'qırımtatarca',   # Crimean Tatar (multiple scripts - defaults to Latin)
+       'crh-latn' => "\xE2\x80\xAAqırımtatarca (Latin)\xE2\x80\xAC",       # Crimean Tatar (Latin)
+       'crh-cyrl' => "\xE2\x80\xAAкъырымтатарджа (Кирилл)\xE2\x80\xAC",       # Crimean Tatar (Cyrillic)
        'cs' => 'česky',       # Czech
        'csb' => 'kaszëbsczi', # Cassubian
        'cu' => 'словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ',      # Old Church Slavonic (ancient language)
        'ki' => 'Gĩkũyũ',    # Gikuyu
        'kiu' => 'Kırmancki',  # Kirmanjki
        'kj' => 'Kwanyama',     # Kwanyama
-       'kk' => 'Ò\9aазақша',       # Kazakh (multiple scripts - defaults to Cyrillic)
+       'kk' => 'Ò\9bазақша',       # Kazakh (multiple scripts - defaults to Cyrillic)
        'kk-arab' => "\xE2\x80\xABقازاقشا (تٴوتە)\xE2\x80\xAC",     # Kazakh Arabic
-       'kk-cyrl' => "\xE2\x80\xAAÒ\9aазақша (кирил)\xE2\x80\xAC",     # Kazakh Cyrillic
-       'kk-latn' => "\xE2\x80\xAAQazaqşa (latın)\xE2\x80\xAC",       # Kazakh Latin
+       'kk-cyrl' => "\xE2\x80\xAAÒ\9bазақша (кирил)\xE2\x80\xAC",     # Kazakh Cyrillic
+       'kk-latn' => "\xE2\x80\xAAqazaqşa (latın)\xE2\x80\xAC",       # Kazakh Latin
        'kk-cn' => "\xE2\x80\xABقازاقشا (جۇنگو)\xE2\x80\xAC",       # Kazakh (China)
-       'kk-kz' => "\xE2\x80\xAAÒ\9aазақша (Қазақстан)\xE2\x80\xAC",       # Kazakh (Kazakhstan)
-       'kk-tr' => "\xE2\x80\xAAQazaqşa (Türkïya)\xE2\x80\xAC",      # Kazakh (Turkey)
+       'kk-kz' => "\xE2\x80\xAAÒ\9bазақша (Қазақстан)\xE2\x80\xAC",       # Kazakh (Kazakhstan)
+       'kk-tr' => "\xE2\x80\xAAqazaqşa (Türkïya)\xE2\x80\xAC",      # Kazakh (Turkey)
        'kl' => 'kalaallisut',  # Inuktitut, Greenlandic/Greenlandic/Kalaallisut (kal)
        'km' => 'ភាសាខ្មែរ',  # Khmer, Central
        'kn' => 'ಕನ್ನಡ',      # Kannada
        'ko-kp' => '한국어 (조선)',        # Korean (DPRK)
        'koi' => 'Перем Коми', # Komi-Permyak
        'kr' => 'Kanuri',               # Kanuri, Central
-       'krc' => 'Ð\9aÑ\8aаÑ\80аÑ\87ай-Ð\9cалкъар', # Karachay-Balkar
+       'krc' => 'кÑ\8aаÑ\80аÑ\87ай-малкъар', # Karachay-Balkar
        'kri' => 'Krio', # Krio
        'krj' => 'Kinaray-a', # Kinaray-a
        'ks' => 'कॉशुर / کٲشُر', # Kashmiri (multiple scripts - defaults to Perso-Arabic)
        'ku'  => 'Kurdî',      # Kurdish (multiple scripts - defaults to Latin)
        'ku-latn' => "\xE2\x80\xAAKurdî (latînî)\xE2\x80\xAC",       # Northern Kurdish (Latin script)
        'ku-arab' => "\xE2\x80\xABكوردي (عەرەبی)\xE2\x80\xAC",       # Northern Kurdish (Arabic script) (falls back to ckb)
-       'kv' => 'Ð\9aоми',     # Komi-Zyrian (Cyrillic is common script but also written in Latin script)
+       'kv' => 'коми',     # Komi-Zyrian (Cyrillic is common script but also written in Latin script)
        'kw' => 'kernowek',             # Cornish
        'ky' => 'Кыргызча',     # Kirghiz
        'la' => 'Latina',               # Latin
        'lad' => 'Ladino',      # Ladino
        'lb' => 'Lëtzebuergesch',      # Luxemburguish
-       'lbe' => 'Ð\9bакку',  # Lak
-       'lez' => 'Ð\9bезги',  # Lezgi
+       'lbe' => 'лакку',  # Lak
+       'lez' => 'лезги',  # Lezgi
        'lfn' => 'Lingua Franca Nova',  # Lingua Franca Nova
        'lg' => 'Luganda',              # Ganda
        'li' => 'Limburgs',     # Limburgian
        'lzz' => 'Lazuri',      # Laz
        'mai' => 'मैथिली', # Maithili
        'map-bms' => 'Basa Banyumasan', # Banyumasan
-       'mdf' => 'Ð\9cокшень',              # Moksha
+       'mdf' => 'мокшень',              # Moksha
        'mg' => 'Malagasy',             # Malagasy
        'mh' => 'Ebon',                 # Marshallese
-       'mhr' => 'Ð\9eлÑ\8bк Ð\9cарий', # Eastern Mari
+       'mhr' => 'олÑ\8bк Ð¼арий', # Eastern Mari
        'mi' => 'Māori',       # Maori
        'min' => 'Baso Minangkabau',    # Minangkabau
        'mk' => 'македонски', # Macedonian
        'mn' => 'монгол', # Halh Mongolian (Cyrillic) (ISO 639-3: khk)
        'mo' => 'молдовеняскэ',     # Moldovan, deprecated
        'mr' => 'मराठी',      # Marathi
-       'mrj' => 'Ð\9aырык мары', # Hill Mari
+       'mrj' => 'кырык мары', # Hill Mari
        'ms' => 'Bahasa Melayu',        # Malay
        'mt' => 'Malti',        # Maltese
        'mus' => 'Mvskoke',     # Muskogee/Creek
        'mwl' => 'Mirandés',   # Mirandese
        'my' => 'မြန်မာဘာသာ',               # Burmese
-       'myv' => 'Эрзянь',        # Erzya
+       'myv' => 'эрзянь',        # Erzya
        'mzn' => 'مازِرونی',            # Mazanderani
        'na' => 'Dorerin Naoero',               # Nauruan
        'nah' => 'Nāhuatl',            # Nahuatl, en:Wikipedia writes Nahuatlahtolli, while another form is Náhuatl
        'tum' => 'chiTumbuka',  # Tumbuka
        'tw' => 'Twi',                  # Twi, (FIXME!)
        'ty' => 'Reo Mā`ohi',  # Tahitian
-       'tyv' => 'Тыва дыл',     # Tyvan
-       'udm' => 'Удмурт',        # Udmurt
+       'tyv' => 'тыва дыл',     # Tyvan
+       'udm' => 'удмурт',        # Udmurt
        'ug' => 'ئۇيغۇرچە / Uyghurche', # Uyghur (multiple scripts - defaults to Arabic)
        'ug-arab' => 'ئۇيغۇرچە', # Uyghur (Arabic script) (default)
        'ug-latn' => 'Uyghurche', # Uyghur (Latin script)
        'uk' => 'українська', # Ukrainian
        'ur' => 'اردو',     # Urdu
-       'uz' => 'Oʻzbekcha',   # Uzbek
+       'uz' => 'oʻzbekcha',   # Uzbek
        've' => 'Tshivenda',            # Venda
        'vec' => 'vèneto',     # Venetian
-       'vep' => 'Vepsän kel’',      # Veps
+       'vep' => 'vepsän kel’',      # Veps
        'vi' => 'Tiếng Việt',       # Vietnamese
        'vls' => 'West-Vlams', # West Flemish
        'vmf' => 'Mainfränkisch', # Upper Franconian, Main-Franconian
        'war' => 'Winaray', # Waray-Waray
        'wo' => 'Wolof',                # Wolof
        'wuu' => '吴语',              # Wu Chinese
-       'xal' => 'Хальмг',                # Kalmyk-Oirat (Kalmuk, Kalmuck, Kalmack, Qalmaq, Kalmytskii Jazyk, Khal:mag, Oirat, Volga Oirat, European Oirat, Western Mongolian)
+       'xal' => 'хальмг',                # Kalmyk-Oirat (Kalmuk, Kalmuck, Kalmack, Qalmaq, Kalmytskii Jazyk, Khal:mag, Oirat, Volga Oirat, European Oirat, Western Mongolian)
        'xh' => 'isiXhosa',             # Xhosan
        'xmf' => 'მარგალური', # Mingrelian
        'yi' => 'ייִדיש', # Yiddish
index 4ba35ea..18d7c8b 100644 (file)
@@ -112,10 +112,10 @@ $messages = array(
 'tog-editondblclick' => 'ܫܚܠܦ ܦܐܬ̈ܐ ܬܪ ܢܩܪܐ ܙܘܓܢܝܐ (ܣܢܝܩ ܠ JavaScript)',
 'tog-editsection' => 'ܡܫܟܚ ܫܘܚܠܦܐ ܕܦܘܣܩ̈ܐ ܒܐܘܪܚܐ ܕܐܝܨܘܪ̈ܐ  [ܫܚܠܦ]',
 'tog-rememberpassword' => 'ܕܟܘܪ ܥܠܠܬܝ ܥܠ ܡܦܐܬܢܐ ܗܢܐ (ܠܡܬܚܐ ܥܠܝܐ ܕ $1 {{PLURAL:$1|ܝܘܡܐ|ܝܘܡܬ̈ܐ}})',
-'tog-watchcreations' => 'ܐܘܣܦ ܦܐܬܬ̈ܐ ܕܒܪܐ ܐܢܐ ܠܪ̈ܗܝܬܝ',
-'tog-watchdefault' => 'ܐܘܣܦ ܦܐܬܬ̈ܐ ܕܫܚܠܦ ܐܢܐ ܠܪ̈ܗܝܬܝ',
-'tog-watchmoves' => 'ܐܘܣܦ ܦܐܬܬ̈ܐ ܕܫܢܐ ܐܢܐ ܠܪ̈ܗܝܬܝ',
-'tog-watchdeletion' => 'Ü\90Ü\98ܣܦ Ü¦Ü\90ܬܬÌ\88Ü\90 Ü\95ܫܦÜ\90 ܐܢܐ ܠܪ̈ܗܝܬܝ',
+'tog-watchcreations' => 'Ü\90Ü\98ܣܦ Ü¦Ü\90ܬܬÌ\88Ü\90 Ü\95Ü\92ܪÜ\90 Ü\90Ü¢Ü\90 Ü\98ܠܠܦÌ\88Ü\90 Ü\95Ü\90ܣܩ Ü\90Ü¢Ü\90 Ü ÜªÌ\88Ü\97Ü\9dܬÜ\9d',
+'tog-watchdefault' => 'Ü\90Ü\98ܣܦ Ü¦Ü\90ܬܬÌ\88Ü\90 Ü\98ܠܦܦÌ\88Ü\90 Ü\95Ü«Ü\9aܠܦ Ü\90Ü¢Ü\90 Ü ÜªÌ\88Ü\97Ü\9dܬÜ\9d',
+'tog-watchmoves' => 'Ü\90Ü\98ܣܦ Ü¦Ü\90ܬܬÌ\88Ü\90 Ü\98ܠܦܦÌ\88Ü\90 Ü\95Ü«Ü¢Ü\90 Ü\90Ü¢Ü\90 Ü ÜªÌ\88Ü\97Ü\9dܬÜ\9d',
+'tog-watchdeletion' => 'Ü\90Ü\98ܣܦ Ü¦Ü\90ܬܬÌ\88Ü\90 Ü\98ܠܦܦÌ\88Ü\90 Ü\95Ü«Ü\90ܦ ܐܢܐ ܠܪ̈ܗܝܬܝ',
 'tog-watchlisthideown' => 'ܛܫܝ ܫܘܚܠܦ̈ܝ ܡܢ ܪ̈ܗܝܬܐ',
 'tog-watchlisthidebots' => 'ܛܫܝ ܫܘܚܠܦ̈ܐ ܕܒܘܬ ܡܢ ܪ̈ܗܝܬܐ',
 'tog-watchlisthideminor' => 'ܛܫܝ ܫܘܚܠܦ̈ܐ ܙܥܘܪ̈ܐ ܡܢ ܪ̈ܗܝܬܐ',
@@ -1110,8 +1110,11 @@ $1',
 'watchlistfor2' => 'ܕ $1 $2',
 'nowatchlist' => 'ܠܝܬ ܠܟ ܡܕܡ ܒܪ̈ܗܝܬܐ ܕܝܠܟ',
 'watchnologin' => 'ܠܝܬܝܟ ܥܠܝܠܐ',
+'watchnologintext' => 'ܐܠܨܐ ܕܬܗܘܐ [[Special:UserLogin|ܥܠܝܠܐ]] ܠܫܚܠܦܬܐ ܕܪ̈ܗܝܬܟ.',
+'addwatch' => 'ܐܘܣܦ ܥܠ ܪ̈ܗܝܬܝ',
 'addedwatchtext' => "ܦܐܬܐ ܕ\"[[:\$1]]\" ܐܬܬܘܣܦܬ ܒ[[Special:Watchlist|ܪ̈ܗܝܬܟ]].
 ܐܝܢܐ ܫܘܚܠܦܐ ܠܦܐܬܐ ܗܕܐ ܒܕܥܬܝܕ ܬܬܓܠܚ ܥܡ ܦܐܬܐ ܕܡܡܠܠܐ ܕܝܠܗ ܬܡܢ, ܘܦܐܬܐ ܬܗܘܐ ܒܣܪܛܐ '''ܚܠܝܡܐ''' ܒܦܐܬܐ ܕ[[Special:RecentChanges|ܫܘܚܠܦ̈ܐ ܚܕ̈ܬܐ]] ܠܦܫܛܬܐ ܕܫܟܚܬܗ.",
+'removewatch' => 'ܫܩܘܠ ܡܢ ܪ̈ܗܝܬܝ',
 'removedwatchtext' => 'ܦܐܬܐ ܕ "[[:$1]]" ܐܫܬܩܠܬ ܡܢ [[Special:Watchlist|ܪ̈ܗܝܬܟ]].',
 'watch' => 'ܪܗܝ',
 'watchthispage' => 'ܪܗܝ ܗܕܐ ܦܐܬܐ',
index bc72730..00687eb 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Bashkir (Ð\91ашҡортса)
+/** Bashkir (башҡортса)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
@@ -322,7 +322,6 @@ $1',
 'versionrequiredtext' => 'Был бит менән эшләү өсөн MediaWiki-ның $1 версияһы кәрәк. [[Special:Version|Ҡулланылған версия тураһында мәғлүмәт битен]] ҡара.',
 
 'ok' => 'Тамам',
-'pagetitle' => '{{SITENAME}} проектынан',
 'retrievedfrom' => 'Сығанағы — «$1»',
 'youhavenewmessages' => 'Яңы $1 бар ($2).',
 'newmessageslink' => 'яңы хәбәр',
index 8ea0efa..0e00fa6 100644 (file)
@@ -1187,16 +1187,26 @@ Ini dae tabi matitingkog.',
 'prefs-help-email' => 'Opsyonal an e-koreo, alagad pwede ka na masosog kan iba sa paagi kan saimong pahina o pahina nin olay na dai kinakaipuhan na ipabisto an identidad mo.',
 'prefs-help-email-required' => 'Kaipuhan an e-koreo.',
 
+# User preference: e-mail validation using jQuery
+'email-address-validity-invalid' => 'Magkaag nin sarong balidong e-koreong address',
+
 # User rights
 'userrights' => 'Pagmaneho kan mga derecho nin paragamit',
 'userrights-lookup-user' => 'Magmaného kan mga grupo nin parágamit',
 'userrights-user-editname' => 'Ilaog an pangaran kan parágamit:',
 'editusergroup' => 'Hirahón an mga Grupo kan Parágamit',
-'editinguser' => "Pighihira an parágamit na '''[[User:$1|$1]]''' ([[User talk:$1|{{int:talkpagelinktext}}]]{{int:pipe-separator}}[[Special:Contributions/$1|{{int:contribslink}}]])",
+'editinguser' => "Sinasanglian an paragamit na karapatan kan paragamit '''[[User:$1|$1]]''' $2",
 'userrights-editusergroup' => 'Hirahón an mga grupo kan parágamit',
 'saveusergroups' => 'Itagama an mga Grupo nin Páragamit',
 'userrights-groupsmember' => 'Myembro kan:',
+'userrights-groupsmember-auto' => 'Implisitong miyembro kan:',
+'userrights-groups-help' => 'Ika puwedeng magbago kan mga grupo na kinabalihan kaining paragamit:
+*An natsekan na kahon minapasabot na an paragamit kabali sa grupong yan.
+*An mayong tsek na kahon minapasabot na an paragamit bakong kabali sa grupong yan.
+* A * minapahiwatig na ika dae puwedeng makapaghale kan grupo kun naidagdag mo na ini, or vice versa.',
 'userrights-reason' => 'Rason:',
+'userrights-no-interwiki' => 'Ika mayo tabing permkso na magliwat sa paragamit na karapatan sa ibang wikis.',
+'userrights-nodatabase' => 'An datos-sarayan $1 bakong eksistido o bakong lokal.',
 'userrights-nologin' => 'Ika kaipuhan na [[Special:UserLogin|maglaog ka]] na igwa nin panindog na administrador bago ka makapagtao nin karapatan sa paragamit.',
 'userrights-notallowed' => 'An saimong panindog mayo tabi nin permiso na magdagdag o maghale nin karapatan kan mga paragamit.',
 'userrights-changeable-col' => 'Mga grupo na mapuwede mong baguhon',
@@ -1291,7 +1301,7 @@ Ini dae tabi matitingkog.',
 'rightslog' => 'Usip nin derechos nin paragamit',
 'rightslogtext' => 'Ini an historial kan mga pagbabâgo sa mga derecho nin parágamit.',
 'rightslogentry' => 'Rinibayab an pagkamyembro ni $1 sa $2 sagkod sa $3',
-'rightslogentry-autopromote' => 'dati na awtomatikong pinagpalangkaw gikan sa $2 sagkod S3',
+'rightslogentry-autopromote' => 'dati na awtomatikong pinagpalangkaw gikan sa $2 sagkod $3',
 'rightsnone' => '(mayô)',
 
 # Associated actions - in the sentence "You do not have permission to X"
@@ -1341,7 +1351,7 @@ Ini dae tabi matitingkog.',
 'recentchanges-label-minor' => 'Ini saro sanang menor na pagliwat',
 'recentchanges-label-bot' => 'Ining pagliwat pinaghimo bilang sarong bot',
 'recentchanges-label-unpatrolled' => 'Ining pagliwat dae pa tabi pinagpatrolyahan',
-'rcnote' => "Mahihiling sa babâ an {{PLURAL:$1| '''1''' pagbabàgo|'''$1''' pagbabàgo}} sa huring {{PLURAL:$2|na aldaw|'''$2''' na aldaw}}, sa $3.",
+'rcnote' => "Yaon sa ibaba iyo {{PLURAL:$1|an '''1''' pagbabago|an mga huring '''$1''' mga pagbabago}} kan nakaaging huring {{PLURAL:$2|aldaw|'''$2''' mga aldaw}}, poon pa kan $5, $4.",
 'rcnotefrom' => "Mahihiling sa babâ an mga pagbabàgo poon kan '''$2''' (hasta '''$1''' ipinapahiling).",
 'rclistfrom' => 'Ipahilíng an mga pagbabàgo poon sa $1',
 'rcshowhideminor' => '$1 saradit na pagligwat',
@@ -1358,10 +1368,14 @@ Ini dae tabi matitingkog.',
 'minoreditletter' => 's',
 'newpageletter' => 'B',
 'boteditletter' => 'b',
-'number_of_watching_users_pageview' => '[$1 nagbabantay na parágamit]',
+'number_of_watching_users_pageview' => '[$1 naka-antabay sa {{PLURAL:$1|paragamit|mga paragamit}}]',
 'rc_categories' => 'Limitado sa mga kategorya (suhayon nin "|")',
 'rc_categories_any' => 'Dawà arín',
+'rc-change-size-new' => '$1 {{PLURAL:$1|byte|bytes}} pagtatapos kan pagbabago',
 'newsectionsummary' => '/* $1 */ bàgong seksyon',
+'rc-enhanced-expand' => 'Magpahiling kan mga detalye (minakaipo nin JavaScript)',
+'rc-enhanced-hide' => 'Itago an mga detalye',
+'rc-old-title' => 'orihinal na pinagmukna bilang "$1"',
 
 # Recent changes linked
 'recentchangeslinked' => 'Mga angay na pagbabàgo',
index b5b0380..1f9e446 100644 (file)
@@ -269,17 +269,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Рэдагаваць сэкцыі па правым пстрыку на загалоўку (патрабуе JavaScript)',
 'tog-showtoc' => 'Паказваць зьмест (для старонак з колькасьцю сэкцый болей за 3)',
 'tog-rememberpassword' => 'Запомніць мяне ў гэтым браўзэры (ня больш за $1 {{PLURAL:$1|дзень|дні|дзён}})',
-'tog-watchcreations' => 'Ð\94адаваÑ\86Ñ\8c Ñ\83 Ð¼Ð¾Ð¹ Ñ\81Ñ\8cпÑ\96Ñ\81 Ð½Ð°Ð·Ñ\96Ñ\80анÑ\8cнÑ\8f Ñ\81Ñ\82аÑ\80онкÑ\96, Ñ\8fкÑ\96Ñ\8f Ñ\8f Ð±Ñ\83дÑ\83 Ñ\81Ñ\82ваÑ\80аÑ\86Ñ\8c',
-'tog-watchdefault' => 'Дадаваць у мой сьпіс назіраньня старонкі, якія я буду рэдагаваць',
-'tog-watchmoves' => 'Дадаваць у мой сьпіс назіраньня старонкі, якія я буду пераносіць',
-'tog-watchdeletion' => 'Дадаваць у мой сьпіс назіраньня старонкі, якія я буду выдаляць',
+'tog-watchcreations' => 'Ð\94адаваÑ\86Ñ\8c Ñ\83 Ð¼Ð¾Ð¹ Ñ\81Ñ\8cпÑ\96Ñ\81 Ð½Ð°Ð·Ñ\96Ñ\80анÑ\8cнÑ\8f Ñ\81Ñ\82воÑ\80анÑ\8bÑ\8f Ð¼Ð½Ð¾Ð¹ Ñ\81Ñ\82аÑ\80онкÑ\96 Ñ\96 Ð·Ð°Ð³Ñ\80Ñ\83жанÑ\8bÑ\8f Ð¼Ð½Ð¾Ð¹ Ñ\84айлÑ\8b',
+'tog-watchdefault' => 'Дадаваць у мой сьпіс назіраньня старонкі і файлы якія я рэдагаваў',
+'tog-watchmoves' => 'Дадаваць у мой сьпіс назіраньня старонкі і файлы якія я пераносіў',
+'tog-watchdeletion' => 'Дадаваць у мой сьпіс назіраньня старонкі і файлы, якія я выдаляю',
 'tog-minordefault' => 'Па змоўчаньні пазначаць усе зьмены дробнымі',
 'tog-previewontop' => 'Паказваць папярэдні прагляд старонкі над полем рэдагаваньня',
 'tog-previewonfirst' => 'Папярэдні прагляд пры першым рэдагаваньні',
 'tog-nocache' => 'Адключыць кэшаваньне старонак у браўзэры',
-'tog-enotifwatchlistpages' => 'Паведамляць мне праз электронную пошту пра зьмены старонак у маім сьпісе назіраньня',
+'tog-enotifwatchlistpages' => 'Ð\9fаведамлÑ\8fÑ\86Ñ\8c Ð¼Ð½Ðµ Ð¿Ñ\80аз Ñ\8dлекÑ\82Ñ\80оннÑ\83Ñ\8e Ð¿Ð¾Ñ\88Ñ\82Ñ\83 Ð¿Ñ\80а Ð·Ñ\8cменÑ\8b Ñ\81Ñ\82аÑ\80онак Ñ\96 Ñ\84айлаÑ\9e Ñ\83 Ð¼Ð°Ñ\96м Ñ\81Ñ\8cпÑ\96Ñ\81е Ð½Ð°Ð·Ñ\96Ñ\80анÑ\8cнÑ\8f',
 'tog-enotifusertalkpages' => 'Паведамляць праз электронную пошту пра зьмены маёй старонкі гутарак',
-'tog-enotifminoredits' => 'Паведамляць праз электронную пошту таксама пра дробныя зьмены старонак',
+'tog-enotifminoredits' => 'Паведамляць праз электронную пошту таксама пра дробныя зьмены старонак і файлаў',
 'tog-enotifrevealaddr' => 'Не хаваць мой адрас электроннай пошты ў паведамленьнях',
 'tog-shownumberswatching' => 'Паказваць колькасьць назіральнікаў',
 'tog-oldsig' => 'Цяперашні подпіс:',
index 2c994a4..d43f729 100644 (file)
@@ -251,7 +251,7 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Възможност за редактиране на раздел при щракване с десния бутон върху заглавие на раздел (изисква Джаваскрипт)',
 'tog-showtoc' => 'Показване на съдържание (за страници с повече от три раздела)',
 'tog-rememberpassword' => 'Запомяне на паролата ми в този браузър (за не повече от $1 {{PLURAL:$1|ден|дни}})',
-'tog-watchcreations' => 'Добавяне на създадените от мен страници към списъка ми за наблюдение',
+'tog-watchcreations' => 'Ð\94обавÑ\8fне Ð½Ð° Ñ\81Ñ\8aздадениÑ\82е Ð¾Ñ\82 Ð¼ÐµÐ½ Ñ\81Ñ\82Ñ\80аниÑ\86и Ð¸ ÐºÐ°Ñ\87ениÑ\82е Ð¾Ñ\82 Ð¼ÐµÐ½ Ñ\84айлове ÐºÑ\8aм Ñ\81пиÑ\81Ñ\8aка Ð¼Ð¸ Ð·Ð° Ð½Ð°Ð±Ð»Ñ\8eдение',
 'tog-watchdefault' => 'Добавяне на редактираните от мен страници към списъка ми за наблюдение',
 'tog-watchmoves' => 'Добавяне на преместените от мен страници към списъка ми за наблюдение',
 'tog-watchdeletion' => 'Добавяне на изтритите от мен страници към списъка ми за наблюдение',
@@ -261,7 +261,7 @@ $messages = array(
 'tog-nocache' => 'Спиране на складирането на страниците от браузъра',
 'tog-enotifwatchlistpages' => 'Уведомяване по е-пощата при промяна на страница от списъка ми за наблюдение',
 'tog-enotifusertalkpages' => 'Уведомяване по е-пощата при промяна на беседата ми',
-'tog-enotifminoredits' => 'Уведомяване по е-пощата даже при малки промени',
+'tog-enotifminoredits' => 'Уведомяване по е-пощата даже при малки промени на страници или файлове',
 'tog-enotifrevealaddr' => 'Показване на електронния ми адрес в известяващите писма',
 'tog-shownumberswatching' => 'Показване на броя на потребителите, наблюдаващи дадена страница',
 'tog-oldsig' => 'Текущ подпис:',
index aeccfb7..982fa9f 100644 (file)
@@ -662,8 +662,8 @@ $2',
 মনে থইস .css বারো .js পাতার নাঙ এতা রূহিবৃত্তির মাতুঙে হুরকা আতর ইকার মেয়েকল অরতাহে, যেসাদে {{ns:user}}:Foo/vector.css; কিন্তু এসাদে চিঙনাঙ নাইব: {{ns:user}}:Foo/Vector.css",
 'updated' => '(আপডেট)',
 'note' => "'''নোট:'''",
-'previewnote' => "'''à¦\8fহান à¦¹à§\81দà§\8dদা à¦\86à¦\97à¦\9aাহান;
-ফারাকহান এপাগাউ ইতু করানি নাইসে!'''",
+'previewnote' => "'''à¦\96িয়াল à¦\95র, à¦\8fহান à¦¹à§\81দà§\8dদা à¦\86à¦\97à¦\9aাহান।'''
+ফারাকহান এপাগাউ ইতু করানি নাইসে!",
 'previewconflict' => 'এরে আগচা এহান পতানির লয়াগত আসে ইকা অহান ইতু করানির পিসে চেইতে কিসারে ইতই অহানর অংতা আহান।',
 'session_fail_preview' => "'''ঙাক্করে দিস! সেশন ডাটা অতা মাঙনায় অতা ইতু নাইসে। কৃপা করিয়া বারো হৎনা কর।
 যদি অহানেউ কাম নাইলে, অহান ইলে তর অ্যাকাউন্টহাত্ত [[Special:UserLogout|লগ আউট]] করিয়া বারো লগ ইন কর।",
@@ -709,6 +709,7 @@ $2',
 
 পাতা এহান তি আরাতা হঙকরতেইতানা কিতা খালকরিয়া চা।
 তর সুবিধারকা পাতা এহানর পুসিসি লগ এহানাত দেনা ইল:",
+'moveddeleted-notice' => 'পাতা এহান থেইকরানি অসে।সূত্র হিসাবেতলে পাতা এহানর থেইকরানির লগ দেনা অইল।',
 'log-fulllog' => 'পুরা লগ চা',
 'edit-gone-missing' => 'পাতাহান আপডেট করানি নুৱারলাং।
 পাতাহান পুস পরসেগা সাত।',
@@ -717,6 +718,9 @@ $2',
 পাতাএহান আগেত্তর আসে।',
 
 # Parser/template warnings
+'post-expand-template-inclusion-warning' => "'''সিঙুইস:''' টেমপ্লেটের ইনক্লুড অংতাহান ডাঙর অসে। টেমপ্লেট কতহান তিলকরানি নুওয়ারতে পারে।",
+'post-expand-template-inclusion-category' => 'যেহাত টেমপ্লেটর ইনক্লুড অংতাহান পাতাহানিত্ত লেমসে',
+'post-expand-template-argument-warning' => "' ' ' সিঙুইস: ' ' ' এরে পাতাহাত তিলসে টেম্পলেট এহার যুক্তি আহান লাম হেলসে। অহানে যুক্তি অহান বেলানি অইল।",
 'post-expand-template-argument-category' => 'পতাহাত পুসিসি মডেলর জর থা পরসেগা',
 'parser-template-loop-warning' => 'মডেলর তরিগ দেখরাং: [[$1]]',
 
@@ -776,6 +780,8 @@ $2',
 'revdelete-unsuppress' => 'সীমাবদ্ধতাহানি নেইকরেদে',
 'revdelete-log' => 'কারণ:',
 'revdel-restore' => 'দৃষ্টিপাত সিলকর',
+'revdel-restore-deleted' => 'পুছিসি সংস্করণহান',
+'revdel-restore-visible' => 'দেহাদেনা একরব সংস্করণহান',
 'pagehist' => 'পাতার ইতিহাসহান',
 'deletedhist' => 'ইতিহাসহান পুস',
 'revdelete-otherreason' => 'আর আর কারণ:',
@@ -812,12 +818,12 @@ $2',
 'mergelogpagetext' => 'তলে হাদি এহানর পাতার ইতিহাসর লগে আরাক পাতার ইতিহাস তিলকরিসি অতার লাতঙগ দেনা ইল।',
 
 # Diffs
-'history-title' => '"$1"-র à¦°à¦¿à¦­à¦¿à¦¸à¦¨ ইতিহাসহান',
+'history-title' => '"$1"-র à¦ªà¦¤à¦¾à¦¨à¦¿à¦° ইতিহাসহান',
 'lineno' => 'লাইন $1:',
 'compareselectedversions' => 'বাসাইল সংস্করণহানি তুলনা কর',
 'showhideselectedversions' => 'বাসিসি রিভিশনহানি দেখাদে/গুর',
 'editundo' => 'আলকর',
-'diff-multi' => '({{PLURAL:$1|হমবুকর রিভিসন আহান|$1 হমবুকর রিভিসন হানি}} দেহাদেনা এহাত না মিহিসে।)',
+'diff-multi' => '({{PLURAL:$2|আতাকুরা আগ |$2 গ আতাকুরা}} সম্পাদন অসে {{PLURAL:$1|হমবুকর রিভিসন আহান|$1 হমবুকর রিভিসন হানি}} দেহাদেনা এহাত না মিহিসে।)',
 
 # Search results
 'searchresults' => 'বিসারলে অতার ফলাফল',
@@ -837,6 +843,7 @@ $2',
 'shown-title' => 'হারি পাতাত $1 {{PLURAL:$1|ফলাফল|ফলাফলহানি}} দেহাদে',
 'viewprevnext' => 'চা ($1 {{int:pipe-separator}} $2) ($3)',
 'searchmenu-legend' => 'বিসারানির অপশনহানি',
+'searchmenu-exists' => "'''উইকি এহাত \"[[:\$1]]\" নাঙে পাতা আহান আসে'''",
 'searchmenu-new' => "'''\"[[:\$1]]\" নাঙর পাতাহান এরে উইকিত হঙকর!'''",
 'searchhelp-url' => 'Help:পাংলাক',
 'searchmenu-prefix' => '[[Special:PrefixIndex/$1|এরে prefix এতাল আসে পাতাহানি]]',
@@ -851,6 +858,7 @@ $2',
 'searchprofile-everything-tooltip' => 'হাব্বি থাকে বিসারা (য়্যারির পাতাতউ)',
 'searchprofile-advanced-tooltip' => 'নিজর লেপকরা নাঙথাকে বিসারা',
 'search-result-size' => '$1 ({{PLURAL:$2|1 ৱাহি|$2 ৱাহিহানি}})',
+'search-result-category-size' => '{{PLURAL: $1 | 1 সদস্য | $1 সদস্যগি}} ({{PLURAL: $2 | 1 উপবিষয়থাকহানি | $2 হান}}, {{PLURAL: $3 | 1 ফাইল | $3 ফাইল}})',
 'search-result-score' => 'মান্নপা $1%',
 'search-redirect' => '(বারোআলথক $1)',
 'search-section' => '(অনুচ্ছেদ $1)',
@@ -865,6 +873,7 @@ $2',
 'searcheverything-enable' => 'হাব্বি নাঙথাকে বিসারা',
 'searchrelated' => 'সাকেই আসে',
 'searchall' => 'হাব্বি',
+'showingresultsheader' => "'''$4''' র কা {{PLURAL:$5|ফলহান '''$3''' র '''$1'''|ফলহানি '''$3''' র মা '''$1 - $2'''}}",
 'nonefound' => "'''নোট''': অকরাতই হুদ্দা কতহান নাঙরফাম বিসারানি অসিল।
 তর বিসারানিহান ''all:'' ব্যবহার করিয়া হারি কন্টেন্টর মা বিসারানিরকা লেপকর (য়্যারির পাতা, মডেল আদি), নাইলে প্রিফিক্স হিসেবে তর হাদাপাসত নাঙলাম ব্যবহার কর।",
 'search-nonefound' => 'তি বিসারার অহানর লগে মান্নাপাতা নাপারাঙ।',
@@ -932,6 +941,8 @@ $2',
 'email' => 'ইমেইল',
 'prefs-help-realname' => 'আয়ৌপা নাংহান নাদলেউ চলের।
 যদি তি দের অতাইলে তর কামর থাকাত দেনাত সুবিধা অইতই।',
+'prefs-help-email' => 'ই-মেইল ঠিকানা ঐচ্ছিক, তবে খন্তাচাবি পাহুরলে নুৱা করে খন্তাচাবি নেনাত এহান দরকার ইতই।',
+'prefs-help-email-others' => 'তি তর পরিচয় না ফঙ করিয়াউ তর আতাকুরার পাতা বারো য়্যারীর পাতাহানর মাতুঙে আরতারে তর লগে যোগাযোগ করানি দেনা পারর।',
 'prefs-advancedediting' => 'উচ্চতর অপশন',
 'prefs-advancedrc' => 'উচ্চতর অপশন',
 'prefs-advancedrendering' => 'উচ্চতর অপশন',
@@ -983,6 +994,10 @@ $2',
 'recentchanges' => 'হাদিএহান পতাসিতা',
 'recentchanges-legend' => 'হাদি এহানর পতানির পছনহানি',
 'recentchanges-feed-description' => 'ফিড এহানর মা পাতা এহার পতানিহানর গজে মিল্লেং দে।',
+'recentchanges-label-newpage' => 'সম্পাদনা এহানে নুৱা পাতা আহান হঙকরল',
+'recentchanges-label-minor' => 'এহান হুরকা সম্পাদনাহান',
+'recentchanges-label-bot' => 'সম্পাদনা এহান বটগই করিসেহানে',
+'recentchanges-label-unpatrolled' => 'সম্পাদনা এহান এবাকাউ পরীক্ষা করিয়া নাচাসি',
 'rcnote' => "গেলগা {{PLURAL:$2|দিনে|'''$2''' দিনে}} অসে {{PLURAL:$1|'''১'''|'''$1'''}}হান সিলপা তলে দেহানি ইল (যেহানর এপাগার খেন্তাম বারো তারিখ $5, $4)।",
 'rcnotefrom' => "তলে গেলগা '''$2''' ত্ত পতাসিতা দেনা অইল ('''$1''' পেয়া)।",
 'rclistfrom' => 'নুৱাতা পতাসিতা $1 পাতাহানাত্ত চিঙকরিয়া',
@@ -1076,6 +1091,7 @@ $2',
 'filehist' => 'ফাইলর ইতিহাস',
 'filehist-help' => 'দিন/সময়-র গজে যাতিলে ঔ খেন্তাম পেয়া হঙিসে ফাইলগ চ পারতেই।',
 'filehist-deleteall' => 'হাব্বি পুস',
+'filehist-revert' => 'আগর অঙতাত আলকরে যাগা',
 'filehist-current' => 'এপাগা',
 'filehist-datetime' => 'দিন/সময়',
 'filehist-thumb' => 'হুরকাকরে ফটকগি',
@@ -1085,10 +1101,11 @@ $2',
 'filehist-filesize' => 'ফাইলর সাইজহান',
 'filehist-comment' => 'মতহান',
 'filehist-missing' => 'ফাইলগ মাঙুইসে',
-'imagelinks' => 'ফাà¦\87লর à¦\9cà§\81রনহানি',
+'imagelinks' => 'ফাà¦\87লর à¦¬à§\8dযবহার',
 'linkstoimage' => 'এরে ফাইলর লগে {{PLURAL:$1|পাতার মিলাপ|$1 পাতাহানির মিলাপ}} আসে:',
 'nolinkstoimage' => 'ফাইল এগর লগে মিলাপ অসে অসাদে কোন পাতা নেই।',
 'sharedupload' => 'ফাইল এগ $1ত্ত আহিসেগ বারো অন্যান্য প্রকল্পতউ ব্যবহৃত ইতে পারে।',
+'sharedupload-desc-here' => 'এরে ফাইলএগ $1 ত্ত বারো আর প্রকল্পত মিহিতে পারে। এহানর [$2 ফাইলর বিবরণ পাতা]-র গজে তলে হবাকরে মুকিয়া মাতানি ইল।',
 'uploadnewversion-linktext' => 'এরে ফাইল এগর নুৱা সংস্করনহান আপলোড কর',
 'shared-repo-from' => '$1 রাঙতো',
 
@@ -1127,6 +1144,7 @@ $2',
 'statistics-pages' => 'পাতাহানি',
 
 'disambiguations' => 'সন্দই চুমকরের পাতাহানি',
+'disambiguationspage' => 'Template:সন্দই চুম',
 
 'doubleredirects' => 'আলথকে যানা দ্বিমাউ মাতের',
 
@@ -1166,6 +1184,7 @@ $2',
 'deadendpages' => 'যে পাতাহানিত্ত কোন মিলাপ নেই',
 'protectedpages' => 'লুকরিসি পাতাহানি',
 'listusers' => 'আতাকুরার লাতংগ',
+'usercreated' => 'লিঙ্গ: $3 হঙিল $1 তারিখে, খেন্তাম: $2',
 'newpages' => 'নুৱা পাতাহানি',
 'newpages-username' => 'আতাকুরা:',
 'ancientpages' => 'পুরানা পাতাহানি',
@@ -1210,6 +1229,7 @@ Also see [[Special:WantedCategories|wanted categories]].',
 
 # Special:LinkSearch
 'linksearch' => 'বারেদের লগে মিলাপ',
+'linksearch-line' => '$2 ত্ত $1 এরে লিংক এহান আহিসেহান',
 
 # Special:ListUsers
 'listusers-submit' => 'দেহাদে',
@@ -1246,6 +1266,7 @@ Also see [[Special:WantedCategories|wanted categories]].',
 # Watchlist
 'watchlist' => 'মর তালাবি',
 'mywatchlist' => 'মর তালাবি',
+'watchlistfor2' => '$1 ($2)-র কা',
 'addedwatchtext' => "\"<nowiki>\$1</nowiki>\" পাতা এহান তর [[Special:Watchlist|আহির-আরুম তালিকা]]-ত তিলকরানি ইল। পিসেদে এরে পাতা এহান বারো পাতা এহানর লগে সাকেই আসে য়্যারী পাতাত অইতই হারি জাতর পতানি এহানাত তিলকরানি অইতই। অতাবাদেউ [[Special:RecentChanges|হাদি এহানর পতানিহানি]]-ত পাতা এহানরে '''গাঢ়করা''' মেয়েকে দেহা দেনা অইতই যাতে তি নুঙিকরে পাতা এহান চিনে পারবেতা।
 
 পিসেদে তি পাতা এহানরে থেইকরানি মনেইলে \"আহির-আরুমেত্ত থেইকরেদে\" ট্যাবগত ক্লিক করিস৷",
@@ -1271,6 +1292,7 @@ Also see [[Special:WantedCategories|wanted categories]].',
 'confirmdeletetext' => 'তি যে পাতাহান পুসানি লেপুইসত অহানর লগে ইতিহাসহানউ পুসতই।
 তি লেপকর যে তি এহান করতেই বুলিয়া, বারো তি এহানর পিসহান হারপাসত লগে [[{{MediaWiki:Policy-url}}|পলিসিহান]] ইলয়া তি কামএহান করানিত লেপুইসত।',
 'actioncomplete' => 'কামহান লমিল।',
+'actionfailed' => 'অ্যাকশনহান পুরা নাইল',
 'deletedtext' => '"$1" পুসানি অইল।
 চা $2 এহার বারে আগে আসে পুসানির লাতংগ।',
 'dellogpage' => 'পুসিসিতার লাতংগ',
@@ -1328,6 +1350,7 @@ Also see [[Special:WantedCategories|wanted categories]].',
 # Undelete
 'undeletebtn' => 'বারোইতুকর',
 'undeletelink' => 'চা/আলথক কর',
+'undeleteviewlink' => 'দেহাদে',
 
 # Namespace form on various pages
 'namespace' => 'নাঙরথাক:',
@@ -1346,9 +1369,12 @@ Also see [[Special:WantedCategories|wanted categories]].',
 'sp-contributions-newbies' => 'হুদ্দা নুৱা একাউন্টর অবদানহানি দেহাদে',
 'sp-contributions-newbies-sub' => 'নুৱা একাউন্টর কা',
 'sp-contributions-blocklog' => 'থেপকরিসি লগ',
+'sp-contributions-uploads' => 'আপলোডহানি',
+'sp-contributions-logs' => 'লগহানি',
 'sp-contributions-talk' => 'অতারা',
 'sp-contributions-search' => 'অবদানহানি বিসারা',
 'sp-contributions-username' => 'আইপি (IP) ঠিকানা নাইলে আতাকুরার নাঙহান:',
+'sp-contributions-toponly' => 'হুদ্দা অরে সম্পাদনা অহানি দেহাদে যেহানি হাদি এহানর সংস্করণহাত তিলসে।',
 'sp-contributions-submit' => 'বিসারা',
 
 # What links here
@@ -1359,13 +1385,14 @@ Also see [[Special:WantedCategories|wanted categories]].',
 'nolinkshere' => "পাতা '''[[:$1]]'''হানাত কোন মিলাপ নেই।",
 'isredirect' => 'বুলনদের পাতা',
 'istemplate' => 'বরানি',
-'isimage' => 'à¦\9bবি মিলাপ',
+'isimage' => 'ফাà¦\87ল মিলাপ',
 'whatlinkshere-prev' => '{{PLURAL:$1|পিসেদে|পিসেদে $1}}',
 'whatlinkshere-next' => '{{PLURAL:$1|থাংনা|থাংনা $1}}',
 'whatlinkshere-links' => '← মিলাপহানি',
 'whatlinkshere-hideredirs' => '$1 হানি আলথকর দিশা দেহার',
 'whatlinkshere-hidetrans' => '$1 ট্রান্সক্লুশন',
 'whatlinkshere-hidelinks' => '$1 মিলাপহানি',
+'whatlinkshere-hideimages' => '$1 ছবি মিলাপ',
 'whatlinkshere-filters' => 'চালুনী',
 
 # Block/unblock
@@ -1399,7 +1426,7 @@ Also see [[Special:WantedCategories|wanted categories]].',
 'badipaddress' => 'আইপি ঠিকানাহান গ্রহনযোগ্যনাইসে',
 'blockipsuccesssub' => 'থেপকরানিহান চুমিল',
 'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] রে থেপকরিয়া থসি <br />থেপকরানিহান খাল করানি থকিলে,[[Special:BlockList| থেপকরিয়া থসি আইপি ঠিকানার তালিকাহান]] চা।',
-'ipblocklist' => 'থà§\87পà¦\95রিয়া à¦¥à¦¸à¦¿ à¦\86à¦\87পি à¦ à¦¿à¦\95ানা à¦¬à¦¾à¦°à§\8b à¦\86তাà¦\95à§\81রার à¦²à¦¾à¦¤à¦\99গি',
+'ipblocklist' => 'থà§\87পà¦\95রিয়া à¦¥à¦¸à¦¿ à¦\86তাà¦\95à§\81রাগি',
 'blocklink' => 'থেপ কর',
 'unblocklink' => 'ব্লকনাকরি',
 'change-blocklink' => 'ব্লক সিলকর',
@@ -1454,6 +1481,7 @@ Also see [[Special:WantedCategories|wanted categories]].',
 # Namespace 8 related
 'allmessages' => 'সিস্টেমর পৌহানি',
 'allmessagesname' => 'নাং',
+'allmessagesdefault' => 'আদ্যকার টেক্সট',
 'allmessagescurrent' => 'হাদি এহানর ৱাহি',
 'allmessagestext' => 'তলে মিডিয়াউইকির নাঙরথাকে পানা একরের সিস্টেম পৌহানির তালিকাহান দেনা ইল।
 কৃপা করিয়া [//www.mediawiki.org/wiki/Localisation মিডিয়াউইকি অনুবাদর হেইচা পাতাত] বারো [//translatewiki.net translatewiki.net] মিডিয়াউইকি অনুবাদ করানির কা যানা পারর।',
@@ -1535,6 +1563,7 @@ Also see [[Special:WantedCategories|wanted categories]].',
 'tooltip-rollback' => '"রোলব্যাক" এরে পাতার লমিল পতাকুরার পতানিত ক্লিক আহাত আলথক নেনারকা',
 'tooltip-undo' => '"আলথক" এর পতানিহানরে আগর জাগাত নিতইগা বারো আগচা সহকারে পতানির ফরমহান নিকুলতই।
 এহান পতানির সারাংশত কারণহান তিলকরানির সুযোগ দিতই।',
+'tooltip-summary' => 'হুরকাকরে সারাংশহান মাতেদে',
 
 # Attribution
 'anonymous' => '{{SITENAME}}র বেনাঙর {{PLURAL:$1|আতাকুরা|আতাকুরাগি}}',
@@ -1572,7 +1601,7 @@ Also see [[Special:WantedCategories|wanted categories]].',
 'metadata-help' => 'ফাইল এগত আরাকউ হেলপা পৌ খানি তিলুইসে, মনে অরতা ডিজিটাল ক্যামেরাগত্ত নাইলে স্ক্যানারহাত্ত হমাসে। যদি ফাইল এগ মুল অংতাত্ত পতিয়া থার অতা ইলে খানি মানি পৌ না তিলুতে পারে।',
 'metadata-expand' => 'আরাকউ সালকরিসি পৌ চা',
 'metadata-collapse' => 'সালকরিসি পৌ ঝিপা',
-'metadata-fields' => 'এরে পৌ এহান তিলসে EXIF মেটাপৌ অতা ছবির পাতাত দেখাদেনা ইতই, যেপাগা হেলপা উপাত্ত সারণি অতা জিপানি ইতই। হের ক্ষেত্রহানি স্বাভাবিক অবস্থাত জিপিয়া থাইতই।
+'metadata-fields' => 'এরে পৌ এহান তিলসে ছবি মেটাপৌ অতা ছবির পাতাত দেখাদেনা ইতই, যেপাগা হেলপা উপাত্ত সারণি অতা জিপানি ইতই। হের ক্ষেত্রহানি স্বাভাবিক অবস্থাত জিপিয়া থাইতই।
 * make
 * model
 * datetimeoriginal
@@ -1641,6 +1670,9 @@ Also see [[Special:WantedCategories|wanted categories]].',
 'watchlisttools-edit' => 'তর তালাবির পাতা চা বারো পতা',
 'watchlisttools-raw' => 'পেরকা তালাবির পাতা পতা',
 
+# Core parser functions
+'duplicate-defaultsort' => '\' \' \' সিঙুইস: \' \' \'  ডিফল্ট হাজানির কিহানি "$2" আগর ডিফল্ট হাজানির কিহানিরে "$1" উচিত নাকরের।',
+
 # Special:Version
 'version' => 'সংস্করন',
 'version-specialpages' => 'বিশেষ পাতাহানি',
@@ -1666,6 +1698,9 @@ Also see [[Special:WantedCategories|wanted categories]].',
 # Special:BlankPage
 'blankpage' => 'হুদালা পাতাহান',
 
+# External image whitelist
+'external_image_whitelist' => '  #লাইন এহান ঠিক যেসাদে আসে<প্রাক> অসাদে থ<pre> #রেগুলার এক্সপ্রেশনর টুমা তলে (হুদ্দা টুমা / / হমবুকে যেহান যারগা) বহা#এহানি এক্সটার্নাল (hotlinked) ছবির URL-র লগে মিল করানি অইতই#যেতা মিলতই, ছবি হিসাবে দেহাদেনা অইতই, নাইলে হুদ্দা ছবির লিংক দেহানি অইতই#যে লাইন হানর পয়লা  # আসে অরে লাইনহানি মন্তব্যহানি হিসাবে ব্যবহার করানি অসে#এহান কেস-অসংবেদী#এহার রেখার গজে regex টুমা বহা। এরে লাইন এহান ঠিক যেসাদে আসে অসাদে থ।</pre>',
+
 # Special:Tags
 'tag-filter' => '[[Special:Tags|ট্যাগ]] সাকানি:',
 'tag-filter-submit' => 'সাকানি',
index df7a4f5..ce988d8 100644 (file)
@@ -193,15 +193,15 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Kemmañ ur rann dre glikañ a-zehou<br /> war titl ar rann',
 'tog-showtoc' => 'Diskouez an daolenn<br /> (evit ar pennadoù zo ouzhpenn 3 rann enno)',
 'tog-rememberpassword' => "Derc'hel soñj eus ma ger-tremen war an urzhiataer-mañ (evit $1 devezh{{PLURAL:$1||}} d'ar muiañ)",
-'tog-watchcreations' => 'Evezhiañ ar pajennoù krouet ganin',
-'tog-watchdefault' => 'Evezhiañ ar pennadoù savet pe kemmet ganin',
-'tog-watchmoves' => "Ouzhpennañ da'm roll evezhiañ ar pajennoù adanvet ganin",
-'tog-watchdeletion' => "Ouzhpennañ da'm roll evezhiañ ar pajennoù diverket ganin",
+'tog-watchcreations' => "Ouzhpennañ ar pajennoù krouet ganin da'm roll evezhiañ",
+'tog-watchdefault' => "Ouzhpennañ ar pajennoù kemmet ganin da'm roll evezhiañ",
+'tog-watchmoves' => "Ouzhpennañ ar pajennoù dilec'hiet ganin da'm roll evezhiañ",
+'tog-watchdeletion' => "Ouzhpennañ ar pajennoù diverket ganin da'm roll evezhiañ",
 'tog-minordefault' => "Sellet ouzh ar c'hemmoù degaset ganin<br /> evel kemmoù dister dre ziouer",
 'tog-previewontop' => 'Rakwelet tres ar bajenn a-us ar prenestr skridaozañ',
 'tog-previewonfirst' => 'Rakwelet tres ar bajenn kerkent hag an aozadenn gentañ',
 'tog-nocache' => 'Diweredekaat krubuilh ar pajennoù gant ar merdeer',
-'tog-enotifwatchlistpages' => 'Kas ur postel din pa vez kemmet ur bajenn evezhiet ganin',
+'tog-enotifwatchlistpages' => 'Kas ur postel din pa vez kemmet ur bajenn zo war ma roll evezhiañ',
 'tog-enotifusertalkpages' => 'Kas ur postel din pa vez kemmet ma fajenn gaozeal',
 'tog-enotifminoredits' => 'Kas ur postel din, ha pa vije evit kemenn kemmoù dister',
 'tog-enotifrevealaddr' => "Lakaat ma chomlec'h postel war wel er posteloù kemenn-diwall",
@@ -535,6 +535,8 @@ Kasit keloù d'ur [[Special:ListUsers/sysop|merer]], en ur verkañ dezhañ choml
 'cannotdelete' => 'Dibosupl diverkañ ar bajenn pe ar restr "$1".
 Marteze e o bet diverket gant unan bennak all dija.',
 'cannotdelete-title' => 'N\'haller ket diverkañ ar bajenn "$1"',
+'delete-hook-aborted' => "Nullet ar c'hemmañ gant un astenn.
+Abeg dianav.",
 'badtitle' => 'Titl fall',
 'badtitletext' => "Faziek pe c'houllo eo titl ar bajenn goulennet; pe neuze eo faziek al liamm etreyezhel pe etrewiki.
 Marteze ez eus ennañ arouezennoù n'haller ket degemer en titloù.",
@@ -3807,6 +3809,8 @@ A-hend-all e c'hallit ober gant ar furmskrid eeunaet dindan. Ouzhpennet e vo hoc
 'api-error-empty-file' => "Ar restr hoc'h eus roet a oa goullo.",
 'api-error-emptypage' => "N'eo ket aotreet krouiñ pajennoù goullo.",
 'api-error-fetchfileerror' => 'Fazi diabarzh : aet ez eus un dra bennak a-dreuz en ur glask adtapout ar restr.',
+'api-error-fileexists-forbidden' => 'Bez\' ez eus c\'hoazh eus ur restr anvet "$1" ha n\'hall ket bezañ friket.',
+'api-error-fileexists-shared-forbidden' => "Bez' ez eus c'hoazh eus ur restr anvet \"\$1\" er c'havlec'h kenrannañ restroù ha n'hall ket bezañ friket anezhi.",
 'api-error-file-too-large' => "Ar restr hoc'h eus roet a oa re vras.",
 'api-error-filename-tooshort' => 'Re verr eo anv ar restr.',
 'api-error-filetype-banned' => 'Difennet eo ar seurt restroù',
index 5b3df80..4d25907 100644 (file)
@@ -207,8 +207,8 @@ $messages = array(
 'tog-editsectiononrightclick' => "Habilita l'edició per seccions en clicar amb el botó dret sobre els títols de les seccions (cal JavaScript)",
 'tog-showtoc' => 'Mostra la taula de continguts (per pàgines amb més de 3 seccions)',
 'tog-rememberpassword' => 'Recorda la sessió al navegador (per un màxim de {{PLURAL:$1|dia|dies}})',
-'tog-watchcreations' => 'Vigila les pàgines que he creat',
-'tog-watchdefault' => 'Afegeix les pàgines que edito a la meua llista de seguiment',
+'tog-watchcreations' => 'Afegeix les pàgines que vagi creant a la llista de seguiment',
+'tog-watchdefault' => 'Afegeix les pàgines que vagi creant a la llista de seguiment',
 'tog-watchmoves' => 'Afegeix les pàgines que reanomeni a la llista de seguiment',
 'tog-watchdeletion' => 'Afegeix les pàgines que elimini a la llista de seguiment',
 'tog-minordefault' => 'Marca totes les contribucions com a edicions menors per defecte',
@@ -550,6 +550,8 @@ Aviseu-ho llavors a un [[Special:ListUsers/sysop|administrador]], deixant-li cla
 'cannotdelete' => "No s'ha pogut esborrar la pàgina o fitxer «$1».
 Potser ja ha estat esborrat per algú altre.",
 'cannotdelete-title' => 'No es pot suprimir la pàgina " $1 "',
+'delete-hook-aborted' => 'Un «hook» ha interromput la supressió.
+No ha donat cap explicació.',
 'badtitle' => 'El títol no és correcte',
 'badtitletext' => 'El títol de la pàgina que heu introduït no és correcte, és en blanc o conté un enllaç trencat amb un altre projecte. També podria contenir algun caràcter no acceptat als títols de pàgina.',
 'perfcached' => "Les dades següents es troben a la memòria cau i podrien no estar al dia. Hi ha un màxim {{PLURAL:$1|d'un resultat|de $1 resultats}} disponibles a la memòria cau.",
@@ -2048,7 +2050,7 @@ Vegeu també [[Special:WantedCategories|les categories soŀlicitades]].",
 'sp-deletedcontributions-contribs' => 'contribucions',
 
 # Special:LinkSearch
-'linksearch' => "Recerca d'enllaços externs",
+'linksearch' => "Cerca d'enllaços externs",
 'linksearch-pat' => 'Patró de cerca:',
 'linksearch-ns' => 'Espai de noms:',
 'linksearch-ok' => 'Cerca',
@@ -3767,6 +3769,8 @@ Altrament, podeu fer servir un senzill formulari a continuació. El vostre comen
 'api-error-empty-file' => 'El fitxer que heu tramès està buit.',
 'api-error-emptypage' => 'No es permet la creació de pàgines noves en blanc.',
 'api-error-fetchfileerror' => 'Error intern: quelcom no ha funcionat en accedir al fitxer.',
+'api-error-fileexists-forbidden' => "Ja existeix un fitxer amb el nom «$1» i no pot sobreescriure's.",
+'api-error-fileexists-shared-forbidden' => "Ja existeix un fitxer amb el nom «$1» al repositori de fitxers compartits i no pot sobreescriure's.",
 'api-error-file-too-large' => 'El fitxer que heu tramès és massa gran.',
 'api-error-filename-tooshort' => 'El nom del fitxer és massa curt.',
 'api-error-filetype-banned' => 'Aquest tipus de fitxer està prohibit.',
index cb6fdff..70eb7e4 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Chechen (Ð\9dохчийн)
+/** Chechen (нохчийн)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index c3abfdc..a03795c 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Crimean Turkish (Cyrillic script) (â\80ªÐ\9aъырымтатарджа (Кирилл)‬)
+/** Crimean Turkish (Cyrillic script) (â\80ªÐºъырымтатарджа (Кирилл)‬)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index 84a2ee2..691349f 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Crimean Turkish (Latin script) (‪Qırımtatarca (Latin)‬)
+/** Crimean Turkish (Latin script) (‪qırımtatarca (Latin)‬)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index ae36d32..62e7ed2 100644 (file)
@@ -369,8 +369,8 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Zapnout možnost editace části stránky pomocí kliknutí pravým tlačítkem na nadpisy stránky (JavaScript)',
 'tog-showtoc' => 'Zobrazovat obsah (na stránkách s více než třemi nadpisy)',
 'tog-rememberpassword' => 'Zapamatovat si mé přihlášení v tomto prohlížeči (maximálně $1 {{PLURAL:$1|den|dny|dní}})',
-'tog-watchcreations' => 'Přidávat mnou založené stránky ke sledovaným',
-'tog-watchdefault' => 'Přidávat mnou editované stránky ke sledovaným',
+'tog-watchcreations' => 'Přidávat mnou založené stránky a načtené soubory ke sledovaným',
+'tog-watchdefault' => 'Přidávat mnou editované stránky a soubory ke sledovaným',
 'tog-watchmoves' => 'Přidávat mnou přesouvané stránky mezi sledované',
 'tog-watchdeletion' => 'Přidávat stránky, které smažu, mezi sledované',
 'tog-minordefault' => 'Označit editaci implicitně jako malá editace',
@@ -1110,7 +1110,7 @@ Zřejmě byla smazána.',
 'expensive-parserfunction-warning' => 'Varování: Tato stránka obsahuje příliš mnoho volání výkonnostně náročných funkcí parseru.
 
 Povolený limit je $2, v současné chvíli však {{PLURAL:$1|zde jedno volání je|zde jsou $2 volání|zde je $2 volání}}.',
-'expensive-parserfunction-category' => 'Stránky s příliš vysokým počtem volání funkcí parseru.',
+'expensive-parserfunction-category' => 'Stránky s příliš mnoho voláními náročných funkcí parseru',
 'post-expand-template-inclusion-warning' => 'Varování: Objem vkládaných šablon je příliš velký.
 Některé šablony nebudou vloženy.',
 'post-expand-template-inclusion-category' => 'Stránky překračující povolenou velikost vložených šablon',
index b03d293..6344d99 100644 (file)
@@ -412,17 +412,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Einzelne Abschnitte per Rechtsklick bearbeiten (benötigt JavaScript)',
 'tog-showtoc' => 'Anzeige eines Inhaltsverzeichnisses auf Seiten mit mehr als drei Überschriften',
 'tog-rememberpassword' => 'Mit diesem Browser dauerhaft angemeldet bleiben (maximal $1 {{PLURAL:$1|Tag|Tage}})',
-'tog-watchcreations' => 'Selbst erstellte Seiten automatisch beobachten',
-'tog-watchdefault' => 'Selbst geänderte Seiten automatisch beobachten',
-'tog-watchmoves' => 'Selbst verschobene Seiten automatisch beobachten',
-'tog-watchdeletion' => 'Selbst gelöschte Seiten automatisch beobachten',
+'tog-watchcreations' => 'Selbst erstellte Seiten und hochgeladene Dateien automatisch beobachten',
+'tog-watchdefault' => 'Selbst geänderte Seiten und Dateien automatisch beobachten',
+'tog-watchmoves' => 'Selbst verschobene Seiten und Dateien automatisch beobachten',
+'tog-watchdeletion' => 'Selbst gelöschte Seiten und Dateien automatisch beobachten',
 'tog-minordefault' => 'Eigene Änderungen standardmäßig als geringfügig markieren',
 'tog-previewontop' => 'Vorschau oberhalb des Bearbeitungsfensters anzeigen',
 'tog-previewonfirst' => 'Beim ersten Bearbeiten immer die Vorschau anzeigen',
 'tog-nocache' => 'Seitencache des Browsers deaktivieren',
-'tog-enotifwatchlistpages' => 'Bei Änderungen an beobachteten Seiten E-Mails senden',
+'tog-enotifwatchlistpages' => 'Bei Änderungen an beobachteten Seiten oder Dateien E-Mails senden',
 'tog-enotifusertalkpages' => 'Bei Änderungen an meiner Benutzer-Diskussionsseite E-Mails senden',
-'tog-enotifminoredits' => 'Auch bei kleinen Änderungen an Seiten E-Mails senden',
+'tog-enotifminoredits' => 'Auch bei kleinen Änderungen an Seiten und Dateien E-Mails senden',
 'tog-enotifrevealaddr' => 'Meine E-Mail-Adresse in Benachrichtigungs-E-Mails anzeigen',
 'tog-shownumberswatching' => 'Anzahl der beobachtenden Benutzer anzeigen',
 'tog-oldsig' => 'Vorhandene Signatur:',
index 0b3ee25..8478b7d 100644 (file)
@@ -174,17 +174,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Wobźěłanje wótstawkow pśez kliknjenje z pšaweju tastu myški zmóžniś (JavaScript)',
 'tog-showtoc' => 'Wopśimjeśe pokazaś, jolic ma bok wěcej nježli 3 nadpisma',
 'tog-rememberpassword' => 'Z toś tym wobglědowakom pśizjawjony wóstaś (za maksimalnje $1 {{PLURAL:$1|źeń|dnja|dny|dnjow}})',
-'tog-watchcreations' => 'Boki, kótarež załožyjom, awtomatiski wobglědowaś',
-'tog-watchdefault' => 'Boki, kótarež změnijom, awtomatiski wobglědowaś',
-'tog-watchmoves' => 'Boki, kótarež som pśesunuł, awtomatiski wobglědowaś',
-'tog-watchdeletion' => 'Boki, kótarež som wulašował, awtomatiski wobglědowaś',
+'tog-watchcreations' => 'Boki, kótarež napórajom a dataje, kótarež nagrawam, wobglědowaś',
+'tog-watchdefault' => 'Boki a dataje , kótarež změnijom, wobglědowaś',
+'tog-watchmoves' => 'Boki a dataje, kótarež som pśesunuł, wobglědowaś',
+'tog-watchdeletion' => 'Boki a dataje, kótarež som wulašował, wobglědowaś',
 'tog-minordefault' => 'Wšykne móje změny ako małe markěrowaś',
 'tog-previewontop' => 'Zespominanje wušej wobźěłowańskego póla pokazaś',
 'tog-previewonfirst' => 'Pśi prědnem wobźěłanju pśecej zespominanje pokazaś',
 'tog-nocache' => 'Cache bokow wobglědowaka znjemóžniś',
-'tog-enotifwatchlistpages' => 'E-mail pósłaś, jolic se wobglědowany bok změnja',
+'tog-enotifwatchlistpages' => 'E-mail pósłaś, jolic se wobglědowany bok abo wobglědowana dataja  změnja',
 'tog-enotifusertalkpages' => 'E-mail pósłaś, změnijo-lic se mój diskusijny bok',
-'tog-enotifminoredits' => 'E-mail teke małych změnow dla pósłaś',
+'tog-enotifminoredits' => 'E-mail teke małych změnow bokow a datajow dla pósłaś',
 'tog-enotifrevealaddr' => 'Móju e-mailowu adresu w e-mailowych pówěźeńkach pokazaś',
 'tog-shownumberswatching' => 'Licbu wobglědujucych wužywarjow pokazaś',
 'tog-oldsig' => 'Eksistěrujuca signatura:',
index ffef8a1..48ec9b9 100644 (file)
@@ -760,7 +760,7 @@ XHTML id names.
 'index-category'                 => 'Indexed pages',
 'noindex-category'               => 'Noindexed pages',
 'broken-file-category'           => 'Pages with broken file links',
-'categoryviewer-pagedlinks'      => '($1) ($2)',
+'categoryviewer-pagedlinks'      => '($1) ($2)', # only translate this message to other languages if you have to change it
 
 'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD', # only translate this message to other languages if you have to change it
 
@@ -964,88 +964,90 @@ This might also indicate a bug in the software used by {{SITENAME}}.',
 A list of valid special pages can be found at [[Special:SpecialPages|{{int:specialpages}}]].',
 
 # General errors
-'error'                => 'Error',
-'databaseerror'        => 'Database error',
-'dberrortext'          => 'A database query syntax error has occurred.
+'error'                         => 'Error',
+'databaseerror'                 => 'Database error',
+'dberrortext'                   => 'A database query syntax error has occurred.
 This may indicate a bug in the software.
 The last attempted database query was:
 <blockquote><tt>$1</tt></blockquote>
 from within function "<tt>$2</tt>".
 Database returned error "<tt>$3: $4</tt>".',
-'dberrortextcl'        => 'A database query syntax error has occurred.
+'dberrortextcl'                 => 'A database query syntax error has occurred.
 The last attempted database query was:
 "$1"
 from within function "$2".
 Database returned error "$3: $4"',
-'laggedslavemode'      => "'''Warning:''' Page may not contain recent updates.",
-'readonly'             => 'Database locked',
-'enterlockreason'      => 'Enter a reason for the lock, including an estimate of when the lock will be released',
-'readonlytext'         => 'The database is currently locked to new entries and other modifications, probably for routine database maintenance, after which it will be back to normal.
+'laggedslavemode'               => "'''Warning:''' Page may not contain recent updates.",
+'readonly'                      => 'Database locked',
+'enterlockreason'               => 'Enter a reason for the lock, including an estimate of when the lock will be released',
+'readonlytext'                  => 'The database is currently locked to new entries and other modifications, probably for routine database maintenance, after which it will be back to normal.
 
 The administrator who locked it offered this explanation: $1',
-'missing-article'      => 'The database did not find the text of a page that it should have found, named "$1" $2.
+'missing-article'               => 'The database did not find the text of a page that it should have found, named "$1" $2.
 
 This is usually caused by following an outdated diff or history link to a page that has been deleted.
 
 If this is not the case, you may have found a bug in the software.
 Please report this to an [[Special:ListUsers/sysop|administrator]], making note of the URL.',
-'missingarticle-rev'   => '(revision#: $1)',
-'missingarticle-diff'  => '(Diff: $1, $2)',
-'readonly_lag'         => 'The database has been automatically locked while the slave database servers catch up to the master',
-'internalerror'        => 'Internal error',
-'internalerror_info'   => 'Internal error: $1',
-'fileappenderrorread'  => 'Could not read "$1" during append.',
-'fileappenderror'      => 'Could not append "$1" to "$2".',
-'filecopyerror'        => 'Could not copy file "$1" to "$2".',
-'filerenameerror'      => 'Could not rename file "$1" to "$2".',
-'filedeleteerror'      => 'Could not delete file "$1".',
-'directorycreateerror' => 'Could not create directory "$1".',
-'filenotfound'         => 'Could not find file "$1".',
-'fileexistserror'      => 'Unable to write to file "$1": File exists.',
-'unexpected'           => 'Unexpected value: "$1"="$2".',
-'formerror'            => 'Error: Could not submit form.',
-'badarticleerror'      => 'This action cannot be performed on this page.',
-'cannotdelete'         => 'The page or file "$1" could not be deleted.
+'missingarticle-rev'            => '(revision#: $1)',
+'missingarticle-diff'           => '(Diff: $1, $2)',
+'readonly_lag'                  => 'The database has been automatically locked while the slave database servers catch up to the master',
+'internalerror'                 => 'Internal error',
+'internalerror_info'            => 'Internal error: $1',
+'fileappenderrorread'           => 'Could not read "$1" during append.',
+'fileappenderror'               => 'Could not append "$1" to "$2".',
+'filecopyerror'                 => 'Could not copy file "$1" to "$2".',
+'filerenameerror'               => 'Could not rename file "$1" to "$2".',
+'filedeleteerror'               => 'Could not delete file "$1".',
+'directorycreateerror'          => 'Could not create directory "$1".',
+'filenotfound'                  => 'Could not find file "$1".',
+'fileexistserror'               => 'Unable to write to file "$1": File exists.',
+'unexpected'                    => 'Unexpected value: "$1"="$2".',
+'formerror'                     => 'Error: Could not submit form.',
+'badarticleerror'               => 'This action cannot be performed on this page.',
+'cannotdelete'                  => 'The page or file "$1" could not be deleted.
 It may have already been deleted by someone else.',
-'cannotdelete-title'   => 'Cannot delete page "$1"',
-'delete-hook-aborted'  => 'Deletion aborted by hook.
+'cannotdelete-title'            => 'Cannot delete page "$1"',
+'delete-hook-aborted'           => 'Deletion aborted by hook.
 It gave no explanation.',
-'badtitle'             => 'Bad title',
-'badtitletext'         => 'The requested page title was invalid, empty, or an incorrectly linked inter-language or inter-wiki title.
+'badtitle'                      => 'Bad title',
+'badtitletext'                  => 'The requested page title was invalid, empty, or an incorrectly linked inter-language or inter-wiki title.
 It may contain one or more characters which cannot be used in titles.',
-'perfcached'           => 'The following data is cached and may not be up to date. A maximum of {{PLURAL:$1|one result is|$1 results are}} available in the cache.',
-'perfcachedts'         => 'The following data is cached, and was last updated $1. A maximum of {{PLURAL:$4|one result is|$4 results are}} available in the cache.',
-'querypage-no-updates' => 'Updates for this page are currently disabled.
+'perfcached'                    => 'The following data is cached and may not be up to date. A maximum of {{PLURAL:$1|one result is|$1 results are}} available in the cache.',
+'perfcachedts'                  => 'The following data is cached, and was last updated $1. A maximum of {{PLURAL:$4|one result is|$4 results are}} available in the cache.',
+'querypage-no-updates'          => 'Updates for this page are currently disabled.
 Data here will not presently be refreshed.',
-'wrong_wfQuery_params' => 'Incorrect parameters to wfQuery()<br />
+'wrong_wfQuery_params'          => 'Incorrect parameters to wfQuery()<br />
 Function: $1<br />
 Query: $2',
-'viewsource'           => 'View source',
-'viewsource-title'     => 'View source for $1',
-'actionthrottled'      => 'Action throttled',
-'actionthrottledtext'  => 'As an anti-spam measure, you are limited from performing this action too many times in a short space of time, and you have exceeded this limit.
+'viewsource'                    => 'View source',
+'viewsource-title'              => 'View source for $1',
+'actionthrottled'               => 'Action throttled',
+'actionthrottledtext'           => 'As an anti-spam measure, you are limited from performing this action too many times in a short space of time, and you have exceeded this limit.
 Please try again in a few minutes.',
-'protectedpagetext'    => 'This page has been protected to prevent editing.',
-'viewsourcetext'       => 'You can view and copy the source of this page:',
-'viewyourtext'         => "You can view and copy the source of '''your edits''' to this page:",
-'protectedinterface'   => 'This page provides interface text for the software, and is protected to prevent abuse.',
-'editinginterface'     => "'''Warning:''' You are editing a page which is used to provide interface text for the software.
+'protectedpagetext'             => 'This page has been protected to prevent editing.',
+'viewsourcetext'                => 'You can view and copy the source of this page:',
+'viewyourtext'                  => "You can view and copy the source of '''your edits''' to this page:",
+'protectedinterface'            => 'This page provides interface text for the software, and is protected to prevent abuse.',
+'editinginterface'              => "'''Warning:''' You are editing a page which is used to provide interface text for the software.
 Changes to this page will affect the appearance of the user interface for other users.
 For translations, please consider using [//translatewiki.net/wiki/Main_Page?setlang=en translatewiki.net], the MediaWiki localisation project.",
-'sqlhidden'            => '(SQL query hidden)',
-'cascadeprotected'     => 'This page has been protected from editing, because it is included in the following {{PLURAL:$1|page, which is|pages, which are}} protected with the "cascading" option turned on:
+'sqlhidden'                     => '(SQL query hidden)',
+'cascadeprotected'              => 'This page has been protected from editing, because it is included in the following {{PLURAL:$1|page, which is|pages, which are}} protected with the "cascading" option turned on:
 $2',
-'namespaceprotected'   => "You do not have permission to edit pages in the '''$1''' namespace.",
-'customcssprotected'   => "You do not have permission to edit this CSS page, because it contains another user's personal settings.",
-'customjsprotected'    => "You do not have permission to edit this JavaScript page, because it contains another user's personal settings.",
-'ns-specialprotected'  => 'Special pages cannot be edited.',
-'titleprotected'       => 'This title has been protected from creation by [[User:$1|$1]].
+'namespaceprotected'            => "You do not have permission to edit pages in the '''$1''' namespace.",
+'customcssprotected'            => "You do not have permission to edit this CSS page, because it contains another user's personal settings.",
+'customjsprotected'             => "You do not have permission to edit this JavaScript page, because it contains another user's personal settings.",
+'ns-specialprotected'           => 'Special pages cannot be edited.',
+'titleprotected'                => 'This title has been protected from creation by [[User:$1|$1]].
 The reason given is "\'\'$2\'\'".',
-'filereadonlyerror'    => 'Unable to modify the file "$1" because the file repository "$2" is in read-only mode.
+'filereadonlyerror'             => 'Unable to modify the file "$1" because the file repository "$2" is in read-only mode.
 
 The administrator who locked it offered this explanation: "$3".',
 'invalidtitle-knownnamespace'   => 'Invalid title with namespace "$2" and text "$3"',
 'invalidtitle-unknownnamespace' => 'Invalid title with unknown namespace number $1 and text "$2"',
+'exception-nologin'             => 'Not logged in',
+'exception-nologin-text'        => 'This page or action requires you to be logged in on this wiki.',
 
 # Virus scanner
 'virus-badscanner'     => "Bad configuration: Unknown virus scanner: ''$1''",
@@ -1089,8 +1091,6 @@ Do not forget to change your [[Special:Preferences|{{SITENAME}} preferences]].',
 Please choose a different name.',
 'loginerror'                 => 'Login error',
 'createaccounterror'         => 'Could not create account: $1',
-'exception-nologin'          => 'Not logged in',
-'exception-nologin-text'     => 'This page or action requires you to be logged in on this wiki.',
 'nocookiesnew'               => 'The user account was created, but you are not logged in.
 {{SITENAME}} uses cookies to log in users.
 You have cookies disabled.
@@ -1382,7 +1382,7 @@ Custom .css and .js pages use a lowercase title, e.g. {{ns:user}}:Foo/vector.css
 'note'                             => "'''Note:'''",
 'previewnote'                      => "'''Remember that this is only a preview.'''
 Your changes have not yet been saved!",
-'continue-editing'                 => "Continue editing",
+'continue-editing'                 => 'Continue editing',
 'previewconflict'                  => 'This preview reflects the text in the upper text editing area as it will appear if you choose to save.',
 'session_fail_preview'             => "'''Sorry! We could not process your edit due to a loss of session data.'''
 Please try again.
@@ -1668,7 +1668,7 @@ Note that using the navigation links will reset this column.',
 'mergehistory-comment'             => 'Merged [[:$1]] into [[:$2]]: $3',
 'mergehistory-same-destination'    => 'Source and destination pages cannot be the same',
 'mergehistory-reason'              => 'Reason:',
-'mergehistory-revisionrow'         => '$1 ($2) $3 . . $4 $5 $6',
+'mergehistory-revisionrow'         => '$1 ($2) $3 . . $4 $5 $6', # only translate this message to other languages if you have to change it
 
 # Merge log
 'mergelog'           => 'Merge log',
@@ -1985,6 +1985,7 @@ Your e-mail address is not revealed when other users contact you.',
 'right-writeapi'              => 'Use of the write API',
 'right-delete'                => 'Delete pages',
 'right-bigdelete'             => 'Delete pages with large histories',
+'right-deletelogentry'        => 'Delete and undelete specific log entries',
 'right-deleterevision'        => 'Delete and undelete specific revisions of pages',
 'right-deletedhistory'        => 'View deleted history entries, without their associated text',
 'right-deletedtext'           => 'View deleted text and changes between deleted revisions',
@@ -2295,14 +2296,15 @@ If the problem persists, contact an [[Special:ListUsers/sysop|administrator]].',
 'backend-fail-read'          => 'Could not read file $1.',
 'backend-fail-create'        => 'Could not write file $1.',
 'backend-fail-maxsize'       => 'Could not write file $1 because it is larger than {{PLURAL:$2|one byte|$2 bytes}}.',
-'backend-fail-usable'        => 'Could not write file $1 due to insufficient permissions or missing directories/containers.',
 'backend-fail-readonly'      => 'The storage backend "$1" is currently read-only. The reason given is: "\'\'$2\'\'"',
 'backend-fail-synced'        => 'The file "$1" is in an inconsistent state within the internal storage backends',
 'backend-fail-connect'       => 'Could not connect to storage backend "$1".',
 'backend-fail-internal'      => 'An unknown error occurred in storage backend "$1".',
 'backend-fail-contenttype'   => 'Could not determine the content type of the file to store at "$1".',
 'backend-fail-batchsize'     => 'Storage backend given a batch of $1 file {{PLURAL:$1|operation|operations}}; the limit is $2 {{PLURAL:$2|operation|operations}}.',
+'backend-fail-usable'        => 'Could not write file $1 due to insufficient permissions or missing directories/containers.',
 
+# File journal errors
 'filejournal-fail-dbconnect' => 'Could not connect to the journal database for storage backend "$1".',
 'filejournal-fail-dbquery'   => 'Could not update the journal database for storage backend "$1".',
 
@@ -2696,8 +2698,8 @@ It may contain one or more characters which cannot be used in titles.',
 
 # SpecialCachedPage
 'cachedspecial-viewing-cached-ttl' => 'You are viewing a cached version of this page, which can be up to $1 old.',
-'cachedspecial-viewing-cached-ts' => 'You are viewing a cached version of this page, which might not be completely actual.',
-'cachedspecial-refresh-now' => 'View latest.',
+'cachedspecial-viewing-cached-ts'  => 'You are viewing a cached version of this page, which might not be completely actual.',
+'cachedspecial-refresh-now'        => 'View latest.',
 
 # Special:Categories
 'categories'                    => 'Categories',
@@ -3062,7 +3064,7 @@ It may have already been undeleted.',
 $1',
 'undelete-show-file-confirm'   => 'Are you sure you want to view the deleted revision of the file "<nowiki>$1</nowiki>" from $2 at $3?',
 'undelete-show-file-submit'    => 'Yes',
-'undelete-revisionrow'        => "$1 $2 ($3) $4 . . $5 $6 $7",
+'undelete-revisionrow'         => '$1 $2 ($3) $4 . . $5 $6 $7', # only translate this message to other languages if you have to change it
 
 # Namespace form on various pages
 'namespace'                     => 'Namespace:',
@@ -4618,44 +4620,44 @@ You can also [[Special:EditWatchlist|use the standard editor]].',
 'duplicate-defaultsort' => '\'\'\'Warning:\'\'\' Default sort key "$2" overrides earlier default sort key "$1".',
 
 # Special:Version
-'version'                       => 'Version',
-'version-summary'               => '', # do not translate or duplicate this message to other languages
-'version-extensions'            => 'Installed extensions',
-'version-specialpages'          => 'Special pages',
-'version-parserhooks'           => 'Parser hooks',
-'version-variables'             => 'Variables',
-'version-antispam'              => 'Spam prevention',
-'version-skins'                 => 'Skins',
-'version-api'                   => 'API', # only translate this message to other languages if you have to change it
-'version-other'                 => 'Other',
-'version-mediahandlers'         => 'Media handlers',
-'version-hooks'                 => 'Hooks',
-'version-extension-functions'   => 'Extension functions',
-'version-parser-extensiontags'  => 'Parser extension tags',
-'version-parser-function-hooks' => 'Parser function hooks',
-'version-hook-name'             => 'Hook name',
-'version-hook-subscribedby'     => 'Subscribed by',
-'version-version'               => '(Version $1)',
-'version-svn-revision'          => '(r$2)', # only translate this message to other languages if you have to change it
-'version-license'               => 'License',
-'version-poweredby-credits'     => "This wiki is powered by '''[//www.mediawiki.org/ MediaWiki]''', copyright © 2001-$1 $2.",
-'version-poweredby-others'      => '[{{SERVER}}{{SCRIPTPATH}}/CREDITS others]',
-'version-license-info'          => 'MediaWiki 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.
+'version'                               => 'Version',
+'version-summary'                       => '', # do not translate or duplicate this message to other languages
+'version-extensions'                    => 'Installed extensions',
+'version-specialpages'                  => 'Special pages',
+'version-parserhooks'                   => 'Parser hooks',
+'version-variables'                     => 'Variables',
+'version-antispam'                      => 'Spam prevention',
+'version-skins'                         => 'Skins',
+'version-api'                           => 'API', # only translate this message to other languages if you have to change it
+'version-other'                         => 'Other',
+'version-mediahandlers'                 => 'Media handlers',
+'version-hooks'                         => 'Hooks',
+'version-extension-functions'           => 'Extension functions',
+'version-parser-extensiontags'          => 'Parser extension tags',
+'version-parser-function-hooks'         => 'Parser function hooks',
+'version-hook-name'                     => 'Hook name',
+'version-hook-subscribedby'             => 'Subscribed by',
+'version-version'                       => '(Version $1)',
+'version-svn-revision'                  => '(r$2)', # only translate this message to other languages if you have to change it
+'version-license'                       => 'License',
+'version-poweredby-credits'             => "This wiki is powered by '''[//www.mediawiki.org/ MediaWiki]''', copyright © 2001-$1 $2.",
+'version-poweredby-others'              => '[{{SERVER}}{{SCRIPTPATH}}/CREDITS others]',
+'version-license-info'                  => 'MediaWiki 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.
 
 MediaWiki 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 [{{SERVER}}{{SCRIPTPATH}}/COPYING 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 or [//www.gnu.org/licenses/old-licenses/gpl-2.0.html read it online].',
-'version-software'              => 'Installed software',
-'version-software-product'      => 'Product',
-'version-software-version'      => 'Version',
-'version-entrypoints'           => 'Entry point URLs',
+'version-software'                      => 'Installed software',
+'version-software-product'              => 'Product',
+'version-software-version'              => 'Version',
+'version-entrypoints'                   => 'Entry point URLs',
 'version-entrypoints-header-entrypoint' => 'Entry point',
-'version-entrypoints-header-url' => 'URL',
-'version-entrypoints-articlepath' => '[https://www.mediawiki.org/wiki/Manual:$wgArticlePath Article path]',
-'version-entrypoints-scriptpath' => '[https://www.mediawiki.org/wiki/Manual:$wgScriptPath Script path]',
-'version-entrypoints-index-php' => '[https://www.mediawiki.org/wiki/Manual:index.php index.php]',
-'version-entrypoints-api-php'   => '[https://www.mediawiki.org/wiki/Manual:api.php api.php]',
-'version-entrypoints-load-php'  => '[https://www.mediawiki.org/wiki/Manual:load.php load.php]',
+'version-entrypoints-header-url'        => 'URL',
+'version-entrypoints-articlepath'       => '[https://www.mediawiki.org/wiki/Manual:$wgArticlePath Article path]', # only translate this message to other languages if you have to change it
+'version-entrypoints-scriptpath'        => '[https://www.mediawiki.org/wiki/Manual:$wgScriptPath Script path]', # only translate this message to other languages if you have to change it
+'version-entrypoints-index-php'         => '[https://www.mediawiki.org/wiki/Manual:index.php index.php]', # do not translate or duplicate this message to other languages
+'version-entrypoints-api-php'           => '[https://www.mediawiki.org/wiki/Manual:api.php api.php]', # do not translate or duplicate this message to other languages
+'version-entrypoints-load-php'          => '[https://www.mediawiki.org/wiki/Manual:load.php load.php]', # do not translate or duplicate this message to other languages
 
 # Special:FilePath
 'filepath'         => 'File path',
@@ -4843,9 +4845,9 @@ Otherwise, you can use the easy form below. Your comment will be added to the pa
 'api-error-empty-file'                    => 'The file you submitted was empty.',
 'api-error-emptypage'                     => 'Creating new, empty pages is not allowed.',
 'api-error-fetchfileerror'                => 'Internal error: Something went wrong while fetching the file.',
-'api-error-file-too-large'                => 'The file you submitted was too large.',
 'api-error-fileexists-forbidden'          => 'A file with name "$1" already exists, and cannot be overwritten.',
 'api-error-fileexists-shared-forbidden'   => 'A file with name "$1" already exists in the shared file repository, and cannot be overwritten.',
+'api-error-file-too-large'                => 'The file you submitted was too large.',
 'api-error-filename-tooshort'             => 'The filename is too short.',
 'api-error-filetype-banned'               => 'This type of file is banned.',
 'api-error-filetype-missing'              => 'The filename is missing an extension.',
index 1ce4396..a41f196 100644 (file)
@@ -368,7 +368,7 @@ $messages = array(
 'tog-previewontop' => 'Mostrar previsualización antes del cuadro de edición',
 'tog-previewonfirst' => 'Mostrar previsualización en la primera edición',
 'tog-nocache' => 'Desactivar la caché de páginas del navegador',
-'tog-enotifwatchlistpages' => 'Enviarme un correo electrónico cuando se modifique una página en mi lista de seguimiento',
+'tog-enotifwatchlistpages' => 'Enviarme un correo electrónico cuando se modifique una página o un archivo de mi lista de seguimiento',
 'tog-enotifusertalkpages' => 'Enviarme un correo electrónico cuando se modifique mi página de discusión',
 'tog-enotifminoredits' => 'Notificarme también los cambios menores de páginas',
 'tog-enotifrevealaddr' => 'Revelar mi dirección de correo electrónico en los correos de notificación',
index 8a691fa..132d383 100644 (file)
@@ -1490,7 +1490,7 @@ See ei tohi olla pikem kui $1 {{PLURAL:$1|sümbol|sümbolit}}.',
 'right-read' => 'Lugeda lehekülgi',
 'right-edit' => 'Redigeerida lehekülje sisu',
 'right-createpage' => 'Luua lehekülgi (mis pole arutelu leheküljed)',
-'right-createtalk' => 'Luua arutelu lehekülgi',
+'right-createtalk' => 'Luua arutelulehekülgi',
 'right-createaccount' => 'Luua uusi kasutajakontosid',
 'right-minoredit' => 'Märkida muudatusi pisimuudatustena',
 'right-move' => 'Teisaldada lehekülgi',
index eea2d63..32b6804 100644 (file)
@@ -1165,7 +1165,7 @@ $2
 'storedversion' => 'نسخهٔ ذخیره شده',
 'nonunicodebrowser' => "'''هشدار: مرورگر شما با استانداردهای یونیکد سازگار نیست.'''
 راه حلی به کار گرفته شده تا شما بتوانید صفحه‌ها را با امنیت ویرایش کنید: کاراکترهای غیر ASCII به صورت کدهایی در مبنای شانزده به شما نشان داده می‌شوند.",
-'editingold' => "'''هشدار: شما در حال ویرایش نسخه‌ای قدیمی از این صفحه هستید.''
+'editingold' => "'''هشدار: شما در حال ویرایش نسخه‌ای قدیمی از این صفحه هستید.'''
 اگر ذخیره‌اش کنید، هر تغییری که پس از این نسخه انجام شده‌است از بین خواهد رفت.",
 'yourdiff' => 'تفاوت‌ها',
 'copyrightwarning' => "لطفاً توجه داشته باشید که فرض می‌شود کلیهٔ مشارکت‌های شما با {{SITENAME}} تحت «$2» منتشر می‌شوند (برای جزئیات بیشتر به $1 مراجعه کنید).
index b15c71b..0ea99ab 100644 (file)
@@ -326,15 +326,15 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Muokkaa osioita napsauttamalla otsikkoa hiiren oikealla painikkeella (JavaScript)',
 'tog-showtoc' => 'Näytä sisällysluettelo sivuille, joilla yli 3 otsikkoa',
 'tog-rememberpassword' => 'Muista kirjautumisen tässä selaimessa (enintään $1 {{PLURAL:$1|päivä|päivää}})',
-'tog-watchcreations' => 'Lisää luomani sivut tarkkailulistalle',
-'tog-watchdefault' => 'Lisää muokkaamani sivut tarkkailulistalle',
-'tog-watchmoves' => 'Lisää siirtämäni sivut tarkkailulistalle',
-'tog-watchdeletion' => 'Lisää poistamani sivut tarkkailulistalle',
+'tog-watchcreations' => 'Lisää luomani sivut tarkkailulistalleni',
+'tog-watchdefault' => 'Lisää muokkaamani sivut tarkkailulistalleni',
+'tog-watchmoves' => 'Lisää siirtämäni sivut tarkkailulistalleni',
+'tog-watchdeletion' => 'Lisää poistamani sivut tarkkailulistalleni',
 'tog-minordefault' => 'Muutokset ovat oletuksena pieniä',
 'tog-previewontop' => 'Näytä esikatselu muokkauskentän yläpuolella',
 'tog-previewonfirst' => 'Näytä esikatselu heti, kun muokkaus aloitetaan',
 'tog-nocache' => 'Älä tallenna sivuja selaimen välimuistiin',
-'tog-enotifwatchlistpages' => 'Lähetä sähköpostiviesti tarkkailtujen sivujen muutoksista',
+'tog-enotifwatchlistpages' => 'Lähetä sähköpostiviesti tarkkailulistallani olevien sivujen muutoksista',
 'tog-enotifusertalkpages' => 'Lähetä sähköpostiviesti, kun käyttäjäsivun keskustelusivu muuttuu',
 'tog-enotifminoredits' => 'Lähetä sähköpostiviesti myös pienistä muokkauksista',
 'tog-enotifrevealaddr' => 'Näytä sähköpostiosoitteeni muille lähetetyissä ilmoituksissa',
@@ -698,6 +698,7 @@ Lukituksen asettanut ylläpitäjä on antanut seuraavan syyn toimenpiteelle: "$3
 'invalidtitle-knownnamespace' => 'Virheellinen sivunimi, nimiavaruus "$2" ja teksti "$3"',
 'invalidtitle-unknownnamespace' => 'Virheellinen sivunimi, tuntematon nimiavaruus numero $1 ja teksti $2',
 'exception-nologin' => 'Et ole kirjautuneena',
+'exception-nologin-text' => 'Tämä sivu tai toiminto edellyttää sisäänkirjautumista tähän wikiin.',
 
 # Virus scanner
 'virus-badscanner' => "Virheellinen asetus: Tuntematon virustutka: ''$1''",
@@ -1784,8 +1785,12 @@ $1',
 'backend-fail-read' => 'Tiedostoa $1 ei voitu lukea.',
 'backend-fail-create' => 'Tiedostoa $1 ei voitu luoda.',
 'backend-fail-maxsize' => 'Tiedostoa $1 ei voitu luoda, koska se on suurempi kuin {{PLURAL:$2|yksi tavu|$2 tavua}}.',
+'backend-fail-readonly' => 'Taustajärjestelmä "$1" on tällä hetkellä vain lukutilassa. Syy tähän on: "\'\'$2\'\'"',
+'backend-fail-synced' => 'Tiedoston "$1" tila ei vastaa tiedoston tilaa sisäisissä taustajärjestelmissä.',
 'backend-fail-connect' => 'Varastojärjestelmään "$1" ei saada yhteyttä.',
+'backend-fail-internal' => 'Tuntematon virhe taustajärjestelmässä "$1".',
 'backend-fail-contenttype' => 'Tiedostoa ei voitu tallentaa kohteeseen $1, koska tiedostomuotoa ei voitu määrittää.',
+'backend-fail-batchsize' => 'Taustajärjestelmälle on annettu $1 {{PLURAL:$1|tiedostotoiminto|toimintoa}}; enimmäismäärä on $2 {{PLURAL:$2|tiedostotoiminto|toimintoa}}.',
 'backend-fail-usable' => 'Ei voitu luoda tiedostoa $1, koska käyttöoikeudet eivät riittäneet tai hakemisto puuttuu.',
 
 # Lock manager
@@ -3843,6 +3848,8 @@ Muussa tapauksessa voit käyttää alla olevaa helpompaa lomaketta. Kommenttisi
 'api-error-empty-file' => 'Määrittämäsi tiedosto on tyhjä.',
 'api-error-emptypage' => 'Ei ole sallittua luoda uutta, tyhjää sivua.',
 'api-error-fetchfileerror' => 'Sisäinen virhe: jotakin meni pieleen tiedoston haussa.',
+'api-error-fileexists-forbidden' => 'Tiedosto nimellä "$1" on jo olemassa eikä sitä voi korvata.',
+'api-error-fileexists-shared-forbidden' => 'Tiedosto nimeltä "$1" on jo olemassa yhteisessä tietovarastossa eikä sitä voi korvata.',
 'api-error-file-too-large' => 'Määrittämäsi tiedosto on liian iso.',
 'api-error-filename-tooshort' => 'Tiedoston nimi on liian lyhyt.',
 'api-error-filetype-banned' => 'Tämän tyyppisiä tiedosta ei voi tallentaa.',
index a1c070e..14ceb55 100644 (file)
@@ -379,17 +379,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Activer la modification de sections par clic droit sur leurs titres (nécessite JavaScript)',
 'tog-showtoc' => 'Afficher la table des matières (pour les pages ayant plus de 3 sections)',
 'tog-rememberpassword' => 'Se souvenir de mon identification avec ce navigateur (au maximum $1 {{PLURAL:$1|jour|jours}})',
-'tog-watchcreations' => 'Ajouter les pages que je crée à ma liste de suivi',
-'tog-watchdefault' => 'Ajouter les pages que je modifie à ma liste de suivi',
-'tog-watchmoves' => 'Ajouter les pages que je renomme à ma liste de suivi',
-'tog-watchdeletion' => 'Ajouter les pages que je supprime à ma liste de suivi',
+'tog-watchcreations' => 'Ajouter les pages que je crée et les fichiers que j’importe à ma liste de suivi',
+'tog-watchdefault' => 'Ajouter les pages et les fichiers que je modifie à ma liste de suivi',
+'tog-watchmoves' => 'Ajouter les pages et les fichiers que je renomme à ma liste de suivi',
+'tog-watchdeletion' => 'Ajouter les pages et les fichiers que je supprime à ma liste de suivi',
 'tog-minordefault' => 'Marquer mes modifications comme mineures par défaut',
 'tog-previewontop' => 'Afficher la prévisualisation au-dessus de la zone de modification',
 'tog-previewonfirst' => 'Afficher la prévisualisation lors de la première modification',
 'tog-nocache' => 'Désactiver le cache des pages par le navigateur',
-'tog-enotifwatchlistpages' => 'M’avertir par courriel lorsqu’une page de ma liste de suivi est modifiée',
+'tog-enotifwatchlistpages' => 'M’avertir par courriel lorsqu’une page ou un fichier de ma liste de suivi est modifiée',
 'tog-enotifusertalkpages' => 'M’avertir par courriel si ma page de discussion est modifiée',
-'tog-enotifminoredits' => "M'avertir par courriel même en cas de modifications mineures des pages",
+'tog-enotifminoredits' => "M'avertir par courriel même en cas de modifications mineures des pages ou des fichiers",
 'tog-enotifrevealaddr' => 'Afficher mon adresse de courriel dans les courriels de notification',
 'tog-shownumberswatching' => 'Afficher le nombre d’utilisateurs qui suivent cette page',
 'tog-oldsig' => 'Signature existante :',
index 39b419a..fd91b54 100644 (file)
@@ -33,17 +33,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Änkelte stöögne ma ruchts kliken beårbe (brükt JavaScript)',
 'tog-showtoc' => 'Wis en inhåltsferteeknis for side ma mäs ås trii ouerschrafte',
 'tog-rememberpassword' => 'Aw diheere komputer foon duur önjmälde (maksimool for $1 {{PLURAL:$1|däi|deege}})',
-'tog-watchcreations' => 'Seelew måågede side automatisch bekiike',
-'tog-watchdefault' => 'Seelew änrede side automaatisch bekiike',
-'tog-watchmoves' => 'Seelew ferschääwene side automaatisch bekiike',
-'tog-watchdeletion' => 'Seelew wächhåålde side automatisch bekiike',
+'tog-watchcreations' => "Salew maaget sidjen an huuchlooset dateien leewen uun't uug behual",
+'tog-watchdefault' => "Salew feranert sidjen an dateien leewen uun't uug behual",
+'tog-watchmoves' => "Salew fersköwen sidjen an dateien leewen uun't uug behual",
+'tog-watchdeletion' => "Salew stregen sidjen an dateien leewen uun't uug behual",
 'tog-minordefault' => 'Äine änringe gewöönlik ås latj mårkiire',
 'tog-previewontop' => 'Forbekiiken boowen dåt beårbingswaning wise',
 'tog-previewonfirst' => 'Bai dåt jarst beårben åltens dåt forbekiiken wise',
 'tog-nocache' => 'Sidecache foon e browser deaktiwiire',
-'tog-enotifwatchlistpages' => 'Bai änringe önj bekiikede side E-maile siinje',
+'tog-enotifwatchlistpages' => "Sjüür mi en E-Mail, wan sidjen of dateien feranert wurd, diar ik uun't uug behual wal",
 'tog-enotifusertalkpages' => 'Bai änringe tu min brüker-diskusjoonssid E-Maile siinje',
-'tog-enotifminoredits' => 'Uk bai latje änringe tu bekiikede side E-maile siinje',
+'tog-enotifminoredits' => 'Sjüür mi uk bi letj feranrangen faan sidjen an dateien en E-Mail',
 'tog-enotifrevealaddr' => 'Min E-mail-adräs önj tising-E-maile wise',
 'tog-shownumberswatching' => 'Wis di tål foon wåchende brükere',
 'tog-oldsig' => 'Aktuel signatuur:',
index 2bca803..9629923 100644 (file)
@@ -225,7 +225,7 @@ $messages = array(
 # User preference toggles
 'tog-underline' => 'Subliñar as ligazóns:',
 'tog-justify' => 'Xustificar os parágrafos',
-'tog-hideminor' => 'Agochar as edicións pequenas na páxina de cambios recentes',
+'tog-hideminor' => 'Agochar as edicións pequenas nos cambios recentes',
 'tog-hidepatrolled' => 'Agochar as edicións patrulladas nos cambios recentes',
 'tog-newpageshidepatrolled' => 'Agochar as páxinas revisadas da lista de páxinas novas',
 'tog-extendwatchlist' => 'Expandir a lista de vixilancia para mostrar todos os cambios e non só os máis recentes',
@@ -235,19 +235,19 @@ $messages = array(
 'tog-editondblclick' => 'Editar as páxinas logo de facer dobre clic (cómpre JavaScript)',
 'tog-editsection' => 'Permitir a edición de seccións mediante as ligazóns "[editar]"',
 'tog-editsectiononrightclick' => 'Permitir a edición de seccións premendo co botón dereito nos títulos das seccións (cómpre JavaScript)',
-'tog-showtoc' => 'Mostrar o índice (para páxinas con máis de tres cabeceiras)',
+'tog-showtoc' => 'Mostrar o índice (para as páxinas con máis de tres cabeceiras)',
 'tog-rememberpassword' => 'Lembrar o meu contrasinal neste navegador (ata $1 {{PLURAL:$1|día|días}})',
-'tog-watchcreations' => 'Engadir as páxinas que cree á miña lista de vixilancia',
-'tog-watchdefault' => 'Engadir as páxinas que edite á miña lista de vixilancia',
-'tog-watchmoves' => 'Engadir as páxinas que mova á miña lista de vixilancia',
-'tog-watchdeletion' => 'Engadir as páxinas que borre á miña lista de vixilancia',
+'tog-watchcreations' => 'Engadir as páxinas que cree e os ficheiros que cargue á miña lista de vixilancia',
+'tog-watchdefault' => 'Engadir as páxinas e os ficheiros que edite á miña lista de vixilancia',
+'tog-watchmoves' => 'Engadir as páxinas e os ficheiros que mova á miña lista de vixilancia',
+'tog-watchdeletion' => 'Engadir as páxinas e os ficheiros que borre á miña lista de vixilancia',
 'tog-minordefault' => 'Marcar por omisión todas as edicións como pequenas',
-'tog-previewontop' => 'Mostrar o botón de vista previa antes da caixa de edición e non despois dela',
+'tog-previewontop' => 'Mostrar a vista previa antes da caixa de edición',
 'tog-previewonfirst' => 'Mostrar a vista previa na primeira edición',
 'tog-nocache' => 'Desactivar a memoria caché do navegador',
-'tog-enotifwatchlistpages' => 'Enviádeme unha mensaxe de correo electrónico cando unha páxina da miña lista de vixilancia cambie',
+'tog-enotifwatchlistpages' => 'Enviádeme unha mensaxe de correo electrónico cando unha páxina ou un ficheiro da miña lista de vixilancia sufra un cambio',
 'tog-enotifusertalkpages' => 'Enviádeme unha mensaxe de correo electrónico cando a miña páxina de conversa cambie',
-'tog-enotifminoredits' => 'Enviádeme tamén unha mensaxe de correo electrónico cando se produzan edicións pequenas nas páxinas',
+'tog-enotifminoredits' => 'Enviádeme tamén unha mensaxe de correo electrónico cando se produzan edicións pequenas nas páxinas ou nos ficheiros',
 'tog-enotifrevealaddr' => 'Revelar o meu enderezo de correo electrónico nos correos de notificación',
 'tog-shownumberswatching' => 'Mostrar o número de usuarios que están a vixiar',
 'tog-oldsig' => 'Sinatura actual:',
@@ -264,8 +264,8 @@ $messages = array(
 'tog-watchlisthideanons' => 'Agochar as edicións dos usuarios anónimos na lista de vixilancia',
 'tog-watchlisthidepatrolled' => 'Agochar as edicións patrulladas na lista de vixilancia',
 'tog-nolangconversion' => 'Desactivar a conversión de variantes',
-'tog-ccmeonemails' => 'Enviar ao meu enderezo copia das mensaxes que envíe a outros usuarios',
-'tog-diffonly' => 'Non mostrar o contido da páxina debaixo das diferenzas entre edicións (dif)',
+'tog-ccmeonemails' => 'Enviádeme ao meu enderezo unha copia das mensaxes de correo electrónico que envíe a outros usuarios',
+'tog-diffonly' => 'Non mostrar o contido da páxina debaixo das diferenzas entre edicións',
 'tog-showhiddencats' => 'Mostrar as categorías ocultas',
 'tog-noconvertlink' => 'Desactivar a conversión dos títulos de ligazón',
 'tog-norollbackdiff' => 'Omitir as diferenzas despois de levar a cabo unha reversión de edicións',
@@ -1307,7 +1307,7 @@ Note que os seus índices do contido de {{SITENAME}} poden estar desactualizados
 'prefs-rc' => 'Cambios recentes',
 'prefs-watchlist' => 'Lista de vixilancia',
 'prefs-watchlist-days' => 'Número de días que mostrar na lista de vixilancia:',
-'prefs-watchlist-days-max' => 'Máximo $1 {{PLURAL:$1|día|días}}',
+'prefs-watchlist-days-max' => 'Máximo: $1 {{PLURAL:$1|día|días}}',
 'prefs-watchlist-edits' => 'Número máximo de edicións que mostrar na lista de vixilancia completa:',
 'prefs-watchlist-edits-max' => 'Número máximo: 1000',
 'prefs-watchlist-token' => 'Pase para a lista de vixilancia:',
@@ -1329,7 +1329,7 @@ Note que os seus índices do contido de {{SITENAME}} poden estar desactualizados
 'stub-threshold' => 'Límite superior para o formato de <a href="#" class="stub">ligazóns de bosquexo</a> (bytes):',
 'stub-threshold-disabled' => 'Desactivado',
 'recentchangesdays' => 'Número de días a mostrar nos cambios recentes:',
-'recentchangesdays-max' => 'Máximo $1 {{PLURAL:$1|día|días}}',
+'recentchangesdays-max' => 'Máximo: $1 {{PLURAL:$1|día|días}}',
 'recentchangescount' => 'Número de edicións a mostrar por defecto:',
 'prefs-help-recentchangescount' => 'Isto inclúe os cambios recentes, os historiais e mais os rexistros.',
 'prefs-help-watchlist-token' => 'Ao encher este campo cunha clave secreta xerarase unha fonte de novas RSS para a súa lista de vixilancia.
index f2f3162..867e9a5 100644 (file)
@@ -367,17 +367,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'עריכת פסקאות על ידי לחיצה ימנית על כותרות הפסקאות (דרוש JavaScript)',
 'tog-showtoc' => 'הצגת תוכן עניינים (עבור דפים עם יותר מ־3 כותרות)',
 'tog-rememberpassword' => 'שמירת הכניסה שלי בדפדפן זה ({{PLURAL:$1|ליום אחד|ל־$1 ימים|ליומיים}} לכל היותר)',
-'tog-watchcreations' => 'מעקב אחרי דפים שיצרתי',
-'tog-watchdefault' => 'מעקב אחרי דפים שערכתי',
-'tog-watchmoves' => 'מעקב אחרי דפים שהעברתי',
-'tog-watchdeletion' => 'מעקב אחרי דפים שמחקתי',
+'tog-watchcreations' => 'מעקב אחרי דפים שיצרתי וקבצים שהעליתי',
+'tog-watchdefault' => '×\9eעק×\91 ×\90×\97ר×\99 ×\93פ×\99×\9d ×\95ק×\91צ×\99×\9d ×©×¢×¨×\9bת×\99',
+'tog-watchmoves' => '×\9eעק×\91 ×\90×\97ר×\99 ×\93פ×\99×\9d ×\95ק×\91צ×\99×\9d ×©×\94×¢×\91רת×\99',
+'tog-watchdeletion' => '×\9eעק×\91 ×\90×\97ר×\99 ×\93פ×\99×\9d ×\95ק×\91צ×\99×\9d ×©×\9e×\97קת×\99',
 'tog-minordefault' => 'הגדרת כל פעולת עריכה כמשנית אם לא צוין אחרת',
 'tog-previewontop' => 'הצגת תצוגה מקדימה לפני תיבת העריכה (או: אחריה)',
 'tog-previewonfirst' => 'הצגת תצוגה מקדימה בעריכה ראשונה',
 'tog-nocache' => 'מניעת אחסון הדפים בזיכרון המטמון בדפדפן',
-'tog-enotifwatchlistpages' => 'ש×\9c×\99×\97ת ×\93×\95×\90\9c ×\90×\9c×\99×\9a ×\9b×\90שר × ×¢×©×\94 ×©×\99× ×\95×\99 ×\91×\93פ×\99×\9d ברשימת המעקב שלך',
+'tog-enotifwatchlistpages' => 'ש×\9c×\99×\97ת ×\93×\95×\90\9c ×\90×\9c×\99×\9a ×\9b×\90שר × ×¢×©×\94 ×©×\99× ×\95×\99 ×\91×\93×£ ×\90×\95 ×\91ק×\95×\91×¥ ברשימת המעקב שלך',
 'tog-enotifusertalkpages' => 'שליחת דוא"ל אליך כאשר נעשה שינוי בדף שיחת המשתמש שלך',
-'tog-enotifminoredits' => 'שליחת דוא"ל אליך גם על עריכות משניות של דפים',
+'tog-enotifminoredits' => 'שליחת דוא"ל אליך גם על עריכות משניות של דפים וקבצים',
 'tog-enotifrevealaddr' => 'חשיפת כתובת הדוא"ל שלך בהודעות דוא"ל',
 'tog-shownumberswatching' => 'הצגת מספר המשתמשים העוקבים אחרי הדף',
 'tog-oldsig' => 'החתימה הקיימת:',
index 0a077ed..56d624b 100644 (file)
@@ -32,17 +32,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Bhaag ke title pe right click kare pe bhaag ke badle ke laabu karo  (JavaScript)',
 'tog-showtoc' => 'Dhyan suchi dekhao (uu panna khatir jon me tiin se jaada heading hai)',
 'tog-rememberpassword' => 'Ii browser me (jaada se jaada $1 {{PLURAL:$1|din|din}}) talak hamaar login ke yaad rakho.',
-'tog-watchcreations' => 'Hamaar banawa waala panna ke hamaar dhyaan suchi me jorro',
-'tog-watchdefault' => 'Ham se badla gae panna ke hamaar dhyaan suchi me jorro',
-'tog-watchmoves' => 'Uu panna jiske naam ham badla hai ke hamaar dhyaan suchi me jorro',
-'tog-watchdeletion' => 'Uu panna jiske ham mitaya hai ke hamaar dhyaan suchi me jorro',
+'tog-watchcreations' => 'Hamaar banawa waala panna aur upload karaa gais file ke hamaar dhyaan suchi me jorro',
+'tog-watchdefault' => 'Ham se badla gais panna aur file ke hamaar dhyaan suchi me jorro',
+'tog-watchmoves' => 'Uu panna aur file jiske naam ham badla hai ke hamaar dhyaan suchi me jorro',
+'tog-watchdeletion' => 'Uu panna, aur file jiske ham mitaya hai ke hamaar dhyaan suchi me jorro',
 'tog-minordefault' => 'Mamuli badlao ke apne se nishaan lagao',
 'tog-previewontop' => 'Badlao waala dabba se pahile ek jhalak dekhao',
 'tog-previewonfirst' => 'Hamaar pahila badlao pe jhalak dekhao',
 'tog-nocache' => 'Browser pe panna ke bachae me rok lagao',
-'tog-enotifwatchlistpages' => 'Jab hamaar dhyaan suchi ke koi panna ke badla jae tab hame E-mail karo',
+'tog-enotifwatchlistpages' => 'Jab hamaar dhyaan suchi ke koi panna, nai to file, ke badla jae tab hame E-mail karo',
 'tog-enotifusertalkpages' => 'Jab hamaar baat waala panna ke badla jae tab hame E-mail karo',
-'tog-enotifminoredits' => 'Panna me mamuli badlao khatir bhi hame E-mail karo',
+'tog-enotifminoredits' => 'Panna aur file me mamuli badlao khatir bhi hame E-mail karo',
 'tog-enotifrevealaddr' => 'Notification E-mail me hamaar E-mail address ke dekhao.',
 'tog-shownumberswatching' => 'Ketna sadasya dekhe hai ke number dekhao',
 'tog-oldsig' => 'Abhi ke signature:',
@@ -503,6 +503,7 @@ Ii sab feature khatir koi e-mail nai bheja jaai.',
 'invalidemailaddress' => 'E-mail address ke nai lewa jae sake hai kahe ki iske format kharaab hai.
 Meharbaani kar ke achchha address ke enter karo nai to uu field ke khali kar do.',
 'cannotchangeemail' => 'Ii wiki me account e-mail ke badla nai jaawe sake hae',
+'emaildisabled' => 'Ii site e-mail nai bheje sake hae.',
 'accountcreated' => 'Account banae dewa gais hai',
 'accountcreatedtext' => '$1 khatir user account banae dewa gais hai.',
 'createaccount-title' => '{{SITENAME}} khatir account creation',
@@ -702,7 +703,8 @@ Yaad rakhna ki custom .css aur .js panna owercase title use kare hai, jaise ki {
 'updated' => '(Update kar dewa gais hai)',
 'note' => "'''Dhyan rakkho:'''",
 'previewnote' => "'''Ii khaali ek jhalak dekhae hai'''
-Tumar badlao abhi save nai bhais hai!",
+Tumar badlao abhi bachawa nai gais hai!",
+'continue-editing' => 'Badalte raho',
 'previewconflict' => 'Ii preview uu text dekhae hai jon ki uppar ke text editing area me dekhai agar aap iske save karaa.',
 'session_fail_preview' => "''' Maaf karna! Ham log aap ke badlao ke process nai kare paya hai due to a loss of session data.
 Fir se kosis karna.
@@ -718,6 +720,7 @@ Ii badlao ke reject kar dewa gais hai to prevent corruption of the page text.
 Ii kabhi kabhi hoe hai jab aap ek buggy web-based anonymous proxy service ke use karta hai.'''",
 'edit_form_incomplete' => 'Edit form ke kuchh hissa server ke lage nai pahunche paais hae; fir se check karo ki aap ke badlao form me hae, aur fir se form ke bhejo.',
 'editing' => '$1 badlawa jae hai',
+'creating' => '$1 banaata hae',
 'editingsection' => 'Sampadan $1 (bhaag)',
 'editingcomment' => '$1 ke badla jae hai (nawaa section)',
 'editconflict' => 'Badle me conflict: $1',
@@ -784,6 +787,7 @@ Janae hai ki iske koi mitae dii hai.',
 'edit-no-change' => 'Aap ke badle ke kosis ke ignore kar dewa gais hai, kahe ki text ke badla nai gais hai.',
 'edit-already-exists' => 'Nawaa panna nai banae sakaa hai.
 Ii naam ke panna abhi hai.',
+'defaultmessagetext' => 'Default message text',
 
 # Parser/template warnings
 'expensive-parserfunction-warning' => "'''Chetauni''': Ii panna me bahut jaada expensive parser function calls hai.
@@ -799,6 +803,12 @@ Ii sab arguments ke omit kar dewa gais hai.',
 'parser-template-loop-warning' => 'Template loop ke pawa gais hai: [[$1]]',
 'parser-template-recursion-depth-warning' => 'Template recursion depth limit se jaada hoe gais hae ($1)',
 'language-converter-depth-warning' => 'Bhasa anuwaad ke gahiraai ijajat se jaada hoe gais hae ($1)',
+'node-count-exceeded-category' => 'Panna jahaan pe node-count bahut jaada hoe gais hae',
+'node-count-exceeded-warning' => 'Panna, node-count se jaada hae',
+'expansion-depth-exceeded-category' => 'Panna jahaan pe expansion depth ke exceed karaa gais hae',
+'expansion-depth-exceeded-warning' => 'Panna expansion depth ke exceed karis hae',
+'parser-unstrip-loop-warning' => 'Unstrip loop ke pawa gai shae',
+'parser-unstrip-recursion-limit' => 'Unstrip recursion limit ke exceed karaa gais hae ($1)',
 
 # "Undo" feature
 'undo-success' => 'Ii badlao ke pahile jaise karaa jaae sake hai.
@@ -943,7 +953,7 @@ Meharbani ka ke logs ke check karo.',
 # Suppression log
 'suppressionlog' => 'Dabae waala log',
 'suppressionlogtext' => 'Niche ke suchi me administrators se lukawa gais deletions au rukawat hae.
-Abhi ke laabu rukawat ke suchi ke khatir [[Special:BlockList|IP block list]] ke dekho.',
+Abhi ke laabu rukawat ke suchi ke khatir [[Special:BlockList|block list]] ke dekho.',
 
 # History merging
 'mergehistory' => 'Panna ke itihass ke jorro',
@@ -978,6 +988,8 @@ Ii baat ke dhyan me rakhna ki navigation jorr ke kaam me laae se ii column reset
 
 # Diffs
 'history-title' => '"$1" ke sansodhan ke itihaas',
+'difference-title' => '"$1" ke revisions ke biich ke antar',
+'difference-title-multipage' => 'Panna "$1" aur "$2" ke biich ke antar',
 'difference-multipage' => '(Panna ke biich ke antar)',
 'lineno' => 'Rekha $1:',
 'compareselectedversions' => 'Chuna gae version ke compare karo',
@@ -1075,6 +1087,7 @@ Yaad rakhna ki uu log ke {{SITENAME}} ke index saait purana hoi.',
 'prefs-beta' => 'Nawaa features',
 'prefs-datetime' => 'Tarik aur time',
 'prefs-labs' => 'Try kare waala features',
+'prefs-user-pages' => 'Sadasya ke panna',
 'prefs-personal' => 'Sadasya ke profile',
 'prefs-rc' => 'Nawaa badlao',
 'prefs-watchlist' => 'Dhyan suchi',
@@ -1514,6 +1527,7 @@ Agar jo problem fir nai khatam hoe tab [[Special:ListUsers/sysop|administrator]]
 'upload-too-many-redirects' => 'Ii URL me bahut jaada redirects hae.',
 'upload-unknown-size' => 'Nai pataa ki ketnaa barraa hae',
 'upload-http-error' => 'Ek HTTP galti hoe gais hae: $1',
+'upload-copy-upload-invalid-domain' => 'Ii domain se copy upload nai karaa jaae sake hae.',
 
 # File backend
 'backend-fail-stream' => 'File $1 ke stream nai kare sakaa hae.',
@@ -1531,12 +1545,24 @@ Agar jo problem fir nai khatam hoe tab [[Special:ListUsers/sysop|administrator]]
 'backend-fail-writetemp' => 'Temporary file me nai likhe sakaa hae.',
 'backend-fail-closetemp' => 'Temporary file ke nai band kare sakaa hae.',
 'backend-fail-read' => 'File $1 ke nai parrhe sakaa hae.',
-'backend-fail-create' => 'File $1 ke nai banae sakaa hae.',
+'backend-fail-create' => 'File $1 pe nai likha jaae sake hae.',
+'backend-fail-maxsize' => 'File $1 ke nai likhe sakaa hae kaahe ki ii {{PLURAL:$2|ek byte|$2 byte}} se barraa hae.',
 'backend-fail-readonly' => 'Storage backend "$1" abhi khaali read-only hae. Iske kaaran hae: "$2"',
 'backend-fail-synced' => 'File "$1" internal storage backends me ek inconsistent state me hae',
 'backend-fail-connect' => 'Storage backend "$1" se connect nai kare sakaa hae.',
 'backend-fail-internal' => 'Storage backend "$1" me ek unknown error hoe gais hae.',
 'backend-fail-contenttype' => 'Ii nai pataa lagae sakaa hae ki "$1" me bachae ke khaatir file kon rakam ke hae.',
+'backend-fail-batchsize' => 'Storage backend ke  $1 file {{PLURAL:$1|operation|operations}} ke ek batch ke dewa gais hae ; limit  $2 {{PLURAL:$2|operation|operation}} hae.',
+'backend-fail-usable' => 'File $1 ke nai likhe sakaa hae kaahe ki iske khatir jaruri ijajat nai hae, nai to directories/containers nai hae.',
+
+# File journal errors
+'filejournal-fail-dbconnect' => 'Storage backend "$1" ke khatir journal database se nai jorre sakaa hae.',
+'filejournal-fail-dbquery' => 'Storage backend "$1" ke khatir journal database ke nai badle sakaa hae.',
+
+# Lock manager
+'lockmanager-notlocked' => '"$1" ke  nai khole sakaa hae; ii lock nai hae.',
+'lockmanager-fail-closelock' => '"$1" ke khatir lock file ke nai band kare sakaa hae.',
+'lockmanager-fail-deletelock' => '"$1" ke khatir lock file ke nai mitae sakaa hae.',
 
 # Special:UploadStash
 'uploadstash' => 'Gupt file ke upload karo',
index ccc1e7d..e4d78b2 100644 (file)
@@ -168,17 +168,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Wobdźěłowanje jednotliwych wotrězkow přez kliknjenje z prawej tastu na nadpisma wotrězkow zmóžnić (wužaduje sej JavaScript)',
 'tog-showtoc' => 'Zapis wobsaha pokazać (za strony z wjace hač 3 nadpismami)',
 'tog-rememberpassword' => 'Přizjewjenje na tutym wobhladowaku sej spomjatkować (za maksimalnje $1 {{PLURAL:$1|dźeń|dnjej|dny|dnjow}})',
-'tog-watchcreations' => 'Strony, kotrež wutworjam, swojim wobkedźbowankam přidać',
-'tog-watchdefault' => 'Strony, kotrež wobdźěłuju, swojim wobkedźbowankam přidać',
-'tog-watchmoves' => 'Sam přesunjene strony wobkedźbowankam přidać',
-'tog-watchdeletion' => 'Sam wušmórnjene strony wobkedźbowankam přidać',
+'tog-watchcreations' => 'Strony, kotrež wutworjam a dataje, kotrež nahrawam, swojim wobkedźbowankam přidać',
+'tog-watchdefault' => 'Strony a dataje, kotrež wobdźěłuju, swojim wobkedźbowankam přidać',
+'tog-watchmoves' => 'Sam přesunjene strony a dataje wobkedźbowankam přidać',
+'tog-watchdeletion' => 'Sam wušmórnjene strony a dataje wobkedźbowankam přidać',
 'tog-minordefault' => 'Wšě změny standardnje jako snadne woznamjenić',
 'tog-previewontop' => 'Přehlad před wobdźěłanskim polom pokazać',
 'tog-previewonfirst' => 'Při prěnim wobdźěłanju přehlad pokazać',
 'tog-nocache' => 'Pufrowanje stronow wobhladowaka znjemóžnić',
-'tog-enotifwatchlistpages' => 'E-mejlku pósłać, hdyž so strona z wobkedźbowankow změni',
+'tog-enotifwatchlistpages' => 'E-mejlku pósłać, hdyž so strona abo dataja z wobkedźbowankow změni',
 'tog-enotifusertalkpages' => 'E-mejlku pósłać, hdyž so moja wužiwarska diskusijna strona změni',
-'tog-enotifminoredits' => 'Tež dla snadnych změnow e-mejlki pósłać',
+'tog-enotifminoredits' => 'Tež za snadne změny stronow a datajow e-mejl pósłać',
 'tog-enotifrevealaddr' => 'Moju e-mejlowu adresu w e-mejlowych zdźělenkach wotkryć',
 'tog-shownumberswatching' => 'Ličbu wobkedźbowacych wužiwarjow pokazać',
 'tog-oldsig' => 'Eksistowaca signatura:',
index 45f8f94..0d7362b 100644 (file)
@@ -335,9 +335,9 @@ $messages = array(
 'tog-previewontop' => 'Előnézet megjelenítése a szerkesztőablak előtt',
 'tog-previewonfirst' => 'Előnézet első szerkesztésnél',
 'tog-nocache' => 'A lapok gyorstárazásának letiltása a böngészőben',
-'tog-enotifwatchlistpages' => 'Kapjak értesítést e-mailben, ha egy általam figyelt lap megváltozik',
+'tog-enotifwatchlistpages' => 'Kapjak értesítést e-mailben, ha egy általam figyelt lap vagy fájl megváltozik',
 'tog-enotifusertalkpages' => 'Kapjak értesítést e-mailben, ha megváltozik a vitalapom',
-'tog-enotifminoredits' => 'Kapjak értesítést e-mailben a lapok apró változtatásairól',
+'tog-enotifminoredits' => 'Kapjak értesítést e-mailben a lapok és fájlok apró változtatásairól',
 'tog-enotifrevealaddr' => 'Jelenjen meg az e-mail címem a figyelmeztető e-mailekben',
 'tog-shownumberswatching' => 'A lapot figyelő szerkesztők számának megjelenítése',
 'tog-oldsig' => 'A jelenlegi aláírás:',
index 68af34e..8fc05b0 100644 (file)
@@ -157,17 +157,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Activar modification de sectiones con clic-a-dextra super lor titulos (require JavaScript)',
 'tog-showtoc' => 'Monstrar tabula de contento (in paginas con plus de 3 sectiones)',
 'tog-rememberpassword' => 'Memorar mi contrasigno in iste navigator (pro un maximo de $1 {{PLURAL:$1|die|dies}})',
-'tog-watchcreations' => 'Adder le paginas que io crea a mi observatorio',
-'tog-watchdefault' => 'Adder le paginas que io modifica a mi observatorio',
-'tog-watchmoves' => 'Adder le paginas que io renomina a mi observatorio',
-'tog-watchdeletion' => 'Adder le paginas que io dele a mi observatorio',
+'tog-watchcreations' => 'Adder le paginas que io crea e le files que io incarga a mi observatorio',
+'tog-watchdefault' => 'Adder le paginas e files que io modifica a mi observatorio',
+'tog-watchmoves' => 'Adder le paginas e files que io renomina a mi observatorio',
+'tog-watchdeletion' => 'Adder le paginas e files que io dele a mi observatorio',
 'tog-minordefault' => 'Marcar omne modificationes initialmente como minor',
 'tog-previewontop' => 'Monstrar previsualisation ante le quadro de modification',
 'tog-previewonfirst' => 'Monstrar previsualisation al prime modification',
 'tog-nocache' => "Disactivar le ''cache'' de paginas in le navigator",
-'tog-enotifwatchlistpages' => 'Notificar me via e-mail quando un pagina in mi observatorio es modificate',
+'tog-enotifwatchlistpages' => 'Notificar me per e-mail quando un pagina o file in mi observatorio es modificate',
 'tog-enotifusertalkpages' => 'Notificar me via e-mail quando mi pagina de discussion es modificate',
-'tog-enotifminoredits' => 'Notificar me etiam de modificationes minor de paginas',
+'tog-enotifminoredits' => 'Notificar me etiam de modificationes minor de paginas e files',
 'tog-enotifrevealaddr' => 'Revelar mi adresse de e-mail in messages de notification',
 'tog-shownumberswatching' => 'Monstrar le numero de usatores que observa le pagina',
 'tog-oldsig' => 'Signatura existente:',
@@ -918,10 +918,10 @@ Tu pote retornar e modificar un pagina existente, o [[Special:UserLogin|aperir u
 'permissionserrors' => 'Errores de permissiones',
 'permissionserrorstext' => 'Tu non ha le permission de facer isto, pro le sequente {{PLURAL:$1|motivo|motivos}}:',
 'permissionserrorstext-withaction' => 'Tu non ha le permission de $2, pro le sequente {{PLURAL:$1|motivo|motivos}}:',
-'recreate-moveddeleted-warn' => "'''Attention: Tu es super le puncto de recrear un pagina que esseva anteriormente delite.'''
+'recreate-moveddeleted-warn' => "'''Attention: Tu es sur le puncto de recrear un pagina que esseva anteriormente delite.'''
 
 Tu deberea considerar si il es appropriate continuar a modificar iste pagina.
-Le registro de deletiones e de renominationes pro iste pagina se trova infra pro major commoditate:",
+Ecce le registro de deletiones e de renominationes pro iste pagina:",
 'moveddeleted-notice' => 'Iste pagina ha essite delite.
 In basso se revela le registro de deletiones e de modificationes del pagina pro ulterior informationes.',
 'log-fulllog' => 'Vider le registro complete',
index 4cca325..bb6185e 100644 (file)
@@ -38,11 +38,11 @@ $messages = array(
 # User preference toggles
 'tog-underline' => 'Pinag-ugisan ti silpo:',
 'tog-justify' => 'Limpiaen dagiti parapo',
-'tog-hideminor' => 'Ilemmeng dagiti babassit a panagbaliw kadagiti naudi a panagisukat',
+'tog-hideminor' => 'Ilemmeng dagiti bassit a panagbaliw kadagiti naudi a panagisukat',
 'tog-hidepatrolled' => 'Ilemmeng dagiti napatruliaan nga inurnos kadagiti naudi a sinuk-sukatan',
 'tog-newpageshidepatrolled' => 'Ilemmeng dagiti napatruliaan a panid idiay baro a listaan ti panid',
 'tog-extendwatchlist' => 'Ipalawa ti listaan ti bambantayan tapno maipakita amin a nasukatan, tapno saan laeng a dagiti nabiit',
-'tog-usenewrc' => 'Pannakabaliw ti grupo babaen ti panid iti kaudian a panagbaliw ken pakabantayan (masapul ti JavaScript)',
+'tog-usenewrc' => 'Dagiti grupo panagbaliw babaen ti panid kadagiti kinaudi a panagbaliw ken banbantayan  (masapul ti JavaScript)',
 'tog-numberheadings' => 'Automatiko a pabilangan dagiti paulo',
 'tog-showtoolbar' => 'Ipakita ti ramit ti panag-urnos (masapul ti JavaScript)',
 'tog-editondblclick' => 'Urnosen dagiti panid iti mamindua a panagtakla (masapul ti JavaScript)',
@@ -50,17 +50,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Ikkan ti pakabaelan ti paset  a panag-urnos no agtakla ti kanawan kadagiti paset a titulo (masapul ti JavaScript)',
 'tog-showtoc' => 'Ipakita ti tabla dagiti linaon (para kadagiti panid nga addan ti ad-adu ngem dagiti 3 a paulo)',
 'tog-rememberpassword' => 'Laglagipem ti iseserrekko iti daytoy a pagbasabasa (iti kaadu nga $1 {{PLURAL:$1|aldaw|al-aldaw}})',
-'tog-watchcreations' => 'Inayon dagiti inaramidko a pampanid iti listaan ti bambantayak',
-'tog-watchdefault' => 'Inayon dagiti inurnos ko a pampanid iti listaan ti bambantayak',
-'tog-watchmoves' => 'Inayon dagiti inyalisko a pampanid iti listaan ti bambantayak',
-'tog-watchdeletion' => 'Inayon dagiti inikkatko a pampanid iti listaan ti bambantayak',
+'tog-watchcreations' => 'Agnayon kadagiti panid a pinartuatko ken papeles  nga inpanko idiay listaan ti bambantayak',
+'tog-watchdefault' => 'Agnayon kadagiti panid ken papeles nga inurnosko idiay listaan ti bambantayak',
+'tog-watchmoves' => 'Agnayon kadagiti panid ken papeles nga inyalisko idiay listaan ti bambantayak',
+'tog-watchdeletion' => 'Agnayon kadagiti panid ken papeles nga inikkatko idiay listaan ti bambantayak',
 'tog-minordefault' => 'Markaan amin nga  inurnos a kas sigud a bassit',
 'tog-previewontop' => 'Ipakita ti panag-padas sakbay ti kahon ti inurnos',
 'tog-previewonfirst' => 'Ipakita ti na-ipadas iti umuna nga inurnos',
 'tog-nocache' => 'Ibaldado ti pagcache ti pabasabasam iti daytoy a panid',
-'tog-enotifwatchlistpages' => 'E-suratannak no adda mabaliwan a panid iti listaan dagiti bambantayak a pampanid',
+'tog-enotifwatchlistpages' => 'E-suratannak no mabaliwan ti panid wenno papeles idiay listaan dagiti bambantayak',
 'tog-enotifusertalkpages' => 'E-suratannak no mabaliwan ti panidko a tungtongan ti agar-aramat',
-'tog-enotifminoredits' => 'E-suratannak met kadagiti bassit a panag-urnos ti pampanid',
+'tog-enotifminoredits' => 'E-suratannak pay para kadagiti bassit a panag-urnos kadagiti panid ken papeles',
 'tog-enotifrevealaddr' => 'Iparang ti pagtaengan ti e-surat ko kadagiti pammalagip ti  e-surat',
 'tog-shownumberswatching' => 'Ipakita ti bilang dagiti agbambantay nga agar-aramat',
 'tog-oldsig' => 'Ti adda a pirma:',
@@ -70,7 +70,7 @@ $messages = array(
 'tog-showjumplinks' => 'Ikkan ti pakabaelan  a  "mapan iti"  agpalaka a sumrek kadagiti panagsilpo',
 'tog-uselivepreview' => 'Usaren ti panag-padas tattan (masapul ti JavaScript) (suuten laeng)',
 'tog-forceeditsummary' => 'Pakaammuannak no sumrek ti blanko a pakabuklan ti panag-urnos',
-'tog-watchlisthideown' => 'Ilemmeng idiay listaan ti bambantayan dagiti inurnos ko',
+'tog-watchlisthideown' => 'Ilemmeng idiay listaan ti bambantayan dagiti inurnosko',
 'tog-watchlisthidebots' => 'Ilemmeng idiay listaan ti bambantayan dagiti inurnos ti bot',
 'tog-watchlisthideminor' => 'Ilemmeng idiay listaan ti bambantayan dagiti bassit nga  inurnos',
 'tog-watchlisthideliu' => 'Ilemmeng idiay listaan ti bambantayan dagiti inurnos ti nakastrek nga agar-aramat',
@@ -392,8 +392,8 @@ Panngaasi nga  ibagam kadagiti [[Special:ListUsers/sysop|administrador]], isurat
 'cannotdelete' => 'Ti panid wenno ti papeles "$1" ket saan a maikkat.
 Amangan no addan sabali a nangikkat.',
 'cannotdelete-title' => 'Saan a maikkat ti panid  "$1"',
-'delete-hook-aborted' => 'Naukasen ti kawit ti panagborra.
-Awan ti palawag.',
+'delete-hook-aborted' => 'Inukas ti kawit ti panagborra.
+Awan ti intedna a palawag.',
 'badtitle' => 'Madi a titulo',
 'badtitletext' => 'Ti nakiddaw a titulo ti panid ket imbalido, blanko, wenno saan nga umno a naisilpo a titulo a maki-pagsasao wenno maki-wiki.
 Adda ngata nagyan a maysa wenno ad-adu pay a kababalin a saan a mausar iti titulo.',
@@ -430,7 +430,8 @@ Ti naited a rason ket ''$2''.",
 Ti administrador a nagserra ket nagited iti daytoy a panagilawlawag "\'\'$3\'\'".',
 'invalidtitle-knownnamespace' => 'Imbalido a titulo nga adda ti nagan ti lugar "$2" ken testo "$3"',
 'invalidtitle-unknownnamespace' => 'Imbalido a titulo nga adda di-amammo a nagan ti lugar a numero $1 ken testo "$2"',
-'exception-nologin-text' => 'Masapol daytoy a panid wenno aramid ket nakastrekkayo kadaytoy a wiki.',
+'exception-nologin' => 'Saan a nakastrek',
+'exception-nologin-text' => 'Daytoy a panid wenno aramid ket makasapul kenka ti sumrek iti daytoy a wiki.',
 
 # Virus scanner
 'virus-badscanner' => 'Madi di panaka-aramidna: Di am-ammo a birus a panagskan: "$1"',
@@ -1596,7 +1597,7 @@ No ti parikut ket agsubli latta, kontaken ti [[Special:ListUsers/sysop|administr
 'backend-fail-connect' => 'Saan a makaikapet idiay pagidulinan a kalikudan  "$1".',
 'backend-fail-internal' => 'Adda di amammo a biddut ti napasamak idiay pagidulinan a kalikudan "$1".',
 'backend-fail-contenttype' => 'Saan a maammoan ti kita ti linaon ti papeles nga idulin idiay "$1".',
-'backend-fail-batchsize' => 'Naidatag nga {{PLURAL:$1|aramid|aramid}} iti intar: $1; beddeng nga {{PLURAL:$2|aramid|aramid}}: $2.',
+'backend-fail-batchsize' => 'Nagited ti nagipenpenan ti bunggoy iti $1 a papeles {{PLURAL:$1|nga aramid|nga ar-aramid}}; ti patingga ket $2 {{PLURAL:$2|nga aramid|nga ar-aramid}}.',
 'backend-fail-usable' => 'Saan a masuratan ti papeles $1 gapu ta awan ti makaanay a pammalubos wenno awan dagiti direktorio/pangikabilan.',
 
 # Lock manager
@@ -3672,6 +3673,8 @@ Nupay kasta, mau-sarmo ti nakabuklan dita baba. Ti komentario nga itedmo ket mai
 'api-error-empty-file' => 'Ti papeles nga intedmo ket awan ti nagyan na.',
 'api-error-emptypage' => 'Agar-aramid ti baro, dagiti awan ti linaon na a panid ket saan a maipalubos.',
 'api-error-fetchfileerror' => 'Kinauneg a biddut: Addaan ti dakes a napasamak idi agalala ti papeles.',
+'api-error-fileexists-forbidden' => 'Ti papeles nga agnagan ti "$1" ket addan, ken saan a mabalin a masuratan manen.',
+'api-error-fileexists-shared-forbidden' => 'Ti papeles nga agnagan ti "$1" ket adda idiay pagbibingayan a repositorio ti papeles, ken saan a mabalin a masuratan manen.',
 'api-error-file-too-large' => 'Ti papeles nga intedmo ket dakkel unay.',
 'api-error-filename-tooshort' => 'Ti nagan daytoy a papeles ket bassit unay.',
 'api-error-filetype-banned' => 'Ti kita daytoy a papeles ket maiparit.',
@@ -3710,4 +3713,6 @@ Nupay kasta, mau-sarmo ti nakabuklan dita baba. Ti komentario nga itedmo ket mai
 'duration-centuries' => '$1 {{PLURAL:$1|siglo|sig-siglo}}',
 'duration-millennia' => '$1 {{PLURAL:$1|milenio|mil-milenio}}',
 
+# Unknown messages
+'lockmanager-fail-svr-acquire' => 'Saan a makaala kadagiti kandado ti server $1.',
 );
index 4477f76..7b19079 100644 (file)
@@ -277,17 +277,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Modifica delle sezioni tramite clic destro sul titolo (richiede JavaScript)',
 'tog-showtoc' => "Mostra l'indice per le pagine con più di 3 sezioni",
 'tog-rememberpassword' => 'Ricorda la password su questo browser (per un massimo di $1 {{PLURAL:$1|giorno|giorni}})',
-'tog-watchcreations' => 'Aggiungi le pagine create agli osservati speciali',
-'tog-watchdefault' => 'Aggiungi le pagine modificate agli osservati speciali',
-'tog-watchmoves' => 'Aggiungi le pagine spostate agli osservati speciali',
-'tog-watchdeletion' => 'Aggiungi le pagine cancellate agli osservati speciali',
+'tog-watchcreations' => 'Aggiungi le pagine create e i file caricati agli osservati speciali',
+'tog-watchdefault' => 'Aggiungi le pagine e i file modificati agli osservati speciali',
+'tog-watchmoves' => 'Aggiungi le pagine e i file spostati agli osservati speciali',
+'tog-watchdeletion' => 'Aggiungi le pagine e i file cancellati agli osservati speciali',
 'tog-minordefault' => 'Indica ogni modifica come minore (solo come predefinito)',
 'tog-previewontop' => "Mostra l'anteprima sopra la casella di modifica e non sotto",
 'tog-previewonfirst' => "Mostra l'anteprima per la prima modifica",
 'tog-nocache' => 'Disabilitare la cache delle pagine del browser',
-'tog-enotifwatchlistpages' => 'Segnalami via e-mail le modifiche alle pagine osservate',
+'tog-enotifwatchlistpages' => 'Inviami una email quando viene modificata una pagina o un file presente tra gli osservati speciali',
 'tog-enotifusertalkpages' => 'Segnalami via e-mail le modifiche alla mia pagina di discussione',
-'tog-enotifminoredits' => 'Segnalami via e-mail anche le modifiche minori',
+'tog-enotifminoredits' => 'Inviami una email anche per le modifiche minori di pagine e file',
 'tog-enotifrevealaddr' => 'Rivela il mio indirizzo e-mail nei messaggi di avviso',
 'tog-shownumberswatching' => 'Mostra il numero di utenti che hanno la pagina in osservazione',
 'tog-oldsig' => 'Firma attuale:',
index f11e38f..96c93d9 100644 (file)
@@ -164,17 +164,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'სექციის რედაქტირების ნებართვა მარჯვენა ღილაკზე დაჭერით<br />სექციის სათაურებზე (ჯავასკრიპტი)',
 'tog-showtoc' => 'აჩვენე სარჩევი (სამ ქვესათაურზე მეტის მქონე გვერდებისთვის)',
 'tog-rememberpassword' => 'დამიმახსოვრე ამ კომპიუტერზე (მაქსიმუმ $1 {{PLURAL:$1|დღე|დღე}})',
-'tog-watchcreations' => 'დაამატე ჩემი კონტროლის სიას ჩემს მიერ შექმნილი გვერდები',
-'tog-watchdefault' => 'დაამატე ჩემი კონტროლის სიას ჩემს მიერ რედაქტირებული გვერდები',
-'tog-watchmoves' => 'ჩასვი გვერდები, რომლებიც მე გადამაქვს, ჩემს კონტროლის სიაში',
-'tog-watchdeletion' => 'ჩასვი გვერდები, რომლებსაც მე ვშლი, ჩემს კონტროლის სიაში',
+'tog-watchcreations' => 'დაამატე ჩემი კონტროლის სიას ჩემს მიერ შექმნილი გვერდები და ატვირთული ფაილები',
+'tog-watchdefault' => 'დაამატე ჩემი კონტროლის სიას ჩემს მიერ რედაქტირებული გვერდები და ფაილები',
+'tog-watchmoves' => 'ჩასვი გვერდები და ფაილები, რომლებიც მე გადამაქვს, ჩემს კონტროლის სიაში',
+'tog-watchdeletion' => 'ჩასვი გვერდები და ფაილები, რომლებსაც მე ვშლი, ჩემს კონტროლის სიაში',
 'tog-minordefault' => 'ყველა რედაქტირების მონიშვნა, როგორც უმნიშვნელო, უპირობოდ',
 'tog-previewontop' => 'წინასწარი გადახედვის ჩვენება რედაქტირების დაფამდე',
 'tog-previewonfirst' => 'წინასწარი ჩვენება პირველივე რედაქტირებაზე',
 'tog-nocache' => 'გათიშეთ ბრაუზერში გვერდების ქეშირება',
-'tog-enotifwatchlistpages' => 'მომწერე ელ. ფოსტით, როცა გვერდი, რომელსაც მე ვაკონტროლებ, შეიცვლება',
+'tog-enotifwatchlistpages' => 'მომწერე ელ. ფოსტით, როცა გვერდი ან ფაილი, რომელსაც მე ვაკონტროლებ, შეიცვლება',
 'tog-enotifusertalkpages' => 'მომწერე თუ ჩემი განხილვის გვერდი შეიცვლება',
-'tog-enotifminoredits' => 'მომწერე ასევე გვერდების მცირე რედაქტირებისას',
+'tog-enotifminoredits' => 'მომწერე ასევე გვერდებისა და ფაილების მცირე რედაქტირებისას',
 'tog-enotifrevealaddr' => 'აჩვენე ჩემი ელ-ფოსტის მისამართი შეტყობინების წერილებში',
 'tog-shownumberswatching' => 'მაკონტროლებელ მომხმარებელთა რიცხვის ჩვენება',
 'tog-oldsig' => 'არსებული ხელმოწერა:',
index 04f8a7c..bca56ac 100644 (file)
@@ -645,7 +645,9 @@ $2',
 'token_suffix_mismatch' => "'''Уи гъэтэрэзыгъуэр къэштауэ щыткъым, уи программэм тэрэзу дэмыгъэ пыгъэувэхэм зэремылэжьым щхьэкӀэ 
 гъэтэрэзыгъуэ лъэныкъуэгъым. Гъэтэрэзыгъуэр къыкӀимыштар тхыгъэм и тхылъыр къуаншэ мыхъун щхьэкӀэ.
 Апхуэдэ хэукъуэгъуэхэр къызхэкӀыфынур аноним зыщӀ уэб-проксихэм, хэукъуэгъуэ зыхэлъхэм.'''",
+'edit_form_incomplete' => "'''Гъэтэрэзыгъуэ формэм и Ӏыхьэ гуэрэхэр серверым нэсакъым. Хэплъэ, уи гъэтэрэзыгъуэхэр зэӀымхьами, иджыри зэ гъэкӀуэж.'''",
 'editing' => 'Гъэтэрэзын: $1',
+'creating' => 'ЩӀын $1',
 'editingsection' => 'Гъэтэрэзын $1 (секцэр)',
 'editingcomment' => 'Гъэтэрэзыгъуэ $1 (лъэныкъуэгъу/секциэ щӀэ)',
 'editconflict' => 'Редактированием зэпэщӀэуэныгъэ: $1',
@@ -880,15 +882,42 @@ $1",
 
 # History merging
 'mergehistory' => 'Гъэтэрэзыгъуэхэм я тхыдэ зыуэ зэхэгъэхьа',
+'mergehistory-header' => 'Мы напэкӀуэцӀым деж зы пщӀыфынущ зэщымыщ напэкӀуэцӀытӀым я гъэтэрэзыгъуэ тхыдэр.
+Гу лъытэ, уи гъэтэрэзыгъуэм напэкӀуэцӀым и тхыдэр псоуэ къигъэнэн зэрыхуейм.',
+'mergehistory-box' => 'НапэкӀуэцӀитӀым я гъэтэрэзыгъуэ тхыдэр зы щӀын:',
+'mergehistory-from' => 'Япэреуэ напэкӀуэцӀыр:',
+'mergehistory-into' => 'НапэкӀуэцӀыр псоуэ:',
+'mergehistory-list' => 'Гъэтэрэзыгъуэхэм я тхыдэ зы хъур',
+'mergehistory-go' => 'Гъэтэрэзыгъуэ зы хъухэр гъэлъэгъуэн',
+'mergehistory-submit' => 'Гъэтэрэзыгъуэхэр зы щӀын',
+'mergehistory-empty' => 'Гъэтэрэзыгъуэ зы щӀынхэр къэгъуэтакъым.',
+'mergehistory-success' => '$3 {{PLURAL:$3|гъэтэрэзыгъуэ}} [[:$1]] щыщхэр {{PLURAL:$3|ехьэкӀащ|ехьэкӀахэщ}} [[:$2]].',
+'mergehistory-fail' => 'НапэкӀуэцӀхэм я тхыдэр зы щӀын хъуакъым, напэкӀуэцӀым и щыпхъэхэм еплъщ я зэфӀэкӀыгъуэхэм.',
+'mergehistory-no-source' => 'Япэрей щытыкӀэу напӀэкӀуэцӀ «$1» гъуэтакъым.',
+'mergehistory-invalid-source' => 'КъыхэхыпӀэм псалъащхьэ захуэ иӀэн хуейщ.',
+'mergehistory-invalid-destination' => 'НапэкӀуэцӀ псоуэ щытым псалъащхьэ зауэ иӀэн хуейщ.',
+'mergehistory-autocomment' => 'ЕхьэкӀыгъуэ [[:$1]] къыхэхауэ [[:$2]] гъэкӀуэн',
+'mergehistory-comment' => 'ЕхьэкӀыгъуэ [[:$1]] къыхэхауэ [[:$2]] гъэкӀуэн: $3',
+'mergehistory-same-destination' => 'Япэрейуэ щыт напэкӀуэцӀымрэ напэкӀуэцӀ псоуэ щытымрэ зэщхьэщыкӀын хуейщ',
+'mergehistory-reason' => 'Зытеухуар:',
 
 # Merge log
+'mergelog' => 'Зэхэгъэхьэгъуэхэм я тхылъ',
+'pagemerge-logentry' => 'Зы щӀащ [[$1]]-рэ [[$2]]-рэ (версиэхэр $3 нэгъунэ)',
 'revertmerge' => 'Зыхэдзын',
+'mergelogpagetext' => 'ИщӀагъымкӀэ зэхэгъэхьэгъуэхэм я тхыдэм и тхылъыр напэкӀуэцӀым еуэ гъэлъэгъуащ',
 
 # Diffs
-'history-title' => '$1 - зэхъуэкIыныгъэм и тхыдэ',
+'history-title' => '"$1" и зэхъуэкIыныгъэм и тхыдэр',
+'difference-title' => '"$1" версиэм я зэщхьэщыкӀыгъуэ',
+'difference-title-multipage' => 'НапэкӀуэцӀ "$1"-рэ "$2"-рэ я зэщхьэщыкӀыгъуэхэр',
+'difference-multipage' => '(НапэкӀуэцӀхэм я зэщхьэщыкӀыгъуэр)',
 'lineno' => 'Сатыр $1:',
 'compareselectedversions' => 'Хэха версиэхэр зэгъэпщэн',
+'showhideselectedversions' => 'Гъэлъэгъуэн/гъэпшкӀун версиэ хэхахэр',
 'editundo' => 'щӀегъуэжын',
+'diff-multi' => '({{PLURAL:$1|курыт версиэ $1-м ер гъэлъэгъуакъым|курыт версиэхэр $1-м ер гъэлъэгъуакъым}} {{PLURAL:$2|цӀыхухэт 2$|цӀыхухэтхэм 2$}})',
+'diff-multi-manyusers' => '({{PLURAL:$1|курыт версиэ $1-м ер гъэлъэгъуащ|курыт версиэхэр $1-м ер гъэлъэгъуащ}}, щӀахэщ $2 нэхъыбэу {{PLURAL:$2|цӀыхухэт|цӀыхухэтхэм}})',
 
 # Search results
 'searchresults' => 'Лъыхъуэным къыхэкӀахэр',
index 6e412ea..8106cc0 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Kazakh (Cyrillic script) (â\80ªÒ\9aазақша (кирил)‬)
+/** Kazakh (Cyrillic script) (â\80ªÒ\9bазақша (кирил)‬)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index 3068bda..ef977dd 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Kazakh (Latin script) (‪Qazaqşa (latın)‬)
+/** Kazakh (Latin script) (‪qazaqşa (latın)‬)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index abcac3d..ac94583 100644 (file)
@@ -351,17 +351,17 @@ $messages = array(
 'tog-editsectiononrightclick' => '제목을 오른쪽 클릭해서 부분 편집하기 (자바스크립트 필요)',
 'tog-showtoc' => '문서의 차례 보여주기 (머릿글이 4개 이상인 경우)',
 'tog-rememberpassword' => '이 브라우저에서 로그인 상태를 저장하기 (최대 $1일)',
-'tog-watchcreations' => '내가 처음 만드는 문서를 주시문서 목록에 추가',
-'tog-watchdefault' => '내가 편집하는 문서 주시문서 목록에 추가',
-'tog-watchmoves' => '내가 이동하는 문서 주시문서 목록에 추가',
-'tog-watchdeletion' => '내가 삭제하는 문서 주시문서 목록에 추가',
+'tog-watchcreations' => '내가 만드는 문서와 내가 올린 파일을 주시문서 목록에 추가',
+'tog-watchdefault' => '내가 편집하는 문서와 파일을 주시문서 목록에 추가',
+'tog-watchmoves' => '내가 이동하는 문서와 파일을 주시문서 목록에 추가',
+'tog-watchdeletion' => '내가 삭제하는 문서와 파일을 주시문서 목록에 추가',
 'tog-minordefault' => '사소한 편집을 기본적으로 선택하기',
 'tog-previewontop' => '편집 상자 앞에 미리 보기 보기',
 'tog-previewonfirst' => '처음 편집할 때 미리 보기 보기',
 'tog-nocache' => '브라우저의 문서 캐시 끄기',
-'tog-enotifwatchlistpages' => '주시문서 바뀌면 이메일로 알림',
+'tog-enotifwatchlistpages' => '주시문서 목록에 속한 문서나 파일이 바뀌면 이메일로 알림',
 'tog-enotifusertalkpages' => '내 토론 문서가 바뀌면 이메일로 알림',
-'tog-enotifminoredits' => '사소한 편집도 이메일로 알림',
+'tog-enotifminoredits' => '문서나 파일의 사소한 편집도 이메일로 알림',
 'tog-enotifrevealaddr' => '알림 메일에 내 이메일 주소를 밝히기',
 'tog-shownumberswatching' => '주시 사용자 수 보기',
 'tog-oldsig' => '현재 서명:',
@@ -494,7 +494,7 @@ $messages = array(
 'vector-action-move' => '이동',
 'vector-action-protect' => '보호',
 'vector-action-undelete' => '되살리기',
-'vector-action-unprotect' => 'ë³´í\98¸ ì\84¤ì \95 ë³\80ê²½',
+'vector-action-unprotect' => 'ë³´í\98¸ ì\84¤ì \95 ë°\94꾸기',
 'vector-simplesearch-preference' => '향상된 검색어 제안 사용하기 (벡터 스킨 전용)',
 'vector-view-create' => '만들기',
 'vector-view-edit' => '편집',
@@ -529,9 +529,9 @@ $messages = array(
 'undelete_short' => '편집 $1개 되살리기',
 'viewdeleted_short' => '삭제된 편집 $1개 보기',
 'protect' => '보호',
-'protect_change' => 'ë³´í\98¸ ì\88\98ì¤\80 ë³\80ê²½',
+'protect_change' => 'ë³´í\98¸ ì\88\98ì¤\80 ë°\94꾸기',
 'protectthispage' => '이 문서 보호하기',
-'unprotect' => 'ë³´í\98¸ ì\84¤ì \95 ë³\80ê²½',
+'unprotect' => 'ë³´í\98¸ ì\84¤ì \95 ë°\94꾸기',
 'unprotectthispage' => '이 문서의 보호 설정을 변경하기',
 'newpage' => '새 문서',
 'talkpage' => '토론 문서',
@@ -1174,7 +1174,7 @@ $2개 보다 적게 써야 하지만 지금은 $1개를 쓰고 있습니다.",
 'page_last' => '마지막',
 'histlegend' => "비교하려는 판을 선택한 다음 엔터나 아래의 버튼을 누르세요.<br />
 설명: '''({{int:cur}})''' = 최신 판과 비교, '''({{int:last}})''' = 이전 판과 비교, '''{{int:minoreditletter}}'''= 사소한 편집",
-'history-fieldset-title' => '문ì\84\9cì\9d\98 ë°\94ë\80\90 ë\82´ë ¥ 찾기',
+'history-fieldset-title' => '문ì\84\9cì\9d\98 ë°\94ë\80\9c ë\82´ì\97­ 찾기',
 'history-show-deleted' => '삭제된 것만',
 'histfirst' => '초기',
 'histlast' => '최신',
@@ -1322,7 +1322,7 @@ $1",
 'mergelogpagetext' => '다음은 한 문서의 역사를 다른 문서의 역사와 합친 최근 기록입니다.',
 
 # Diffs
-'history-title' => '"$1"  ë¬¸ì\84\9cì\9d\98 ë³\80ê²½ ë\82´ë ¥',
+'history-title' => '"$1"  ë¬¸ì\84\9cì\9d\98 ë°\94ë\80\9c ë\82´ì\97­',
 'difference-title' => '"$1"의 두 판 사이의 차이',
 'difference-title-multipage' => '문서 "$1"(와)과 "$2" 사이의 차이',
 'difference-multipage' => '(문서 사이의 차이)',
@@ -1385,7 +1385,7 @@ $1",
 'showingresultsnum' => "'''$2'''번 부터의 '''결과 $3개''' 입니다.",
 'showingresultsheader' => "'''$4''' 검색어에 대하여 결과 '''$3'''개 중 {{PLURAL:$5|'''$1'''개|'''$1 - $2'''번째}}를 보여 주고 있습니다.",
 'nonefound' => "'''참고''': 몇개의 이름공간만 기본 찾을 범위입니다. 토론이나 틀 등의 모든 자료를 찾하기 위해서는 접두어로 '''all:''' 어떤 이름공간을 위해서는 접두어로 그 이름공간을 쓸 수 있습니다.",
-'search-nonefound' => '찾 결과가 없습니다.',
+'search-nonefound' => '찾 결과가 없습니다.',
 'powersearch' => '고급 찾기',
 'powersearch-legend' => '고급 찾기',
 'powersearch-ns' => '다음의 이름공간에서 찾기:',
@@ -1537,7 +1537,7 @@ HTML 태그를 확인하세요.',
 'userrights-lookup-user' => '사용자 권한 관리',
 'userrights-user-editname' => '계정 이름:',
 'editusergroup' => '사용자 그룹 편집',
-'editinguser' => "ì\82¬ì\9a©ì\9e\90 '''[[User:$1|$1]]''' $2ì\9d\98 ê¶\8cí\95\9c ë³\80ê²½",
+'editinguser' => "ì\82¬ì\9a©ì\9e\90 '''[[User:$1|$1]]''' $2ì\9d\98 ê¶\8cí\95\9c ë°\94ê¿\88",
 'userrights-editusergroup' => '사용자 그룹 편집',
 'saveusergroups' => '사용자 권한 저장',
 'userrights-groupsmember' => '현재 권한:',
@@ -1595,7 +1595,7 @@ HTML 태그를 확인하세요.',
 'right-reupload-own' => '자신이 이미 올린 파일 덮어쓰기',
 'right-reupload-shared' => '공용의 파일을 무시하고 로컬에서 파일 올리기',
 'right-upload_by_url' => 'URL 주소에서 파일 올리기',
-'right-purge' => '확인 없이 문서의 캐시를 갱신',
+'right-purge' => '확인 없이 문서의 캐시를 새로 고침',
 'right-autoconfirmed' => '준보호된 문서 편집',
 'right-bot' => '봇의 편집으로 취급',
 'right-nominornewtalk' => '토론 문서를 새로 만들때 사소한 편집 사용 불가능',
@@ -1616,7 +1616,7 @@ HTML 태그를 확인하세요.',
 'right-ipblock-exempt' => 'IP 차단, 자동 차단, 광역 차단을 무시',
 'right-proxyunbannable' => '프록시 자동 차단을 적용하지 않음',
 'right-unblockself' => '자기 자신을 차단 해제하기',
-'right-protect' => 'ë³´í\98¸ ì\88\98ì¤\80 ë³\80ê²½ 및 보호된 문서 편집',
+'right-protect' => 'ë³´í\98¸ ì\88\98ì¤\80 ë°\94꾸기 및 보호된 문서 편집',
 'right-editprotected' => '보호된 문서 편집 (연쇄적 보호 제외)',
 'right-editinterface' => '사용자 인터페이스를 편집',
 'right-editusercssjs' => '다른 사용자의 CSS와 자바스크립트 문서를 편집',
@@ -1909,7 +1909,7 @@ URL이 올바르고 접근 가능한지를 확인하고 다시 시도해주세
 'backend-fail-connect' => '"$1" 저장 백엔드에 접속하지 못했습니다.',
 'backend-fail-internal' => '"$1" 저장 백엔드에 알 수 없는 오류가 발생했습니다.',
 'backend-fail-contenttype' => '"$1"에 저장하기 위한 파일의 내용 유형을 판별하지 못했습니다.',
-'backend-fail-batchsize' => '저장 백엔드에서 파일 {{PLURAL:$1|작업}} $1개가 쌓여 있습니다; 한계는 $2개입니다.',
+'backend-fail-batchsize' => '저장 백엔드에서 파일 {{PLURAL:$1|작업}} $1개가 쌓여 있습니다. 한계는 $2개입니다.',
 'backend-fail-usable' => '파일 저장 권한이 없거나 저장 위치가 빠졌기 때문에 $1 파일을 저장할 수 없습니다.',
 
 # File journal errors
@@ -1917,12 +1917,12 @@ URL이 올바르고 접근 가능한지를 확인하고 다시 시도해주세
 'filejournal-fail-dbquery' => '저장소 백엔드 "$1"에 대한 저널 데이터베이스에서 새로 고칠 수 없습니다.',
 
 # Lock manager
-'lockmanager-notlocked' => '‘$1’ 경로의 잠금을 풀 수 없습니다. 해당 경로는 잠겨 있지 않습니다.',
-'lockmanager-fail-closelock' => '‘$1’에 대한 잠금 파일을 닫지 못했습니다.',
-'lockmanager-fail-deletelock' => '‘$1’에 대한 잠금 파일을 삭제하지 못했습니다.',
-'lockmanager-fail-acquirelock' => '‘$1’에 대한 잠금이 실패했습니다.',
-'lockmanager-fail-openlock' => '‘$1’에 대한 잠금 파일을 열지 못했습니다.',
-'lockmanager-fail-releaselock' => '‘$1’에 대한 잠금을 해제하지 못했습니다.',
+'lockmanager-notlocked' => '"$1" 경로의 잠금을 풀 수 없습니다. 해당 경로는 잠겨 있지 않습니다.',
+'lockmanager-fail-closelock' => '"$1"에 대한 잠금 파일을 닫지 못했습니다.',
+'lockmanager-fail-deletelock' => '"$1"에 대한 잠금 파일을 삭제하지 못했습니다.',
+'lockmanager-fail-acquirelock' => '"$1"에 대한 잠금이 실패했습니다.',
+'lockmanager-fail-openlock' => '"$1"에 대한 잠금 파일을 열지 못했습니다.',
+'lockmanager-fail-releaselock' => '"$1"에 대한 잠금을 해제하지 못했습니다.',
 'lockmanager-fail-db-bucket' => '데이터베이스의 버킷 $1의 잠금을 풀지 못했습니다.',
 'lockmanager-fail-db-release' => '데이터베이스 $1의 잠금을 풀지 못했습니다.',
 'lockmanager-fail-svr-release' => '서버 $1의 잠금을 풀지 못했습니다.',
@@ -1937,33 +1937,36 @@ URL이 올바르고 접근 가능한지를 확인하고 다시 시도해주세
 
 # Special:UploadStash
 'uploadstash' => '파일 올리기 임시 저장',
-'uploadstash-summary' => '이 페이지는 위키에 등록되지는 않았지만 올리는 과정 중에 있는 파일을 열람할 수 있습니다. 이 파일들은 올린이 외에는 볼 수 없습니다.',
+'uploadstash-summary' => '이 문서는 위키에 등록되지는 않았지만 올리는 과정 중에 있는 파일을 열람할 수 있습니다. 이 파일들은 올린이 외에는 볼 수 없습니다.',
 'uploadstash-clear' => '임시 저장한 파일 제거하기',
 'uploadstash-nofiles' => '임시 저장한 파일이 없습니다.',
-'uploadstash-badtoken' => 'ì\9d´ ë\8f\99ì\9e\91ì\9d\84 ì\88\98í\96\89í\95\98ë\8a\94 ë\8d° ì\8b¤í\8c¨í\96\88ì\8aµë\8b\88ë\8b¤. í\8e¸ì§\91 í\86 í\81°ì\9d´ ë§\8cë£\8cë\90\98ì\97\88ì\9d\84 ê°\80ë\8a¥ì\84±ì\9d´ ì\9e\88ì\8aµë\8b\88ë\8b¤. ë\8b¤ì\8b\9c ì\8b\9cë\8f\84í\95´ ë³´ì\8b­ì\8b\9cì\98¤.',
+'uploadstash-badtoken' => 'ì\9d´ ë\8f\99ì\9e\91ì\9d\84 ì\88\98í\96\89í\95\98ë\8a\94 ë\8d° ì\8b¤í\8c¨í\96\88ì\8aµë\8b\88ë\8b¤. í\8e¸ì§\91 í\86 í\81°ì\9d´ ë§\8cë£\8cë\90\98ì\97\88ì\9d\84 ê°\80ë\8a¥ì\84±ì\9d´ ì\9e\88ì\8aµë\8b\88ë\8b¤. ë\8b¤ì\8b\9c ì\8b\9cë\8f\84í\95\98ì\84¸ì\9a\94.',
 'uploadstash-errclear' => '파일을 제거하는 데 실패했습니다.',
 'uploadstash-refresh' => '파일 목록을 새로고침',
 'invalid-chunk-offset' => '청크 오프셋이 잘못되었습니다.',
 
 # img_auth script messages
 'img-auth-accessdenied' => '접근 거부됨',
-'img-auth-nopathinfo' => '서버가 이 정보를 받을 수 있도록 설정되어 있지 않습니다.
+'img-auth-nopathinfo' => 'PATH_INFO를 잃었습니다.
+서버가 이 정보를 받을 수 있도록 설정되어 있지 않습니다.
 이러한 경우는 서버가 CGI 기반이고 img_auth를 지원하지 않을 때 나타날 수 있습니다.
 https://www.mediawiki.org/wiki/Manual:Image_Authorization 을 참고하십시오.',
-'img-auth-notindir' => 'ì\9a\94ì²­í\95\9c ê²½ë¡\9cê°\80 ì\84¤ì \95í\95\9c ì\97\85ë¡\9cë\93\9c 디렉토리에 없습니다.',
-'img-auth-badtitle' => '"$1"에서 바른 제목을 만들 수 없습니다.',
+'img-auth-notindir' => 'ì\9a\94ì²­í\95\9c ê²½ë¡\9cê°\80 ì\84¤ì \95í\95\9c ì\98¬ë¦¬ê¸° 디렉토리에 없습니다.',
+'img-auth-badtitle' => '"$1"에서 바른 제목을 만들 수 없습니다.',
 'img-auth-nologinnWL' => '당신은 로그인하지 않았으며 "$1" 파일은 화이트리스트에 존재하지 않습니다.',
 'img-auth-nofile' => '"$1" 파일이 없습니다.',
 'img-auth-isdir' => '"$1" 디렉토리에 접근을 시도했습니다.
 파일에만 접근할 수 있습니다.',
-'img-auth-streaming' => '‘$1’ 파일을 전송하는 중입니다.',
-'img-auth-public' => 'img_auth.php는 개인 위키 파일을 외부 사이트로 전송하는 기능입니다. 이 기능은 기본적으로 공개적인 위키에서 사용하도록 설계되어 있으며, 보안적인 문제로 기본적으로 img_auth.php 기능은 비활성화되어 있습니다.',
-'img-auth-noread' => '‘$1’ 파일을 볼 권한이 없습니다.',
+'img-auth-streaming' => '"$1" 파일을 전송하는 중입니다.',
+'img-auth-public' => 'img_auth.php는 개인 위키 파일을 외부 사이트로 전송하는 기능입니다.
+이 기능은 기본적으로 공개적인 위키에서 사용하도록 설계되어 있습니다.
+보안적인 문제로 기본적으로 img_auth.php 기능은 비활성화되어 있습니다.',
+'img-auth-noread' => '"$1" 파일을 볼 권한이 없습니다.',
 'img-auth-bad-query-string' => 'URL에 잘못된 쿼리 문자열이 있습니다.',
 
 # HTTP errors
 'http-invalid-url' => '잘못된 URL: $1',
-'http-invalid-scheme' => '‘$1’로 시작하는 URL은 지원되지 않습니다.',
+'http-invalid-scheme' => '"$1"로 시작하는 URL은 지원되지 않습니다.',
 'http-request-error' => '알 수 없는 오류로 HTTP 요청에 실패했습니다.',
 'http-read-error' => 'HTTP 읽기 오류.',
 'http-timed-out' => 'HTTP 요청 시간 초과.',
@@ -1976,7 +1979,9 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization 을 참고하십시오
 'upload-curl-error6-text' => 'URL에 접근할 수 없습니다.
 URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'upload-curl-error28' => '업로드 시간 초과',
-'upload-curl-error28-text' => '사이트에서 응답하는 시간이 너무 깁니다. 사이트 접속이 가능한지 확인한 다음 다시 시도해주세요. 해당 사이트에 접속이 많을 경우, 접속이 원활한 시간대에 시도해주세요.',
+'upload-curl-error28-text' => '사이트에서 응답하는 시간이 너무 깁니다.
+사이트 접속이 가능한지 확인한 다음 다시 시도해주세요.
+해당 사이트에 접속이 많을 경우 접속이 원활한 시간대에 시도해주세요.',
 
 'license' => '라이선스:',
 'license-header' => '라이선스',
@@ -2040,7 +2045,7 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'shared-repo-from' => '($1)',
 'shared-repo' => '공용 저장소',
 'shared-repo-name-wikimediacommons' => '위키미디어 공용',
-'filepage.css' => '/* 이 CSS 설정은 파일 설명 페이지에 포함되며, 또한 해외 클라이언트 위키에 포함됩니다 */',
+'filepage.css' => '/* 이 CSS 설정은 파일 설명 문서에 포함되며, 또한 해외 클라이언트 위키에 포함됩니다 */',
 
 # File reversion
 'filerevert' => '$1 되돌리기',
@@ -2074,7 +2079,8 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 
 # MIME search
 'mimesearch' => 'MIME 찾기',
-'mimesearch-summary' => 'MIME 타입에 해당하는 파일을 검색합니다. MIME 값을 <tt>image/jpeg</tt> 형태로 입력해주세요.',
+'mimesearch-summary' => 'MIME 타입에 해당하는 파일을 찾습니다.
+다음 형태로 입력해주세요: 내용종류/하위종류, 예를 들어 <tt>image/jpeg</tt>',
 'mimetype' => 'MIME 종류:',
 'download' => '다운로드',
 
@@ -2087,7 +2093,6 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 # Unused templates
 'unusedtemplates' => '사용하지 않는 틀 목록',
 'unusedtemplatestext' => '다른 문서에서 사용하지 않는 {{ns:template}} 이름공간 문서의 목록입니다.
-
 삭제하기 전에 사용 여부를 다시 확인해 주세요.',
 'unusedtemplateswlh' => '다른 링크',
 
@@ -2108,12 +2113,12 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'statistics-header-hooks' => '기타 통계',
 'statistics-articles' => '일반 문서',
 'statistics-pages' => '전체 문서',
-'statistics-pages-desc' => '토론 문서, 넘겨주기 문서 등을 포함.',
+'statistics-pages-desc' => '토론 문서, 넘겨주기 문서 등을 포함하는 모든 문서.',
 'statistics-files' => '올려져 있는 파일',
 'statistics-edits' => '{{SITENAME}} 설치 후 문서의 전체 편집 횟수',
 'statistics-edits-average' => '문서당 평균 편집 횟수',
 'statistics-views-total' => '총 방문 수',
-'statistics-views-total-desc' => '존재하지 않는 문서나 특수 문서에 대한 방문수는 집계ë\90\98ì§\80 ì\95\8aì\95\98ì\8aµë\8b\88ë\8b¤.',
+'statistics-views-total-desc' => '존재하지 않는 문서나 특수 문서에 대한 방문수는 집계í\95\98ì§\80 ì\95\8aì\95\98ì\8aµë\8b\88ë\8b¤.',
 'statistics-views-peredit' => '편집당 방문 횟수',
 'statistics-users' => '등록된 [[Special:ListUsers|사용자]]',
 'statistics-users-active' => '활동적인 사용자',
@@ -2121,7 +2126,7 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'statistics-mostpopular' => '가장 많이 읽힌 문서',
 
 'disambiguations' => '동음이의 문서를 가리키는 문서 목록',
-'disambiguationspage' => 'Template:Disambig',
+'disambiguationspage' => 'Template:disambig',
 'disambiguations-text' => "다음의 문서들은 '''동음이의 문서'''를 가리키고 있습니다.
 그 링크를 다른 적절한 문서로 연결해 주어야 합니다.<br />
 [[MediaWiki:Disambiguationspage]]에서 링크된 틀을 사용하는 문서를 동음이의 문서로 간주합니다.",
@@ -2130,7 +2135,8 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'doubleredirectstext' => '이 문서는 다른 넘겨주기 문서로 넘겨주고 있는 문서의 목록입니다.
 매 줄에는 첫 번째 문서와 두 번째 문서의 링크가 있습니다. 그리고 보통 첫 번째 문서가 넘겨주어야 할 "실제" 문서인 두 번째 넘겨주기의 대상이 있습니다.
 <del>취소선이 그인</del> 부분은 이미 해결되었습니다.',
-'double-redirect-fixed-move' => '[[$1]] 문서를 옮겼습니다. 이 문서는 이제 [[$2]] 문서로 넘겨줍니다.',
+'double-redirect-fixed-move' => '[[$1]] 문서를 옮겼습니다.
+이 문서는 이제 [[$2]] 문서로 넘겨줍니다.',
 'double-redirect-fixed-maintenance' => '[[$1]]에서 [[$2]]로 이중 넘겨주기를 고치는 중',
 'double-redirect-fixer' => '넘겨주기 수리꾼',
 
@@ -2196,7 +2202,7 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'listusers-editsonly' => '기여가 있는 사용자만 보기',
 'listusers-creationsort' => '계정 등록일 순으로 정렬',
 'usereditcount' => '편집 $1회',
-'usercreated' => '$1 $2에 계정 {{GENDER:$3|생성됨}}',
+'usercreated' => '$1 $2에 계정 {{GENDER:$3|만들어짐}}',
 'newpages' => '새 문서 목록',
 'newpages-username' => '이름:',
 'ancientpages' => '오래된 문서 목록',
@@ -2219,7 +2225,7 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'booksources-search-legend' => '책 찾기',
 'booksources-go' => '찾기',
 'booksources-text' => '아래의 목록은 새 책이나 중고 책을 판매하는 외부 사이트로, 원하는 책의 정보를 얻을 수 있습니다:',
-'booksources-invalid-isbn' => '입력한 ISBN이 잘못된 것으로 보입니다; 원본과 대조해 보십시오.',
+'booksources-invalid-isbn' => '입력한 ISBN이 잘못된 것으로 보입니다. 원본과 대조해 보세요.',
 
 # Special:Log
 'specialloguserlabel' => '작업 수행자:',
@@ -2246,8 +2252,9 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'allpagesnext' => '다음',
 'allpagessubmit' => '표시',
 'allpagesprefix' => '다음으로 시작하는 문서 보기:',
-'allpagesbadtitle' => '문서 제목이 잘못되었거나 다른 사이트로 연결되는 인터위키를 가지고 있습니다. 문서 제목에 사용할 수 없는 문자를 사용했을 수 있습니다.',
-'allpages-bad-ns' => '{{SITENAME}}에서는 ‘$1’ 이름공간을 사용하지 않습니다.',
+'allpagesbadtitle' => '문서 제목이 잘못되었거나 다른 사이트로 연결되는 인터위키를 가지고 있습니다.
+문서 제목에 사용할 수 없는 문자를 사용했을 수 있습니다.',
+'allpages-bad-ns' => '{{SITENAME}}에서는 "$1" 이름공간을 사용하지 않습니다.',
 'allpages-hide-redirects' => '넘겨주기 숨기기',
 
 # SpecialCachedPage
@@ -2270,7 +2277,7 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'sp-deletedcontributions-contribs' => '기여',
 
 # Special:LinkSearch
-'linksearch' => '외부 링크 검색',
+'linksearch' => '외부 링크 찾기',
 'linksearch-pat' => '검색 패턴:',
 'linksearch-ns' => '이름공간:',
 'linksearch-ok' => '검색',
@@ -2309,18 +2316,18 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'listgrouprights-rights' => '권한',
 'listgrouprights-helppage' => 'Help:사용자 권한 그룹',
 'listgrouprights-members' => '(사용자 목록)',
-'listgrouprights-addgroup' => '{{PLURAL:$2}}권한 부여: $1',
-'listgrouprights-removegroup' => '{{PLURAL:$2}}권한 회수: $1',
+'listgrouprights-addgroup' => '{{PLURAL:$2|권한}} 부여: $1',
+'listgrouprights-removegroup' => '{{PLURAL:$2|권한}} 회수: $1',
 'listgrouprights-addgroup-all' => '모든 권한을 부여',
 'listgrouprights-removegroup-all' => '모든 권한을 회수',
-'listgrouprights-addgroup-self' => '자신에게 다음 {{PLURAL:$2|권한|권한}}을 부여: $1',
-'listgrouprights-removegroup-self' => '자신에게서 다음 {{PLURAL:$2|권한|권한}}을 해제: $1',
+'listgrouprights-addgroup-self' => '자신에게 다음 {{PLURAL:$2|권한}}을 부여: $1',
+'listgrouprights-removegroup-self' => '자신에게서 다음 {{PLURAL:$2|권한}}을 해제: $1',
 'listgrouprights-addgroup-self-all' => '자신에게 모든 권한을 부여',
 'listgrouprights-removegroup-self-all' => '자신의 계정에서 모든 권한을 해제',
 
 # E-mail user
 'mailnologin' => '보낼 이메일 주소가 없음',
-'mailnologintext' => '다른 사용자에게 이메일을 보내려면, [[Special:UserLogin|로그인]]한 다음 [[Special:Preferences|사용자 환경 설정]]에서 자신의 이메일 주소를 저장해야 합니다.',
+'mailnologintext' => '다른 사용자에게 이메일을 보내려면 [[Special:UserLogin|로그인]]한 다음 [[Special:Preferences|사용자 환경 설정]]에서 자신의 이메일 주소를 저장해야 합니다.',
 'emailuser' => '이메일 보내기',
 'emailpage' => '사용자에게 이메일 보내기',
 'emailpagetext' => '이 사용자가 환경 설정에 올바른 이메일 주소를 적었다면, 아래 양식을 통해 이메일을 보낼 수 있습니다.
@@ -2334,7 +2341,7 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'nowikiemailtitle' => '이메일이 허용되지 않음',
 'nowikiemailtext' => '이 사용자는 다른 사용자로부터의 이메일을 받지 않도록 설정하였습니다.',
 'emailnotarget' => '수신자로 없는 사용자를 지정하였거나 계정 이름이 잘못되었습니다.',
-'emailtarget' => '수신자 계정 입력',
+'emailtarget' => '수신자 계정 이름 입력',
 'emailusername' => '계정 이름:',
 'emailusernamesubmit' => '확인',
 'email-legend' => '{{SITENAME}}의 다른 사용자에게 이메일을 보내기',
@@ -2347,7 +2354,7 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'emailccsubject' => '$1에게 보낸 메일 사본: $2',
 'emailsent' => '이메일 보냄',
 'emailsenttext' => '이메일을 보냈습니다.',
-'emailuserfooter' => '이 이메일은 {{SITENAME}}의 $1 사용자가 $2 사용자에게 "이메일 보내기" 기능을 통해 ì \84ì\86¡ë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.',
+'emailuserfooter' => '이 이메일은 {{SITENAME}}의 $1 사용자가 $2 사용자에게 "이메일 보내기" 기능을 통해 ë³´ë\83\88ì\8aµë\8b\88ë\8b¤.',
 
 # User Messenger
 'usermessage-summary' => '시스템 메시지 남기기',
@@ -2382,7 +2389,7 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'watchlistcontains' => '문서 $1개를 주시하고 있습니다.',
 'iteminvalidname' => '"$1" 항목에 문제가 발생했습니다. 이름이 잘못되었습니다...',
 'wlnote' => "다음은 최근 '''$2'''시간 동안 바뀐 문서 '''$1'''개 입니다. ($3 $4 기준)",
-'wlshowlast' => '최근 $1 시간 $2 일 또는 $3 동안에 바뀐 문서',
+'wlshowlast' => '최근 $1시간 $2일 또는 $3 동안에 바뀐 문서',
 'watchlist-options' => '주시문서 목록 설정',
 
 # Displayed when you click the "watch" button and it is in the process of watching
@@ -2397,8 +2404,8 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 'changed' => '바꾸',
 'created' => '만들',
 'enotif_subject' => '{{SITENAME}}에서 $PAGEEDITOR 사용자가 $PAGETITLE 문서를 $CHANGEDORCREATED었습니다.',
-'enotif_lastvisited' => 'ë§\88ì§\80ë§\89ì\9c¼ë¡\9c ë°©ë¬¸í\95\9c ë\92¤ ì\83\9d긴 ëª¨ë\93  ë³\80ê²½ ì\82¬í\95­ì\9d\84 ë³´ë ¤ë©´ $1ì\9d\84(를) ë³´ì\8b­ì\8b\9cì\98¤.',
-'enotif_lastdiff' => 'ì\9d´ ë°\94ë\80\90 ë\82´ì\9a©ì\9d\84 ë³´ë ¤ë©´ $1ì\9d\84 ë³´ì\8b­ì\8b\9cì\98¤.',
+'enotif_lastvisited' => 'ë§\88ì§\80ë§\89ì\9c¼ë¡\9c ë°©ë¬¸í\95\9c ë\92¤ ì\83\9d긴 ëª¨ë\93  ë°\94ë\80\9c ì\82¬í\95­ì\9d\84 ë³´ë ¤ë©´ $1ì\9d\84(를) ë³´ì\84¸ì\9a\94.',
+'enotif_lastdiff' => 'ì\9d´ ë°\94ë\80\90 ë\82´ì\9a©ì\9d\84 ë³´ë ¤ë©´ $1ì\9d\84 ë³´ì\84¸ì\9a\94.',
 'enotif_anon_editor' => '익명 사용자 $1',
 'enotif_body' => '$WATCHINGUSERNAME님,
 
@@ -2415,7 +2422,7 @@ $NEWPAGE
 이 문서를 열기 전에는 다른 알림 이메일을 더 이상 보내지 않습니다.
 모든 주시 문서의 알림 딱지를 초기화할 수도 있습니다.
 
-             {{SITENAME}} 알림 시스템
+{{SITENAME}} 알림 시스템
 
 --
 이메일 알림 설정을 바꾸시려면 이곳을 방문해주세요:
@@ -2433,19 +2440,21 @@ $UNWATCHURL
 # Delete
 'deletepage' => '문서 삭제하기',
 'confirm' => '확인',
-'excontent' => '내용: ‘$1’',
-'excontentauthor' => "내용: ‘$1’ (유일한 편집자는 '[[Special:Contributions/$2|$2]]')",
-'exbeforeblank' => '비우기 전의 내용: ‘$1’',
+'excontent' => '내용: "$1"',
+'excontentauthor' => '내용: "$1" (유일한 편집자는 "[[Special:Contributions/$2|$2]]")',
+'exbeforeblank' => '비우기 전의 내용: "$1"',
 'exblank' => '빈 문서',
-'delete-confirm' => '‘$1’ 삭제',
+'delete-confirm' => '"$1" 삭제',
 'delete-legend' => '삭제',
 'historywarning' => "'''주의:''' 삭제하려는 문서에 과거 편집 내역 약 $1개가 있습니다:",
-'confirmdeletetext' => '문서와 문서 역사를 삭제하려고 합니다. 삭제하려는 문서가 맞는지, 이 문서를 삭제하는 것이 [[{{MediaWiki:Policy-url}}|정책]]에 맞는 행동인지를 확인해 주세요.',
+'confirmdeletetext' => '문서와 문서 역사를 삭제하려고 합니다.
+삭제하려는 문서가 맞는지, 이 문서를 삭제하는 것이 [[{{MediaWiki:Policy-url}}|정책]]에 맞는 행동인지를 확인해 주세요.',
 'actioncomplete' => '명령 완료',
 'actionfailed' => '명령 실패',
-'deletedtext' => '‘$1’ 문서를 삭제했습니다. 최근 삭제 기록은 $2에 있습니다.',
+'deletedtext' => '"$1" 문서를 삭제했습니다.
+최근 삭제 기록은 $2에 있습니다.',
 'dellogpage' => '삭제 기록',
-'dellogpagetext' => '아래의 목록은 최근에 삭제된 문서입니다.',
+'dellogpagetext' => '아래의 목록은 최근에 삭제된 문서입니다.',
 'deletionlog' => '삭제 기록',
 'reverted' => '이전 버전으로 되돌렸습니다.',
 'deletecomment' => '이유:',
@@ -2454,9 +2463,10 @@ $UNWATCHURL
 'deletereason-dropdown' => '*일반적인 삭제 이유
 ** 작성자의 요청
 ** 저작권 침해
-** 잘못된 문서',
+** 훼손 행위',
 'delete-edit-reasonlist' => '삭제 이유 편집',
-'delete-toobig' => '이 문서에는 편집 역사가 $1개 있습니다. 편집 역사가 긴 문서를 삭제하면 {{SITENAME}}에 큰 혼란을 줄 수 있기 때문에 삭제할 수 없습니다.',
+'delete-toobig' => '이 문서에는 편집 역사가 $1개 있습니다.
+편집 역사가 긴 문서를 삭제하면 {{SITENAME}}에 큰 혼란을 줄 수 있기 때문에 삭제할 수 없습니다.',
 'delete-warning-toobig' => '이 문서에는 편집 역사가 $1개 있습니다.
 편집 역사가 긴 문서를 삭제하면 {{SITENAME}} 데이터베이스 동작에 큰 영향을 줄 수 있습니다.
 주의해 주세요.',
@@ -2466,29 +2476,33 @@ $UNWATCHURL
 'rollback_short' => '되돌리기',
 'rollbacklink' => '되돌리기',
 'rollbackfailed' => '되돌리기 실패',
-'cantrollback' => '편집을 되돌릴 수 없습니다. 문서를 편집한 사용자가 한명뿐입니다.',
-'alreadyrolled' => '[[:$1]]에서 [[User:$2|$2]] ([[User talk:$2|토론]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) 의 편집을 되돌릴 수 없습니다; 누군가가 이미 문서를 고치거나 되돌렸습니다.
+'cantrollback' => '편집을 되돌릴 수 없습니다.
+문서를 편집한 사용자가 한명뿐입니다.',
+'alreadyrolled' => '[[:$1]]에서 [[User:$2|$2]] ([[User talk:$2|토론]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]])의 편집을 되돌릴 수 없습니다.
+누군가가 이미 문서를 고치거나 되돌렸습니다.
 
-마지막으로 이 문서를 편집한 사용자는 [[User:$3|$3]] ([[User talk:$3|토론]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) 입니다.',
-'editcomment' => "편집 요약: ''$1''",
+마지막으로 이 문서를 편집한 사용자는 [[User:$3|$3]] ([[User talk:$3|토론]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])입니다.',
+'editcomment' => '편집 요약: "$1"',
 'revertpage' => '[[Special:Contributions/$2|$2]]([[User talk:$2|토론]])의 편집을 [[User:$1|$1]]의 마지막 버전으로 되돌림',
 'revertpage-nouser' => '(계정 이름 삭제됨)의 편집을 [[User:$1|$1]]의 마지막 편집으로 되돌림',
 'rollback-success' => '$1의 편집을 $2의 마지막 버전으로 되돌렸습니다.',
 
 # Edit tokens
 'sessionfailure-title' => '세션 손실',
-'sessionfailure' => '로그인 세션에 문제가 발생한 것 같습니다. 세션 하이재킹을 막기 위해 동작이 취소되었습니다. 브라우저의 "뒤로" 버튼을 누르고 문서를 새로고침한 후에 다시 시도해 주세요.',
+'sessionfailure' => '로그인 세션에 문제가 발생한 것 같습니다.
+세션 하이재킹을 막기 위해 동작이 취소되었습니다.
+브라우저의 뒤로 버튼을 누르고 문서를 새로 고침한 후에 다시 시도해 주세요.',
 
 # Protect
 'protectlogpage' => '문서 보호 기록',
-'protectlogtext' => 'ì\95\84ë\9e\98ì\9d\98 ëª©ë¡\9dì\9d\80 ë¬¸ì\84\9c ë³´í\98¸ì\97\90 ê´\80í\95\9c ë³\80ê²½ 사항에 대한 기록입니다.
\98\84ì\9e¬ ë³´í\98¸ë\90\9c ë¬¸ì\84\9cì\9d\98 ëª©ë¡\9dì\97\90 ë\8c\80í\95´ì\84\9cë\8a\94 [[Special:ProtectedPages|ë³´í\98¸ë\90\9c ë¬¸ì\84\9c ëª©ë¡\9d]]ì\9d\84 ì°¸ê³ í\95\98ì\8b­ì\8b\9cì\98¤.',
-'protectedarticle' => '‘[[$1]]’ 문서를 보호함',
-'modifiedarticleprotection' => '‘[[$1]]’ 문서의 보호 설정을 변경함',
-'unprotectedarticle' => '‘[[$1]]’ 문서를 보호 해제함',
+'protectlogtext' => 'ì\95\84ë\9e\98ì\9d\98 ëª©ë¡\9dì\9d\80 ë¬¸ì\84\9c ë³´í\98¸ì\97\90 ê´\80í\95\9c ë°\94ë\80\9c 사항에 대한 기록입니다.
\98\84ì\9e¬ ë³´í\98¸ë\90\9c ë¬¸ì\84\9cì\9d\98 ëª©ë¡\9dì\97\90 ë\8c\80í\95´ì\84\9cë\8a\94 [[Special:ProtectedPages|ë³´í\98¸ë\90\9c ë¬¸ì\84\9c ëª©ë¡\9d]]ì\9d\84 ì°¸ê³ í\95\98ì\84¸ì\9a\94.',
+'protectedarticle' => '"[[$1]]" 문서를 보호함',
+'modifiedarticleprotection' => '"[[$1]]" 문서의 보호 설정을 바꿈',
+'unprotectedarticle' => '"[[$1]]" 문서를 보호 해제함',
 'movedarticleprotection' => '문서의 보호 설정을 "[[$2]]"에서 "[[$1]]"으로 옮김',
-'protect-title' => '‘$1’ 보호하기',
-'protect-title-notallowed' => '‘$1’ 문서의 보호 수준 보기',
+'protect-title' => '"$1" 보호하기',
+'protect-title-notallowed' => '"$1" 문서의 보호 수준 보기',
 'prot_1movedto2' => '[[$1]] 문서를 [[$2]] 문서로 이동함',
 'protect-badnamespace-title' => '보호할 수 없는 이름공간',
 'protect-badnamespace-text' => '이 이름공간에 있는 문서는 보호할 수 없습니다.',
@@ -2498,14 +2512,17 @@ $UNWATCHURL
 'protect_expiry_invalid' => '보호 기간이 잘못되었습니다.',
 'protect_expiry_old' => '기한을 과거로 입력했습니다.',
 'protect-unchain-permissions' => '다른 보호 설정을 수동으로 설정하기',
-'protect-text' => "'''$1''' 문서의 보호 수준을 보거나 변경할 수 있습니다.",
-'protect-locked-blocked' => "차단된 동안에는 보호 설정을 바꿀 수 없습니다. '''$1''' 문서의 보호 설정은 다음과 같습니다:",
-'protect-locked-dblock' => "데이터베이스가 잠겨 문서 보호 설정을 바꿀 수 없습니다. '''$1''' 문서의 현재 설정은 다음과 같습니다:",
-'protect-locked-access' => "문서 보호 수준을 변경할 권한이 없습니다. '''$1''' 문서의 권한은 다음과 같습니다.",
+'protect-text' => "'''$1''' 문서의 보호 수준을 보거나 바꿀 수 있습니다.",
+'protect-locked-blocked' => "차단된 동안에는 보호 설정을 바꿀 수 없습니다.
+'''$1''' 문서의 보호 설정은 다음과 같습니다:",
+'protect-locked-dblock' => "데이터베이스가 잠겨 문서 보호 설정을 바꿀 수 없습니다.
+'''$1''' 문서의 현재 설정은 다음과 같습니다:",
+'protect-locked-access' => "문서 보호 수준을 바꿀 권한이 없습니다.
+'''$1''' 문서의 권한은 다음과 같습니다.",
 'protect-cascadeon' => '다음 {{PLURAL:$1|문서}}에 연쇄적 보호가 작동하고 있어 그 문서에 속한 이 문서도 현재 보호됩니다.
-사용자께서는 이 문서의 보호 설정을 바꾸실 수 있지만, 연쇄적 보호에는 영향을 주지 않습니다.',
+사용자는 이 문서의 보호 설정을 바꾸실 수 있지만 연쇄적 보호에는 영향을 주지 않습니다.',
 'protect-default' => '모든 사용자에게 허용',
-'protect-fallback' => '‘$1’ 권한 필요',
+'protect-fallback' => '"$1" 권한 필요',
 'protect-level-autoconfirmed' => '등록된 사용자만 가능',
 'protect-level-sysop' => '관리자만 가능',
 'protect-summary-cascade' => '연쇄적',
@@ -2556,7 +2573,8 @@ $UNWATCHURL
 'undeleterevisions' => '판 $1개 보관중',
 'undeletehistory' => '문서를 되살리면 모든 역사가 같이 복구됩니다.
 문서가 삭제된 뒤 같은 이름의 문서가 만들어졌다면, 복구되는 역사는 지금 역사의 과거 부분에 나타날 것입니다.',
-'undeleterevdel' => '복구하려는 문서의 최신판이 삭제되어 있는 경우 문서를 복구시킬 수 없습니다. 이러한 경우, 삭제된 최신판 문서의 체크박스를 선택 해제하거나 숨김을 해제해야 합니다.',
+'undeleterevdel' => '복구하려는 문서의 최신판이 삭제되어 있는 경우 문서를 복구시킬 수 없습니다.
+이러한 경우, 삭제된 최신판 문서의 체크박스를 선택 해제하거나 숨김을 해제해야 합니다.',
 'undeletehistorynoadmin' => '이 문서는 삭제되었습니다.
 삭제된 이유와 삭제되기 전에 이 문서를 편집한 사용자들이 아래에 나와 있습니다.
 삭제된 문서의 내용을 보려면 관리자 권한이 필요합니다.',
@@ -2577,7 +2595,7 @@ $UNWATCHURL
 다른 사용자가 이미 복구했을 수도 있습니다.',
 'undeletedpage' => "'''$1 문서가 복구되었습니다.'''
 
-[[Special:Log/delete|삭제 기록]]에서 최근의 삭제/복구 기록을 볼 수 있습니다.",
+[[Special:Log/delete|삭제 기록]]에서 최근의 삭제와 복구 기록을 볼 수 있습니다.",
 'undelete-header' => '최근에 삭제한 문서에 대한 기록은 [[Special:Log/delete|여기]]에서 볼 수 있습니다.',
 'undelete-search-title' => '삭제된 문서 찾기',
 'undelete-search-box' => '삭제된 문서 찾기',
@@ -2591,7 +2609,7 @@ $UNWATCHURL
 이미 복구되었을 수 있습니다.',
 'undelete-error' => '문서 복구 중 오류',
 'undelete-error-short' => '파일 복구 오류: $1',
-'undelete-error-long' => '파일을 복구하는 중 오류 발생:
+'undelete-error-long' => '파일을 복구하는 중 오류가 발생했습니다:
 
 $1',
 'undelete-show-file-confirm' => '정말 "<nowiki>$1</nowiki>" 파일의 삭제된 $2 $3 버전을 보시겠습니까?',
@@ -2602,7 +2620,7 @@ $1',
 'invert' => '선택 반전',
 'tooltip-invert' => '선택한 이름공간에 있는 문서의 바뀜을 숨기려면 이 상자에 체크해주세요.',
 'namespace_association' => '관련된 이름공간',
-'tooltip-namespace_association' => '선택한 이름공간과 관련된 토론/본문 이름공간을 같이 선택합니다.',
+'tooltip-namespace_association' => '선택한 이름공간과 관련된 토론이나 본문 이름공간을 같이 선택합니다.',
 'blanknamespace' => '(일반)',
 
 # Contributions
@@ -2624,10 +2642,11 @@ $1',
 'sp-contributions-logs' => '기록',
 'sp-contributions-talk' => '토론',
 'sp-contributions-userrights' => '사용자 권한 관리',
-'sp-contributions-blocked-notice' => '이 사용자는 현재 차단되어 있습니다. 해당 사용자의 차단 기록은 다음과 같습니다.',
+'sp-contributions-blocked-notice' => '이 사용자는 현재 차단되어 있습니다.
+해당 사용자의 차단 기록은 다음과 같습니다:',
 'sp-contributions-blocked-notice-anon' => '이 IP 주소는 현재 차단되어 있습니다.
\95\84ë\9e\98ì\9d\98 ìµ\9cê·¼ ì°¨ë\8b¨ ê¸°ë¡\9dì\9d\84 ì°¸ê³ í\95\98ì\8b­ì\8b\9cì\98¤.',
-'sp-contributions-search' => '기여 검색',
°¨ë\8b¨ ê¸°ë¡\9dì\9d\80 ë\8b¤ì\9d\8cê³¼ ê°\99ì\8aµë\8b\88ë\8b¤:',
+'sp-contributions-search' => '기여 찾기',
 'sp-contributions-username' => 'IP 주소 혹은 계정 이름:',
 'sp-contributions-toponly' => '최신판만 보기',
 'sp-contributions-submit' => '찾기',
@@ -2684,19 +2703,19 @@ $1',
 'ipbotheroption' => '수동으로 지정',
 'ipbotherreason' => '다른 이유/추가적인 이유:',
 'ipbhidename' => '사용자 이름을 편집 역사에서 숨기기',
-'ipbwatchuser' => '이 사용자 문서와 사용자토론 문서를 주시하기',
-'ipb-disableusertalk' => '차단된 동안 자신의 사용자토론 문서를 편집하지 못하도록 막기',
+'ipbwatchuser' => '이 사용자 문서와 사용자 토론 문서를 주시하기',
+'ipb-disableusertalk' => '차단된 동안 자신의 사용자 토론 문서를 편집하지 못하도록 막기',
 'ipb-change-block' => '이 설정으로 이 사용자를 다시 차단합니다',
 'ipb-confirm' => '차단 확인',
 'badipaddress' => '잘못된 IP 주소',
 'blockipsuccesssub' => '차단 완료',
 'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] 사용자가 차단되었습니다.<br />
 차단된 사용자 목록은 [[Special:BlockList|여기]]에서 볼 수 있습니다.',
-'ipb-blockingself' => '자기 자신을 차단하려고 합니다. 정말로 실행할까요?',
+'ipb-blockingself' => '자기 자신을 차단하려고 합니다! 정말로 실행할까요?',
 'ipb-confirmhideuser' => '당신은 사용자를 차단하면서 "계정 숨기기" 설정을 선택했습니다. 이로써 모든 기록에서 이 사용자의 계정 이름을 숨기게 됩니다. 정말로 계정을 숨기시겠습니까?',
 'ipb-edit-dropdown' => '차단 이유 목록 편집하기',
 'ipb-unblock-addr' => '$1 차단 해제하기',
-'ipb-unblock' => '사용자/IP 주소 차단 해제하기',
+'ipb-unblock' => '사용자 또는 IP 주소 차단 해제하기',
 'ipb-blocklist' => '현재 차단 기록 보기',
 'ipb-blocklist-contribs' => '$1의 기여',
 'unblockip' => '사용자 차단 해제',
@@ -2712,7 +2731,7 @@ $1',
 'blocklist-tempblocks' => '기한이 정해진 차단을 숨기기',
 'blocklist-addressblocks' => '당일 IP 차단을 숨기기',
 'blocklist-rangeblocks' => '광역 차단을 숨기기',
-'blocklist-timestamp' => '날짜/시각',
+'blocklist-timestamp' => '날짜·시각',
 'blocklist-target' => '차단 대상',
 'blocklist-expiry' => '차단 기한',
 'blocklist-by' => '차단한 관리자',
@@ -2720,34 +2739,36 @@ $1',
 'blocklist-reason' => '이유',
 'ipblocklist-submit' => '찾기',
 'ipblocklist-localblock' => '로컬 차단',
-'ipblocklist-otherblocks' => '다른 {{PLURAL:$1|}}차단 기록',
+'ipblocklist-otherblocks' => '다른 {{PLURAL:$1|차단}} 기록',
 'infiniteblock' => '무기한',
 'expiringblock' => '$1 $2에 해제',
 'anononlyblock' => '익명 사용자만',
 'noautoblockblock' => '자동 차단 비활성화됨',
-'createaccountblock' => '계정 생성 금지됨',
+'createaccountblock' => '계정 만들기 금지됨',
 'emailblock' => '이메일 차단됨',
 'blocklist-nousertalk' => '자신의 토론 문서 편집 불가',
 'ipblocklist-empty' => '차단된 사용자가 없습니다.',
 'ipblocklist-no-results' => '당신이 입력한 IP 주소나 사용자는 차단되지 않았습니다.',
 'blocklink' => '차단',
 'unblocklink' => '차단 해제',
-'change-blocklink' => 'ì°¨ë\8b¨ ì\84¤ì \95 ë³\80ê²½',
+'change-blocklink' => 'ì°¨ë\8b¨ ì\84¤ì \95 ë°\94꾸기',
 'contribslink' => '기여',
 'emaillink' => '이메일 보내기',
 'autoblocker' => '당신의 IP 주소는 최근에 "[[User:$1|$1]]" 사용자가 사용하였기 때문에 자동으로 차단되었습니다.
 $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'blocklogpage' => '차단 기록',
-'blocklog-showlog' => '이 사용자는 과거에 차단된 기록이 있습니다. 해당 사용자의 차단 기록은 다음과 같습니다.',
-'blocklog-showsuppresslog' => '이 사용자는 과거에 차단된 적이 있으며, 그 기록이 숨겨져 있습니다. 해당 사용자의 차단 기록은 다음과 같습니다.',
+'blocklog-showlog' => '이 사용자는 과거에 차단된 기록이 있습니다.
+해당 사용자의 차단 기록은 다음과 같습니다:',
+'blocklog-showsuppresslog' => '이 사용자는 과거에 차단된 적이 있으며, 그 기록이 숨겨져 있습니다.
+해당 사용자의 차단 기록은 다음과 같습니다:',
 'blocklogentry' => '[[$1]] 사용자를 $2 차단함 $3',
-'reblock-logentry' => '[[$1]] ì\82¬ì\9a©ì\9e\90ì\9d\98 ì°¨ë\8b¨ ê¸°ê°\84ì\9d\84 $2(ì\9c¼)ë¡\9c ë³\80ê²½ $3',
+'reblock-logentry' => '[[$1]] ì\82¬ì\9a©ì\9e\90ì\9d\98 ì°¨ë\8b¨ ê¸°ê°\84ì\9d\84 $2(ì\9c¼)ë¡\9c ë°\94ê¿\88 $3',
 'blocklogtext' => '이 목록은 사용자 차단/차단 해제 기록입니다.
 자동으로 차단된 IP 주소는 여기에 나오지 않습니다.
 [[Special:BlockList|여기]]에서 현재 차단된 사용자 목록을 볼 수 있습니다.',
 'unblocklogentry' => '$1을 차단 해제했습니다.',
 'block-log-flags-anononly' => 'IP만 막음',
-'block-log-flags-nocreate' => '계정 생성 금지됨',
+'block-log-flags-nocreate' => '계정 만들기 금지됨',
 'block-log-flags-noautoblock' => '자동 차단 비활성화됨',
 'block-log-flags-noemail' => '이메일 막음',
 'block-log-flags-nousertalk' => '자신의 토론 문서 편집 불가',
@@ -2759,10 +2780,9 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'ipb_hide_invalid' => '해당 계정은 막을 수 없습니다. 기여량이 너무 많습니다.',
 'ipb_already_blocked' => '"$1" 사용자는 이미 차단됨',
 'ipb-needreblock' => '$1 사용자는 이미 차단되었습니다. 차단 설정을 바꾸시겠습니까?',
-'ipb-otherblocks-header' => '다른 {{PLURAL:$1|}}차단 기록',
+'ipb-otherblocks-header' => '다른 {{PLURAL:$1|차단}} 기록',
 'unblock-hideuser' => '이 계정 이름이 숨겨져 있기 때문에 이 사용자를 차단 해제할 수 없습니다.',
-'ipb_cant_unblock' => '오류: 차단 ID $1이(가) 존재하지 않습니다.
-이미 차단 해제되었을 수 있습니다.',
+'ipb_cant_unblock' => '오류: 차단 ID $1이(가) 존재하지 않습니다. 이미 차단 해제되었을 수 있습니다.',
 'ipb_blocked_as_range' => '오류: IP 주소 $1은 직접 차단되지 않았기 때문에 차단 해제할 수 없습니다.
 하지만 $2로 광역 차단되었기 때문에, 광역 차단 해제로 차단을 해제할 수 있습니다.',
 'ip_range_invalid' => 'IP 범위가 잘못되었습니다.',
@@ -2770,54 +2790,58 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'blockme' => '자가 차단',
 'proxyblocker' => '프록시 차단',
 'proxyblocker-disabled' => '이 기능은 비활성되어 있습니다.',
-'proxyblockreason' => '당신의 IP 주소는 공개 프록시로 밝혀져 자동으로 차단됩니다. 만약 인터넷 사용에 문제가 있다면 인터넷 서비스 공급자에게 문의해주세요.',
+'proxyblockreason' => '당신의 IP 주소는 공개 프록시로 밝혀져 자동으로 차단됩니다.
+만약 인터넷 사용에 문제가 있다면 인터넷 서비스 공급자나 기술 지원팀에게 문의해주세요.',
 'proxyblocksuccess' => '완료.',
 'sorbsreason' => '당신의 IP 주소는 {{SITENAME}}에서 사용하는 DNSBL 공개 프록시 목록에 들어 있습니다.',
-'sorbs_create_account_reason' => '당신의 IP 주소는 {{SITENAME}}에서 사용하는 DNSBL 공개 프록시 목록에 들어 있습니다. 계정을 만들 수 없습니다.',
+'sorbs_create_account_reason' => '당신의 IP 주소는 {{SITENAME}}에서 사용하는 DNSBL 공개 프록시 목록에 들어 있습니다.
+계정을 만들 수 없습니다.',
 'cant-block-while-blocked' => '당신이 차단되어 있는 동안에는 다른 사용자를 차단할 수 없습니다.',
-'cant-see-hidden-user' => '당신이 차단하려 하는 사용자는 이미 차단되었고 계정 숨김 처리되었습니다. 당신이 계정 숨기기 권한을 갖고 있지 않기 때문에, 이 사용자의 차단 기록을 보거나 차단 설정을 변경할 수 없습니다.',
+'cant-see-hidden-user' => '당신이 차단하려 하는 사용자는 이미 차단되었고 계정 숨김 처리되었습니다.
+당신이 계정 숨기기 권한을 갖고 있지 않기 때문에, 이 사용자의 차단 기록을 보거나 차단 설정을 바꿀 수 없습니다.',
 'ipbblocked' => '당신은 차단되어 있기 때문에 다른 사용자를 차단하거나 차단을 해제할 수 없습니다.',
 'ipbnounblockself' => '당신은 자기 스스로를 차단 해제할 수 없습니다.',
 
 # Developer tools
 'lockdb' => '데이터베이스 잠그기',
 'unlockdb' => '데이터베이스 잠금 해제',
-'lockdbtext' => 'ë\8d°ì\9d´í\84°ë² ì\9d´ì\8a¤ë¥¼ ì\9e ê·¸ë©´ ëª¨ë\93  ì\82¬ì\9a©ì\9e\90ì\9d\98 í\8e¸ì§\91, í\99\98ê²½ ì\84¤ì \95 ë³\80ê²½, 주시문서 편집 등 데이터베이스를 요구하는 모든 기능이 정지됩니다.
+'lockdbtext' => 'ë\8d°ì\9d´í\84°ë² ì\9d´ì\8a¤ë¥¼ ì\9e ê·¸ë©´ ëª¨ë\93  ì\82¬ì\9a©ì\9e\90ì\9d\98 í\8e¸ì§\91, í\99\98ê²½ ì\84¤ì \95 ë°\94꾸기, 주시문서 편집 등 데이터베이스를 요구하는 모든 기능이 정지됩니다.
 정말로 잠가야 하는지를 다시 한번 확인해주세요. 관리 작업이 끝난 뒤에는 데이터베이스 잠금을 풀어야 합니다.',
-'unlockdbtext' => 'ë\8d°ì\9d´í\84°ë² ì\9d´ì\8a¤ë¥¼ ì\9e ê¸\88 í\95´ì \9cí\95\98ë©´ ëª¨ë\93  ì\82¬ì\9a©ì\9e\90ì\9d\98 í\8e¸ì§\91, í\99\98ê²½ ì\84¤ì \95 ë³\80ê²½, 주시문서 편집 등 데이터베이스를 요구하는 모든 기능이 복구됩니다.
+'unlockdbtext' => 'ë\8d°ì\9d´í\84°ë² ì\9d´ì\8a¤ë¥¼ ì\9e ê¸\88 í\95´ì \9cí\95\98ë©´ ëª¨ë\93  ì\82¬ì\9a©ì\9e\90ì\9d\98 í\8e¸ì§\91, í\99\98ê²½ ì\84¤ì \95 ë°\94꾸기, 주시문서 편집 등 데이터베이스를 요구하는 모든 기능이 복구됩니다.
 정말로 잠금을 해제하려는지를 다시 한번 확인해주세요.',
 'lockconfirm' => '네, 데이터베이스를 잠급니다.',
 'unlockconfirm' => '네, 데이터베이스를 잠금 해제합니다.',
 'lockbtn' => '데이터베이스 잠그기',
 'unlockbtn' => '데이터베이스 잠금 해제',
-'locknoconfirm' => 'í\99\95ì\9d¸ ì²´í\81¬ë°\95ì\8a¤를 선택하지 않았습니다.',
+'locknoconfirm' => 'í\99\95ì\9d¸ ì\83\81ì\9e\90를 선택하지 않았습니다.',
 'lockdbsuccesssub' => '데이터베이스 잠김',
 'unlockdbsuccesssub' => '데이터베이스 잠금 해제됨',
 'lockdbsuccesstext' => '데이터베이스가 잠겼습니다.<br />
 관리가 끝나면 잊지 말고 [[Special:UnlockDB|잠금을 풀어]] 주세요.',
 'unlockdbsuccesstext' => '데이터베이스 잠금 상태가 해제되었습니다.',
-'lockfilenotwritable' => '데이터베이스 잠금 파일에 쓰기 권한이 없습니다. 데이터베이스를 잠그거나 잠금 해제하려면, 웹 서버에서 이 파일의 쓰기 권한을 설정해야 합니다.',
+'lockfilenotwritable' => '데이터베이스 잠금 파일에 쓰기 권한이 없습니다.
+데이터베이스를 잠그거나 잠금 해제하려면, 웹 서버에서 이 파일의 쓰기 권한을 설정해야 합니다.',
 'databasenotlocked' => '데이터베이스가 잠겨 있지 않습니다.',
 'lockedbyandtime' => '($1이 $2 $3에 잠금)',
 
 # Move page
-'move-page' => '이동 $1',
+'move-page' => '$1 이동',
 'move-page-legend' => '문서 이동하기',
-'movepagetext' => "아래 양식을 채워 문서의 이름을 바꾸고 모든 역사를 새 이름으로 옮길 수 있습니다.
-기존의 문서는 새 문서로 넘겨주는 문서가 됩니다.
-원래 이름을 가리키는 넘겨주기를 자동으로 ê°±ì\8b í\95  ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤.
-만약 이 설정을 선택하지 않았다면 [[Special:DoubleRedirects|이중 넘겨주기]]와 [[Special:BrokenRedirects|끊긴 넘겨주기]] 확인해주세요.
\8b¹ì\8b ì\9d\80 ë§\81í\81¬ê°\80 ì\9d\98ë\8f\84í\95\9c ë¬¸ì\84\9c를 ê°\80리í\82¤ê²\8c í\95  ì±\85ì\9e\84ì\9d´ ì\9e\88ì\8aµ니다.
+'movepagetext' => "아래의 양식을 사용해 문서의 이름을 바꾸고 문서의 모든 역사를 새 이름으로 옮길 수 있습니다.
+이전의 제목은 새 제목으로 넘겨줄 것입니다.
+원래 이름을 가리키는 넘겨주기를 자동으로 ì\83\88ë¡\9c ê³ ì¹  ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤.
+만약 이 설정을 선택하지 않았다면 [[Special:DoubleRedirects|이중 넘겨주기]]와 [[Special:BrokenRedirects|끊긴 넘겨주기]]가 있는지 확인해주세요.
\8b¹ì\8b ì\9d\80 ë\84\98겨주기 ë§\81í\81¬ê°\80 ì \9cë\8c\80ë¡\9c í\96¥í\95\98ê³  ì\9e\88ë\8a\94ì§\80 í\99\95ì\9d¸í\95\98ì\97¬ì\95¼ í\95©니다.
 
 만약 문서의 새 이름으로 된 문서가 존재할 때, 그 문서가 비었거나 넘겨주기 문서이고 문서 역사가 없을 때에만 이동합니다. 그렇지 않을 경우에는 이동하지 '''않습니다'''.
 이것은 실수로 이동한 문서를 되돌릴 수는 있지만, 이미 존재하는 문서 위에 덮어씌울 수는 없다는 것을 의미합니다.
 
 '''주의!'''
\9e\90주 ì\82¬ì\9a©í\95\98ë\8a\94 ë¬¸ì\84\9c를 ì\9d´ë\8f\99í\95\98ë©´ ì\9c\84í\97\98í\95\9c ê²°ê³¼ë¥¼ ê°\80ì ¸ì\98¬ 수 있습니다.
-이동하기 전에, 이 문서를 이동해도 문제가 없다는 것을 확인해주세요.",
-'movepagetext-noredirectfixer' => "ì\9d´ ì\96\91ì\8b\9dì\9d\84 ì\9d´ì\9a©í\95´ ë¬¸ì\84\9cì\9d\98 ì\9d´ë¦\84ì\9d\84 ë°\94꾸고 ë¬¸ì\84\9cì\9d\98 ëª¨ë\93  ì\97­ì\82¬ë¥¼ ì\98®ê¸¸ ê²\83ì\9e\85니다.
\9d¸ê¸° ì\9e\88ë\8a\94 ë¬¸ì\84\9cì\9d¼ ê²½ì\9a° ì\8b¬ê°\81í\95\98ê³  ì\98\88ì\83\81í\95\98ì§\80 ëª»í\95\9c ë¬¸ì \9c를 ì´\88ë\9e\98í\95  수 있습니다.
+문서를 이동하기 전에 이러한 행동이 초래할 수 있는 결과에 대해 숙지하시기 바랍니다.",
+'movepagetext-noredirectfixer' => "ì\95\84ë\9e\98ì\9d\98 ì\96\91ì\8b\9dì\9d\84 ì\82¬ì\9a©í\95´ ë¬¸ì\84\9cì\9d\98 ì\9d´ë¦\84ì\9d\84 ë°\94꾸고 ë¬¸ì\84\9cì\9d\98 ëª¨ë\93  ì\97­ì\82¬ë¥¼ ì\83\88 ì\9d´ë¦\84ì\9c¼ë¡\9c ì\98®ê¸¸ ì\88\98 ì\9e\88ì\8aµ니다.
 이전의 제목은 새 제목으로 넘겨줄 것입니다.
-[[Special:DoubleRedirects|ì\9d´ì¤\91 ë\84\98겨주기]]ë\82\98 [[Special:BrokenRedirects|ë\81\8a긴 ë\84\98겨주기]]ê°\80 ì\9e\88ë\8a\94ì§\80 í\99\95ì\9d¸í\95´ì£¼ì\8b­ì\8b\9cì\98¤.
+[[Special:DoubleRedirects|ì\9d´ì¤\91 ë\84\98겨주기]]ë\82\98 [[Special:BrokenRedirects|ë\81\8a긴 ë\84\98겨주기]]ê°\80 ì\9e\88ë\8a\94ì§\80 í\99\95ì\9d¸í\95´ì£¼ì\84¸ì\9a\94.
 당신은 넘겨주기 링크가 제대로 향하고 있는지 확인하여야 합니다.
 
 참고로 새 제목을 가진 문서가 이미 있다면 다음 경우에 해당하지 않으면 이 문서는 옮겨지지 '''않을''' 것입니다.
@@ -2827,7 +2851,7 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 이는 당신이 실수로 문서를 옮겼을 때 되돌릴 수 있으며 이미 있는 문서를 덮어쓸 수 없음을 의미합니다.
 
 '''경고!'''
-방문 수나 이 문서를 향하는 링크가 많은 문서일 경우 심각한 문제를 초래할 수 있습니다.
+인기 있는 문서일 경우 심각하고 예상하지 못한 문제를 초래할 수 있습니다.
 문서를 이동하기 전에 이러한 행동이 초래할 수 있는 결과에 대해 숙지하시기 바랍니다.",
 'movepagetalktext' => "딸린 토론 문서도 자동으로 이동합니다. 하지만 다음의 경우는 '''이동하지 않습니다''':
 * 이동할 이름으로 된 문서가 이미 있는 경우
@@ -2835,7 +2859,7 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 
 이 경우에는 문서를 직접 이동하거나 두 문서를 합쳐야 합니다.",
 'movearticle' => '문서 이동하기',
-'moveuserpage-warning' => "'''경고:''' 당신은 사용자 문서를 옮기려 하고 있습니다. 사용자 문서만 이동되며 계정 이름이 바뀌지 않는다는 점을 명심해주시기 바랍니다.",
+'moveuserpage-warning' => "'''경고:''' 당신은 사용자 문서를 옮기려 하고 있습니다. 사용자 문서만 이동되며 계정 이름이 바뀌지 '''않는다'''는 점을 명심해주시기 바랍니다.",
 'movenologin' => '로그인하지 않음',
 'movenologintext' => '문서를 이동하려면 [[Special:UserLogin|로그인]]해야 합니다.',
 'movenotallowed' => '문서를 이동할 권한이 없습니다.',
@@ -2846,7 +2870,7 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'move-watch' => '문서 주시하기',
 'movepagebtn' => '이동',
 'pagemovedsub' => '문서 이동함',
-'movepage-moved' => "'''‘$1’ 문서를 ‘$2’ 문서로 이동했습니다.'''",
+'movepage-moved' => '\'\'\'"$1" 문서를 "$2" 문서로 이동했습니다.\'\'\'',
 'movepage-moved-redirect' => '넘겨주기 문서를 만들었습니다.',
 'movepage-moved-noredirect' => '넘겨주기 문서를 남기지 않았습니다.',
 'articleexists' => '문서가 이미 존재하거나 이름이 올바르지 않습니다.
@@ -2857,25 +2881,25 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'movetalk' => '딸린 토론도 함께 이동합니다.',
 'move-subpages' => '하위 문서도 함께 ($1개 이하) 이동합니다.',
 'move-talk-subpages' => '토론 문서의 하위 문서도 ($1개까지) 함께 이동합니다.',
-'movepage-page-exists' => '이동할 수 없습니다. ‘$1’ 문서가 이미 존재합니다.',
-'movepage-page-moved' => '‘$1’ 문서를 ‘$2’ 문서로 이동했습니다.',
-'movepage-page-unmoved' => '‘$1’ 문서를 ‘$2’ 문서로 이동할 수 없습니다.',
+'movepage-page-exists' => '이동할 수 없습니다. "$1" 문서가 이미 존재합니다.',
+'movepage-page-moved' => '"$1" 문서를 "$2" 문서로 이동했습니다.',
+'movepage-page-unmoved' => '"$1" 문서를 "$2" 문서로 이동할 수 없습니다.',
 'movepage-max-pages' => '문서를 최대 $1개 이동했습니다. 나머지 문서는 자동 이동하지 않습니다.',
 'movelogpage' => '이동 기록',
-'movelogpagetext' => 'ì\95\84ë\9e\98ë\8a\94 ì\98®ê²¨ì§\84 문서의 목록입니다.',
-'movesubpage' => '{{PLURAL:$1}}하위 문서',
+'movelogpagetext' => 'ì\95\84ë\9e\98ë\8a\94 ì\9d´ë\8f\99í\95\9c 문서의 목록입니다.',
+'movesubpage' => '{{PLURAL:$1|하위 문서}}',
 'movesubpagetext' => '이 문서에는 다음 하위 문서 $1개가 있습니다.',
 'movenosubpage' => '이 문서에는 하위 문서가 존재하지 않습니다.',
 'movereason' => '이유:',
 'revertmove' => '되돌리기',
 'delete_and_move' => '삭제하고 이동',
 'delete_and_move_text' => '== 삭제 필요 ==
-
-이동하려는 제목으로 된 ‘[[:$1]]’ 문서가 이미 존재합니다.
+이동하려는 제목으로 된 "[[:$1]]" 문서가 이미 존재합니다.
 삭제하고 이동할까요?',
 'delete_and_move_confirm' => '네. 문서를 삭제합니다',
 'delete_and_move_reason' => '"[[$1]]"에서 문서를 이동하기 위해 삭제함',
-'selfmove' => '이동하려는 제목이 원래 제목과 같습니다. 이동할 수 없습니다.',
+'selfmove' => '이동하려는 제목이 원래 제목과 같습니다.
+이동할 수 없습니다.',
 'immobile-source-namespace' => '"$1" 이름공간에 속한 문서는 이동시킬 수 없습니다.',
 'immobile-target-namespace' => '"$1" 이름공간에 속한 문서는 이동시킬 수 없습니다.',
 'immobile-target-namespace-iw' => '인터위키 링크를 넘어 문서를 이동할 수 없습니다.',
@@ -2885,13 +2909,13 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'nonfile-cannot-move-to-file' => '파일이 아닌 문서를 파일 이름공간으로 옮길 수 없습니다.',
 'imagetypemismatch' => '새 파일의 확장자가 원래의 확장자와 일치하지 않습니다.',
 'imageinvalidfilename' => '새 파일 이름이 잘못되었습니다.',
-'fix-double-redirects' => '기존 이름을 가리키는 넘겨주기를 갱신',
+'fix-double-redirects' => '기존 이름을 가리키는 넘겨주기를 새로 고침',
 'move-leave-redirect' => '이동 후 넘겨주기를 남기기',
 'protectedpagemovewarning' => "'''경고:''' 이 문서는 관리자만이 이동할 수 있도록 잠겨 있습니다.
-최근의 기록을 참용으로 제공합니다:",
+최근의 기록을 참용으로 제공합니다:",
 'semiprotectedpagemovewarning' => "'''알림:''' 이 문서는 등록된 사용자만이 이동할 수 있도록 잠겨 있습니다.
-ìµ\9cê·¼ ê¸°ë¡\9d ë\82´ì\9a©ì\9d´ ì°¸ì¡°ì\9a©ì\9c¼ë¡\9c ì \9cê³µë\90©ë\8b\88ë\8b¤:",
-'move-over-sharedrepo' => '== í\8c\8cì\9d¼ì\9d´ ì¡´ì\9e¬í\95©ë\8b\88ë\8b¤ ==
+ìµ\9cê·¼ ê¸°ë¡\9d ë\82´ì\9a©ì\9d\84 ì°¸ê³ ì\9a©ë¡\9c ì \9cê³µí\95©ë\8b\88ë\8b¤:",
+'move-over-sharedrepo' => '== í\8c\8cì\9d¼ì\9d´ ì¡´ì\9e¬í\95¨ ==
 [[:$1]] 파일이 공용 저장소에 있습니다. 이 이름으로 파일을 옮기면 공용의 파일을 덮어쓰게 될 것입니다.',
 'file-exists-sharedrepo' => '당신이 선택한 파일 이름은 공용 저장소에서 사용 중입니다.
 다른 이름을 선택해주세요.',
@@ -2902,7 +2926,7 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 
 문서를 내보내려면, 내보내려는 문서 제목을 한 줄에 하나씩 입력해주세요. 그리고 문서의 전체 역사가 필요한지, 혹은 현재 버전만이 필요한지를 선택해 주세요.
 
-특정 문서를 내보내려면, 예를 들어 ‘[[{{MediaWiki:Mainpage}}]]’ 문서를 내보내려면 [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] 링크를 사용할 수도 있습니다.',
+특정 문서를 내보내려면, 예를 들어 "[[{{MediaWiki:Mainpage}}]]" 문서를 내보내려면 [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] 링크를 사용할 수도 있습니다.',
 'exportall' => '모든 문서를 내보내기',
 'exportcuronly' => '현재 버전만 포함하고, 전체 역사는 포함하지 않음',
 'exportnohistory' => "----
@@ -2922,8 +2946,9 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'allmessagesname' => '이름',
 'allmessagesdefault' => '기본 내용',
 'allmessagescurrent' => '현재 문자열',
-'allmessagestext' => '미디어위키 이름공간에 있는 모든 시스템 메시지의 목록입니다. 미디어위키의 번역 작업에 관심이 있으면 [//www.mediawiki.org/wiki/Localisation 미디어위키 지역화]나 [//translatewiki.net translatewiki.net]에 참가해주세요.',
-'allmessagesnotsupportedDB' => "'''\$wgUseDatabaseMessages'''가 해제되어 있어서 이 문서는 쓸 수 없습니다.",
+'allmessagestext' => '미디어위키 이름공간에 있는 모든 시스템 메시지의 목록입니다.
+미디어위키의 번역 작업에 관심이 있으면 [//www.mediawiki.org/wiki/Localisation 미디어위키 지역화]나 [//translatewiki.net translatewiki.net]에 참가해주세요.',
+'allmessagesnotsupportedDB' => "'''\$wgUseDatabaseMessages'''가 비활성화되어 있어서 이 문서를 사용할 수 없습니다.",
 'allmessages-filter-legend' => '필터',
 'allmessages-filter' => '수정 상태로 거르기:',
 'allmessages-filter-unmodified' => '수정되지 않음',
@@ -2939,18 +2964,18 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'thumbnail_error' => '섬네일을 만드는 중 오류 발생: $1',
 'djvu_page_error' => 'DjVu 페이지 범위 벗어남',
 'djvu_no_xml' => 'DjVu 파일의 XML 정보를 읽을 수 없음',
-'thumbnail-temp-create' => '임시 섬네일 파일을 생성할 수 없습니다.',
+'thumbnail-temp-create' => '임시 섬네일 파일을 만들 수 없습니다.',
 'thumbnail-dest-create' => '대상 경로에 섬네일을 저장할 수 없습니다.',
 'thumbnail_invalid_params' => '섬네일 매개변수가 잘못되었습니다.',
-'thumbnail_dest_directory' => '새 목적 디렉토리를 생성할 수 없습니다.',
+'thumbnail_dest_directory' => '새 목적 디렉토리를 만들 수 없습니다.',
 'thumbnail_image-type' => '해당 파일 형식은 지원하지 않습니다',
-'thumbnail_gd-library' => 'GD 라이브러리 설정이 잘못되었습니다. $1 함수를 찾을 수 없습니다.',
+'thumbnail_gd-library' => 'GD 라이브러리 설정이 잘못되었습니다: $1 함수를 찾을 수 없습니다.',
 'thumbnail_image-missing' => '파일을 찾을 수 없습니다: $1',
 
 # Special:Import
 'import' => '문서 가져오기',
 'importinterwiki' => '다른 위키에서 문서 가져오기',
-'import-interwiki-text' => '문서를 가져올 위키를 선택하고, 문서 제목을 입력해주세요.
+'import-interwiki-text' => '문서를 가져올 위키를 선택하고 문서 제목을 입력해주세요.
 편집 날짜와 편집자의 이름이 보존될 것입니다.
 모든 가져오기는 [[Special:Log/import|가져오기 기록]]에 기록될 것입니다.',
 'import-interwiki-source' => '원본 위키/문서:',
@@ -2960,12 +2985,12 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'import-interwiki-namespace' => '새 이름공간:',
 'import-upload-filename' => '파일 이름:',
 'import-comment' => '이유:',
-'importtext' => 'ì\9b\90본 ì\9c\84í\82¤ì\97\90ì\84\9c [[Special:Export|ë\82´ë³´ë\82´ê¸°]] ê¸°ë\8a¥ì\9d\84 ì\82¬ì\9a©í\95´ í\8c\8cì\9d¼ì\9d\84 ë\82´ë ¤ë°\9bì\9c¼ì\8b­ì\8b\9cì\98¤.
+'importtext' => 'ì\9b\90본 ì\9c\84í\82¤ì\97\90ì\84\9c [[Special:Export|ë\82´ë³´ë\82´ê¸°]] ê¸°ë\8a¥ì\9d\84 ì\82¬ì\9a©í\95´ í\8c\8cì\9d¼ì\9d\84 ë\82´ë ¤ë°\9bì\9c¼ì\84¸ì\9a\94.
 그리고 당신의 컴퓨터에 저장해 둔 후 여기에 올려주세요.',
 'importstart' => '문서를 가져오는 중...',
 'import-revision-count' => '판 $1개',
 'importnopages' => '가져올 문서가 없습니다.',
-'imported-log-entries' => '로그 항목 $1개를 가져왔습니다.',
+'imported-log-entries' => '기록 항목 $1개를 가져왔습니다.',
 'importfailed' => '가져오기 실패: <nowiki>$1</nowiki>',
 'importunknownsource' => '알 수 없는 가져오기 자료 유형',
 'importcantopen' => '파일을 열 수 없습니다.',
@@ -2973,12 +2998,12 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'importnotext' => '내용이 없습니다.',
 'importsuccess' => '가져오기 완료!',
 'importhistoryconflict' => '문서 역사가 충돌하는 판이 있습니다. (이전에 이 문서를 가져온 적이 있을 수도 있습니다)',
-'importnosources' => '문서를 가져올 출처가 정의되지 않았고, 문서 역사 올리기가 비활성화되었습니다.',
-'importnofile' => 'ê°\80ì ¸ì\98¤ê¸°ì\9a© í\8c\8cì\9d¼ì\9d´ ì\97\85ë¡\9cë\93\9cë\90\98지 않았습니다.',
+'importnosources' => '문서를 가져올 출처가 정의되지 않았고 문서 역사 올리기가 비활성화되었습니다.',
+'importnofile' => 'ê°\80ì ¸ì\98¤ê¸°ì\9a© í\8c\8cì\9d¼ì\9d´ ì\98¬ë ¤ì§\80지 않았습니다.',
 'importuploaderrorsize' => '파일 올리기를 통한 가져오기에 실패했습니다.
 파일이 허용된 크기 제한보다 큽니다.',
 'importuploaderrorpartial' => '가져오기 파일을 올리는 데 실패하였습니다.
\8c\8cì\9d¼ì\9d´ ë¶\80ë¶\84ì \81ì\9c¼ë¡\9cë§\8c ì\97\85ë¡\9cë\93\9cë\90\98ì\97\88습니다.',
\8c\8cì\9d¼ì\9d´ ë¶\80ë¶\84ì \81ì\9c¼ë¡\9cë§\8c ì\98¬ë ¤ì¡\8c습니다.',
 'importuploaderrortemp' => '가져오기 파일을 올리는 데 실패했습니다.
 임시 폴더가 존재하지 않습니다.',
 'import-parse-failure' => 'XML 문서 분석 실패',
@@ -2986,10 +3011,11 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'import-nonewrevisions' => '이전에 이미 모든 판을 가져왔습니다.',
 'xml-error-string' => '$3단 $2줄 (바이트 $4)에서 $1: $5',
 'import-upload' => 'XML 데이터 올리기',
-'import-token-mismatch' => '세션 데이터가 손실되었습니다. 다시 시도해주세요.',
+'import-token-mismatch' => '세션 데이터가 손실되었습니다.
+다시 시도하세요.',
 'import-invalid-interwiki' => '해당 위키에서 문서를 가져올 수 없습니다.',
-'import-error-edit' => '현재 문서를 편집할 권한이 없기 때문에 ‘$1’ 문서를 불러올 수 없습니다.',
-'import-error-create' => '현재 문서를 생성할 권한이 없기 때문에 ‘$1’ 문서를 불러올 수 없습니다.',
+'import-error-edit' => '현재 문서를 편집할 권한이 없기 때문에 "$1" 문서를 불러올 수 없습니다.',
+'import-error-create' => '현재 문서를 만들 권한이 없기 때문에 "$1" 문서를 불러올 수 없습니다.',
 'import-error-interwiki' => '문서 "$1"은 제목이 바깥 고리(인터위키)용으로 할당되어 있기 때문에 가져오지 않습니다.',
 'import-error-special' => '문서 "$1"은 특수 문서에 속해 있기 때문에 가져오지 않습니다.',
 'import-error-invalid' => '문서 "$1"은 제목이 잘못되었기 때문에 가져오지 않습니다.',
@@ -2997,7 +3023,7 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 # Import log
 'importlogpage' => '가져오기 기록',
 'importlogpagetext' => '다른 위키에서 가져온 문서 기록입니다.',
-'import-logentry-upload' => ' 사용자가 파일 올리기를 통해 [[$1]] 문서를 가져왔습니다.',
+'import-logentry-upload' => '사용자가 파일 올리기를 통해 [[$1]] 문서를 가져왔습니다.',
 'import-logentry-upload-detail' => '판 $1개',
 'import-logentry-interwiki' => '$1 문서를 다른 위키에서 가져왔습니다.',
 'import-logentry-interwiki-detail' => '$2에서 판 $1개를 가져옴',
@@ -3008,9 +3034,9 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'javascripttest-title' => '$1 테스트 실행',
 'javascripttest-pagetext-noframework' => '이 페이지는 자바스크립트 테스트를 실행하기 위한 용도로 할당되어 있습니다.',
 'javascripttest-pagetext-unknownframework' => '실험용 프레임워크 "$1"를 알 수 없습니다.',
-'javascripttest-pagetext-frameworks' => 'ë\8b¤ì\9d\8c ì\8b¤í\97\98ì\9a© í\94\84ë \88ì\9e\84ì\9b\8cí\81¬ ì¤\91 í\95\98ë\82\98를 ì\84 í\83\9dí\95\98ì\8b­ì\8b\9cì\98¤: $1',
-'javascripttest-pagetext-skins' => '실험할 스킨을 고르십시오:',
-'javascripttest-qunit-intro' => 'mediawiki.orgì\9d\98 [$1 í\85\8cì\8a¤í\8a¸ ì\84¤ëª\85ì\84\9c]를 ì°¸ê³ í\95\98ì\8b­ì\8b\9cì\98¤.',
+'javascripttest-pagetext-frameworks' => 'ë\8b¤ì\9d\8c ì\8b¤í\97\98ì\9a© í\94\84ë \88ì\9e\84ì\9b\8cí\81¬ ì¤\91 í\95\98ë\82\98를 ì\84 í\83\9dí\95\98ì\84¸ì\9a\94: $1',
+'javascripttest-pagetext-skins' => '실험할 스킨을 선택하세요:',
+'javascripttest-qunit-intro' => 'mediawiki.orgì\9d\98 [$1 í\85\8cì\8a¤í\8a¸ ì\84¤ëª\85ì\84\9c]를 ì°¸ê³ í\95\98ì\84¸ì\9a\94.',
 'javascripttest-qunit-heading' => '미디어위키 자바스크립트 QUnit 실험군',
 
 # Tooltip help for the actions
@@ -3019,7 +3045,7 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'tooltip-pt-mytalk' => '내 토론 문서',
 'tooltip-pt-anontalk' => '현재 사용하는 IP를 위한 사용자 토론 문서',
 'tooltip-pt-preferences' => '사용자 환경 설정',
-'tooltip-pt-watchlist' => '주시문서 목록',
+'tooltip-pt-watchlist' => '주시문서에 대한 바뀜 목록',
 'tooltip-pt-mycontris' => '내가 편집한 글',
 'tooltip-pt-login' => '꼭 로그인해야 하는 것은 아니지만, 로그인을 권장합니다.',
 'tooltip-pt-anonlogin' => '꼭 필요한 것은 아니지만, 로그인을 하면 편리한 점이 많습니다.',
@@ -3027,28 +3053,29 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'tooltip-ca-talk' => '문서의 내용에 대한 토론 문서',
 'tooltip-ca-edit' => '문서를 편집할 수 있습니다. 저장하기 전에 미리보기를 해 주세요.',
 'tooltip-ca-addsection' => '문단 추가하기',
-'tooltip-ca-viewsource' => '문서가 잠겨 있습니다. 문서의 소스만 볼 수 있습니다.',
-'tooltip-ca-history' => '문서의 과거 버전들',
+'tooltip-ca-viewsource' => '문서가 잠겨 있습니다.
+문서의 내용만 볼 수 있습니다.',
+'tooltip-ca-history' => '문서의 과거 판',
 'tooltip-ca-protect' => '문서 보호하기',
-'tooltip-ca-unprotect' => 'ì\9d´ ë¬¸ì\84\9cì\9d\98 ë³´í\98¸ ì\84¤ì \95ì\9d\84 ë³\80ê²½í\95\98기',
+'tooltip-ca-unprotect' => 'ì\9d´ ë¬¸ì\84\9cì\9d\98 ë³´í\98¸ ì\84¤ì \95ì\9d\84 ë°\94꾸기',
 'tooltip-ca-delete' => '문서 삭제하기',
-'tooltip-ca-undelete' => 'ì\82­ì \9cë\90\9c ë¬¸ì\84\9c 복구하기',
+'tooltip-ca-undelete' => 'ì\82­ì \9cë\90\98기 ì \84ì\97\90 ì\9d´ ë¬¸ì\84\9cì\9d\98 ì\99\84ë£\8cí\95\9c í\8e¸ì§\91 복구하기',
 'tooltip-ca-move' => '문서 이동하기',
 'tooltip-ca-watch' => '이 문서를 주시문서 목록에 추가합니다.',
 'tooltip-ca-unwatch' => '이 문서를 주시문서 목록에서 제거합니다.',
 'tooltip-search' => '{{SITENAME}} 찾기',
-'tooltip-search-go' => '이 이름의 문서가 존재하면 그 문서로 바로가기',
+'tooltip-search-go' => '이 이름의 문서가 존재하면 그 문서로 바로 가기',
 'tooltip-search-fulltext' => '이 문자열이 포함된 문서 찾기',
 'tooltip-p-logo' => '대문 방문하기',
 'tooltip-n-mainpage' => '대문으로',
 'tooltip-n-mainpage-description' => '대문으로',
-'tooltip-n-portal' => '프로젝트 소개, 당신이 할 수 있는 것, 사이트맵',
-'tooltip-n-currentevents' => '최근의 소식을 봅니다.',
+'tooltip-n-portal' => '프로젝트 소개, 여러분이 할 수 있는 것, 무언가를 찾는 곳',
+'tooltip-n-currentevents' => '최근의 소식을 봅니다',
 'tooltip-n-recentchanges' => '이 위키에서 최근 바뀐 내용의 목록',
-'tooltip-n-randompage' => '임의 문서로 갑니다.',
+'tooltip-n-randompage' => '임의 문서로 갑니다',
 'tooltip-n-help' => '도움말',
 'tooltip-t-whatlinkshere' => '여기로 연결된 모든 문서의 목록',
-'tooltip-t-recentchangeslinked' => 'ì\97¬ê¸°ë¡\9c ì\97°ê²°ë\90\9c ëª¨ë\93  ë¬¸ì\84\9cì\9d\98 ë³\80ê²½ 내역',
+'tooltip-t-recentchangeslinked' => 'ì\97¬ê¸°ë¡\9c ì\97°ê²°ë\90\9c ëª¨ë\93  ë¬¸ì\84\9cì\9d\98 ë°\94ë\80\9c 내역',
 'tooltip-feed-rss' => '이 문서의 RSS 피드입니다.',
 'tooltip-feed-atom' => '이 문서의 Atom 피드입니다.',
 'tooltip-t-contributions' => '이 사용자의 기여 목록을 봅니다.',
@@ -3056,7 +3083,7 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'tooltip-t-upload' => '파일을 올립니다.',
 'tooltip-t-specialpages' => '모든 특수 문서의 목록',
 'tooltip-t-print' => '이 문서의 인쇄용 버전',
-'tooltip-t-permalink' => '이 개정판에 대한 고유링크',
+'tooltip-t-permalink' => '이 판에 대한 고유링크',
 'tooltip-ca-nstab-main' => '문서 내용을 봅니다.',
 'tooltip-ca-nstab-user' => '사용자 문서 내용을 봅니다.',
 'tooltip-ca-nstab-media' => '미디어 문서 내용을 봅니다.',
@@ -3069,17 +3096,16 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'tooltip-ca-nstab-category' => '분류 문서 내용을 봅니다.',
 'tooltip-minoredit' => '사소한 편집으로 표시하기',
 'tooltip-save' => '편집 내용을 저장하기',
-'tooltip-preview' => '편집 미리 보기. 저장하기 전에 꼭 미리 보기를 해 주세요.',
-'tooltip-diff' => 'ì\9e\90ì\8b ì\9d´ ë³\80ê²½í\95\9c 것 보기',
-'tooltip-compareselectedversions' => '이 문서에서 선택한 두 버전간의 차이를 비교',
+'tooltip-preview' => '편집 미리 보기. 저장하기 전에 꼭 미리 보기를 해 주세요!',
+'tooltip-diff' => 'ì\9e\90ì\8b ì\9d´ ë°\94ê¾¼ 것 보기',
+'tooltip-compareselectedversions' => '이 문서에서 선택한 두 간의 차이를 비교',
 'tooltip-watch' => '이 문서를 주시문서 목록에 추가',
 'tooltip-watchlistedit-normal-submit' => '항목 제거하기',
-'tooltip-watchlistedit-raw-submit' => '주시문서 목록 갱신하기',
+'tooltip-watchlistedit-raw-submit' => '주시문서 목록 새로 고침',
 'tooltip-recreate' => '문서를 편집하는 중 삭제되어도 새로 만들기',
 'tooltip-upload' => '파일 올리기 시작',
 'tooltip-rollback' => '"되돌리기" 기능을 사용하면 이 문서에 대한 마지막 기여자의 편집을 모두 되돌릴 수 있습니다.',
-'tooltip-undo' => '"편집 취소" 기능을 사용하면 이 편집이 되돌려지고, 차이보기 기능이 미리보기 형식으로 나타납니다.
-편집 요약에 이 편집을 왜 되돌리는지에 대한 이유를 쓸 수 있습니다.',
+'tooltip-undo' => '"편집 취소" 기능을 사용하면 이 편집이 되돌려지고, 차이보기 기능이 미리보기 형식으로 나타납니다. 편집 요약에 이 편집을 왜 되돌리는지에 대한 이유를 쓸 수 있습니다.',
 'tooltip-preferences-save' => '환경 설정 저장하기',
 'tooltip-summary' => '짧은 편집 요약을 적어주세요',
 
@@ -3094,7 +3120,7 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'simple.css' => '/* 이 CSS 설정은 모든 심플 스킨에 적용됩니다 */',
 'modern.css' => '/* 이 CSS 설정은 모든 모던 스킨에 적용됩니다 */',
 'vector.css' => '/* 이 CSS 설정은 모든 벡터 스킨에 적용됩니다 */',
-'print.css' => '/* ì\9d´ CSS ì\84¤ì \95ì\9d\80 ì\9cë ¥/ì\9d¸ì\87\84 화면에 적용됩니다 */',
+'print.css' => '/* ì\9d´ CSS ì\84¤ì \95ì\9d\80 ì\9d¸ì\87\84 ì¶\9cë ¥ 화면에 적용됩니다 */',
 'handheld.css' => '/* 이 CSS 설정은 $wgHandheldStyle에 설정한 스킨을 기반으로 한 휴대 기기에 적용됩니다 */',
 'noscript.css' => '/* 이 CSS 설정은 자바스크립트를 비활성화한 사용자에 적용됩니다 */',
 'group-autoconfirmed.css' => '/* 이 CSS 설정은 자동 인증된 사용자에만 적용됩니다 */',
@@ -3122,15 +3148,15 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'notacceptable' => '클라이언트에서 인식 가능한 출력 포맷이 없습니다.',
 
 # Attribution
-'anonymous' => '{{PLURAL:$1}}{{SITENAME}} 익명 사용자',
+'anonymous' => '{{SITENAME}} 익명 {{PLURAL:$1|사용자}}',
 'siteuser' => '{{SITENAME}} 사용자 $1',
 'anonuser' => '{{SITENAME}} 익명 사용자 $1',
 'lastmodifiedatby' => '이 문서는 $3 사용자가 $1 $2에 마지막으로 바꾸었습니다.',
-'othercontribs' => '$1ì\9d\98 ì\9e\91ì\97\85ì\9d\84 ë°\94í\83\95ì\9c¼ë¡\9c í\95¨.',
+'othercontribs' => '$1ì\9d\98 ì\9e\91ì\97\85ì\9d\84 ë°\94í\83\95ì\9c¼ë¡\9c í\95©ë\8b\88ë\8b¤.',
 'others' => '기타',
-'siteusers' => '{{PLURAL:$2}}{{SITENAME}} 사용자 $1',
-'anonusers' => '{{PLURAL:$2}}{{SITENAME}} 익명 사용자 $1',
-'creditspage' => '문서 기여자',
+'siteusers' => '{{SITENAME}} {{PLURAL:$2|사용자}} $1',
+'anonusers' => '{{SITENAME}} 익명 {{PLURAL:$2|사용자}} $1',
+'creditspage' => '문서 기여자',
 'nocredits' => '이 문서에서는 기여자 정보가 없습니다.',
 
 # Spam protection
@@ -3184,22 +3210,23 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 'log-show-hide-patrol' => '검토 기록을 $1',
 
 # Image deletion
-'deletedrevision' => '예전 버전 $1이(가) 삭제되었습니다.',
+'deletedrevision' => '예전 $1 판이 삭제되었습니다.',
 'filedeleteerror-short' => '파일 삭제 오류: $1',
 'filedeleteerror-long' => '파일을 삭제하는 도중 오류가 발생했습니다:
 
 $1',
-'filedelete-missing' => '‘$1’ 파일을 삭제할 수 없습니다. 파일이 존재하지 않습니다.',
-'filedelete-old-unregistered' => '입력한 파일의 "$1" 버전이 데이터베이스에 존재하지 않습니다.',
+'filedelete-missing' => '"$1" 파일을 삭제할 수 없습니다. 파일이 존재하지 않습니다.',
+'filedelete-old-unregistered' => '입력한 파일의 "$1" 이 데이터베이스에 존재하지 않습니다.',
 'filedelete-current-unregistered' => '"$1" 이라는 이름을 가진 파일이 데이터베이스에 존재하지 않습니다.',
-'filedelete-archive-read-only' => '웹 서버의 파일 저장 위치 ‘$1’에 쓰기 권한이 없습니다.',
+'filedelete-archive-read-only' => '웹 서버의 "$1" 파일 저장 위치에 쓰기 권한이 없습니다.',
 
 # Browsing diffs
 'previousdiff' => '← 이전 편집',
 'nextdiff' => '다음 편집 →',
 
 # Media information
-'mediawarning' => "'''경고''': 이 파일에 악성 코드가 포함되어 있을 수 있습니다. 파일을 실행하면 컴퓨터에 문제가 생길 가능성이 있습니다.",
+'mediawarning' => "'''경고''': 이 파일에 악성 코드가 포함되어 있을 수 있습니다.
+파일을 실행하면 컴퓨터에 문제가 생길 가능성이 있습니다.",
 'imagemaxsize' => "그림 최대 크기:<br />''(파일 문서에 적용되는 기능)''",
 'thumbsize' => '섬네일 크기:',
 'widthheightpage' => '$1 × $2, $3페이지',
@@ -3207,7 +3234,7 @@ $1',
 'file-info-size' => '$1 × $2 픽셀, 파일 크기: $3, MIME 종류: $4',
 'file-info-size-pages' => '$1 × $2 픽셀, 파일 크기: $3, MIME 형식: $4, $5{{PLURAL:$5|쪽}}',
 'file-nohires' => '최대 해상도입니다.',
-'svg-long-desc' => 'SVG 파일, 실제 크기 $1 × $2 픽셀, 파일 크기 $3',
+'svg-long-desc' => 'SVG 파일, 실제 크기 $1 × $2 픽셀, 파일 크기: $3',
 'show-big-image' => '최대 해상도',
 'show-big-image-preview' => '미리 보기 크기: $1.',
 'show-big-image-other' => '다른 {{PLURAL:$2|해상도}}: $1.',
@@ -3255,7 +3282,8 @@ Variants for Chinese language
 
 # Metadata
 'metadata' => '메타데이터',
-'metadata-help' => '이 파일은 카메라/스캐너에서 기록한 부가 정보를 가지고 있습니다. 프로그램에서 파일을 편집할 경우, 새로 저장한 그림 파일에 일부 부가 정보가 빠질 수 있습니다.',
+'metadata-help' => '이 파일은 카메라나 스캐너에서 기록한 부가 정보를 가지고 있습니다.
+프로그램에서 파일을 편집할 경우, 새로 저장한 그림 파일에 일부 부가 정보가 빠질 수 있습니다.',
 'metadata-expand' => '자세한 정보 보이기',
 'metadata-collapse' => '자세한 정보 숨기기',
 'metadata-fields' => '파일 메타데이터 표가 접혀 있을 때, 이 메시지에 올라와 있는 다음 속성값만이 기본적으로 보이게 됩니다.
@@ -3296,7 +3324,7 @@ Variants for Chinese language
 'exif-primarychromaticities' => '색도의 우선 색',
 'exif-ycbcrcoefficients' => '색 공간 변환 표 계수',
 'exif-referenceblackwhite' => '흑백 값에 대한 정보',
-'exif-datetime' => 'í\8c\8cì\9d¼ì\9d´ ë³\80ê²½ë\90\9c 날짜와 시간',
+'exif-datetime' => 'í\8c\8cì\9d¼ì\9d´ ë°\94ë\80\90 날짜와 시간',
 'exif-imagedescription' => '그림 제목',
 'exif-make' => '카메라 제조사',
 'exif-model' => '카메라 모델',
@@ -3404,7 +3432,7 @@ Variants for Chinese language
 'exif-objectname' => '짧은 제목',
 'exif-specialinstructions' => '사진 이용에 대한 특이 사항',
 'exif-headline' => '표제어',
-'exif-credit' => '제공자',
+'exif-credit' => '기여자/제공자',
 'exif-source' => '출처',
 'exif-editstatus' => '그림의 편집/구성',
 'exif-urgency' => '긴급',
@@ -3443,7 +3471,7 @@ Variants for Chinese language
 'exif-disclaimer' => '면책 조항',
 'exif-contentwarning' => '콘텐츠 경고',
 'exif-giffilecomment' => 'GIF 파일 주석',
-'exif-intellectualgenre' => '콘텐츠 정보',
+'exif-intellectualgenre' => '항목 종류',
 'exif-subjectnewscode' => '주제 코드',
 'exif-scenecode' => 'IPTC 장면 코드',
 'exif-event' => '묘사된 사건',
@@ -3458,9 +3486,9 @@ Variants for Chinese language
 'exif-compression-3' => 'CCITT 그룹-3 팩스 인코딩',
 'exif-compression-4' => 'CCITT 그룹-4 팩스 인코딩',
 'exif-compression-6' => 'JPEG (오래됨)',
-'exif-compression-8' => 'ì\95\95축 (Adobe)',
+'exif-compression-8' => 'ì\88\98축 (Adobe)',
 'exif-compression-32773' => 'PackBits (매킨토시 RLE)',
-'exif-compression-32946' => 'ì\95\95축 (PKZIP)',
+'exif-compression-32946' => 'ì\88\98축 (PKZIP)',
 
 'exif-copyrighted-true' => '저작권의 보호를 받음',
 'exif-copyrighted-false' => '퍼블릭 도메인',
@@ -3669,7 +3697,7 @@ Variants for Chinese language
 
 # External editor support
 'edit-externally' => '이 파일을 외부 프로그램을 사용해서 편집하기',
-'edit-externally-help' => '(ì\9e\90ì\84¸í\95\9c ì \95ë³´ë\8a\94 [//www.mediawiki.org/wiki/Manual:External_editors ì\84¤ì¹\98 ë°©ë²\95\9d\84 ì°¸ê³ í\95\98ì\8b­ì\8b\9cì\98¤)',
+'edit-externally-help' => '(ì\9e\90ì\84¸í\95\9c ì \95ë³´ë\8a\94 [//www.mediawiki.org/wiki/Manual:External_editors ì\84¤ì¹\98 ë°©ë²\95\9d\84 ì°¸ê³ í\95\98ì\84¸ì\9a\94)',
 
 # 'all' in various places, this might be different for inflected languages
 'watchlistall2' => '모든 기간',
@@ -3684,53 +3712,66 @@ Variants for Chinese language
 아래의 버튼을 누르면 인증 메일을 보냅니다.
 메일에는 인증 코드가 들어있는 링크가 있습니다.
 그 링크를 웹 브라우저로 열면 인증이 완료됩니다.',
-'confirmemail_pending' => '이미 확인 이메일을 보냈습니다. 계정을 최근에 만들었다면 이메일을 보내는 데에 몇 분이 걸릴 수 있으므로 잠시 후에 다시 확인해 주세요.',
+'confirmemail_pending' => '이미 확인 이메일을 보냈습니다.
+계정을 최근에 만들었다면 이메일을 보내는 데에 몇 분이 걸릴 수 있으므로 잠시 후에 다시 확인해 주세요.',
 'confirmemail_send' => '인증 코드를 메일로 보내기',
 'confirmemail_sent' => '인증 이메일을 보냈습니다.',
-'confirmemail_oncreate' => '확인 이메일을 보냈습니다. 이 확인 과정은 로그인하는 데에 필요하지는 않지만, 위키 프로그램에서 제공하는 이메일 기능을 사용하기 위해서 필요합니다.',
+'confirmemail_oncreate' => '확인 이메일을 보냈습니다.
+이 확인 과정은 로그인하는 데에 필요하지는 않지만, 위키 프로그램에서 제공하는 이메일 기능을 사용하기 위해서 필요합니다.',
 'confirmemail_sendfailed' => '{{SITENAME}}에서 인증 이메일을 보낼 수 없습니다.
 이메일 주소를 잘못 입력했는지 확인해주세요.
 
 메일 서버로부터의 응답: $1',
-'confirmemail_invalid' => '인증 코드가 올바르지 않습니다. 인증 코드가 만료되었을 수도 있습니다.',
+'confirmemail_invalid' => '인증 코드가 올바르지 않습니다.
+인증 코드가 만료되었을 수도 있습니다.',
 'confirmemail_needlogin' => '이메일 주소를 인증하려면 $1이 필요합니다.',
-'confirmemail_success' => '이메일 주소가 인증되었습니다. 이제 로그인해서 위키를 사용하세요.',
+'confirmemail_success' => '이메일 주소가 인증되었습니다.
+이제 [[Special:UserLogin|로그인]]해서 위키를 사용하세요.',
 'confirmemail_loggedin' => '이메일 주소가 인증되었습니다.',
 'confirmemail_error' => '당신의 인증을 저장하는 도중 오류가 발생했습니다.',
 'confirmemail_subject' => '{{SITENAME}} 이메일 주소 인증',
-'confirmemail_body' => '$1 아이피 주소를 사용하는 사용자가 {{SITENAME}}의 ‘$2’ 계정에 이메일 인증 신청을 했습니다.
+'confirmemail_body' => '$1 IP 주소를 사용하는 사용자가
+{{SITENAME}}의 "$2" 계정에 이메일 인증 신청을 했습니다.
 
-이 계정이 당신의 계정이라면, 아래 주소를 열어서 이메일 인증을 해 주세요.
+이 계정이 당신의 계정이고 {{SITENAME}}에서 이메일 기능을 활성화하려면
+아래 주소를 열어서 이메일 인증을 해 주세요:
 
 $3
 
-당신의 계정이 아니라면, 이메일 인증 신청을 취소하기 위해 아래의 주소를 열어주세요.
+당신의 계정이 아니라면,
+이메일 인증 신청을 취소하기 위해 아래의 주소를 열어주세요:
 
 $5
 
 인증 코드는 $4에 만료됩니다.',
-'confirmemail_body_changed' => '$1 IP 주소를 사용하는 사용자가 {{SITENAME}}의 "$2" 계정의 이메일 주소를 바꾸었습니다.
+'confirmemail_body_changed' => '$1 IP 주소를 사용하는 사용자가
+{{SITENAME}}의 "$2" 계정의 이메일 주소를 바꾸었습니다.
 
-이 계정이 당신의 계정인지 확인하고 {{SITENAME}}의 이메일 기능을 활성화하려면 아래 링크를 클릭하여 이메일 인증을 해 주세요.
+이 계정이 당신의 계정이고 {{SITENAME}}에서 이메일 기능을 활성화하려면
+아래 주소를 열어서 이메일 인증을 해 주세요:
 
 $3
 
-이 계정이 당신의 것이 아니라면 다음 링크를 열어 이메일 주소 변경을 취소하십시오.
+당신의 계정이 아니라면,
+이메일 인증 신청을 취소하기 위해 아래의 주소를 열어주세요:
 
 $5
 
 인증 코드는 $4에 만료됩니다.',
-'confirmemail_body_set' => 'IP 주소 $1을 사용하는 누군가가 {{SITENAME}}의 "$2" 계정의 이메일 주소를 지정하였습니다.
+'confirmemail_body_set' => 'IP 주소 $1을 사용하는 사용자가
+{{SITENAME}}의 "$2" 계정의 이메일 주소를 지정하였습니다.
 
-이 계정이 당신의 것이며 {{SITENAME}}에서 이메일 기능을 다시 활성화하려면 이 링크를 열어 주십시오:
+이 계정이 당신의 계정이고 {{SITENAME}}에서 이메일 기능을
+다시 활성화하려면 아래 주소를 열어서 이메일 인증을 해 주세요:
 
 $3
 
-만약 이 계정이 당신의 것이 아니라면 다음 링크를 클릭해 이메일 주소 인증을 취소하십시오:
+당신의 계정이 아니라면,
+이메일 인증 신청을 취소하기 위해 아래의 주소를 열어주세요:
 
 $5
 
\9d´ ì\9d¸ì¦\9d ì½\94ë\93\9cë\8a\94 $4ì\97\90 ë§\8cë£\8cë\90©ë\8b\88ë\8b¤.',
+인증 코드는 $4에 만료됩니다.',
 'confirmemail_invalidated' => '이메일 확인이 취소됨',
 'invalidateemail' => '이메일 확인 취소',
 
@@ -3740,17 +3781,17 @@ $5
 'scarytranscludetoolong' => '[URL이 너무 깁니다]',
 
 # Delete conflict
-'deletedwhileediting' => "'''주의''': 당신이 이 문서를 편집하던 중에 이 문서가 삭제되었습니다.",
+'deletedwhileediting' => "'''주의''': 당신이 이 문서를 편집하던 중에 이 문서가 삭제되었습니다!",
 'confirmrecreate' => '[[User:$1|$1]] 사용자([[User talk:$1|토론]])가 당신이 편집하는 도중에 문서를 삭제했습니다. 삭제 이유는 다음과 같습니다:
 : $2
-문서를 다시 ì\83\9dì\84±í\95´ì\95¼ í\95\98ë\8a\94ì§\80 í\99\95ì\9d¸í\95´ì£¼ì\84¸ì\9a\94.',
-'confirmrecreate-noreason' => '[[User:$1|$1]] ì\82¬ì\9a©ì\9e\90([[User talk:$1|í\86 ë¡ ]])ê°\80 ë\8b¹ì\8b ì\9d\98 í\8e¸ì§\91 ë\8f\84ì¤\91 ë¬¸ì\84\9c를 ì\82­ì \9cí\96\88ì\8aµë\8b\88ë\8b¤. ë¬¸ì\84\9c를 ë\8b¤ì\8b\9c ì\83\9dì\84±í\95´ì\95¼ í\95\98ë\8a\94ì§\80 í\99\95ì\9d¸í\95´ì£¼ì\84¸ì\9a\94.',
+문서를 다시 ë§\8cë\93¤ì\96´ì\95¼ í\95\98ë\8a\94ì§\80 í\99\95ì\9d¸í\95´ì£¼ì\84¸ì\9a\94.',
+'confirmrecreate-noreason' => '[[User:$1|$1]] ì\82¬ì\9a©ì\9e\90([[User talk:$1|í\86 ë¡ ]])ê°\80 ë\8b¹ì\8b ì\9d´ í\8e¸ì§\91í\95\98ë\8a\94 ë\8f\84ì¤\91ì\97\90 ë¬¸ì\84\9c를 ì\82­ì \9cí\96\88ì\8aµë\8b\88ë\8b¤. ë¬¸ì\84\9c를 ë\8b¤ì\8b\9c ë§\8cë\93¤ì\96´ì\95¼ í\95\98ë\8a\94ì§\80 í\99\95ì\9d¸í\95´ì£¼ì\84¸ì\9a\94.',
 'recreate' => '새로 만들기',
 
 # action=purge
 'confirm_purge_button' => '확인',
 'confirm-purge-top' => '문서의 캐시를 지울까요?',
-'confirm-purge-bottom' => '문서를 새로고침하는 것은 캐시를 갱신하고 가장 최근의 버전이 나타나게 할 것입니다.',
+'confirm-purge-bottom' => '문서를 새로 고침하는 것은 캐시를 새로 고치고 가장 최근의 판이 나타나게 할 것입니다.',
 
 # action=watch/unwatch
 'confirm-watch-button' => '확인',
@@ -3761,7 +3802,7 @@ $5
 # Multipage image navigation
 'imgmultipageprev' => '← 이전 페이지',
 'imgmultipagenext' => '다음 페이지 →',
-'imgmultigo' => '이동',
+'imgmultigo' => '이동!',
 'imgmultigoto' => '$1 페이지로 가기',
 
 # Table pager
@@ -3772,32 +3813,33 @@ $5
 'table_pager_first' => '처음 문서',
 'table_pager_last' => '마지막 문서',
 'table_pager_limit' => '문서당 $1개 항목 보이기',
-'table_pager_limit_label' => '페이지당 항목 수:',
+'table_pager_limit_label' => '문서당 항목 수:',
 'table_pager_limit_submit' => '확인',
 'table_pager_empty' => '결과 없음',
 
 # Auto-summaries
 'autosumm-blank' => '문서를 비움',
-'autosumm-replace' => '문서 내용을 ‘$1’으로 바꿈',
+'autosumm-replace' => '문서 내용을 "$1"으로 바꿈',
 'autoredircomment' => '[[$1]] 문서로 넘겨주기',
 'autosumm-new' => '새 문서: $1',
 
 # Live preview
 'livepreview-loading' => '불러오는 중...',
-'livepreview-ready' => '불러 오는 중… 준비!',
-'livepreview-failed' => '실시간 미리 보기 실패! 일반 미리 보기를 이용해주세요.',
+'livepreview-ready' => '불러 오는 중... 준비!',
+'livepreview-failed' => '실시간 미리 보기 실패!
+일반 미리 보기를 이용하세요.',
 'livepreview-error' => '연결에 실패하였습니다: $1 "$2"
-일반 미리보기를 이용하십시오.',
+일반 미리 보기를 이용하세요.',
 
 # Friendlier slave lag warnings
 'lag-warn-normal' => '최근 $1초 안에 바뀐 문서는 이 목록에서 빠졌을 수 있습니다.',
-'lag-warn-high' => 'ë\8d°ì\9d´í\84°ë² ì\9d´ì\8a¤ ì\84\9cë²\84ì\9d\98 ê³¼ë\8f\84í\95\9c ë¶\80í\95\98 ë\95\8c문ì\97\90 ìµ\9cê·¼ $1ì´\88 ì\95\88ì\97\90 ë³\80ê²½ë\90\9c 문서 목록은 표시되지 않을 수 있습니다.',
+'lag-warn-high' => 'ë\8d°ì\9d´í\84°ë² ì\9d´ì\8a¤ ì\84\9cë²\84ì\9d\98 ê³¼ë\8f\84í\95\9c ë¶\80í\95\98 ë\95\8c문ì\97\90 ìµ\9cê·¼ $1ì´\88 ì\95\88ì\97\90 ë°\94ë\80\90 문서 목록은 표시되지 않을 수 있습니다.',
 
 # Watchlist editor
 'watchlistedit-numitems' => '토론 문서를 제외하고 문서 $1개를 주시하고 있습니다.',
 'watchlistedit-noitems' => '주시문서 목록이 비어 있습니다.',
 'watchlistedit-normal-title' => '주시문서 목록 편집하기',
-'watchlistedit-normal-legend' => '주시목록에서 문서 제거하기',
+'watchlistedit-normal-legend' => '주ì\8b\9c문ì\84\9c ëª©ë¡\9dì\97\90ì\84\9c ë¬¸ì\84\9c ì \9cê±°í\95\98기',
 'watchlistedit-normal-explain' => "주시문서 목록에 있는 문서의 제목이 아래에 나열되어 있습니다.
 주시문서 목록에서 제거하려는 문서가 있으면, 각 항목의 체크박스를 선택한 다음 '{{int:Watchlistedit-normal-submit}}'를 클릭해주세요.
 또는 [[Special:EditWatchlist/raw|목록을 직접 편집]]할 수도 있습니다.",
@@ -3806,11 +3848,12 @@ $5
 'watchlistedit-raw-title' => '주시문서 목록 직접 편집하기',
 'watchlistedit-raw-legend' => '주시문서 목록 직접 편집하기',
 'watchlistedit-raw-explain' => "주시문서 목록의 각 항목이 나와 있습니다. 필요한 항목을 직접 추가하거나 제거할 수 있습니다.
-각 줄마다 하나의 제목을 쓰고, 수정을 마쳤다면 '{{int:Watchlistedit-raw-submit}}'을 누르면 됩니다.
+각 줄마다 하나의 제목을 입력하세요.
+수정을 마쳤다면 '{{int:Watchlistedit-raw-submit}}'을 누르면 됩니다.
 또는 [[Special:EditWatchlist|일반적인 편집기]]를 쓸 수도 있습니다.",
 'watchlistedit-raw-titles' => '목록:',
-'watchlistedit-raw-submit' => '주시문서 목록 갱신',
-'watchlistedit-raw-done' => '주시문서 목록을 갱신했습니다.',
+'watchlistedit-raw-submit' => '주시문서 목록 새로 고침',
+'watchlistedit-raw-done' => '주시문서 목록을 새로 고쳤습니다.',
 'watchlistedit-raw-added' => '문서 $1개를 추가했습니다:',
 'watchlistedit-raw-removed' => '문서 $1개를 제거했습니다:',
 
@@ -3845,7 +3888,7 @@ $5
 'version-version' => '(버전 $1)',
 'version-license' => '라이선스',
 'version-poweredby-credits' => "이 위키는 '''[//www.mediawiki.org/ MediaWiki]'''를 기반으로 작동합니다. Copyright © 2001-$1 $2.",
-'version-poweredby-others' => '그 외 다른 개발자',
+'version-poweredby-others' => '[{{SERVER}}{{SCRIPTPATH}}/CREDITS 그 외 다른 개발자]',
 'version-license-info' => "미디어위키는 자유 소프트웨어입니다. 당신은 자유 소프트웨어 재단이 발표한 GNU 일반 공중 사용 허가서 버전 2나 그 이후 버전에 따라 이 파일을 재배포하거나 수정할 수 있습니다.
 
 미디어위키가 유용하게 사용될 수 있기를 바라지만 '''상용으로 사용'''되거나 '''특정 목적에 맞을 것'''이라는 것을 '''보증하지 않습니다'''. 자세한 내용은 GNU 일반 공중 사용 허가서 전문을 참고하십시오.
@@ -3874,8 +3917,8 @@ $5
 'fileduplicatesearch-filename' => '파일 이름:',
 'fileduplicatesearch-submit' => '찾기',
 'fileduplicatesearch-info' => '$1 × $2 픽셀<br />파일 크기: $3<br />MIME 유형: $4',
-'fileduplicatesearch-result-1' => '‘$1’ 파일과 중복된 파일이 없습니다.',
-'fileduplicatesearch-result-n' => '"$1"파일은 중복 파일이 $2개 있습니다.',
+'fileduplicatesearch-result-1' => '"$1" 파일과 중복된 파일이 없습니다.',
+'fileduplicatesearch-result-n' => '"$1" 파일은 중복 파일이 $2개 있습니다.',
 'fileduplicatesearch-noresults' => '"$1"이라는 이름을 가진 파일이 없습니다.',
 
 # Special:SpecialPages
@@ -3902,8 +3945,8 @@ $5
 
 # External image whitelist
 'external_image_whitelist' => ' #이 줄은 그대로 두십시오<pre>
-#ì \95ê·\9c í\91\9cí\98\84ì\8b\9d(// ì\82¬ì\9d´ì\97\90 ì\9e\88ë\8a\94 ë¶\80ë¶\84\9d\84 ì\95\84ë\9e\98ì\97\90 ì\8d¨ ì£¼ì\8b­ì\8b\9cì\98¤.
-#이 목록은 외부 이미지의 URL과 대조ë\90  ê²\83ì\9e\85ë\8b\88ë\8b¤.
+#ì \95ê·\9c í\91\9cí\98\84ì\8b\9d(// ì\82¬ì\9d´ì\97\90 ì\9e\88ë\8a\94 ë¶\80ë¶\84\9d\84 ì\95\84ë\9e\98ì\97\90 ì\9e\85ë ¥í\95\98ì\84¸ì\9a\94.
+#이 목록은 외부 이미지의 URL과 대조í\95  ê²\83ì\9e\85ë\8b\88ë\8b¤.
 #이 목록과 일치하는 것은 그림이 직접 보여지지만, 그렇지 않은 경우 그림을 가리키는 링크만 보이게 될 것입니다.
 # "#"으로 시작하는 줄은 주석으로 간주됩니다.
 #이 목록은 대소문자를 구별하지 않습니다.
@@ -3917,7 +3960,7 @@ $5
 'tags-title' => '태그',
 'tags-intro' => '이 페이지는 소프트웨어에서 편집에 대해 표시하는 태그와 그 의미를 설명하는 목록입니다.',
 'tags-tag' => '태그 이름',
-'tags-display-header' => 'ë³\80ê²½ 목록의 모양',
+'tags-display-header' => 'ë°\94ë\80\9c 목록의 모양',
 'tags-description-header' => '태그에 대한 설명',
 'tags-hitcount-header' => '태그된 바뀜',
 'tags-edit' => '편집',
@@ -3942,7 +3985,7 @@ $5
 'dberr-again' => '잠시 후에 다시 시도해주세요.',
 'dberr-info' => '(데이터베이스에 접속할 수 없습니다: $1)',
 'dberr-usegoogle' => '그 동안 구글을 통해 검색할 수도 있습니다.',
-'dberr-outofdate' => '참고로, 구글의 내용 개요는 오래된 것일 수도 있습니다.',
+'dberr-outofdate' => '참고로 구글의 내용 개요는 오래된 것일 수도 있습니다.',
 'dberr-cachederror' => '다음은 요청한 문서의 캐시된 복사본이며, 최신이 아닐 수도 있습니다.',
 
 # HTML forms
@@ -3954,7 +3997,7 @@ $5
 'htmlform-int-toohigh' => '당신이 입력한 값은 최대값 $1 이상입니다.',
 'htmlform-required' => '이 값은 필수 항목입니다',
 'htmlform-submit' => '저장',
-'htmlform-reset' => 'ë³\80ê²½í\95\9c 것을 되돌리기',
+'htmlform-reset' => 'ë°\94ê¾¼ 것을 되돌리기',
 'htmlform-selectorother-other' => '기타',
 
 # SQLite database support
@@ -3965,9 +4008,9 @@ $5
 'logentry-delete-delete' => '$1 사용자가 $3 문서를 삭제하였습니다.',
 'logentry-delete-restore' => '$1 사용자가 $3 문서를 복구하였습니다.',
 'logentry-delete-event' => '$1 사용자가 $3의 기록 $5개에 대해 표시 설정을 바꾸었습니다: $4',
-'logentry-delete-revision' => '$1 사용자가 $3 문서의 {{PLURAL:$5|$5}}개 편집의 설정을 변경하였습니다: $4',
-'logentry-delete-event-legacy' => '$1 ì\82¬ì\9a©ì\9e\90ê°\80 $3 ë¬¸ì\84\9c ê¸°ë¡\9dì\9d\98 í\91\9cì\8b\9c ì\84¤ì \95ì\9d\84 ë³\80ê²½í\95\98ì\98\80습니다.',
-'logentry-delete-revision-legacy' => '$1 ì\82¬ì\9a©ì\9e\90ê°\80 $3 ë¬¸ì\84\9c í\8e¸ì§\91ì\9d\98 í\91\9cì\8b\9c ì\84¤ì \95ì\9d\84 ë³\80ê²½í\95\98ì\98\80습니다.',
+'logentry-delete-revision' => '$1 사용자가 $3 문서의 {{PLURAL:$5|$5개 편집}}의 설정을 바꾸었습니다: $4',
+'logentry-delete-event-legacy' => '$1 ì\82¬ì\9a©ì\9e\90ê°\80 $3 ë¬¸ì\84\9c ê¸°ë¡\9dì\9d\98 í\91\9cì\8b\9c ì\84¤ì \95ì\9d\84 ë°\94꾸ì\97\88습니다.',
+'logentry-delete-revision-legacy' => '$1 ì\82¬ì\9a©ì\9e\90ê°\80 $3 ë¬¸ì\84\9c í\8e¸ì§\91ì\9d\98 í\91\9cì\8b\9c ì\84¤ì \95ì\9d\84 ë°\94꾸ì\97\88습니다.',
 'logentry-suppress-delete' => '$1 사용자가 $3 문서를 숨겼습니다.',
 'logentry-suppress-event' => '$1 사용자가 비공개적으로 $3의 {{PLURAL:$5|기록 $5개}}에 대해 표시 설정을 바꾸었습니다: $4',
 'logentry-suppress-revision' => '$1 사용자가 비공개적으로 $3 문서의 {{PLURAL:$5|판 $5개}}에 대해 표시 설정을 바꾸었습니다: $4',
@@ -3981,16 +4024,16 @@ $5
 'revdelete-uname-unhid' => '계정 이름 숨김 해제됨',
 'revdelete-restricted' => '관리자에게 제한을 적용함',
 'revdelete-unrestricted' => '관리자에 대한 제한을 해제함',
-'logentry-move-move' => '$1 사용자가 $3 문서를 $4로 옮겼습니다.',
-'logentry-move-move-noredirect' => '$1 사용자가 $3 문서를 넘겨주기를 생성하지 않고 $4로 옮겼습니다.',
-'logentry-move-move_redir' => '$1 사용자가 $3 문서를 $4로 옮기면서 넘겨주기를 덮어썼습니다.',
-'logentry-move-move_redir-noredirect' => '$1 사용자가 $3 문서를 $4로 넘겨주기를 남기지 않으면서 옮기면서 옮길 대상에 있던 넘겨주기를 덮어썼습니다..',
+'logentry-move-move' => '$1 사용자가 $3 문서를 $4 문서로 옮겼습니다.',
+'logentry-move-move-noredirect' => '$1 사용자가 $3 문서를 넘겨주기를 만들지 않고 $4 문서로 옮겼습니다.',
+'logentry-move-move_redir' => '$1 사용자가 $3 문서를 $4 문서로 옮기면서 넘겨주기를 덮어썼습니다.',
+'logentry-move-move_redir-noredirect' => '$1 사용자가 $3 문서를 $4 문서로 넘겨주기를 남기지 않으면서 옮기면서 옮길 대상에 있던 넘겨주기를 덮어썼습니다.',
 'logentry-patrol-patrol' => '$1 사용자가 $3 문서의 $4판을 검토한 것으로 표시했습니다.',
 'logentry-patrol-patrol-auto' => '$1 사용자가 자동적으로 $3 문서의 $4판을 검토한 것으로 표시했습니다.',
-'logentry-newusers-newusers' => '$1 사용자가 계정을 ì\83\9dì\84±í\96\88ì\8aµë\8b\88ë\8b¤.',
-'logentry-newusers-create' => '$1 사용자가 계정을 ì\83\9dì\84±í\96\88ì\8aµë\8b\88ë\8b¤.',
-'logentry-newusers-create2' => '$1 사용자가 $3 계정을 ì\83\9dì\84±í\96\88ì\8aµë\8b\88ë\8b¤.',
-'logentry-newusers-autocreate' => '$1 ê³\84ì \95ì\9d´ ì\9e\90ë\8f\99ì \81ì\9c¼ë¡\9c ì\83\9dì\84±ë\90\98었습니다.',
+'logentry-newusers-newusers' => '$1 사용자가 계정을 ë§\8cë\93¤ì\97\88ì\8aµë\8b\88ë\8b¤.',
+'logentry-newusers-create' => '$1 사용자가 계정을 ë§\8cë\93¤ì\97\88ì\8aµë\8b\88ë\8b¤.',
+'logentry-newusers-create2' => '$1 사용자가 $3 계정을 ë§\8cë\93¤ì\97\88ì\8aµë\8b\88ë\8b¤.',
+'logentry-newusers-autocreate' => '$1 ê³\84ì \95ì\9d\84 ì\9e\90ë\8f\99ì \81ì\9c¼ë¡\9c ë§\8cë\93¤었습니다.',
 'newuserlog-byemail' => '이메일로 보낸 비밀번호',
 
 # Feedback
@@ -4004,7 +4047,7 @@ $5
 'feedback-error1' => '오류: API 실행 결과를 인식할 수 없음',
 'feedback-error2' => '오류: 편집 실패',
 'feedback-error3' => '오류: API가 응답하지 않음',
-'feedback-thanks' => '감사합니다! ‘[$2 $1]’ 문서에 당신의 의견을 남겼습니다.',
+'feedback-thanks' => '감사합니다! "[$2 $1]" 문서에 당신의 의견을 남겼습니다.',
 'feedback-close' => '완료',
 'feedback-bugcheck' => '감사합니다! 혹시 해당 사항이 [$1 기존의 버그 보고서]에 올라와 있는지 확인해주세요.',
 'feedback-bugnew' => '확인했습니다. 새로운 버그 보고서를 작성합니다.',
@@ -4014,9 +4057,9 @@ $5
 'api-error-badtoken' => '내부 오류: 토큰이 잘못되었습니다.',
 'api-error-copyuploaddisabled' => '이 서버에서 URL을 통해 파일 올리기가 비활성화되어 있습니다.',
 'api-error-duplicate' => '이 위키에 내용이 똑같은 {{PLURAL:$1|[$2 다른 파일]}}이 있습니다.',
-'api-error-duplicate-archive' => '같은 내용을 담고 있던 {{PLURAL:$1|[$2 다른 파일]}}이 있었지만 이 {{PLURAL:$1|파일|파일들}}은 삭제되었습니다.',
+'api-error-duplicate-archive' => '같은 내용을 담고 있던 {{PLURAL:$1|[$2 다른 파일]}}이 있었지만 이 {{PLURAL:$1|파일}}은 삭제되었습니다.',
 'api-error-duplicate-archive-popup-title' => '중복된 {{PLURAL:$1|파일}}이 이미 삭제되었습니다.',
-'api-error-duplicate-popup-title' => '중복된 {{PLURAL:$1|파일}}',
+'api-error-duplicate-popup-title' => '중복된 {{PLURAL:$1|파일}}입니다.',
 'api-error-empty-file' => '당신이 올리려는 파일이 비어 있습니다.',
 'api-error-emptypage' => '새 문서로 빈 문서를 만들 수 없습니다.',
 'api-error-fetchfileerror' => '내부 오류: 파일을 불러오는 중 문제가 발생했습니다.',
@@ -4034,7 +4077,7 @@ $5
 'api-error-missingparam' => '내부 오류: 요청 중 매개변수가 누락되었습니다.',
 'api-error-missingresult' => '내부 오류: 파일의 복제가 성공했는지 판단할 수 없습니다.',
 'api-error-mustbeloggedin' => '파일을 올리기 위해서는 로그인해야 합니다.',
-'api-error-mustbeposted' => '이 소프트웨어에 버그가 있습니다; 올바른 HTTP 전송 방식을 사용하지 않았습니다.',
+'api-error-mustbeposted' => '내부 오류: HTTP POST에 요청이 필요합니다.',
 'api-error-noimageinfo' => '파일 올리기는 성공했지만 서버가 파일에 대해 어떠한 정보도 주지 않았습니다.',
 'api-error-nomodule' => '내부 오류: 올리기 모듈이 설정되지 않았습니다.',
 'api-error-ok-but-empty' => '내부 오류: 서버에서 응답이 없습니다.',
@@ -4042,9 +4085,9 @@ $5
 'api-error-stashfailed' => '내부 오류: 서버가 임시 파일을 저장하지 못했습니다.',
 'api-error-timeout' => '서버가 제 시간 내에 응답하지 않았습니다.',
 'api-error-unclassified' => '알 수 없는 오류가 발생했습니다.',
-'api-error-unknown-code' => '알 수 없는 오류: "$1"',
+'api-error-unknown-code' => '알 수 없는 오류: "$1".',
 'api-error-unknown-error' => '내부 오류: 파일을 올리려 하는 도중에 무엇인가가 잘못되었습니다.',
-'api-error-unknown-warning' => 'ì\9b\90ì\9d¸ ë¶\88ëª\85ì\9d\98 ê²½ê³ : $1',
+'api-error-unknown-warning' => 'ì\95\8c ì\88\98 ì\97\86ë\8a\94 ê²½ê³ : "$1".',
 'api-error-unknownerror' => '알 수 없는 오류: "$1".',
 'api-error-uploaddisabled' => '이 위키에서 파일 올리기가 비활성화되어 있습니다.',
 'api-error-verification-error' => '파일이 손상되었거나 잘못된 확장자를 사용하고 있습니다.',
index f484421..aa5460a 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Karachay-Balkar (Ð\9aÑ\8aаÑ\80аÑ\87ай-Ð\9cалкъар)
+/** Karachay-Balkar (кÑ\8aаÑ\80аÑ\87ай-малкъар)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index 2d6b020..41f89bf 100644 (file)
@@ -226,17 +226,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Avschnedde met Räächs-Klicke op de Üvverschrefte ändere (bruch Java_Skripp)',
 'tog-showtoc' => 'Zeich en Enhaldsüvversich bei Sigge met mieh wie drei Üvverschrefte dren',
 'tog-rememberpassword' => 'Op Duur enlogge op dämm Kompjuter un för dää Brauser (hält {{PLURAL:$1|för eine Daach|bes op $1 Dääsch|bloß för hück}})',
-'tog-watchcreations' => 'Dun de Sigge, die ich neu aanläje, för ming Oppassliss vürschlage',
-'tog-watchdefault' => 'Dun de Sigge för ming Oppassliss vürschlage, die ich aanpacken un änder',
-'tog-watchmoves' => 'Dun ming selfs ömjenante Sigge automatisch för ming Oppassliss vürschlage',
-'tog-watchdeletion' => 'Dun Sigge, die ich fottjeschmesse han, för ming Oppassliss vürschlage',
+'tog-watchcreations' => 'Donn Sigge beim neu Aanlääje automattesch för ming Oppaßlėß vörschlonn',
+'tog-watchdefault' => 'Donn Sigge beim Ändere automattesch för ming Oppaßlėß vörschlonn',
+'tog-watchmoves' => 'Donn Sigge beim Ömnänne automattesch för ming Oppaßlėß vörschlonn',
+'tog-watchdeletion' => 'Donn Sigge beim Fottschmiiße automattesch för ming Oppaßlėß vörschlonn',
 'tog-minordefault' => 'Dun all ming Änderunge jedes Mol als klein Mini-Änderunge vürschlage',
 'tog-previewontop' => 'Zeich de Vör-Aansich üvver däm Feld för dä Tex enzejevve aan.',
 'tog-previewonfirst' => 'Zeich de Vör-Aansich tirek för et eetste Mol beim Bearbeide aan',
 'tog-nocache' => 'Dun et Sigge Zweschespeichere en Dingem Brauser avschalte',
-'tog-enotifwatchlistpages' => 'Scheck en E-Mail, wann en Sigg us ming Oppassliss jeändert wood',
+'tog-enotifwatchlistpages' => 'Scheck mer en <i lang="en">e-mail</i>, wann en Sigg us minge Oppaßlėß verändert woode es',
 'tog-enotifusertalkpages' => 'Scheck mer en E-Mail, wann ming Klaaf Sigg jeändert weed',
-'tog-enotifminoredits' => 'Scheck mer och en E-Mail för de klein Mini-Änderunge',
+'tog-enotifminoredits' => 'Scheck mer och en <i lang="en">e-mail</i> för de klein Mini-Änderonge',
 'tog-enotifrevealaddr' => 'Zeich dä Andere ming E-Mail Adress aan, en de Benohrichtijunge per E-Mail',
 'tog-shownumberswatching' => 'Zeich de Aanzahl Metmaacher, die op die Sigg am oppasse sin',
 'tog-oldsig' => 'Esu&nbsp;süht&nbsp;Ding „Ongerschreff“&nbsp;us:',
@@ -568,6 +568,8 @@ un doht em och de URL vun dä Sigg heh sage.',
 'badarticleerror' => 'Dat jeiht met heh dä Sigg nit ze maache.',
 'cannotdelete' => 'De Sigg oder de Datei „$1“ fottzeschmieße es nit müjjelich. Maach sin, dat ene andere Metmaacher flöcker wor, hät et vürher jedon, un jetz es se ald fott.',
 'cannotdelete-title' => 'Mer künne di Sigg „$1“ nit fott schmiiße.',
+'delete-hook-aborted' => 'Et Fottschmiiße wood affjebroche övver ene sujenannte „Hoke“ en de ẞoffwäer.
+Ene Jrond weße mer nit.',
 'badtitle' => 'Verkihrte Üvverschreff',
 'badtitletext' => 'De Üvverschreff es esu nit en Odenung. Et muss jet dren stonn.
 Et künnt sin, dat ein vun de speziell Zeiche dren steiht,
@@ -2336,7 +2338,7 @@ Mieh övver de einzel Rääschte fenkt Er op de [[{{MediaWiki:Listgrouprights-he
 Adress en Dinge [[Special:Preferences|ming Enstellunge]] stonn han, öm en E-Mail aan andere Metmaacher ze
 schecke.',
 'emailuser' => 'E-mail aan dä Metmaacher',
-'emailpage' => 'E-mail aan ene Metmaacher',
+'emailpage' => 'Verscheck <i lang="en">e-mail</i> aan ene Metmaacher',
 'emailpagetext' => 'Wann heh dä Metmaacher en Adräß för sing <i lang="en">e-mail</i> aanjejovve hätt en singe Enstellunge,
 un die deit et och, dann kanns De met däm Fomular hee unge en einzel <i lang="en">e-mail</i> aan dä Metmaacher schecke.
 
@@ -2364,7 +2366,7 @@ Alles klor?',
 'emailsend' => 'Avschecke',
 'emailccme' => 'Scheck mer en Kopie vun dä E-Mail.',
 'emailccsubject' => 'En Kopie vun Dinger E-Mail aan $1: $2',
-'emailsent' => 'E-Mail es ungerwähs',
+'emailsent' => 'De <i lang="en">e-mail</i> es ongerwähs',
 'emailsenttext' => 'Ding E-Mail es jetz lossjescheck woode.',
 'emailuserfooter' => 'Hee di e-mail hät dä „$1“ an dä „$2“ jescheck, un doför {{GRAMMAR:en dative|{{SITENAME}}}} dat „{{int:emailuser}}“ jebruch.',
 
@@ -3173,6 +3175,7 @@ Esu kam_mer noch en Aanmerkung en „{{int:summary}}“ maache.',
 'spambot_username' => 'SPAM fottschmieße',
 'spam_reverting' => 'De letzte Version ohne de Links op „$1“ widder zerröckjehollt.',
 'spam_blanking' => 'All die Versione hatte Links op „$1“, die sin jetz erus jemaht.',
+'spam_deleting' => 'All di Versione met Lenks op „$1“ wääde fott jeschmeße',
 
 # Info page
 'pageinfo-title' => 'Övver di Sigg: „$1“',
@@ -4129,6 +4132,8 @@ Wat De doh enjiß, kütt met Dingem Metmaachername un Dingem Brauser op die Sigg
 'api-error-empty-file' => 'En dä huhjelaade Dattei wohr jaa_nix dren.',
 'api-error-emptypage' => 'Neu läddijje Sigge aanzelääje es verbodde.',
 'api-error-fetchfileerror' => 'Fähler: Beim eronger Laade hät jät nit jeflupp.',
+'api-error-fileexists-forbidden' => 'En Dattei mem Name „$1“ es ald doh un mer künne se nit övverschriive.',
+'api-error-fileexists-shared-forbidden' => 'En Dattei mem Name „$1“ es ald en ene jemeinsamme Sammlong, un mer künne se nit övverschriive.',
 'api-error-file-too-large' => 'De huhjelaade Dattei wohr ze jruß.',
 'api-error-filename-tooshort' => 'Der Name för di Dattei es ze koot.',
 'api-error-filetype-banned' => 'Di Zoot Dattei es nit zohjelohße.',
index f2c488b..2e08a4c 100644 (file)
@@ -190,17 +190,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Eenzel Abschnitter mat Rietsklick änneren (JavaScript)',
 'tog-showtoc' => 'Inhaltsverzeechnes weise bei Säite mat méi wéi dräi Iwwerschrëften',
 'tog-rememberpassword' => 'Meng Umeldung mat dësem Browser(fir maximal $1 {{PLURAL:$1|Dag|Deeg}}) verhalen',
-'tog-watchcreations' => 'Säiten déi ech nei uleeën automatesch op meng Iwwerwaachungslëscht setzen',
-'tog-watchdefault' => 'Säiten déi ech änneren op meng Iwwerwaachungslëscht setzen',
-'tog-watchmoves' => 'Säiten déi ech réckelen automatesch op meng Iwwerwaachungslëscht setzen',
-'tog-watchdeletion' => 'Säiten déi ech läschen op meng Iwwerwaachungslëscht setzen',
+'tog-watchcreations' => 'Säiten déi ech uleeën a Fichieren déi ech eroplueden op meng Iwwerwaachungslëscht derbäisetzen',
+'tog-watchdefault' => 'Säiten a Fichieren déi ech änneren op meng Iwwerwaachungslëscht derbäisetzen',
+'tog-watchmoves' => 'Säiten a Fichieren déi ech réckelen automatesch op meng Iwwerwaachungslëscht derbäisetzen',
+'tog-watchdeletion' => 'Säiten a Fichieren déi ech läschen op meng Iwwerwaachungslëscht derbäisetzen',
 'tog-minordefault' => "All Ännerungen automatesch als 'Kleng Ännerungen' markéieren.",
 'tog-previewontop' => "Déi ''nach-net gespäichert Versioun'' iwwer der Ännerungsfënster weisen",
 'tog-previewonfirst' => "Beim éischten Änneren déi  ''nach net gespäichert Versioun'' weisen.",
 'tog-nocache' => 'Säitecache vum Browser desaktivéieren',
-'tog-enotifwatchlistpages' => 'Schéckt mir eng E-Mail wann eng vun de Säiten op menger Iwwerwaachungslëscht geännert gëtt',
+'tog-enotifwatchlistpages' => 'Schéckt mir eng E-Mail wann eng Säit oder e Fichier op menger Iwwerwaachungslëscht geännert gëtt',
 'tog-enotifusertalkpages' => 'Schéckt mir E-Maile wa meng Diskussiounssäit geännert gëtt.',
-'tog-enotifminoredits' => 'Schéckt mir och bei klengen Ännerungen op vu mir iwwerwaachte Säiten eng E-Mail.',
+'tog-enotifminoredits' => 'Schéckt mir och bei klengen Ännerungen op vu mir iwwerwaachte Säiten oder Fichieren eng E-Mail.',
 'tog-enotifrevealaddr' => 'Meng E-Mailadress an de Benoriichtigungsmaile weisen.',
 'tog-shownumberswatching' => "D'Zuel vun de Benotzer déi dës Säit iwwerwaache weisen",
 'tog-oldsig' => 'Aktuell Ënnerschrëft:',
index 787e1be..82798f2 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Lezghian (Ð\9bезги)
+/** Lezghian (лезги)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index 5aa5b08..30fd62a 100644 (file)
@@ -192,10 +192,10 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Įjungti skyrelių redagavimą paspaudus skyrelio pavadinimą dešiniuoju pelės klavišu (JavaScript)',
 'tog-showtoc' => 'Rodyti turinį, jei puslapyje daugiau nei 3 skyreliai',
 'tog-rememberpassword' => 'Prisiminti prisijungimo informaciją šioje naršyklėje (daugiausiai $1 {{PLURAL:$1|dieną|dienas|dienų}})',
-'tog-watchcreations' => 'Pridėti puslapius, kuriuos sukuriu, į stebimų sąrašą',
-'tog-watchdefault' => 'Pridėti puslapius, kuriuos redaguoju, į stebimų sąrašą',
-'tog-watchmoves' => 'Pridėti puslapius, kuriuos perkeliu, į stebimų sąrašą',
-'tog-watchdeletion' => 'Pridėti puslapius, kuriuos ištrinu, į stebimų sąrašą',
+'tog-watchcreations' => 'Pridėti puslapius, kuriuos aš sukuriu, į stebimų sąrašą',
+'tog-watchdefault' => 'Pridėti puslapius, kuriuos aš redaguoju, į stebimų sąrašą',
+'tog-watchmoves' => 'Pridėti puslapius, kuriuos aš perkeliu, į stebimų sąrašą',
+'tog-watchdeletion' => 'Pridėti puslapius, kuriuos aš ištrinu, į stebimų sąrašą',
 'tog-minordefault' => 'Pagal nutylėjimą pažymėti redagavimus kaip smulkius',
 'tog-previewontop' => 'Rodyti peržiūrą virš redagavimo lauko',
 'tog-previewonfirst' => 'Rodyti peržiūrą pirmą kartą pakeitus',
@@ -366,7 +366,7 @@ $messages = array(
 'create-this-page' => 'Sukurti šį puslapį',
 'delete' => 'Trinti',
 'deletethispage' => 'Ištrinti šį puslapį',
-'undelete_short' => 'Atstatyti $1 {{PLURAL:$1:redagavimą|redagavimus|redagavimų}}',
+'undelete_short' => 'Atkurti $1 {{PLURAL:$1:redagavimą|redagavimus|redagavimų}}',
 'viewdeleted_short' => 'Peržiūrėti $1 {{PLURAL:$1|ištrintą keitimą|ištrintus keitimus|ištrintų keitimų}}',
 'protect' => 'Užrakinti',
 'protect_change' => 'keisti',
@@ -906,10 +906,10 @@ Jūs galite grįžti ir redaguoti jau esantį puslapį, arba [[Special:UserLogin
 'permissionserrorstext-withaction' => 'Jūs neturite leidimo $2 dėl {{PLURAL:$1|šios priežasties|šių priežasčių}}:',
 'recreate-moveddeleted-warn' => "'''Dėmesio: Jūs atkuriate puslapį, kuris anksčiau buvo ištrintas.'''
 
-Jūs turite nuspręsti, ar reikėtų toliau redaguoti šį puslapį.
-Dėl patogumo čia pateikta šio puslapio šalinimų ir perkėlimų istorija:",
+Turėtumėte nuspręsti, ar reikėtų toliau redaguoti šį puslapį.
+Jūsų patogumui čia pateikiamas šio puslapio šalinimų ir perkėlimų sąrašas:",
 'moveddeleted-notice' => 'Šis puslapis buvo ištrintas.
-Žemiau pateikta puslapio šalinimų ir pervadinimų istorija.',
+Žemiau pateikiamas puslapio šalinimų ir pervadinimų sąrašas.',
 'log-fulllog' => 'Rodyti visą istoriją',
 'edit-hook-aborted' => 'Keitimas nutrauktas užlūžimo.
 Tam nėra paaiškinimo.',
@@ -1026,8 +1026,8 @@ Kaip administratorius, jūs galite ją pamatyti; daugiau detalių gali būti [{{
 'revdelete-show-file-submit' => 'Taip',
 'revdelete-selected' => "'''{{PLURAL:$2|Pasirinkta [[:$1]] versija|Pasirinktos [[:$1]] versijos}}:'''",
 'logdelete-selected' => "'''{{PLURAL:$1|Pasirinktas istorijos įvykis|Pasirinkti istorijos įvykiai}}:'''",
-'revdelete-text' => "'''Ištrintos versijos bei įvykiai vistiek dar bus rodomi puslapio istorijoje ir specialiųjų veiksmų istorijoje, bet jų turinio dalys nebus viešai prieinamos.'''
-Kiti administratoriai iš {{SITENAME}} vistiek galės pasiekti paslėptą turinį ir galės jį atkurti vėl per tą pačią sąsają, nebent yra nustatyti papildomi apribojimai.",
+'revdelete-text' => "'''Ištrintos versijos bei įvykiai vis tiek dar bus rodomi puslapio istorijoje ir specialiųjų veiksmų sąraše, bet jų turinio dalys nebus viešai prieinamos.'''
+Kiti administratoriai iš {{SITENAME}} vis tiek galės pasiekti paslėptą turinį ir galės jį atkurti per tą pačią sąsają, nebent yra nustatyti papildomi apribojimai.",
 'revdelete-confirm' => 'Prašome patvirtinti, kad jūs tai ketinate padaryti, kad jūs suprantate padarinius, ir kad jūs tai darote pagal [[{{MediaWiki:Policy-url}}|politiką]].',
 'revdelete-suppress-text' => "Ištrynimas turėtų būti taikomas '''tik''' šiais atvejais:
 * Netinkama asmeninė informacija
@@ -1078,7 +1078,7 @@ Prašome patikrinti sąrašus.',
 'revdelete-offender' => 'Versijos autorius:',
 
 # Suppression log
-'suppressionlog' => 'Trynimo istorija',
+'suppressionlog' => 'Trynimo sąrašas',
 'suppressionlogtext' => 'Žemiau yra trynimų ir blokavimų sąrašas, įtraukiant turinį, paslėptą nuo administratorių.
 Žiūrėkite [[Special:BlockList|blokavimų sąrašą]], kad rastumėte dabar veikiančius draudimus ir blokavimus.',
 
@@ -1107,7 +1107,7 @@ Prašome patikrinti sąrašus.',
 'mergehistory-reason' => 'Priežastis:',
 
 # Merge log
-'mergelog' => 'Sujungimų istorija',
+'mergelog' => 'Sujungimų sąrašas',
 'pagemerge-logentry' => 'sujungė [[$1]] su [[$2]] (versijos iki $3)',
 'revertmerge' => 'Atskirti',
 'mergelogpagetext' => 'Žemiau yra paskiausių vieno su kitu puslapių sujungimų sąrašas.',
@@ -1227,7 +1227,7 @@ Prašome patikrinti sąrašus.',
 'prefs-rendering' => 'Išvaizda',
 'saveprefs' => 'Išsaugoti',
 'resetprefs' => 'Išvalyti neišsaugotus pakeitimus',
-'restoreprefs' => 'Atstatyti visus numatytuosius nustatymus',
+'restoreprefs' => 'Grąžinti visus numatytuosius nustatymus',
 'prefs-editing' => 'Redagavimas',
 'prefs-edit-boxsize' => 'Redagavimo lango dydis.',
 'rows' => 'Eilutės:',
@@ -1239,7 +1239,7 @@ Prašome patikrinti sąrašus.',
 'recentchangesdays' => 'Rodomos dienos paskutinių keitimų sąraše:',
 'recentchangesdays-max' => '(daugiausiai $1 {{PLURAL:$1|diena|dienos|dienų}})',
 'recentchangescount' => 'Numatytasis rodomas keitimų skaičius:',
-'prefs-help-recentchangescount' => 'Į tai įeina naujausi keitimai, puslapių istorijos ir specialiųjų veiksmų istorijos.',
+'prefs-help-recentchangescount' => 'Į tai įeina naujausi keitimai, puslapių istorijos ir specialiųjų veiksmų sąrašai.',
 'prefs-help-watchlist-token' => 'Įrašius slaptą raktą į šį laukelį, bus sugeneruotas RSS srautas su jūsų stebimųjų sąrašu.
 Bet kas, žinantis šio lauko raktą, galės matyti jūsų stebimų sąrašą, todėl pasirinkite saugią reikšmę.
 Galite panaudoti šią atsitiktinai sugeneruotą reikšmę: $1',
@@ -1270,7 +1270,7 @@ Galite panaudoti šią atsitiktinai sugeneruotą reikšmę: $1',
 'prefs-custom-css' => 'Asmeninis CSS',
 'prefs-custom-js' => 'Asmeninis JavaSript',
 'prefs-common-css-js' => 'Bendras CSS/JS visoms išvaizdoms:',
-'prefs-reset-intro' => 'Jūs galite pasinaudoti šiuo puslapiu atstatyti jūsų nustatymus į svetainės numatytuosius.
+'prefs-reset-intro' => 'Jūs galite pasinaudoti šiuo puslapiu, kad grąžintumėte savo nustatymus į svetainės numatytuosius.
 Tai nebeatšaukiama.',
 'prefs-emailconfirm-label' => 'El. pašto patvirtinimas:',
 'prefs-textboxsize' => 'Redagavimo lango dydis',
@@ -1391,12 +1391,12 @@ teisės",
 'right-writeapi' => 'Naudoti rašymo API',
 'right-delete' => 'Trinti puslapius',
 'right-bigdelete' => 'Ištrinti puslapius su ilga istorija',
-'right-deleterevision' => 'Ištrinti ir atstatyti specifines puslapių versijas',
-'right-deletedhistory' => 'Žiūrėti ištrintų puslapių istoriją nerodant susieto teksto',
+'right-deleterevision' => 'Ištrinti ir atkurti specifines puslapių versijas',
+'right-deletedhistory' => 'Žiūrėti ištrintų puslapių istoriją, nerodant susieto teksto',
 'right-deletedtext' => 'Peržiūrėti ištrintą tekstą ir skirtumus tarp ištrintų puslapio versijų.',
 'right-browsearchive' => 'Ieškoti ištrintų puslapių',
-'right-undelete' => 'Atstatyti puslapį',
-'right-suppressrevision' => 'Peržiūrėti ir atstatyti versijas, paslėptas nuo administratorių',
+'right-undelete' => 'Atkurti puslapį',
+'right-suppressrevision' => 'Peržiūrėti ir atkurti versijas, paslėptas nuo administratorių',
 'right-suppressionlog' => 'Žiūrėti privačius įvykių sąrašus',
 'right-block' => 'Blokuoti redagavimo galimybę kitiems naudotojams',
 'right-blockemail' => 'Blokuoti elektroninio pašto siuntimo galimybę naudotojui',
@@ -1428,7 +1428,7 @@ teisės",
 'right-passwordreset' => 'Peržiūrėti slaptažodžio pakeitimo e-mail laiškus',
 
 # User rights log
-'rightslog' => 'Naudotojų teisių istorija',
+'rightslog' => 'Naudotojų teisių pakeitimai',
 'rightslogtext' => 'Pateikiamas naudotojų teisių pakeitimų sąrašas.',
 'rightslogentry' => 'pakeista $1 grupės narystė iš $2 į $3',
 'rightslogentry-autopromote' => 'buvo automatiškai paaukštintas iš $2 į $3',
@@ -1455,7 +1455,7 @@ teisės",
 'action-deletedhistory' => 'žiūrėti šio ištrinto puslapio istoriją',
 'action-browsearchive' => 'ieškoti ištrintų puslapių',
 'action-undelete' => 'atkurti šį puslapį',
-'action-suppressrevision' => 'peržiūrėti ir atstatyti šią paslėptą reviziją',
+'action-suppressrevision' => 'peržiūrėti ir atkurti šią paslėptą versiją',
 'action-suppressionlog' => 'peržiūrėti šį privatų registrą',
 'action-block' => 'neleisti šiam naudotojui redaguoti',
 'action-protect' => 'pakeisti apsaugos lygius šiam puslapiui',
@@ -1531,8 +1531,8 @@ Puslapiai iš jūsų [[Special:Watchlist|stebimųjų sąrašo]] yra '''paryškin
 'upload-recreate-warning' => "'''Dėmėsio: Failas šiuo pavadinimu buvo ištrintas arba pervadintas.'''
 
 Jūsų patogumui pateiktas įrašas apie šio puslapio trynimą ar pervadinimą:",
-'uploadtext' => "Naudokitės žemiau pateikta forma failų įkėlimui.
-Norėdami peržiūrėti ar ieškoti anksčiau įkeltų paveikslėlių, eikite į [[Special:FileList|įkeltų failų sąrašą]], įkėlimai yra registruojami [[Special:Log/upload|įkėlimų istorijoje]], trynimai — [[Special:Log/delete|trynimų istorijoje]].
+'uploadtext' => "Kad įkeltumėte failą, naudokitės žemiau pateikta forma.
+Norėdami peržiūrėti ar ieškoti anksčiau įkeltų paveikslėlių, eikite į [[Special:FileList|įkeltų failų sąrašą]], įkėlimai yra registruojami [[Special:Log/upload|įkėlimų sąraše]], trynimai — [[Special:Log/delete|trynimų sąraše]].
 
 Norėdami panaudoti įkeltą failą puslapyje, naudokite tokias nuorodas:
 * '''<tt><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Failas.jpg]]</nowiki></tt>''' norėdami naudoti pilną failo versiją
@@ -1541,9 +1541,10 @@ Norėdami panaudoti įkeltą failą puslapyje, naudokite tokias nuorodas:
 'upload-permitted' => 'Leidžiami failų tipai: $1.',
 'upload-preferred' => 'Pageidautini failų tipai: $1.',
 'upload-prohibited' => 'Uždrausti failų tipai: $1.',
-'uploadlog' => 'įkėlimų istorija',
-'uploadlogpage' => 'Įkėlimų istorija',
-'uploadlogpagetext' => 'Žemiau pateikiamas paskutinių failų įkėlimų istorija.',
+'uploadlog' => 'įkėlimų sąrašas',
+'uploadlogpage' => 'Įkėlimų sąrašas',
+'uploadlogpagetext' => 'Žemiau pateikiamas paskutinių failų įkėlimų sąrašas.
+Taip pat galite peržvelgti [[Special:NewFiles|naujausių failų galeriją]].',
 'filename' => 'Failo vardas',
 'filedesc' => 'Aprašymas',
 'fileuploadsummary' => 'Komentaras:',
@@ -1737,8 +1738,8 @@ Dėl saugumo, img_auth.php yra išjungtas.',
 'upload_source_file' => ' (failas jūsų kompiuteryje)',
 
 # Special:ListFiles
-'listfiles-summary' => 'This special page shows all uploaded files.
-When filtered by user, only files where that user uploaded the most recent version of the file are shown.',
+'listfiles-summary' => 'Šiame specialiame puslapyje rodomi visi įkelti failai.
+Kai sąrašas susiaurinamas pagal naudotoją, rodomi tik tie failai, kurių naujausią versiją jis yra įkėlęs.',
 'listfiles_search_for' => 'Ieškoti failo pavadinimo:',
 'imgfile' => 'failas',
 'listfiles' => 'Failų sąrašas',
@@ -1968,12 +1969,13 @@ Primename, kad kitos svetainės gali turėti tiesioginę nuorodą į failą, bet
 # Special:Log
 'specialloguserlabel' => 'Naudotojas:',
 'speciallogtitlelabel' => 'Pavadinimas:',
-'log' => 'Specialiųjų veiksmų istorija',
-'all-logs-page' => 'Visos viešosios istorijos',
-'alllogstext' => 'Bendras visų galimų „{{SITENAME}}“ specialiųjų veiksmų istorijų rodinys.
-Galima sumažinti rezultatų skaičių patikslinant veiksmo rūšį, naudotoją ar susijusį puslapį.',
-'logempty' => 'Istorijoje nėra jokių atitinkančių įvykių.',
+'log' => 'Specialiųjų veiksmų sąrašas',
+'all-logs-page' => 'Visi viešieji sąrašai',
+'alllogstext' => 'Bendrai pateikiamas visų galimų „{{SITENAME}}“ specialiųjų veiksmų sąrašas.
+Galima sumažinti rezultatų skaičių, patikslinant veiksmo rūšį, naudotoją ar susijusį puslapį.',
+'logempty' => 'Sąraše nėra jokių atitinkančių įvykių.',
 'log-title-wildcard' => 'Ieškoti pavadinimų, prasidedančių šiuo tekstu',
+'showhideselectedlogentries' => 'Rodyti/slėpti pasirinktus sąrašo elementus',
 
 # Special:AllPages
 'allpages' => 'Visi puslapiai',
@@ -2120,7 +2122,7 @@ taip pat bus '''paryškinti''' [[Special:RecentChanges|naujausių keitimų sąra
 'watchlist-details' => 'Stebima {{PLURAL:$1|$1 puslapis|$1 puslapiai|$1 puslapių}} neskaičiuojant aptarimų puslapių.',
 'wlheader-enotif' => '* El. pašto priminimai yra įjungti.',
 'wlheader-showupdated' => "* Puslapiai pakeisti nuo tada, kai paskutinį kartą apsilankėte juose, yra pažymėti '''pastorintai'''",
-'watchmethod-recent' => 'tikrinami paskutiniai keitimai stebimiems puslapiams',
+'watchmethod-recent' => 'tikrinami naujausi stebimųjų puslapių pakeitimai',
 'watchmethod-list' => 'ieškoma naujausių keitimų stebimuose puslapiuose',
 'watchlistcontains' => 'Jūsų stebimųjų sąraše yra $1 {{PLURAL:$1|puslapis|puslapiai|puslapių}}.',
 'iteminvalidname' => 'Problema su elementu „$1“, neteisingas vardas...',
@@ -2185,14 +2187,14 @@ Atsiliepimai ir pagalba:
 'delete-legend' => 'Trynimas',
 'historywarning' => "'''Dėmesio:''' Trinamas puslapis turi istoriją su maždaug $1 {{PLURAL:$1|versija|versijomis|versijų}}:",
 'confirmdeletetext' => 'Jūs pasirinkote ištrinti puslapį ar paveikslėlį kartu su visa jo istorija.
-Prašome patvirtinti, kad jūs tikrai norite tai padaryti, žinote apie galimus padarinius, ir kad jūs tai darote atsižvelgdami į [[{{MediaWiki:Policy-url}}|politiką]].',
+Prašome patvirtinti, kad jūs tikrai norite tai padaryti, žinote apie galimus padarinius ir kad tai darote pagal [[{{MediaWiki:Policy-url}}|taisykles]].',
 'actioncomplete' => 'Veiksmas atliktas',
 'actionfailed' => 'Veiksmas atšauktas',
 'deletedtext' => '„$1“ ištrintas.
 Paskutinių šalinimų istorija - $2.',
-'dellogpage' => 'Šalinimų istorija',
+'dellogpage' => 'Šalinimų sąrašas',
 'dellogpagetext' => 'Žemiau pateikiamas paskutinių trynimų sąrašas.',
-'deletionlog' => 'šalinimų istorija',
+'deletionlog' => 'šalinimų sąrašas',
 'reverted' => 'Atkurta į ankstesnę versiją',
 'deletecomment' => 'Priežastis:',
 'deleteotherreason' => 'Kita/papildoma priežastis:',
@@ -2227,8 +2229,8 @@ grąžinta prieš tai buvusi $2 versija.',
 Prašome paspausti „atgal“ ir perkraukite puslapį iš kurio atėjote, ir pamėginkite vėl.',
 
 # Protect
-'protectlogpage' => 'Rakinimų istorija',
-'protectlogtext' => 'Žemiau yra puslapių užrakinimų bei atrakinimų istorija.
+'protectlogpage' => 'Rakinimų sąrašas',
+'protectlogtext' => 'Žemiau yra puslapių užrakinimų bei atrakinimų sąrašas.
 Dabar veikiančių puslapių apsaugų sąrašą rasite [[Special:ProtectedPages|apsaugotų puslapių sąraše]].',
 'protectedarticle' => 'užrakino „[[$1]]“',
 'modifiedarticleprotection' => 'pakeistas „[[$1]]“ apsaugos lygis',
@@ -2270,9 +2272,10 @@ Dabar veikiančių puslapių apsaugų sąrašą rasite [[Special:ProtectedPages|
 'protect-otherreason-op' => 'Kita priežastis',
 'protect-dropdown' => '*Įprastos užrakinimo priežastys
 ** Intensyvus vandalizmas
-** Intensyvus nuorodų reklamavimas
-** Neproduktyvus redagavimo karas
-** Didelės svarbos puslapis',
+** Nuolatinis nepageidautinų nuorodų dėliojimas
+** Beprasmis redagavimo karas
+** Didelės svarbos puslapis
+** Pakartotinis ištrinto puslapio atkūrinėjimas',
 'protect-edit-reasonlist' => 'Keisti užrakinimo priežastis',
 'protect-expiry-options' => '1 valanda:1 hour,1 diena:1 day,1 savaitė:1 week,2 savaitės:2 weeks,1 mėnuo:1 month,3 mėnesiai:3 months,6 mėnesiai:6 months,1 metai:1 year,neribotai:infinite',
 'restriction-type' => 'Leidimas:',
@@ -2293,15 +2296,15 @@ Dabar veikiančių puslapių apsaugų sąrašą rasite [[Special:ProtectedPages|
 'restriction-level-all' => 'bet koks',
 
 # Undelete
-'undelete' => 'Atstatyti ištrintą puslapį',
+'undelete' => 'Atkurti ištrintą puslapį',
 'undeletepage' => 'Rodyti ir atkurti ištrintus puslapius',
 'undeletepagetitle' => "'''Tai sudaryta iš ištrintų [[:$1]] versijų'''.",
 'viewdeletedpage' => 'Rodyti ištrintus puslapius',
 'undeletepagetext' => '{{PLURAL:$1|Šis $1 puslapis buvo ištrintas|Šie $1 puslapiai buvo ištrinti|Šie $1 puslapių buvo ištrinti}}, bet dar yra archyve ir gali būti {{PLURAL:$1|atkurtas|atkurti|atkurti}}.
 Archyvas gali būti periodiškai valomas.',
-'undelete-fieldset-title' => 'Atstatyti versijas',
+'undelete-fieldset-title' => 'Atkurti versijas',
 'undeleteextrahelp' => "Norėdami atkurti visą puslapio istoriją, palikite visas varneles nepažymėtas ir spauskite '''''{{int:undeletebtn}}'''''.
-Norėdami atlikti pasirinktinį atstatymą, pažymėkite varneles tų versijų, kurias norėtumėte atstatyti, ir spauskite '''''{{int:undeletebtn}}'''''.",
+Norėdami atlikti pasirinktinį atkūrimą, pažymėkite varneles tų versijų, kurias norėtumėte atkurti, ir spauskite '''''{{int:undeletebtn}}'''''.",
 'undeleterevisions' => '$1 {{PLURAL:$1|versija|versijos|versijų}} suarchyvuota',
 'undeletehistory' => 'Jei atstatysite puslapį, istorijoje bus atstatytos visos versijos.
 Jei po ištrynimo buvo sukurtas puslapis tokiu pačiu pavadinimu, atstatytos versijos atsiras ankstesnėje istorijoje.',
@@ -2312,7 +2315,7 @@ Tokiais atvejais, jums reikia atžymėti arba atslėpti naujausią ištrintą ve
 'undeleterevision-missing' => 'Neteisinga arba dingusi versija. Jūs turbūt turite blogą nuorodą, arba versija buvo atkurta arba pašalinta iš archyvo.',
 'undelete-nodiff' => 'Nerasta jokių ankstesnių versijų.',
 'undeletebtn' => 'Atkurti',
-'undeletelink' => 'žiūrėti/atstatyti',
+'undeletelink' => 'žiūrėti/atkurti',
 'undeleteviewlink' => 'žiūrėti',
 'undeletereset' => 'Iš naujo',
 'undeleteinvert' => 'Žymėti priešingai',
@@ -2324,7 +2327,7 @@ Tokiais atvejais, jums reikia atžymėti arba atslėpti naujausią ištrintą ve
 'undeletedpage' => "'''$1 buvo atkurtas'''
 
 Peržiūrėkite [[Special:Log/delete|trynimų sąrašą]], norėdami rasti paskutinių trynimų ir atkūrimų sąrašą.",
-'undelete-header' => 'Žiūrėkite [[Special:Log/delete|trynimo istorijoje]] paskiausiai ištrintų puslapių.',
+'undelete-header' => 'Kad sužinotumėte, kurie puslapiai paskiausiai ištrinti, žiūrėkite [[Special:Log/delete|šalinimų sąrašą]].',
 'undelete-search-title' => 'Panaikintų puslapių paieška',
 'undelete-search-box' => 'Ieškoti ištrintų puslapių',
 'undelete-search-prefix' => 'Rodyti puslapius pradedant su:',
@@ -2345,9 +2348,9 @@ $1',
 # Namespace form on various pages
 'namespace' => 'Vardų sritis:',
 'invert' => 'Žymėti priešingai',
-'tooltip-invert' => 'Įjunkite šią parinktį jei norite paslėpti nurodytos vardų srities (ir susijusių, jei įjungta parinktis) puslapių pakeitimus',
-'namespace_association' => 'Susiję vardų',
-'tooltip-namespace_association' => 'Įjunkite šią parinktį, kad taipogi įtrauktumėte pokalbių arba temos sritį, susieta su pasirinkta sritimi',
+'tooltip-invert' => 'Įjunkite šią parinktį, jei norite paslėpti nurodytos vardų srities (ir susijusių, jei įjungta parinktis) puslapių pakeitimus',
+'namespace_association' => 'Susijusi vardų sritis',
+'tooltip-namespace_association' => 'Įjunkite šią parinktį, kad taip pat įtrauktumėte aptarimų arba temos sritį, susijusią su pasirinkta sritimi',
 'blanknamespace' => '(Pagrindinė)',
 
 # Contributions
@@ -2360,13 +2363,13 @@ $1',
 'month' => 'Nuo mėnesio (ir anksčiau):',
 'year' => 'Nuo metų (ir anksčiau):',
 
-'sp-contributions-newbies' => 'Rodyti tik naujų paskyrų įnašus',
-'sp-contributions-newbies-sub' => 'Naujoms paskyroms',
-'sp-contributions-newbies-title' => 'Naudotojų keitimai naujoms paskyroms',
-'sp-contributions-blocklog' => 'Blokavimų istorija',
+'sp-contributions-newbies' => 'Rodyti tik naujų paskyrų keitimus',
+'sp-contributions-newbies-sub' => 'Neseniai prisiregistravusieji',
+'sp-contributions-newbies-title' => 'Naujai užsiregistravusių naudotojų indėlis',
+'sp-contributions-blocklog' => 'Blokavimų sąrašas',
 'sp-contributions-deleted' => 'ištrintas naudotojo indėlis',
 'sp-contributions-uploads' => 'nuotraukos',
-'sp-contributions-logs' => 'Specialiųjų veiksmų istorija',
+'sp-contributions-logs' => 'Specialiųjų veiksmų sąrašas',
 'sp-contributions-talk' => 'Aptarimas',
 'sp-contributions-userrights' => 'naudotojų teisių valdymas',
 'sp-contributions-blocked-notice' => 'Šis naudotojas šiuo metu užblokuotas.
@@ -2398,30 +2401,30 @@ Paskutinis blokavimo įrašas pateikiamas žemiau:',
 'whatlinkshere-filters' => 'Filtrai',
 
 # Block/unblock
-'autoblockid' => 'Autoblock # $1',
+'autoblockid' => 'Automatinis blokavimas # $1',
 'block' => 'Blokuoti naudotoją',
 'unblock' => 'Atblokuoti naudotoją',
 'blockip' => 'Blokuoti naudotoją',
 'blockip-title' => 'Blokuoti naudotoją',
 'blockip-legend' => 'Blokuoti naudotoją',
-'blockiptext' => 'Naudokite šią formą norėdami uždrausti rašymo teises nurodytui IP adresui ar naudotojui. Tai turėtų būti atliekama tam, kad sustabdytumėte vandalizmą, ir pagal [[{{MediaWiki:Policy-url}}|politiką]].
-Žemiau nurodykite tikslią priežastį (pavyzdžiui, nurodydami sugadintus puslapius).',
+'blockiptext' => 'Naudokite šią formą, kad uždraustumėte redagavimo prieigą pasirinktam IP adresui ar naudotojui. Tai turėtų būti atliekama tik tam, kad sustabdytumėte vandalizmą, ir neprieštarauti [[{{MediaWiki:Policy-url}}|projekte galiojančioms taisyklėms]].
+Žemiau pateikite tikslią priežastį (pavyzdžiui, nurodydami sugadintus puslapius).',
 'ipadressorusername' => 'IP adresas arba naudotojo vardas',
 'ipbexpiry' => 'Galiojimo laikas',
 'ipbreason' => 'Priežastis:',
 'ipbreasonotherlist' => 'Kita priežastis',
 'ipbreason-dropdown' => '*Bendrosios blokavimo priežastys
-** Melagingos informacijos įterpimas
+** Klaidingos informacijos įterpimas
 ** Turinio šalinimas iš puslapių
 ** Kitų svetainių reklamavimas
-** Nesąmonių/bet ko įterpimas į puslapius
-** GÄ\85sdinimai/Ä®žeidinėjimai
+** Nesusijusio arba beprasmio teksto įterpimas į puslapius
+** GÄ\85sdinimai/įžeidinėjimai
 ** Piktnaudžiavimas keliomis paskyromis
 ** Nepriimtinas naudotojo vardas',
-'ipb-hardblock' => 'Neleisti prisijungusius naudotojus nuo redagavimo iš šio IP adreso',
+'ipb-hardblock' => 'Neleisti prisijungusiems naudotojams redaguoti iš šio IP adreso',
 'ipbcreateaccount' => 'Neleisti kurti paskyrų',
 'ipbemailban' => 'Neleisti naudotojui siųsti el. pašto',
-'ipbenableautoblock' => 'Automatiškai blokuoti šio naudotojo paskiausiai naudotą IP adresą, bei bet kokius vėlesnius IP adresus, iš kurių jie mėgina redaguoti',
+'ipbenableautoblock' => 'Automatiškai blokuoti paskutinį naudotojo naudotą IP adresą ir visus kitus adresus, iš kurių jis bandys redaguoti',
 'ipbsubmit' => 'Blokuoti šį naudotoją',
 'ipbother' => 'Kitoks laikas',
 'ipboptions' => '2 valandos:2 hours,1 diena:1 day,3 dienos:3 days,1 savaitė:1 week,2 savaitės:2 weeks,1 mėnesis:1 month,3 mėnesiai:3 months,6 mėnesiai:6 months,1 metai:1 year,neribotai:infinite',
@@ -2429,22 +2432,22 @@ Paskutinis blokavimo įrašas pateikiamas žemiau:',
 'ipbotherreason' => 'Kita/papildoma priežastis',
 'ipbhidename' => 'Slėpti naudotojo vardą keitimuose bei sąrašuose',
 'ipbwatchuser' => 'Stebėti šio naudotojo puslapį ir jo aptarimų puslapį',
-'ipb-disableusertalk' => 'Neleiskite šiam nuo redagavimo savo aptarimų puslapis naudotojui užblokuotas',
-'ipb-change-block' => 'Perblokuoti šį naudotoją su šiais nustatymais',
-'ipb-confirm' => 'Patvirtinkite blokas',
+'ipb-disableusertalk' => 'Neleisti redaguoti savo naudotojo aptarimo puslapio',
+'ipb-change-block' => 'Iš naujo užblokuoti naudotoją, naudojant šiuos nustatymus',
+'ipb-confirm' => 'Patvirtinkite blokavimą',
 'badipaddress' => 'Neleistinas IP adresas',
 'blockipsuccesssub' => 'Užblokavimas pavyko',
 'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] buvo užblokuotas.<br />
 Aplankykite [[Special:BlockList|IP blokavimų istoriją]] norėdami jį peržiūrėti.',
-'ipb-blockingself' => 'Jūs ruošiatės blokuoti sau! Ar tikrai norite tai padaryti?',
-'ipb-confirmhideuser' => 'Jūs ruošiatės užblokuoti tam tikro vartotojo su "slėpti vartotojo" įjungtas. Tai bus nuslopinti vartotojo vardą visuose sąrašuose ir žurnalo įrašus. Ar tikrai norite tai padaryti?',
+'ipb-blockingself' => 'Jūs ruošiatės užblokuoti save! Ar tikrai norite tai padaryti?',
+'ipb-confirmhideuser' => 'Jūs ruošiatės užblokuoti naudotoją, pasirinkę „slėpti naudotoją“ nustatymą. Tai paslėps naudotojo vardą visuose sąrašuose ir žurnalo įrašuose. Ar tikrai norite tai padaryti?',
 'ipb-edit-dropdown' => 'Redaguoti blokavimų priežastis',
 'ipb-unblock-addr' => 'Atblokuoti $1',
 'ipb-unblock' => 'Atblokuoti naudotojo vardą arba IP adresą',
-'ipb-blocklist' => 'Rodyti egzistuončius blokavimus',
+'ipb-blocklist' => 'Rodyti egzistuojančius blokavimus',
 'ipb-blocklist-contribs' => '$1 indėlis',
 'unblockip' => 'Atblokuoti naudotoją',
-'unblockiptext' => 'Naudokite šią formą, kad atkurtumėte rašymo teises
+'unblockiptext' => 'Naudokite šią formą, kad atkurtumėte redagavimo galimybę
 ankščiau užblokuotam IP adresui ar naudotojui.',
 'ipusubmit' => 'Atblokuoti šį adresą',
 'unblocked' => '[[User:$1|$1]] buvo atblokuotas',
@@ -2453,15 +2456,15 @@ ankščiau užblokuotam IP adresui ar naudotojui.',
 'blocklist' => 'Blokuoti naudotojai',
 'ipblocklist' => 'Blokuoti naudotojai',
 'ipblocklist-legend' => 'Rasti užblokuotą naudotoją',
-'blocklist-userblocks' => 'Slėpti į blokus',
+'blocklist-userblocks' => 'Slėpti paskyrų blokavimus',
 'blocklist-tempblocks' => 'Slėpti laikinus blokavimus',
-'blocklist-addressblocks' => 'Slėpti vieną IP blokų',
-'blocklist-rangeblocks' => 'Slėpti diapazono blokai',
-'blocklist-timestamp' => 'Laiko žymė',
-'blocklist-target' => 'Tikslinė',
+'blocklist-addressblocks' => 'Slėpti vieno IP adreso blokavimus',
+'blocklist-rangeblocks' => 'Slėpti IP adresų sričių blokavimus',
+'blocklist-timestamp' => 'Laiko žyma',
+'blocklist-target' => 'Užblokuotasis',
 'blocklist-expiry' => 'Galioja iki',
-'blocklist-by' => 'Blokavimo admin',
-'blocklist-params' => 'Blokuoti duomenys',
+'blocklist-by' => 'Užblokavęs administratorius',
+'blocklist-params' => 'Blokavimo nustatymai',
 'blocklist-reason' => 'Priežastis',
 'ipblocklist-submit' => 'Ieškoti',
 'ipblocklist-localblock' => 'Vietinis blokavimas',
@@ -2474,14 +2477,14 @@ ankščiau užblokuotam IP adresui ar naudotojui.',
 'emailblock' => 'el. paštas užblokuotas',
 'blocklist-nousertalk' => 'negali redaguoti savo aptarimų puslapio',
 'ipblocklist-empty' => 'Blokavimų sąrašas tuščias.',
-'ipblocklist-no-results' => 'Prašomas IP adresas ar naudotojo vardas nėra užblokuotas.',
+'ipblocklist-no-results' => 'Pasirinktas IP adresas ar naudotojo vardas nėra užblokuotas.',
 'blocklink' => 'blokuoti',
 'unblocklink' => 'atblokuoti',
 'change-blocklink' => 'keisti blokavimo nustatymus',
 'contribslink' => 'įnašas',
-'emaillink' => 'pasiųsti e-mail laišką',
+'emaillink' => 'siųsti el. laišką',
 'autoblocker' => 'Jūs buvote automatiškai užblokuotas, nes jūsų IP adresą neseniai naudojo „[[User:$1|$1]]“. Nurodyta naudotojo $1 blokavimo priežastis: „$2“.',
-'blocklogpage' => 'Blokavimų istorija',
+'blocklogpage' => 'Blokavimų sąrašas',
 'blocklog-showlog' => 'Šis naudotojas buvo užblokuotas.
 Pateikiamas paskutinis blokavimo istorijos įrašas.',
 'blocklog-showsuppresslog' => 'Šis naudotojas buvo užblokuotas ir paslėptas anksčiau.
@@ -2499,26 +2502,26 @@ Jei norite pamatyti dabar blokuojamus adresus, žiūrėkite [[Special:BlockList|
 'block-log-flags-nousertalk' => 'negali redaguoti savo naudotojo aptarimo puslapio',
 'block-log-flags-angry-autoblock' => 'išplėstasis automatinis blokavimas įjungtas',
 'block-log-flags-hiddenname' => 'naudotojo vardas paslėptas',
-'range_block_disabled' => 'Administratoriaus galimybė kurti intevalinius blokus yra išjungta.',
+'range_block_disabled' => 'Administratoriams neleidžiama blokuoti IP adresų sričių.',
 'ipb_expiry_invalid' => 'Galiojimo laikas neleistinas.',
-'ipb_expiry_temp' => 'Paslėptų naudotojų vardų blokavimas turi būti nuolatinis.',
+'ipb_expiry_temp' => 'Paslėptų naudotojų vardų blokavimas turi būti neribotas.',
 'ipb_hide_invalid' => 'Negalima paslėpti šios paskyros; ji gali turėti per daug keitimų.',
 'ipb_already_blocked' => '„$1“ jau užblokuotas',
 'ipb-needreblock' => '$1 jau yra užblokuotas. Ar norite pakeisti nustatymus?',
 'ipb-otherblocks-header' => '{{PLURAL:$1|Kitas blokavimas|Kiti blokavimai}}',
-'unblock-hideuser' => 'Jūs negalite atblokuoti vartotoją, nes jų vardas buvo paslėpta.',
+'unblock-hideuser' => 'Jūs negalite atblokuoti šio naudotojo, nes jo vardas buvo paslėptas.',
 'ipb_cant_unblock' => 'Klaida: Blokavimo ID $1 nerastas. Galbūt jis jau atblokuotas.',
 'ipb_blocked_as_range' => 'Klaida: IP $1 nebuvo užblokuotas tiesiogiai, tad negali būti atblokuotas. Tačiau jis buvo užblokuotas kaip srities $2 dalis, kuri gali būti atblokuota.',
 'ip_range_invalid' => 'Neleistina IP sritis.',
 'ip_range_toolarge' => 'Didesni nei /$1 blokai neleidžiami.',
 'blockme' => 'Užblokuoti mane',
-'proxyblocker' => 'Tarpinių serverių blokuotojas',
+'proxyblocker' => 'Tarpinių serverių („proxy“) blokavimo priemonė',
 'proxyblocker-disabled' => 'Ši funkcija yra išjungta.',
 'proxyblockreason' => 'Jūsų IP adresas yra užblokuotas, nes jis yra atvirasis tarpinis serveris. Prašome susisiekti su savo interneto paslaugų tiekėju ar technine pagalba ir praneškite jiems apie šią svarbią saugumo problemą.',
 'proxyblocksuccess' => 'Atlikta.',
 'sorbsreason' => 'Jūsų IP adresas yra įtrauktas į atvirųjų tarpinių serverių DNSBL sąrašą, naudojamą šios svetainės.',
 'sorbs_create_account_reason' => 'Jūsų IP adresas yra įtrauktas į atvirųjų tarpinių serverių DNSBL sąrašą, naudojamą šios svetainės. Jūs negalite sukurti paskyros',
-'cant-block-while-blocked' => 'Jūs negalite blokuoti kitų naudotojų kuomet pats esate užblokuotas.',
+'cant-block-while-blocked' => 'Jūs negalite blokuoti kitų naudotojų, pats būdamas užblokuotas.',
 'cant-see-hidden-user' => 'Naudotojas, kurį bandote užblokuoti, jau yra užblokuotas arba paslėptas.
 Kadangi jūs neturi hideuser teisės, jūs negalite pamatyti arba pakeisti naudotojo blokavimo.',
 'ipbblocked' => 'Jūs negalite blokuoti ar atblokuoti kitų naudotojų, nes pats esate užblokuotas',
@@ -2589,7 +2592,7 @@ dėl to šį veiksmą vykdykite tik įsitikinę, kad suprantate visas pasekmes."
 
 Šiais atvejais jūs savo nuožiūra turite perkelti arba apjungti aptarimo puslapį.",
 'movearticle' => 'Pervardinti puslapį:',
-'moveuserpage-warning' => "'''Dėmesio:''' Jūs ruošiatės perkelti naudotojo puslapį. Atkreipkite dėmesį, kad bus perkeltas tik puslapis, naudotojas ''nebus'' pervadintas.",
+'moveuserpage-warning' => "'''Dėmesio:''' Jūs ruošiatės perkelti naudotojo puslapį. Atkreipkite dėmesį, kad bus perkeltas tik puslapis, naudotojas ''nebus'' pervadintas.",
 'movenologin' => 'Neprisijungęs',
 'movenologintext' => 'Norėdami pervadinti puslapį, turite būti užsiregistravęs naudotojas ir būti  [[Special:UserLogin|prisijungęs]].',
 'movenotallowed' => 'Jūs neturite teisių pervadinti puslapių.',
@@ -2618,7 +2621,7 @@ Prašome sujungti šiuos puslapius.'''",
 'movepage-page-moved' => 'Puslapis $1 perkeltas į $2.',
 'movepage-page-unmoved' => 'Puslapio $1 negalima perkelti į $2.',
 'movepage-max-pages' => 'Daugiausiai $1 {{PLURAL:$1|puslapis buvo perkeltas|puslapiai buvo perkelti|puslapių buvo perkelta}} ir daugiau nebus perkelta automatiškai.',
-'movelogpage' => 'Pervardinimų istorija',
+'movelogpage' => 'Perkėlimų sąrašas',
 'movelogpagetext' => 'Pervardintų puslapių sąrašas.',
 'movesubpage' => '{{PLURAL:$1|Subpuslapis|Subpuslapiai}}',
 'movesubpagetext' => 'Žemiau yra šio puslapio $1 {{PLURAL:$1|subpuslapis|subpuslapiai|subpuslapių}}.',
@@ -2750,7 +2753,7 @@ Išsaugokite jį savo kompiuteryje ir įkelkite jį čia.',
 'import-error-invalid' => 'Puslapis "$1" nebuvo įkeltas, kadangi jo vardas yra neteisingas.',
 
 # Import log
-'importlogpage' => 'Importo istorija',
+'importlogpage' => 'Importavimų sąrašas',
 'importlogpagetext' => 'Administraciniai puslapių importai su keitimų istorija iš kitų wiki projektų.',
 'import-logentry-upload' => 'importuota $1 įkeliant failą',
 'import-logentry-upload-detail' => '$1 {{PLURAL:$1|keitimas|keitimai|keitimų}}',
@@ -2905,7 +2908,7 @@ Leidžia pridėti atmetimo priežastį komentaruose',
 'markedaspatrollederror-noautopatrol' => 'Jums neleidžiama pažymėti savo paties keitimų kaip patikrintų.',
 
 # Patrol log
-'patrol-log-page' => 'Patikrinimo istorija',
+'patrol-log-page' => 'Patikrinimų sąrašas',
 'patrol-log-header' => 'Tai patvirtintų versijų sąrašas.',
 'log-show-hide-patrol' => '$1 patvirtinimų sąrašą',
 
@@ -3687,8 +3690,8 @@ Paveikslėliai yra rodomi pilna raiška, kiti failų tipai paleidžiami tiesiogi
 'sqlite-no-fts' => '$1 be visatekstės paieškos palaikymo',
 
 # New logging system
-'logentry-delete-delete' => '$1 panaikino puslapį $3',
-'logentry-delete-restore' => '$1 atstatė puslapį $3',
+'logentry-delete-delete' => '$1 ištrynė puslapį $3',
+'logentry-delete-restore' => '$1 atkūrė puslapį $3',
 'revdelete-content-hid' => 'turinys paslėptas',
 'revdelete-summary-hid' => 'paslėptas keitimo komentaras',
 'revdelete-uname-hid' => 'paslėptas naudotojo vardas',
index e25ebbd..a8ceae4 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Moksha (Ð\9cокшень)
+/** Moksha (мокшень)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index 42ca8c7..43047f5 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Eastern Mari (Ð\9eлÑ\8bк Ð\9cарий)
+/** Eastern Mari (олÑ\8bк Ð¼арий)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index fd1f344..97f612f 100644 (file)
@@ -342,17 +342,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Овозможи уредување на заглавија со десен клик на нивниот наслов (JavaScript)',
 'tog-showtoc' => 'Покажи содржина (за страници со повеќе од 3 заглавија)',
 'tog-rememberpassword' => 'Запомни ме на овој прелистувач (највеќе до $1 {{PLURAL:$1|ден|дена}})',
-'tog-watchcreations' => 'Додавај ги страниците што ги создавам во списокот на набљудувања',
-'tog-watchdefault' => 'Додавај ги страниците што ги уредувам во списокот на набљудувања',
-'tog-watchmoves' => 'Додавај ги страниците што ги преместувам во списокот на набљудувања',
-'tog-watchdeletion' => 'Додавај ги страниците што ги бришам во списокот на набљудувања',
+'tog-watchcreations' => 'Ð\94одаваÑ\98 Ð³Ð¸ Ñ\81Ñ\82Ñ\80аниÑ\86иÑ\82е Ñ\88Ñ\82о Ð³Ð¸ Ñ\81оздавам Ð¸ Ð¿Ð¾Ð´Ð°Ñ\82оÑ\82екиÑ\82е Ñ\88Ñ\82о Ð³Ð¸ Ð¿Ð¾Ð´Ð¸Ð³Ð°Ð¼ Ð²Ð¾ Ñ\81пиÑ\81окоÑ\82 Ð½Ð° Ð½Ð°Ð±Ñ\99Ñ\83дÑ\83ваÑ\9aа',
+'tog-watchdefault' => 'Додавај ги страниците и податотеките што ги уредувам во списокот на набљудувања',
+'tog-watchmoves' => 'Додавај ги страниците и податотеките што ги преместувам во списокот на набљудувања',
+'tog-watchdeletion' => 'Додавај ги страниците и податотеките што ги бришам во списокот на набљудувања',
 'tog-minordefault' => 'Обележувај ги сите уредувања како ситни по основно',
 'tog-previewontop' => 'Прикажи го прегледот пред кутијата за уредување',
 'tog-previewonfirst' => 'Прикажи преглед на првото уредување',
 'tog-nocache' => 'Оневозможи кеширање на страниците во прелистувачот',
-'tog-enotifwatchlistpages' => 'Испраќај ми е-пошта при промена на страница од мојот список на набљудувања',
+'tog-enotifwatchlistpages' => 'Ð\98Ñ\81пÑ\80аÑ\9cаÑ\98 Ð¼Ð¸ Ðµ-поÑ\88Ñ\82а Ð¿Ñ\80и Ð¿Ñ\80омена Ð½Ð° Ñ\81Ñ\82Ñ\80аниÑ\86а Ð¸Ð»Ð¸ Ð¿Ð¾Ð´Ð°Ñ\82оÑ\82ека Ð¾Ð´ Ð¼Ð¾Ñ\98оÑ\82 Ñ\81пиÑ\81ок Ð½Ð° Ð½Ð°Ð±Ñ\99Ñ\83дÑ\83ваÑ\9aа',
 'tog-enotifusertalkpages' => 'Испраќај ми е-пошта при промена на мојата страница за разговор',
-'tog-enotifminoredits' => 'Испраќај ми е-пошта и за ситни промени во страниците',
+'tog-enotifminoredits' => 'Испраќај ми е-пошта и за ситни промени во страниците и податотеките',
 'tog-enotifrevealaddr' => 'Откриј ја мојата е-поштенска адреса во пораките за известување',
 'tog-shownumberswatching' => 'Прикажи го бројот на корисници кои набљудуваат',
 'tog-oldsig' => 'Постоечки потпис:',
index 15b8cbe..eb824ff 100644 (file)
@@ -342,15 +342,15 @@ $messages = array(
 'tog-editsectiononrightclick' => 'ഉപവിഭാഗങ്ങളുടെ തലക്കെട്ടിൽ റൈറ്റ് ക്ലിക്ക് ചെയ്യുന്നതു വഴി തിരുത്താനനുവദിക്കുക (ജാവാസ്ക്രിപ്റ്റ്)',
 'tog-showtoc' => 'ഉള്ളടക്കപ്പട്ടിക പ്രദർശിപ്പിക്കുക (മൂന്നിൽ കൂടുതൽ ഉപശീർഷകങ്ങളുള്ള താളുകൾക്കു മാത്രം)',
 'tog-rememberpassword' => 'എന്റെ പ്രവേശനം ഈ ബ്രൗസറിൽ ({{PLURAL:$1|ഒരു ദിവസം|$1 ദിവസം}}) ഓർത്തുവെക്കുക',
-'tog-watchcreations' => 'à´\9eാൻ à´¸àµ\83à´·àµ\8dà´\9fà´¿à´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨ à´¤à´¾à´³àµ\81à´\95ൾ ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടികയിൽ ചേർക്കുക',
-'tog-watchdefault' => 'à´\9eാൻ à´¤à´¿à´°àµ\81à´¤àµ\8dà´¤àµ\81à´¨àµ\8dà´¨ à´¤à´¾à´³àµ\81à´\95ൾ ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടികയിൽ ചേർക്കുക',
-'tog-watchmoves' => 'à´\9eാൻ à´¤à´²à´\95àµ\8dà´\95àµ\86à´\9fàµ\8dà´\9fàµ\81 à´®à´¾à´±àµ\8dà´±àµ\81à´¨àµ\8dà´¨ à´¤à´¾à´³àµ\81à´\95ൾ ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടികയിൽ ചേർക്കുക',
-'tog-watchdeletion' => 'à´\9eാൻ à´¨àµ\80à´\95àµ\8dà´\95à´\82 à´\9aàµ\86à´¯àµ\8dà´¯àµ\81à´¨àµ\8dà´¨ à´¤à´¾à´³àµ\81à´\95ൾ ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടികയിൽ ചേർക്കുക',
+'tog-watchcreations' => 'à´\9eാൻ à´¸àµ\83à´·àµ\8dà´\9fà´¿à´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨ à´¤à´¾à´³àµ\81à´\95à´³àµ\81à´\82 à´\9eാൻ à´\85à´ªàµ\8dâ\80\8cà´²àµ\8bà´¡àµ\8d à´\9aàµ\86à´¯àµ\8dà´¯àµ\81à´¨àµ\8dà´¨ à´ªàµ\8dരമാണà´\99àµ\8dà´\99à´³àµ\81à´\82 ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടികയിൽ ചേർക്കുക',
+'tog-watchdefault' => 'à´\9eാൻ à´¤à´¿à´°àµ\81à´¤àµ\8dà´¤àµ\81à´¨àµ\8dà´¨ à´¤à´¾à´³àµ\81à´\95à´³àµ\81à´\82 à´ªàµ\8dരമാണà´\99àµ\8dà´\99à´³àµ\81à´\82 ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടികയിൽ ചേർക്കുക',
+'tog-watchmoves' => 'à´\9eാൻ à´¤à´²à´\95àµ\8dà´\95àµ\86à´\9fàµ\8dà´\9fàµ\81 à´®à´¾à´±àµ\8dà´±àµ\81à´¨àµ\8dà´¨ à´¤à´¾à´³àµ\81à´\95à´³àµ\81à´\82 à´ªàµ\8dരമാണà´\99àµ\8dà´\99à´³àµ\81à´\82 ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടികയിൽ ചേർക്കുക',
+'tog-watchdeletion' => 'à´\9eാൻ à´¨àµ\80à´\95àµ\8dà´\95à´\82 à´\9aàµ\86à´¯àµ\8dà´¯àµ\81à´¨àµ\8dà´¨ à´¤à´¾à´³àµ\81à´\95à´³àµ\81à´\82 à´ªàµ\8dരമാണà´\99àµ\8dà´\99à´³àµ\81à´\82 ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടികയിൽ ചേർക്കുക',
 'tog-minordefault' => 'എല്ലാ തിരുത്തലുകളും സ്വതേ ചെറുതിരുത്തലുകളായി അടയാളപ്പെടുത്തുക',
 'tog-previewontop' => 'തിരുത്തൽ പെട്ടിക്കു മുകളിൽ പ്രിവ്യൂ കാണിക്കുക',
 'tog-previewonfirst' => 'ആദ്യത്തെ തിരുത്തലിന്റെ പ്രിവ്യൂ കാണിക്കുക',
 'tog-nocache' => 'ബ്രൗസറിൽ താളുകൾ തദ്ദേശീയമായി സംഭരിച്ചുവയ്ക്കുന്നത് നിർജ്ജീവമാക്കുക',
-'tog-enotifwatchlistpages' => 'à´\9eാൻ à´¶àµ\8dà´°à´¦àµ\8dധിà´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨ à´¤à´¾à´³àµ\81à´\95ൾà´\95àµ\8dà´\95àµ\81 മാറ്റം സംഭവിച്ചാൽ എനിക്കു ഇമെയിൽ അയക്കുക',
+'tog-enotifwatchlistpages' => 'à´\9eാൻ à´¶àµ\8dà´°à´¦àµ\8dധിà´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨ à´¤à´¾à´³àµ\81à´\95ൾà´\95àµ\8dà´\95àµ\8b à´ªàµ\8dരമാണà´\99àµ\8dà´\99ൾà´\95àµ\8dà´\95àµ\8b മാറ്റം സംഭവിച്ചാൽ എനിക്കു ഇമെയിൽ അയക്കുക',
 'tog-enotifusertalkpages' => 'എന്റെ സം‌വാദം താളിനു മാറ്റം സംഭവിച്ചാൽ ഇമെയിൽ അയക്കുക',
 'tog-enotifminoredits' => 'ചെറുതിരുത്തലുകൾക്കും എനിക്ക് ഇമെയിൽ അയയ്ക്കുക',
 'tog-enotifrevealaddr' => 'വിജ്ഞാപന മെയിലുകളിൽ എന്റെ ഇമെയിൽ വിലാസം വെളിവാക്കാൻ അനുവദിക്കുക',
@@ -1827,6 +1827,10 @@ $1',
 'backend-fail-contenttype' => '"$1" എന്നതിൽ സംഭരിച്ചിരിക്കുന്ന പ്രമാണത്തിന്റെ ഉള്ളടക്ക തരം നിർണ്ണയിക്കാനായില്ല.',
 'backend-fail-usable' => 'ആവശ്യമായത്ര അനുമതിയില്ലാത്തതു കൊണ്ടോ ഡയറക്റ്ററികൾ/കണ്ടൈനറുകൾ ഇല്ലാത്തതു കൊണ്ടോ പ്രമാണം $1 എഴുതിച്ചേർക്കാൻ കഴിഞ്ഞില്ല.',
 
+# File journal errors
+'filejournal-fail-dbconnect' => '"$1" എന്ന ശേഖരണ ബാക്ക്എൻഡിനായി ജേണൽ ഡേറ്റാബേസിനെ ബന്ധപ്പെടാൻ കഴിഞ്ഞില്ല.',
+'filejournal-fail-dbquery' => '"$1" എന്ന ശേഖരണ ബാക്ക്എൻഡിനായി ജേണൽ ഡേറ്റാബേസ് പുതുക്കാൻ കഴിഞ്ഞില്ല.',
+
 # Lock manager
 'lockmanager-notlocked' => '"$1" എന്നതിലെ പൂട്ടൽ അഴിക്കാൻ കഴിഞ്ഞില്ല; അത് പൂട്ടിയിട്ടില്ല.',
 'lockmanager-fail-closelock' => '"$1" എന്നതിന്റെ പൂട്ടൽ പ്രമാണം അടയ്ക്കാൻ കഴിഞ്ഞില്ല.',
index d1b99aa..77a7da9 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Hill Mari (Ð\9aырык мары)
+/** Hill Mari (кырык мары)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index ff028d5..187084a 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Erzya (Эрзянь)
+/** Erzya (эрзянь)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index 5ebe516..36a7bfe 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Norwegian Bokmål (‪Norsk (bokmål)‬)
+/** Norwegian Bokmål (‪norsk (bokmål)‬)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
  * @author Boivie
  * @author Brik
  * @author Byrial
+ * @author Danmichaelo
+ * @author Dittaeva
  * @author Eirik
  * @author EivindJ
  * @author Event
  * @author Finnrind
  * @author Guaca
  * @author H92
+ * @author Haakon K
  * @author Harald Khan
  * @author Jóna Þórunn
  * @author Kph
@@ -317,8 +320,8 @@ $messages = array(
 'tog-extendwatchlist' => 'Utvid overvåkningslisten til å vise alle endringer, ikke bare de siste',
 'tog-usenewrc' => 'Bruk forbedret siste endringer (krever JavaScript)',
 'tog-numberheadings' => 'Autonummerer overskrifter',
-'tog-showtoolbar' => 'Vis verktøylinje (JavaScript)',
-'tog-editondblclick' => 'Rediger sider ved å dobbeltklikke (JavaScript)',
+'tog-showtoolbar' => 'Vis verktøylinje (krever JavaScript)',
+'tog-editondblclick' => 'Rediger sider ved å dobbeltklikke (krever JavaScript)',
 'tog-editsection' => 'Rediger avsnitt ved hjelp av [rediger]-lenke',
 'tog-editsectiononrightclick' => 'Rediger avsnitt ved å høyreklikke på avsnittsoverskrift (JavaScript)',
 'tog-showtoc' => 'Vis innholdsfortegnelse (for sider med flere enn tre avsnitt)',
@@ -328,16 +331,16 @@ $messages = array(
 'tog-watchmoves' => 'Overvåk sider jeg flytter',
 'tog-watchdeletion' => 'Overvåk sider jeg sletter',
 'tog-minordefault' => 'Merk i utgangspunktet alle redigeringer som mindre',
-'tog-previewontop' => 'Flytt forhåndsvisningen foran redigeringsboksen',
-'tog-previewonfirst' => 'Vis forhåndsvisning ved første redigering av en side',
+'tog-previewontop' => 'Vis forhåndsvisningen ovenfor redigeringsboksen',
+'tog-previewonfirst' => 'Vis forhåndsvisning når du begynner å redigere',
 'tog-nocache' => 'Deaktiver nettlesermellomlagring av sider («caching»)',
-'tog-enotifwatchlistpages' => 'Send meg en e-post når sider på overvåkningslisten blir endret',
-'tog-enotifusertalkpages' => 'Send meg en e-post ved endringer av brukerdiskusjonssiden min',
-'tog-enotifminoredits' => 'Send meg en e-post også ved mindre sideendringer',
+'tog-enotifwatchlistpages' => 'Send meg en e-post når sider på overvåkningslisten min blir endret',
+'tog-enotifusertalkpages' => 'Send meg en e-post når brukerdiskusjonssiden min endres',
+'tog-enotifminoredits' => 'Send meg e-post også ved mindre endringer',
 'tog-enotifrevealaddr' => 'Vis min e-postadresse i utgående meldinger',
-'tog-shownumberswatching' => 'Vis antall overvåkende brukere',
+'tog-shownumberswatching' => 'Vis antall brukere som overvåker',
 'tog-oldsig' => 'Nåværende signatur:',
-'tog-fancysig' => 'Håndter signatur som wikitekst (uten automatisk lenke)',
+'tog-fancysig' => 'Behandle signaturen som wikitekst (uten automatisk lenke)',
 'tog-externaleditor' => 'Bruk ekstern behandler som standard (kun for viderekomne brukere, krever spesielle innstillinger på din datamaskin. [//www.mediawiki.org/wiki/Manual:External_editors Mer informasjon.])',
 'tog-externaldiff' => 'Bruk ekstern differanse som standard (kun for viderekomne brukere, krever spesielle innstillinger på din datamaskin. [//www.mediawiki.org/wiki/Manual:External_editors Mer informasjon.])',
 'tog-showjumplinks' => 'Slå på «gå til»-lenker',
@@ -349,7 +352,7 @@ $messages = array(
 'tog-watchlisthideliu' => 'Skjul endringer av innloggede brukere fra overvåkningslisten',
 'tog-watchlisthideanons' => 'Skjul endringer av anonyme brukere fra overvåkningslisten',
 'tog-watchlisthidepatrolled' => 'Skjul patruljerte endringer fra overvåkningslisten',
-'tog-nolangconversion' => 'Slå av variantkonvertering',
+'tog-nolangconversion' => 'Slå av konvertering mellom språkvarianter',
 'tog-ccmeonemails' => 'Send meg kopier av e-poster jeg sender til andre brukere',
 'tog-diffonly' => 'Ikke vis sideinnhold under differ',
 'tog-showhiddencats' => 'Vis skjulte kategorier',
@@ -428,7 +431,7 @@ $messages = array(
 'hidden-category-category' => 'Skjulte kategorier',
 'category-subcat-count' => '{{PLURAL:$2|Denne kategorien har kun følgende underkategori.|Denne kategorien har følgende {{PLURAL:$1|underkategori|$1 underkategorier}}, av totalt $2.}}',
 'category-subcat-count-limited' => 'Kategorien har følgende {{PLURAL:$1|underkategori|$1 underkategorier}}.',
-'category-article-count' => '{{PLURAL:$2|Denne kategorien inneholder kun følgende side.|Følgende {{PLURAL:$1|side|$1 sider}} er i denne kategorien, av totalt $2.}}',
+'category-article-count' => '{{PLURAL:$2|Denne kategorien inneholder kun følgende side.|Under vises $1 av totalt $2 sider som befinner seg i denne kategorien.}}',
 'category-article-count-limited' => 'Følgende {{PLURAL:$1|side|$1 sider}} er i denne kategorien.',
 'category-file-count' => '{{PLURAL:$2|Denne kategorien inneholder kun den følgende filen.|Følgende {{PLURAL:$1|fil|$1 filer}} er i denne kategorien, av totalt $2.}}',
 'category-file-count-limited' => 'Følgende {{PLURAL:$1|fil|$1 filer}} er i denne kategorien.',
@@ -694,9 +697,11 @@ $2',
 'ns-specialprotected' => 'Spesialsider kan ikke redigeres.',
 'titleprotected' => "Denne tittelen har blitt låst for oppretting av [[User:$1|$1]].
 Den angitte grunnen er «''$2''».",
-'filereadonlyerror' => "Kan ikke endre filen «$1» fordi filrepositoriet «$2» er skrivebeskyttet.
+'filereadonlyerror' => 'Kan ikke endre filen «$1» fordi filsamlingen «$2» er skrivebeskyttet.
 
-Oppgitt årsak er «''$3''».",
+Administrators nærmere begrunnelse: «$3».',
+'invalidtitle-knownnamespace' => 'Ugyldig tittel med navnerommet «$2» og teksten «$3»',
+'invalidtitle-unknownnamespace' => 'Ugyldig tittel med ukjent navneromsnummer $1 og teksten «$2»',
 
 # Virus scanner
 'virus-badscanner' => "Dårlig konfigurasjon: Ukjent virusskanner: ''$1''",
@@ -785,6 +790,7 @@ Som et resultat kan det ikke opprettes flere kontoer fra denne IP-adressen.',
 'invalidemailaddress' => 'Din e-postadresse kan ikke aksepteres, fordi den er ugyldig formatert.
 Skriv inn en fungerende e-postadresse eller tøm feltet.',
 'cannotchangeemail' => 'E-postadresser knyttet til brukerkontoer kan ikke endres på denne wikien.',
+'emaildisabled' => 'Dette nettstedet kan ikke sende e-poster.',
 'accountcreated' => 'Konto opprettet',
 'accountcreatedtext' => 'Brukerkontoen for $1 har blitt opprettet.',
 'createaccount-title' => 'Kontooppretting på {{SITENAME}}',
@@ -989,6 +995,7 @@ Siste blokkeringsloggelement kan sees nedenfor.',
 'note' => "'''Merk:'''",
 'previewnote' => "'''Husk at dette bare er en forhåndsvisning.'''
 Endringene dine har ikke blitt lagret ennå!",
+'continue-editing' => 'Fortsett med redigeringen',
 'previewconflict' => 'Slik vil teksten i redigeringsvinduet se ut dersom du lagrer den.',
 'session_fail_preview' => "'''Beklager! Klarte ikke å lagre redigeringen din på grunn av tap av øktdata.'''
 Prøv igjen.
@@ -1001,6 +1008,7 @@ Om det fortsetter å gå galt, prøv å [[Special:UserLogout|logge ut]] og så i
 'token_suffix_mismatch' => "'''Redigeringen din har blitt avvist fordi klienten din ikke hadde punktasjonstegn i redigeringsteksten. Redigeringen har blitt avvist for å hindre ødeleggelse av artikkelteksten. Dette forekommer av og til når man bruker vevbaserte anonyme proxytjenester.'''",
 'edit_form_incomplete' => "'''Deler av redigeringsskjemaet nådde ikke tjeneren; dobbelsjekk at redigeringen er korrekt og prøv igjen.'''",
 'editing' => 'Redigerer $1',
+'creating' => 'Oppretter $1',
 'editingsection' => 'Redigerer $1 (avsnitt)',
 'editingcomment' => 'Redigerer $1 (ny seksjon)',
 'editconflict' => 'Redigeringskonflikt: $1',
@@ -1065,6 +1073,7 @@ Slette- og flytteloggen vises nedenfor.',
 'edit-conflict' => 'Redigeringskonflikt.',
 'edit-no-change' => 'Redigeringen din ble ignorert fordi det ikke var noen endringer.',
 'edit-already-exists' => 'Kunne ikke opprette ny side fordi den finnes fra før.',
+'defaultmessagetext' => 'Standard meldingstekst',
 
 # Parser/template warnings
 'expensive-parserfunction-warning' => 'Advarsel: Denne siden inneholder for mange prosesskrevende parserfunksjoner.
@@ -1080,6 +1089,11 @@ Disse parameterne har blitt utelatt.',
 'parser-template-loop-warning' => 'Mal-loop oppdaget: [[$1]]',
 'parser-template-recursion-depth-warning' => 'Mal er brukt for mange ganger ($1)',
 'language-converter-depth-warning' => 'Dybdegrense for språkkonvertering overskredet ($1)',
+'node-count-exceeded-category' => 'Sider hvor antallet noder er overskredet',
+'node-count-exceeded-warning' => 'Siden har overskredet antallet noder',
+'expansion-depth-exceeded-category' => 'Sider hvor hvor ekspansjonsdybden er overskredet',
+'expansion-depth-exceeded-warning' => 'Sida har overskredet ekspansjonsdybden',
+'parser-unstrip-loop-warning' => '«Unstrip»-loop påvist',
 
 # "Undo" feature
 'undo-success' => 'Redigeringen kan omgjøres. Sjekk sammenligningen under for å bekrefte at du vil gjøre dette, og lagre endringene for å fullføre omgjøringen.',
@@ -1224,7 +1238,7 @@ Vennligst sjekk loggen.',
 # Suppression log
 'suppressionlog' => 'Sideskjulingslogg',
 'suppressionlogtext' => 'Under er en liste over sider som er slettet eller blokkert med innhold skjult fra administratorer.
-Se [[Special:BlockList|IP-blokkeringsliste]] for oversikt over aktuelle utelukkelser og blokkeringer.',
+Se [[Special:BlockList|blokkeringslisten]] for oversikt over aktuelle utelukkelser og blokkeringer.',
 
 # History merging
 'mergehistory' => 'Flett sidehistorikker',
@@ -1257,7 +1271,8 @@ Forsikre deg om at denne endringen vil opprettholde historisk sidekontinuitet.',
 
 # Diffs
 'history-title' => 'Revisjonshistorikk for «$1»',
-'difference' => '(Forskjell mellom revisjoner)',
+'difference-title' => 'Forskjell mellom versjoner av «$1»',
+'difference-title-multipage' => 'Forskjell mellom sidene «$1» og «$2»',
 'difference-multipage' => '(Forskjell mellom sider)',
 'lineno' => 'Linje $1:',
 'compareselectedversions' => 'Sammenlign valgte revisjoner',
@@ -1353,6 +1368,7 @@ For å søke i alle, bruk prefikset ''all:'' (inkluderer diskusjonssider, maler,
 'prefs-beta' => 'Betafunksjoner',
 'prefs-datetime' => 'Dato og tid',
 'prefs-labs' => 'Lab-funksjoner',
+'prefs-user-pages' => 'Brukersider',
 'prefs-personal' => 'Brukerdata',
 'prefs-rc' => 'Siste endringer',
 'prefs-watchlist' => 'Overvåkningsliste',
@@ -1615,7 +1631,7 @@ Den kan maks inneholde $1 {{PLURAL:$1|tegn|tegn}}.',
 'nchanges' => '$1 {{PLURAL:$1|endring|endringer}}',
 'recentchanges' => 'Siste endringer',
 'recentchanges-legend' => 'Alternativ for siste endringer',
-'recentchangestext' => 'Vis de siste endringene til denne siden',
+'recentchanges-summary' => 'Vis de siste endringene til denne siden',
 'recentchanges-feed-description' => 'Følg med på siste endringer i denne wikien med denne matingen.',
 'recentchanges-label-newpage' => 'Denne redigeringen opprettet en ny side',
 'recentchanges-label-minor' => 'Dette er en mindre endring',
@@ -1645,7 +1661,7 @@ Den kan maks inneholde $1 {{PLURAL:$1|tegn|tegn}}.',
 'newsectionsummary' => '/* $1 */ ny seksjon',
 'rc-enhanced-expand' => 'Vis detaljer (krever JavaScript)',
 'rc-enhanced-hide' => 'Skjul detaljer',
-'rc-old-title' => 'opprinnelig opprettet som "$1"',
+'rc-old-title' => 'opprinnelig opprettet som «$1»',
 
 # Recent changes linked
 'recentchangeslinked' => 'Relaterte endringer',
@@ -1696,7 +1712,7 @@ Se [[Special:NewFiles|galleriet over nye filer]] for en mer visuell visning',
 'ignorewarnings' => 'Ignorer eventuelle advarsler',
 'minlength1' => 'Filnavn må være på minst én bokstav.',
 'illegalfilename' => 'Filnavnet «$1» inneholder ugyldige tegn. Gi filen et nytt navn og prøv igjen.',
-'filename-toolong' => 'Filnavn kan ikke overstige 240 bytes.',
+'filename-toolong' => 'Filnavn kan ikke overstige 240 byte.',
 'badfilename' => 'Navnet på filen er blitt endret til «$1».',
 'filetype-mime-mismatch' => 'Filendelsen «.$1» tilsvarer ikke MIME-typen som oppgis i filen ($2).',
 'filetype-badmime' => 'Filer av typen «$1» kan ikke lastes opp.',
@@ -1819,14 +1835,16 @@ Om problemet fortsetter, kontakt en [[Special:ListUsers/sysop|administrator]].',
 'backend-fail-closetemp' => 'Kunne ikke lukke den midlertidige filen.',
 'backend-fail-read' => 'Kunne ikke lese filen $1.',
 'backend-fail-create' => 'Kunne ikke opprette filen $1.',
-'backend-fail-maxsize' => 'Filen $1 ble ikke opprettet fordi den ville blitt større enn {{PLURAL:$2|$2 bytes|$2 byte}}.',
+'backend-fail-maxsize' => 'Kunne ikke skrive filen $1 fordi den er større enn {{PLURAL:$2|én byte|$2 bytes}}.',
 'backend-fail-readonly' => 'Underliggende "$1" er satt skrivebeskyttet fordi: "$2"',
 'backend-fail-synced' => 'Fila «$1» er i en inkonsistent status innen de interne bakstykkene',
 'backend-fail-connect' => 'Kunne ikke koble til filbackend «$1».',
 'backend-fail-internal' => 'En ukjent feil oppsto i filbackend «$1».',
 'backend-fail-contenttype' => 'Kunne ikke avgjøre innholdstypen til filen som skal lagres på «$1».',
 'backend-fail-batchsize' => 'Bakgrunnsprosesseringen belastet med {{PLURAL:$1|en filoperasjon|en samling av $1 filoperasjoner}}; grensen er $2.',
+'backend-fail-usable' => 'Kunne ikke skrive fila $1 på grunn av utilstrekkelige tillatelser eller manglende mapper/beholdere.',
 
+# File journal errors
 'filejournal-fail-dbconnect' => 'Kunne ikke koble til journaldatabasen for lagringssystemet «$1».',
 'filejournal-fail-dbquery' => 'Kunne ikke oppdatere journaldatabasen for lagringssystemet «$1».',
 
@@ -1946,6 +1964,10 @@ En [[Special:WhatLinksHere/$2|fullstendig liste]] er tilgjengelig.',
 Se [$2 filbeskrivelsessida] for mer informasjon.',
 'sharedupload-desc-here' => 'Denne filen er fra $1 og kan brukes av andre prosjekter.
 Beskrivelsen fra [$2 filbeskrivelsessida] vises nedenfor.',
+'sharedupload-desc-edit' => 'Denne filen er fra $1 og kan være i bruk av andre prosjekter.
+Kanskje du vil redigere beskrivelsen på dens [$2 filbeskrivelsesside].',
+'sharedupload-desc-create' => 'Denne filen er fra $1 og kan være i bruk av andre prosjekter.
+Kanskje du vil redigere beskrivelsen på dens [$2 filbeskrivelsesside].',
 'filepage-nofile' => 'Det finnes ingen fil med dette navnet.',
 'filepage-nofile-link' => 'Ingen fil med dette navnet eksisterer, men du kan [$1 laste den opp].',
 'uploadnewversion-linktext' => 'Last opp en ny versjon av denne filen',
@@ -2155,6 +2177,12 @@ Du kan minske antallet resultater ved å velge loggtype, brukernavn eller den si
 'allpagesprefix' => 'Vis sider med prefikset:',
 'allpagesbadtitle' => 'Den angitte sidetittelen var ugyldig eller hadde et interwiki-prefiks. Den kan inneholde ett eller flere tegn som ikke kan brukes i titler.',
 'allpages-bad-ns' => '{{SITENAME}} har ikke navnerommet «$1».',
+'allpages-hide-redirects' => 'Skjul omdirigeringer',
+
+# SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => 'Du ser en mellomlagret versjon av denne siden, som kan være opp til $1 gammel.',
+'cachedspecial-viewing-cached-ts' => 'Du ser på en mellomlagret versjon av denne siden, som kan være ikke helt oppdatert',
+'cachedspecial-refresh-now' => 'Vis siste.',
 
 # Special:Categories
 'categories' => 'Kategorier',
@@ -2637,7 +2665,7 @@ Blokkeringsloggen vises nedenfor.',
 Skjulingsloggen vises nedenfor.',
 'blocklogentry' => 'blokkerte [[$1]] med en varighet på $2 $3',
 'reblock-logentry' => 'endret blokkeringsinnstillinger for [[$1]] med en varighet på $2 $3',
-'blocklogtext' => 'Dette er en logg som viser hvilke brukere som har blitt blokkert og avblokkert. Automatisk blokkerte IP-adresser vises ikke. Se [[Special:BlockList|blokkeringslisten]] for en liste over IP-adresser som er blokkert akkurat nå.',
+'blocklogtext' => 'Dette er en logg som viser hvilke brukere som har blitt blokkert og avblokkert. Automatisk blokkerte IP-adresser vises ikke. Se [[Special:BlockList|blokkeringslisten]] for alle aktive blokkeringer.',
 'unblocklogentry' => 'opphevet blokkeringen av $1',
 'block-log-flags-anononly' => 'kun uregistrerte brukere',
 'block-log-flags-nocreate' => 'kontooppretting deaktivert',
@@ -3746,6 +3774,9 @@ Du skal ha mottatt [{{SERVER}}{{SCRIPTPATH}}/COPYING en kopi av GNU General Publ
 'version-software' => 'Installert programvare',
 'version-software-product' => 'Produkt',
 'version-software-version' => 'Versjon',
+'version-entrypoints' => 'Inngangspunkts-URL-er',
+'version-entrypoints-header-entrypoint' => 'Inngangspunkt',
+'version-entrypoints-header-url' => 'URL',
 
 # Special:FilePath
 'filepath' => 'Filsti',
@@ -3934,4 +3965,15 @@ Om det ikke er tilfellet, kan du bruke det enkle skjemaet som du finner under. K
 'api-error-uploaddisabled' => 'Opplastning har blitt deaktivert på denne wikien.',
 'api-error-verification-error' => 'Filen kan være korrupt, eller ha feil filendelse.',
 
+# Durations
+'duration-seconds' => '$1 {{PLURAL:$1|sekund|sekunder}}',
+'duration-minutes' => '$1 {{PLURAL:$1|minutt|minutter}}',
+'duration-hours' => '$1 {{PLURAL:$1|time|timer}}',
+'duration-days' => '$1 {{PLURAL:$1|dag|dager}}',
+'duration-weeks' => '$1 {{PLURAL:$1|uke|uker}}',
+'duration-years' => '$1 {{PLURAL:$1|år|år}}',
+'duration-decades' => '$1 {{PLURAL:$1|tiår|tiår}}',
+'duration-centuries' => '$1 {{PLURAL:$1|århundre|århundrer}}',
+'duration-millennia' => '$1 {{PLURAL:$1|millennium|millennier}}',
+
 );
index 0f2f267..3ca1d4e 100644 (file)
@@ -314,25 +314,25 @@ $messages = array(
 'tog-hidepatrolled' => 'Wiezigingen die emarkeerd bin verbargen in leste wiezigingen',
 'tog-newpageshidepatrolled' => "Pagina's die emarkeerd bin, verbargen in de lieste mit nieje artikels",
 'tog-extendwatchlist' => 'Volglieste uutbreien, zodat alle wiezigingen zichtbaor bin, en niet allinnig de leste wieziging',
-'tog-usenewrc' => "Gebruuk de pagina uutebreiden leste wiezigingen (hierveur he'j JavaScript neudig)",
+'tog-usenewrc' => "Groepeer wiezigingen per pagina in de liesten leste wiezigingen en mien volglieste (hierveur he'j JavaScript neudig)",
 'tog-numberheadings' => 'Koppen vanzelf nummeren',
 'tog-showtoolbar' => 'Laot de warkbalke zien',
 'tog-editondblclick' => 'Mit dubbelklik bewarken (JavaScript)',
 'tog-editsection' => 'Mit bewarkgedeeltes',
 'tog-editsectiononrightclick' => 'Bewarkgedeelte mit rechtermuusknoppe bewarken (JavaScript)',
-'tog-showtoc' => 'Samenvatting van de onderwarpen laoten zien (mit meer as dree onderwarpen)',
+'tog-showtoc' => 'Samenvatting laoten zien van de zaken die an bod koemen (mit meer as dree onderwarpen)',
 'tog-rememberpassword' => 'Vanzelf anmelden (hooguut $1 {{PLURAL:$1|dag|dagen}})',
-'tog-watchcreations' => "Pagina's die'k anmake op mien volglieste zetten",
-'tog-watchdefault' => "Pagina's die'k wiezige op mien volglieste zetten",
-'tog-watchmoves' => "Pagina's die'k herneume op mien volglieste zetten",
-'tog-watchdeletion' => "Pagina's die'k vortdo op mien volglieste zetten",
+'tog-watchcreations' => "Pagina's die'k anmake en bestaanden die'k opsture op mien volglieste zetten",
+'tog-watchdefault' => "Pagina's en bestaanden die'k wiezige op mien volglieste zetten",
+'tog-watchmoves' => "Pagina's en bestaanden die'k herneume op mien volglieste zetten",
+'tog-watchdeletion' => "Pagina's en bestaanden die'k vortdo op mien volglieste zetten",
 'tog-minordefault' => "Markeer alle veraanderingen as 'kleine wieziging'",
 'tog-previewontop' => 'De naokiekpagina boven t bewarkingsveld zetten',
 'tog-previewonfirst' => 'Naokieken bie eerste wieziging',
 'tog-nocache' => 'De tussenopslag van de webkieker uutzetten',
-'tog-enotifwatchlistpages' => 'Stuur mien n berichjen over paginawiezigingen.',
+'tog-enotifwatchlistpages' => 'Stuur mien n berichjen over pagina- of bestaandswiezigingen uut mien volglieste.',
 'tog-enotifusertalkpages' => 'Stuur mien n berichjen as mien overlegpagina ewiezigd is.',
-'tog-enotifminoredits' => 'Stuur mien oek n berichjen bie kleine bewarkingen',
+'tog-enotifminoredits' => "Stuur mien oek n berichjen bie kleine bewarkingen van pagina's en bestaanden",
 'tog-enotifrevealaddr' => 'Mien netpostadres laoten zien in netposttiejigen',
 'tog-shownumberswatching' => 't Antal gebrukers bekieken die disse pagina volgt',
 'tog-oldsig' => 'Bestaonde haandtekening:',
@@ -579,7 +579,7 @@ $1",
 'editlink' => 'bewark',
 'viewsourcelink' => 'brontekste bekieken',
 'editsectionhint' => 'Bewarkingsveld: $1',
-'toc' => 'Onderwarpen',
+'toc' => 'Kömp an bod',
 'showtoc' => 'Bekieken',
 'hidetoc' => 'Verbarg',
 'collapsible-collapse' => 'Inklappen',
@@ -665,10 +665,12 @@ Meld t dan effen bie n [[Special:ListUsers/sysop|systeembeheerder]] van {{SITENA
 'cannotdelete' => 'De pagina of t bestaand "$1" kon niet vortedaon wörden.
 t Kan ween dat n aander t al vortedaon hef.',
 'cannotdelete-title' => 'Pagina "$1" kan niet vortedaon wörden',
+'delete-hook-aborted' => 't Vortdoon wörden in t wiere eschopt deur n MediaWiki-programmatuuruutbreiding.
+Der is gien veerdere informasie beschikbaor.',
 'badtitle' => 'Ongeldige naam',
 'badtitletext' => 'De naam van de op-evreugen pagina is niet geldig, leeg, of n interwiki-verwiezing naor n onbekende of ongeldige wiki.',
-'perfcached' => 'Disse gegevens koemen uut t tussengeheugen en bin misschien niet aktueel. Der {{PLURAL:$1|is maximaal een resultaot|bin maximaal $1 resultaoten}} beschikbaor in t tussengeheugen.',
-'perfcachedts' => 'Disse gegevens koemen uut t tussengeheugen die veur t lest bie-ewörken is op $2 um $3. Der {{PLURAL:$4|is maximaal een resultaot|bin maximaal $4 resultaoten}} beschikbaor in t tussengeheugen.',
+'perfcached' => 'Disse gegevens koemen uut t tussengeheugen en bin misschien niet aktueel. Der {{PLURAL:$1|is hooguut een resultaot|bin hooguut $1 resultaoten}} beschikbaor in t tussengeheugen.',
+'perfcachedts' => 'Disse gegevens koemen uut t tussengeheugen die veur t lest bie-ewörken is op $2 um $3. Der {{PLURAL:$4|is hooguut een resultaot|bin hooguut $4 resultaoten}} beschikbaor in t tussengeheugen.',
 'querypage-no-updates' => "'''Disse pagina wörden niet meer bie-ewörken.'''",
 'wrong_wfQuery_params' => 'Parameters veur wfQuery() waren verkeerd<br />
 Funksie: $1<br />
@@ -691,9 +693,13 @@ $2',
 'ns-specialprotected' => "Spesiale pagina's kunnen niet bewarkt wörden.",
 'titleprotected' => "t Anmaken van disse pagina is beveiligd deur [[User:$1|$1]].
 De op-egeven reden is ''$2''.",
-'filereadonlyerror' => 'Kon t bestaand "$1" niet anpassen umdat de bestaandsmap "$2" op dit moment allinnig-lezen is.
+'filereadonlyerror' => 'Kon t bestaand "$1" niet anpassen umdat de bestaandsmap "$2" op dit moment op allinnig-lezen steet.
 
-De op-egeven reden is "\'\'$3\'\'".',
+De beheerder gaf hierveur de volgende reden: "$3".',
+'invalidtitle-knownnamespace' => 'Ongeldige titel mit naamruumte "$2" en tekste "$3"',
+'invalidtitle-unknownnamespace' => 'Ongeldige titel mit onbekend naamruumtenummer $1 en tekste "$2"',
+'exception-nologin' => 'Niet an-emeld',
+'exception-nologin-text' => "Um disse pagina te bekieken of disse haandeling uut te kunnen voeren mu'j an-emeld ween bie disse wiki.",
 
 # Virus scanner
 'virus-badscanner' => "Slichte konfigurasie: onbekend antivirusprogramma: ''$1''",
@@ -785,6 +791,7 @@ Um misbruuk te veurkoemen wörden der mer één wachtwoordherinnering per {{PLUR
 'invalidemailaddress' => 't Netpostadres kon niet aksepteerd wörden umdat de opmaak ongeldig is.
 Voer de juuste opmaak van t adres in of laot t veld leeg.',
 'cannotchangeemail' => 't Netpostadres veur n gebruker kan op disse wiki niet ewiezigd wörden.',
+'emaildisabled' => 'Disse webstee kan gien netpost versturen.',
 'accountcreated' => 'Gebrukersprofiel is an-emaakt',
 'accountcreatedtext' => 'De gebrukersnaam veur $1 is an-emaakt.',
 'createaccount-title' => 'Gebrukers anmaken veur {{SITENAME}}',
@@ -980,7 +987,9 @@ De leste regel uut t blokkeerlogboek steet hieronder as referensie:',
 'userinvalidcssjstitle' => "'''Waorschuwing:''' der is gien uutvoering mit de naam \"\$1\". Vergeet niet dat joew eigen .css- en .js-pagina's beginnen mit n kleine letter, bv. \"{{ns:user}}:Naam/'''v'''ector\" in plaotse van \"{{ns:user}}:Naam/'''V'''ector.css\".",
 'updated' => '(Bewark)',
 'note' => "'''Opmarking:'''",
-'previewnote' => "'''NB: je bin de pagina allinnig nog mer an t naokieken; de tekste is nog niet op-esleugen!'''",
+'previewnote' => "'''Waort je: dit is n kontrolepagina.'''
+Joew tekste is niet op-esleugen!",
+'continue-editing' => 'Deurgaon mit bewarken',
 'previewconflict' => "Disse versie laot zien hoe de tekste in t bovenste veld deruut kump te zien a'j de tekste opslaon.",
 'session_fail_preview' => "'''De bewarking kan niet verwarkt wörden wegens n verlies an data.'''
 Probeer t laoter weer.
@@ -994,6 +1003,7 @@ As t dan nog problemen geef, probeer dan um [[Special:UserLogout|opniej an te me
 'token_suffix_mismatch' => "'''De bewarking is eweigerd umdat de webkieker de leestekens in t bewarkingstoken verkeerd behaandeld hef. De bewarking is eweigerd um verminking van de paginatekste te veurkoemen. Dit gebeurt soms as der n web-ebaseerden proxydienst gebruukt wörden waor fouten in zitten.'''",
 'edit_form_incomplete' => "'''Partie delen van t bewarkingsformulier hebben de server niet bereikt. Kiek eers nao of de bewarkingen kloppen en probeer t opniej.'''",
 'editing' => 'Bewark: $1',
+'creating' => 'Bezig mit t anmaken van $1',
 'editingsection' => 'Bewark: $1 (deelpagina)',
 'editingcomment' => 'Bewark: $1 (niej onderwarp)',
 'editconflict' => 'Bewarkingskonflikt: $1',
@@ -1057,6 +1067,7 @@ t Schient dat t vortedaon is.',
 'edit-no-change' => 'Joew bewarking is enegeerd, umdat der gien wieziging an de tekste edaon is.',
 'edit-already-exists' => 'De pagina kon niet an-emaakt wörden.
 t Besteet al.',
+'defaultmessagetext' => 'Standardtekste',
 
 # Parser/template warnings
 'expensive-parserfunction-warning' => 'Waorschuwing: disse pagina gebruukt te veule kostbaore parserfunksies.
@@ -1071,6 +1082,12 @@ Sommigen mallen wörden niet in-evoegd.',
 'parser-template-loop-warning' => 'Der is n kringloop in mallen waoreneumen: [[$1]]',
 'parser-template-recursion-depth-warning' => 'Der is over de rekursiediepte veur mallen is hinne gaon ($1)',
 'language-converter-depth-warning' => 'Je hebben t dieptelimiet veur de taalumzetter bereikt ($1)',
+'node-count-exceeded-category' => "Pagina's die t knuppenantal overschrejen hebben.",
+'node-count-exceeded-warning' => 'Op de pagina is t maximale antal nodes overschrejen',
+'expansion-depth-exceeded-category' => "Pagina's waor de expansiediepte overschrejen is",
+'expansion-depth-exceeded-warning' => 'In disse pagina staon te veul mallen',
+'parser-unstrip-loop-warning' => 'Der is n "unstrip"-lusse evunnen',
+'parser-unstrip-recursion-limit' => 'De rekursielimiet ($1) veur "unstrip" is overschrejen',
 
 # "Undo" feature
 'undo-success' => 'De bewarking kan weerummedreid wörden. Kiek de vergelieking hieronder nao um der wisse van de ween dat alles goed is, en slao de de pagina op um de bewarking weerumme te dreien.',
@@ -1210,7 +1227,8 @@ Kiek de logboeken nao.',
 
 # Suppression log
 'suppressionlog' => 'Verbargingslogboek',
-'suppressionlogtext' => "In de onderstaande lieste staon de vortedaone pagina's en blokkeringen die veur beheerders verbörgen bin. In de [[Special:BlockList|IP-blokkeerlieste]] bin de blokkeringen, die noen van toepassing bin, te bekieken.",
+'suppressionlogtext' => "In de onderstaande lieste staon de vortedaone pagina's en blokkeringen die veur beheerders verbörgen bin. 
+In de [[Special:BlockList|blokkeerlieste]] bin de blokkeringen, die noen van toepassige bin, te bekieken.",
 
 # History merging
 'mergehistory' => "Geschiedenisse van pagina's bie mekaar doon",
@@ -1241,7 +1259,9 @@ Kiek de logboeken nao.',
 'mergelogpagetext' => "Hieronder zie'j n lieste van de leste samenvoegingen van n paginageschiedenisse naor n aandere.",
 
 # Diffs
-'history-title' => 'Geschiedenisse van "$1"',
+'history-title' => 'Versiegeschiedenisse van "$1"',
+'difference-title' => 'Verschil tussen versies van "$1"',
+'difference-title-multipage' => 'Verschil tussen pagina\'s "$1" en "$2"',
 'difference-multipage' => "(Verschil tussen pagina's)",
 'lineno' => 'Regel $1:',
 'compareselectedversions' => 'Vergeliek de ekeuzen versies',
@@ -1336,11 +1356,12 @@ Kiek de logboeken nao.',
 'prefs-beta' => 'Bètafunksies',
 'prefs-datetime' => 'Daotum en tied',
 'prefs-labs' => 'Alphafunksies',
+'prefs-user-pages' => "Gebrukerspagina's",
 'prefs-personal' => 'Gebrukersgegevens',
 'prefs-rc' => 'Leste wiezigingen',
 'prefs-watchlist' => 'Volglieste',
 'prefs-watchlist-days' => 'Antal dagen in de volglieste bekieken:',
-'prefs-watchlist-days-max' => 'Maximaal $1 {{PLURAL:$1|dag|dagen}}',
+'prefs-watchlist-days-max' => 'Hooguut $1 {{PLURAL:$1|dag|dagen}}',
 'prefs-watchlist-edits' => 'Antal wiezigingen in de uutebreiden volglieste:',
 'prefs-watchlist-edits-max' => 'Maximale antal: 1.000',
 'prefs-watchlist-token' => 'Volgliestesleutel',
@@ -1362,7 +1383,7 @@ Kiek de logboeken nao.',
 'stub-threshold' => 'Verwiezingsformattering van <a href="#" class="stub">beginnetjes</a>:',
 'stub-threshold-disabled' => 'uuteschakeld',
 'recentchangesdays' => 'Antal dagen die de lieste "leste wiezigingen" laot zien:',
-'recentchangesdays-max' => '(maximaal $1 {{PLURAL:$1|dag|dagen}})',
+'recentchangesdays-max' => '(hooguut $1 {{PLURAL:$1|dag|dagen}})',
 'recentchangescount' => 'Standard antal bewarkingen um te laoten zien:',
 'prefs-help-recentchangescount' => "Dit geldt veur leste wiezigingen, paginageschiedenisse en logboekpagina's",
 'prefs-help-watchlist-token' => "A'j in dit veld n geheime kode invullen, dan maakt t RSS-voer an veur joew volglieste.
@@ -1792,14 +1813,15 @@ As t probleem zo blif, neem dan kontakt op mit één van de [[Special:ListUsers/
 'backend-fail-writetemp' => 'Kon niet naor n tiedelik bestaand schrieven.',
 'backend-fail-closetemp' => 'Kon niet n tiedelik bestaand sluten.',
 'backend-fail-read' => 'Kon t bestaand $1 niet lezen.',
-'backend-fail-create' => 'Kon t bestaand $1 niet anmaken.',
-'backend-fail-maxsize' => 'Kon t bestaand $1 niet anmaken umdat t groter is as {{PLURAL:$2|één byte|$2 bytes}}.',
+'backend-fail-create' => 'Kon t bestaand $1 niet schrieven.',
+'backend-fail-maxsize' => 'Kon t bestaand $1 niet schrieven umdat t groter is as {{PLURAL:$2|één byte|$2 bytes}}.',
 'backend-fail-readonly' => 'Van de opslag "$1" kan op dit moment allinnig elezen wörden. De op-egeven reden was: "$2"',
 'backend-fail-synced' => 't Bestaand "$1" bevient zich in n inkonsistente toestaand in de interne opslagbackends.',
 'backend-fail-connect' => 'Kon gien verbiending maken mit t opslagbackend "$1".',
 'backend-fail-internal' => 'Der is n onbekende fout op-etreden in t opslagbackend "$1".',
 'backend-fail-contenttype' => 'Kon t inhoudstype van t bestaand um op "$1" op te slaon niet bepaolen.',
 'backend-fail-batchsize' => 'Reeks van $1 bestaands{{PLURAL:$1|operasie|operasies}} in de opslagbackend; de limiet is $2 {{PLURAL:$2|operasie|operasies}}.',
+'backend-fail-usable' => 'Kon t bestaand $1 niet schrieven vanwegen te min rechten of niet-anwezige mappen of houwers.',
 
 # File journal errors
 'filejournal-fail-dbconnect' => 'Kon gien verbiending maken mit de journaaldatabanke veur t opslagbackend "$1".',
@@ -1919,6 +1941,10 @@ De [[Special:WhatLinksHere/$2|hele lieste]] is oek beschikbaor.',
 'sharedupload' => "Dit is n edeeld bestaand op $1 en ku'j oek gebruken veur aandere projekten.",
 'sharedupload-desc-there' => "Dit is n edeeld bestaand op $1 en ku'j oek gebruken veur aandere projekten. Bekiek de [$2 beschrieving van t bestaand] veur meer informasie.",
 'sharedupload-desc-here' => "Dit is n edeeld bestaand op $1 en ku'j oek gebruken veur aandere projekten. De [$2 beschrieving van t bestaand] dergindse, steet hieronder.",
+'sharedupload-desc-edit' => 'Dit besatand kömp van $1 en kan oek in aandere projekten gebruukt wörden.
+Je kunnen de [$2 pagina mit de bestaandsbeschrieving] daor bewarken.',
+'sharedupload-desc-create' => 'Dit besatand kömp van $1 en kan oek in aandere projekten gebruukt wörden.
+Je kunnen de [$2 pagina mit de bestaandsbeschrieving] daor bewarken.',
 'filepage-nofile' => 'Der besteet gien bestaand mit disse naam.',
 'filepage-nofile-link' => 'Der besteet gien bestaand mit disse naam, mer je kunnen t [$1 opsturen].',
 'uploadnewversion-linktext' => 'n Niejere versie van dit bestaand opsturen.',
@@ -2115,6 +2141,7 @@ n Bestaand kan hier dus verkeerd op-eneumen ween.",
 Je kunnen oek kiezen veur bepaolde logboeken en filteren op gebruker (heufdlettergeveulig) en titel (heufdlettergeveulig).',
 'logempty' => 'Der steet gien passende informasie in t logboek.',
 'log-title-wildcard' => 'Zeuk naor titels die beginnen mit disse tekste:',
+'showhideselectedlogentries' => 'Ekeuzen logboekregels laoten zien of verbargen',
 
 # Special:AllPages
 'allpages' => "Alle pagina's",
@@ -2132,6 +2159,12 @@ Je kunnen oek kiezen veur bepaolde logboeken en filteren op gebruker (heufdlette
 'allpagesprefix' => "Pagina's bekieken die beginnen mit:",
 'allpagesbadtitle' => 'De op-egeven paginanaam is ongeldig of der steet n interwikiveurvoegsel in. Meugelikerwieze staon der karakters in de naam die niet gebruukt maggen wörden in paginanamen.',
 'allpages-bad-ns' => '{{SITENAME}} hef gien "$1"-naamruumte.',
+'allpages-hide-redirects' => 'Deurverwiezingen verbargen',
+
+# SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => 'Je bekieken noen n versie uut t tussengeheugen van disse pagina, die hooguut $1 oud is.',
+'cachedspecial-viewing-cached-ts' => 'Je bekieken noen n versie uut t tussengeheugen van disse pagina, t kan ween dat t niet helemaole bie de tied is.',
+'cachedspecial-refresh-now' => 'Leste bekieken.',
 
 # Special:Categories
 'categories' => 'Kategorieën',
@@ -2567,7 +2600,7 @@ De leste regel uut t blokkeerlogboek steet as referensie',
 'badipaddress' => 'Ongeldig IP-adres of onbestaonde gebrukersnaam',
 'blockipsuccesssub' => 'Suksesvol eblokkeerd',
 'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] is noen eblokkeerd.<br />
-Op de [[Special:BlockList|IP-blokkeerlieste]] steet n lieste mit alle blokkeringen.',
+Op de [[Special:BlockList|blokkeerlieste]] steet n lieste mit alle blokkeringen.',
 'ipb-blockingself' => "Hiermee blokkeer je je eigen. Wi'j dat?",
 'ipb-confirmhideuser' => "Hiermee blokkeer je n verbörgen gebruker. Hierveur wörden gebrukersnamen in alle liesten en logboekregels verbörgen. Wi'j dat?",
 'ipb-edit-dropdown' => 'Blokkeerredens bewarken',
@@ -2619,7 +2652,7 @@ t Blokkeerlogboek steet hieronder as referensie:',
 t Logboek mit onderdrokten versies steet hieronder as referensie:',
 'blocklogentry' => 'blokkeren "[[$1]]" veur $2 $3',
 'reblock-logentry' => 'hef de instellingen veur de blokkering van [[$1]] ewiezigd t Löp noen of over $2 $3',
-'blocklogtext' => "Hier zie'j n lieste van de leste blokkeringen en deblokkeringen. Automatiese blokkeringen en deblokkeringen koemen niet in t logboek te staon. Zie de [[Special:BlockList|IP-blokkeerlieste]] veur de lieste van adressen die noen eblokkeerd bin.",
+'blocklogtext' => "Hier zie'j n lieste van de leste blokkeringen en deblokkeringen. Automatiese blokkeringen en deblokkeringen koemen niet in t logboek te staon. Zie de [[Special:BlockList|blokkeerlieste]] veur de lieste van adressen die noen eblokkeerd bin.",
 'unblocklogentry' => 'blokkering van $1 is op-eheven',
 'block-log-flags-anononly' => 'allinnig anoniemen',
 'block-log-flags-nocreate' => 'anmaken van gebrukersprofielen uuteschakeld',
@@ -2976,6 +3009,7 @@ Meestentieds kump dit deur n uutgaonde verwiezing die op de zwarte lieste steet.
 'spambot_username' => 'MediaWiki ongewunste zooi oprumen',
 'spam_reverting' => 'Bezig mit t weerummezetten naor de leste versie die gien verwiezing hef naor $1',
 'spam_blanking' => 'Alle wiezigingen mit n verwiezing naor $1 wörden vortehaold',
+'spam_deleting' => 'In alle versies staon verwiezingen naor $1. Pagina vortedaon',
 
 # Info page
 'pageinfo-title' => 'Informasie over "$1"',
@@ -3269,8 +3303,8 @@ Aandere velden wörden verbörgen.
 'exif-contentwarning' => 'Waorschuwing over inhoud',
 'exif-giffilecomment' => 'Opmarking bie GIF-bestaand',
 'exif-intellectualgenre' => 'Soort onderwarp',
-'exif-subjectnewscode' => 'Onderwarpcode',
-'exif-scenecode' => 'IPTC-scènecode',
+'exif-subjectnewscode' => 'Onderwarpkode',
+'exif-scenecode' => 'IPTC-scènekode',
 'exif-event' => 'Aofebeelden gebeurtenisse',
 'exif-organisationinimage' => 'Aofebeelden organisasie',
 'exif-personinimage' => 'Aofebeeld persoon',
@@ -3591,8 +3625,8 @@ De bevestigingskode zal verlopen op $4.',
 'table_pager_prev' => 'Veurige',
 'table_pager_first' => 'Eerste pagina',
 'table_pager_last' => 'Leste pagina',
-'table_pager_limit' => 'Laot $1 onderwarpen per pagina zien',
-'table_pager_limit_label' => 'Onderwarpen per pagina:',
+'table_pager_limit' => 'Laot $1 resultaoten per pagina zien',
+'table_pager_limit_label' => 'Zaken per pagina:',
 'table_pager_limit_submit' => 'Zeuk',
 'table_pager_empty' => 'Gien resultaoten',
 
@@ -3602,6 +3636,9 @@ De bevestigingskode zal verlopen op $4.',
 'autoredircomment' => 'deurverwiezing naor [[$1]]',
 'autosumm-new' => "Nieje pagina: '$1'",
 
+# Size units
+'size-kilobytes' => '$1 kB',
+
 # Live preview
 'livepreview-loading' => 'An t laojen…',
 'livepreview-ready' => 'An t laojen… ree!',
@@ -3677,6 +3714,9 @@ Samen mit dit programma heur je n [{{SERVER}}{{SCRIPTPATH}}/COPYING kopie van de
 'version-software' => 'Programmatuur die installeerd is',
 'version-software-product' => 'Produkt',
 'version-software-version' => 'Versie',
+'version-entrypoints' => 'Webadressen veur ingangen',
+'version-entrypoints-header-entrypoint' => 'Ingang',
+'version-entrypoints-header-url' => 'Webadres',
 
 # Special:FilePath
 'filepath' => 'Bestaandslokasie',
@@ -3839,6 +3879,8 @@ Aanders ku\'j oek t eenvoudige formulier hieronder gebruken. Joew opmarkingen zu
 'api-error-empty-file' => "t Bestaand da'j op-estuurd hebben is leeg.",
 'api-error-emptypage' => "Je maggen gien lege nieje pagina's anmaken.",
 'api-error-fetchfileerror' => 'Interne fout: der is iets verkeerd egaon mit t ophaolen van t bestaand.',
+'api-error-fileexists-forbidden' => 'Der besteet al n bestaand mit de naam "$1" die niet overschreven kan wörden.',
+'api-error-fileexists-shared-forbidden' => 'Der besteet al n bestaand mit de naam "$1" in de edeelden bestaandsarchief dat niet overschreven kan wörden.',
 'api-error-file-too-large' => "t Bestaand da'j op-estuurd hebben is te groot.",
 'api-error-filename-tooshort' => 'De bestaandsnaam is te kort.',
 'api-error-filetype-banned' => 'Dit bestaandstype is niet toe-estaon.',
@@ -3866,4 +3908,17 @@ Aanders ku\'j oek t eenvoudige formulier hieronder gebruken. Joew opmarkingen zu
 'api-error-uploaddisabled' => 'Je kunnen gien bestaanden opsturen in deze wiki.',
 'api-error-verification-error' => 'Dit bestaand is meugelik beschaodigd of hef n onjuuste extensie.',
 
+# Durations
+'duration-seconds' => '$1 {{PLURAL:$1|sekonde|sekonden}}',
+'duration-minutes' => '$1 {{PLURAL:$1|minuut|minuten}}',
+'duration-hours' => '$1 {{PLURAL:$1|uur|uren}}',
+'duration-days' => '$1 {{PLURAL:$1|dag|dagen}}',
+'duration-weeks' => '$1 {{PLURAL:$1|weke|weken}}',
+'duration-years' => '$1 {{PLURAL:$1|jaor|jaoren}}',
+'duration-decades' => '$1 {{PLURAL:$1|desennium|desennia}}',
+'duration-centuries' => '$1 {{PLURAL:$1|eeuw|eeuwen}}',
+'duration-millennia' => '$1 {{PLURAL:$1|millennium|millennia}}',
+
+# Unknown messages
+'lockmanager-fail-svr-acquire' => 'Kon gien vergrendeling op server $1 zetten.',
 );
index bbbd96c..1bd7c73 100644 (file)
@@ -705,8 +705,8 @@ Maak hiervan melding bij een [[Special:ListUsers/sysop|beheerder]] van {{SITENAM
 'cannotdelete' => 'De pagina of het bestand "$1" kon niet verwijderd worden.
 Mogelijk is deze al door iemand anders verwijderd.',
 'cannotdelete-title' => 'Pagina "$1" kan niet verwijderd worden',
-'delete-hook-aborted' => 'Het verwijderen is afgebroken door een hook.
-Er is geen toelichting beschikbaar.',
+'delete-hook-aborted' => 't Vortdoon is aofebreuken deur n haak.
+Der is gien informasie over beschikbaor.',
 'badtitle' => 'Ongeldige paginanaam',
 'badtitletext' => 'De naam van de opgevraagde pagina was ongeldig, leeg of bevatte een verkeerde intertaal- of interwikinaamverwijzing.
 Wellicht bevat de paginanaam niet toegestane tekens.',
index b06c4c9..f106a4c 100644 (file)
@@ -2133,9 +2133,9 @@ Om du seinare vil fjerne sida frå overvakingslista, klikk på «Fjern overvakin
 'watchnochange' => 'Ingen av sidene i overvakingslista er endra i den valde perioden.',
 'watchlist-details' => '{{PLURAL:$1|Éi side|$1 sider}} er overvaka, utanom diskusjonssider.',
 'wlheader-enotif' => '* Funksjonen for endringsmeldingar per e-post er på.',
-'wlheader-showupdated' => "* Sider som er vortne endra sidan du sist såg på dei er '''utheva'''",
+'wlheader-showupdated' => "* Sider som har blitt endra sidan du sist såg på dei er '''utheva'''",
 'watchmethod-recent' => 'sjekkar siste endringar for dei overvaka sidene',
-'watchmethod-list' => 'sjekkar om dei overvaka sidene er vortne endra i det siste',
+'watchmethod-list' => 'sjekkar om dei overvaka sidene har blitt endra i det siste',
 'watchlistcontains' => 'Overvakingslista di inneheld {{PLURAL:$1|éi side|$1 sider}}.',
 'iteminvalidname' => 'Problem med «$1», ugyldig namn...',
 'wlnote' => "Nedanfor er {{PLURAL:$1|den siste endringa|dei siste '''$1''' endringane}} {{PLURAL:$2|den siste timen|dei siste '''$2''' timane}}, for $3, kl. $4.",
index 036e10b..ccc53c4 100644 (file)
@@ -279,7 +279,7 @@ $messages = array(
 'tog-previewontop' => 'ଏଡ଼ିଟ ବାକ୍ସ ଆଗରୁ ଦେଖଣା ଦେଖାଇବେ',
 'tog-previewonfirst' => 'ପ୍ରଥମ ବଦଳର ଦେଖଣା ଦେଖାଇବେ',
 'tog-nocache' => 'ବ୍ରାଉଜର ପୃଷ୍ଠା ସଂରକ୍ଷଣକୁ ଅଚଳ କରିବେ',
-'tog-enotifwatchlistpages' => 'ମୋ ଦେଖଣାତାଲିକାରେ ଥିବା ପୃଷ୍ଠାରେ କିଛି ବଦଳ ହେଲେ ମୋତେ ଇ-ମେଲ କରିବେ',
+'tog-enotifwatchlistpages' => 'ମୋ ଦେଖଣାତାଲିକାରେ ଥିବା ପୃଷ୍ଠା ବା ଫାଇଲରେ କିଛି ବଦଳ ହେଲେ ମୋତେ ଇ-ମେଲ କରିବେ',
 'tog-enotifusertalkpages' => 'ମୋର ଆଲୋଚନା ପୃଷ୍ଠାରେ କିଛି ବଦଳ ହେଲେ ମୋତେ ଇ-ମେଲ କରିବେ',
 'tog-enotifminoredits' => 'ପୃଷ୍ଠାରେ ଛୋଟ ଛୋଟ ବଦଳ ହେଲେ ବି ମୋତେ ଇ-ମେଲ କରିବେ',
 'tog-enotifrevealaddr' => 'ସୂଚନା ଇ-ମେଲ ରେ ମୋର ଇ-ମେଲ ଠିକଣା ଦେଖାଇବେ',
@@ -501,8 +501,8 @@ $1',
 'mainpage' => 'ପ୍ରଧାନ ପୃଷ୍ଠା',
 'mainpage-description' => 'ପ୍ରଧାନ ପୃଷ୍ଠା',
 'policy-url' => 'Project:ନୀତି',
-'portal' => 'ସà¬\99à­\8dà¬\98 à¬\86ଲà­\8bà¬\9aନା à¬¸à¬­à¬¾',
-'portal-url' => 'Project:ସà¬\99à­\8dà¬\98 à¬\86ଲà­\8bà¬\9aନା à¬¸à¬­à¬¾',
+'portal' => 'ସà¬\99à­\8dà¬\98 à¬¸à­\82à¬\9aନା à¬«à¬³à¬\95',
+'portal-url' => 'Project:ସà¬\99à­\8dà¬\98 à¬¸à­\82à¬\9aନା à¬«à¬³à¬\95',
 'privacy' => 'ଗୁମର ନୀତି',
 'privacypage' => 'Project:ଗୁମର ନୀତି',
 
@@ -520,10 +520,10 @@ $1',
 'newmessageslink' => 'ନୂଆ ମେସେଜ',
 'newmessagesdifflink' => 'ଶେଷ ବଦଳ',
 'youhavenewmessagesmulti' => '$1 ତାରିଖରେ ନୂଆ ଚିଠିଟିଏ ଆସିଛି',
-'editsection' => '<big>ଏହାକୁ ବଦଳାନ୍ତୁ</big>',
-'editold' => '<big>ଏହାକୁ ବଦଳାନ୍ତୁ</big>',
+'editsection' => 'ସମ୍ପାଦନ',
+'editold' => 'ଏହାକୁ ବଦଳାନ୍ତୁ',
 'viewsourceold' => 'ମୂଳାଧାର ଦେଖିବେ',
-'editlink' => '<big>ଏହାକୁ ବଦଳାନ୍ତୁ</big>',
+'editlink' => 'ସମ୍ପାଦନ',
 'viewsourcelink' => 'ମୂଳାଧାର ଦେଖିବେ',
 'editsectionhint' => '$1 ଭାଗଟିକୁ ବଦଳାଇବେ',
 'toc' => 'ଭିତର ଚିଜ',
@@ -2006,7 +2006,7 @@ A page is treated as disambiguation page if it uses a template which is linked f
 
 'brokenredirects' => 'ଭଙ୍ଗା ପୁନପ୍ରେରଣ',
 'brokenredirectstext' => 'ତଳଲିଖିତ ପୁନପ୍ରେରଣ ସବୁ ସ୍ଥିତିହିନ ପୃଷ୍ଠାମାନଙ୍କୁ ପୁନପ୍ରେରିତ ହୋଇଥାଏ :',
-'brokenredirects-edit' => "<big>'''ଏହାକୁ ବଦଳାନ୍ତୁ'''</big>",
+'brokenredirects-edit' => 'ସମ୍ପାଦନ',
 'brokenredirects-delete' => 'ଲିଭାଇବେ',
 
 'withoutinterwiki' => 'ଭାଷାର ଲିଙ୍କ ନଥିବା ପୃଷ୍ଠାମାନ',
@@ -2409,7 +2409,7 @@ $2ଙ୍କ ଦେଇ ଶେଷଥର ହୋଇଥିବା ସଂସ୍କର
 'pagesize' => '(ବାଇଟ)',
 
 # Restrictions (nouns)
-'restriction-edit' => '<big>ଏହାକୁ ବଦଳାନ୍ତୁ</big>',
+'restriction-edit' => 'ଏହାକୁ ବଦଳାନ୍ତୁ',
 'restriction-move' => 'ଘୁଞ୍ଚାଇବେ',
 'restriction-create' => 'ଗଢ଼ନ୍ତୁ',
 'restriction-upload' => 'ଅପଲୋଡ଼ କରନ୍ତୁ',
@@ -3738,7 +3738,7 @@ MediaWiki ଉପଯୋଗୀ ହେବା ଲକ୍ଷରେ ବଣ୍ଟାଯ
 'tags-display-header' => 'ବଦଳ ତାଲିକାରେ ଦେଖଣା',
 'tags-description-header' => 'ଅର୍ଥର ପୁରା ବିବରଣୀ',
 'tags-hitcount-header' => 'ଚିହ୍ନିତ ବଦଳ',
-'tags-edit' => '<big>ଏହାକୁ ବଦଳାନ୍ତୁ</big>',
+'tags-edit' => 'ସମ୍ପାଦନ',
 'tags-hitcount' => '$1 {{PLURAL:$1|ବଦଳ|ବଦଳସବୁ}}',
 
 # Special:ComparePages
index 1105add..50cb0fe 100644 (file)
@@ -758,7 +758,7 @@ $2',
 'prefs-edits' => 'Ивдтыты нымæц:',
 'prefsnologin' => 'Системæйæн дæхи нæ бацамыдтай',
 'changepassword' => 'Пароль ивæн',
-'prefs-skin' => 'ФæлÑ\8bÑ\81Ñ\82',
+'prefs-skin' => 'ЦÑ\8aаÑ\80',
 'skin-preview' => 'Разæркаст',
 'prefs-beta' => 'Бета фадæттæ',
 'prefs-datetime' => 'Датæ æмæ рæстæг',
@@ -1417,7 +1417,7 @@ $3',
 
 # Special:Version
 'version' => 'Фæлтæр',
-'version-skins' => 'ФæлÑ\8bÑ\81Ñ\82',
+'version-skins' => 'ЦÑ\8aаÑ\80',
 'version-other' => 'Æндæр',
 'version-version' => '(Фæлтæр $1)',
 'version-license' => 'Лицензи',
index 64430a7..4e90283 100644 (file)
@@ -233,8 +233,8 @@ Zum die Said aaleche, kannscht do in dem Käschtel unne aafange mid schraiwe (gu
 Wenn do nid hin hoscht welle, no druck in Daim Browser uff '''Zrick'''.",
 'noarticletext' => 'Uff derre Said gebbt\'s noch kää Text. Du kannscht uff annere Saide [[Special:Search/{{PAGENAME}}|den Aitrach suche]], <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} de Logbuchaidrach suche, wu dezu ghert],
 orrer [{{fullurl:{{FULLPAGENAME}}|action=edit}} die Said bearwaide]</span>.',
-'previewnote' => "'''Denk dran, dass des numme e Vorschau isch.'''
-Dai Ännerunge sinn no nid gspaichert worre!",
+'previewnote' => "'''Deng'g droa, dass des numme e Vorschau isch.'''
+Doi Ännerunge sinn no nid gschbaichert worre!",
 'editing' => 'Am $1 bearwaide',
 'editingsection' => '$1 bearwaide (Abschnitt)',
 'yourtext' => 'Doin Tegschd',
@@ -250,7 +250,7 @@ du gebbscht do au zu, dass Du des selwerscht gschriwwe hoscht orrer vun ere effe
 'hiddencategories' => 'Die Said ghert zu {{PLURAL:$1|1 versteckelte Kategorie|$1 versteckelte Kategorie}}:',
 'permissionserrorstext-withaction' => 'Du därfscht nid $2, aus {{PLURAL:$1|dem Grund|denne Grind}}:',
 'moveddeleted-notice' => 'Die Said isch glescht worre.
-De Leschaidrach fer die Said isch do unne aagewwe als Quell.',
+De Leschaidrach fa die Said isch do unne als Kwell aagewwe.',
 
 # History pages
 'viewpagelogs' => 'D Lochbiecher fer die Said aagucke',
index 72a40a5..6699535 100644 (file)
@@ -2651,14 +2651,14 @@ Che as lo salva ansima a sò ordinator e peui che a lo caria ambelessì.",
 'import-noarticle' => "Pa gnun-a pàgina d'amporté.",
 'import-nonewrevisions' => "Tute le revision a j'ero già stàite amportà.",
 'xml-error-string' => '$1 ant la riga $2, colòna $3 (byte $4): $5',
-'import-upload' => 'Carìa dat XML',
-'import-token-mismatch' => 'Perdù ij dat ëd session.
-Për piasì preuva torna.',
+'import-upload' => 'Cariament ëd dat XML',
+'import-token-mismatch' => "Pèrdita dij dat ëd session.
+Për piasì, ch'a preuva torna.",
 'import-invalid-interwiki' => 'As peul pa amportesse da la wiki spessificà.',
-'import-error-edit' => "La pàgina «$1» a l'é pa amportà përchè a peul pa modifichela.",
-'import-error-create' => "La pàgina «$1» a l'é pa amportà përchè a peul pa creela.",
-'import-error-interwiki' => 'La pàgina "$1" a l\'é pa amportà përchè sò nòm a l\'é arzervà për na liura esterna (antërwiki).',
-'import-error-special' => 'La pàgina "$1" a l\'é pa amportà përchè a ponta a në spassi nominal ch\'a përmët pa dle pàgine.',
+'import-error-edit' => "La pàgina «$1» a l'é pa stàita amportà përchè chiel a peul pa modifichela.",
+'import-error-create' => "La pàgina «$1» a l'é pa stàita amportà përchè chiel a peul pa creela.",
+'import-error-interwiki' => "La pàgina «$1» a l'é pa amportà përchè sò nòm a l'é arzervà për na liura esterna (antërwiki).",
+'import-error-special' => "La pàgina «$1» a l'é pa amportà përchè a ponta a në spassi nominal ch'a përmët pa dle pàgine.",
 'import-error-invalid' => "La pàgina «$1» a l'é pa amportà përchè sò nòm a l'é pa bon.",
 
 # Import log
index 29715a9..c93f285 100644 (file)
@@ -1728,7 +1728,7 @@ Consulte a [[Special:NewFiles|galeria de novos ficheiros]] para visioná-los.',
 'minlength1' => 'Os nomes de ficheiros devem de ter pelo menos uma letra.',
 'illegalfilename' => 'O nome do ficheiro "$1" contém caracteres que não são permitidos no título das páginas.
 Altere o nome do ficheiro e tente enviá-lo novamente, por favor.',
-'filename-toolong' => 'Os nomes de arquivo não podem ser superiores a 240 bytes.',
+'filename-toolong' => 'Os nomes de ficheiros não podem ser superiores a 240 bytes.',
 'badfilename' => 'O nome do ficheiro foi alterado para "$1".',
 'filetype-mime-mismatch' => 'A extensão ".$1" não corresponde ao tipo MIME do ficheiro ($2).',
 'filetype-badmime' => 'Não é permitido carregar ficheiros do tipo MIME "$1".',
@@ -1856,10 +1856,10 @@ Caso o problema persista, contacte um [[Special:ListUsers/sysop|administrador]].
 'backend-fail-copy' => 'Não foi possível copiar o ficheiro $1 para $2.',
 'backend-fail-move' => 'Não é possível mover o ficheiro $1 para $2.',
 'backend-fail-opentemp' => 'Não foi possível abrir o ficheiro temporário.',
-'backend-fail-writetemp' => 'Não foi possível gravar para arquivo temporário.',
-'backend-fail-closetemp' => 'Não foi possível fechar o arquivo temporário.',
-'backend-fail-read' => 'Não foi possível ler o arquivo $1.',
-'backend-fail-create' => 'Não foi possível criar o arquivo $1.',
+'backend-fail-writetemp' => 'Não foi possível gravar no ficheiro temporário.',
+'backend-fail-closetemp' => 'Não foi possível fechar o ficheiro temporário.',
+'backend-fail-read' => 'Não foi possível ler o ficheiro $1.',
+'backend-fail-create' => 'Não foi possível escrever o ficheiro $1.',
 'backend-fail-maxsize' => 'Não foi possível criar o ficheiro  $1  porque ele é maior do que  {{PLURAL:$2| um byte| $2  bytes}}.',
 'backend-fail-readonly' => 'O servidor de armazenamento "$1" está actualmente no modo "somente leitura". A razão dada foi: "$2"',
 'backend-fail-synced' => 'O ficheiro" $1 " está em um estado inconsistente dentro da base de dados',
index 7dde544..55fe778 100644 (file)
@@ -150,10 +150,10 @@ This is the toolbar: [[Image:Toolbar.png]]",
 'tog-showtoc' => "[[Special:Preferences]], tab 'Misc'. Offers user to show a table of contents automatically if a page has more than three headings. {{Gender}}",
 'tog-rememberpassword' => "{{Identical|Remember my login on this computer}}[[Special:Preferences]], tab 'User profile', section 'Change password'. Offers user remember login details.  {{Gender}} Parameters:
 * $1 is the number of days the login details are remembered.",
-'tog-watchcreations' => "[[Special:Preferences]], tab 'Watchlist'. Offers user to add created pages and uploaded files to watchlist. {{Gender}}",
-'tog-watchdefault' => "[[Special:Preferences]], tab 'Watchlist'. Offers user to add edited pages and files to watchlist. {{Gender}}",
-'tog-watchmoves' => "[[Special:Preferences]], tab 'Watchlist'. Offers user to add moved pages and files to watchlist. {{Gender}}",
-'tog-watchdeletion' => "[[Special:Preferences]], tab 'Watchlist'. Offers user to add deleted pages and files to watchlist. {{Gender}}",
+'tog-watchcreations' => "[[Special:Preferences]], tab 'Watchlist'. Offers user to add created pages to watchlist. {{Gender}}",
+'tog-watchdefault' => "[[Special:Preferences]], tab 'Watchlist'. Offers user to add edited pages to watchlist. {{Gender}}",
+'tog-watchmoves' => "[[Special:Preferences]], tab 'Watchlist'. Offers user to add moved pages to watchlist. {{Gender}}",
+'tog-watchdeletion' => "[[Special:Preferences]], tab 'Watchlist'. Offers user to add deleted pages to watchlist. {{Gender}}",
 'tog-minordefault' => "[[Special:Preferences]], tab 'Edit'. Offers user to mark all edits minor by default.  {{Gender}}",
 'tog-previewontop' => 'Toggle option used in [[Special:Preferences]]. {{Gender}}',
 'tog-previewonfirst' => 'Toggle option used in [[Special:Preferences]]. {{Gender}}',
@@ -169,7 +169,7 @@ Is only shown if {{msg-mw|tog-enotifusertalkpages}} or/and {{msg-mw|tog-enotifwa
 'tog-fancysig' => 'In user preferences under the signature box.  {{Gender}}',
 'tog-externaleditor' => "[[Special:Preferences]], tab 'Edit'. Offers user to use an external editor by default. {{Gender}}",
 'tog-externaldiff' => "[[Special:Preferences]], tab 'Edit'. Offers user to use an external diff program by default. {{Gender}}",
-'tog-showjumplinks' => 'Toggle option used in [[Special:Preferences]]. The "jump to" part should be the same with {{msg-mw|jumpto}} (or you can use <nowiki>{{int:jumpto}}</nowiki>). Thess links are shown in some of the older skins as "jump to: navigation, search" but they are hidden by default (you can enable them with this option). {{Gender}}',
+'tog-showjumplinks' => 'Toggle option used in [[Special:Preferences]]. The "jump to" part should be the same with {{msg-mw|jumpto}} (or you can use <nowiki>{{int:jumpto}}</nowiki>). These links are shown in some of the older skins as "jump to: navigation, search" but they are hidden by default (you can enable them with this option). {{Gender}}',
 'tog-uselivepreview' => 'Toggle option used in [[Special:Preferences]]. Live preview is an experimental feature (unavailable by default) to use edit preview without loading the page again. {{Gender}}',
 'tog-forceeditsummary' => "Toggle option used in [[Special:Preferences]] to force an edit ''{{msg-mw|summary}}''. {{Gender}}",
 'tog-watchlisthideown' => "[[Special:Preferences]], tab 'Watchlist'. Offers user to hide own edits from watchlist. {{Gender}}",
@@ -1655,6 +1655,15 @@ If someone with this right (bots by default) edits a user talk page and marks it
 'right-writeapi' => '{{doc-right|writeapi}}',
 'right-delete' => '{{doc-right|delete}}',
 'right-bigdelete' => '{{doc-right|bigdelete}}',
+'right-deletelogentry' => '{{doc-right|deletelogentry}}
+This user right is part of the [[mw:RevisionDelete|RevisionDelete]] feature.
+It can be given to the group {{msg|group-sysop|pl=yes}}, although this right is disabled by default.
+
+See also
+* {{msg|right-suppressionlog|pl=yes}}
+* {{msg|right-hideuser|pl=yes}}
+* {{msg|right-suppressrevision|pl=yes}}
+* {{msg|right-deleterevision|pl=yes}}',
 'right-deleterevision' => '{{doc-right|deleterevision}}
 This user right is part of the [[mw:RevisionDelete|RevisionDelete]] feature.
 It can be given to the group {{msg|group-sysop|pl=yes}}, although this right is disabled by default.
@@ -1662,7 +1671,8 @@ It can be given to the group {{msg|group-sysop|pl=yes}}, although this right is
 See also
 * {{msg|right-suppressionlog|pl=yes}}
 * {{msg|right-hideuser|pl=yes}}
-* {{msg|right-suppressrevision|pl=yes}}',
+* {{msg|right-suppressrevision|pl=yes}}
+* {{msg|right-deletelogentry|pl=yes}}',
 'right-deletedhistory' => '{{doc-right|deletedhistory}}',
 'right-deletedtext' => '{{doc-right|deletedtext}}',
 'right-browsearchive' => '{{doc-right|browsearchive}}',
@@ -1674,6 +1684,7 @@ It can be given to the group {{msg|group-suppress|pl=yes}}, although that group
 See also
 * {{msg|right-suppressionlog|pl=yes}}
 * {{msg|right-hideuser|pl=yes}}
+* {{msg|right-deletelogentry|pl=yes}}
 * {{msg|right-deleterevision|pl=yes}}',
 'right-suppressionlog' => '{{doc-right|suppressionlog}}
 This user right is part of the [[mw:RevisionDelete|RevisionDelete]] feature.
@@ -1682,6 +1693,7 @@ It can be given to the group {{msg|group-suppress|pl=yes}}, although that group
 See also
 * {{msg|right-suppressrevision|pl=yes}}
 * {{msg|right-hideuser|pl=yes}}
+* {{msg|right-deletelogentry|pl=yes}}
 * {{msg|right-deleterevision|pl=yes}}',
 'right-block' => '{{doc-right|block}}',
 'right-blockemail' => '{{doc-right|blockemail}}',
@@ -1692,6 +1704,7 @@ It can be given to the group {{msg|group-suppress|pl=yes}}, although that group
 See also
 * {{msg|right-suppressionlog|pl=yes}}
 * {{msg|right-suppressrevision|pl=yes}}
+* {{msg|right-deletelogentry|pl=yes}}
 * {{msg|right-deleterevision|pl=yes}}',
 'right-ipblock-exempt' => '{{doc-right|ipblock-exempt}}
 This user automatically bypasses IP blocks, auto-blocks and range blocks - so I presume - but I am uncertain',
index 2a54e32..20daf46 100644 (file)
@@ -314,17 +314,17 @@ $messages = array(
 pe titlul secțiunii (JavaScript)',
 'tog-showtoc' => 'Arată cuprinsul (pentru paginile cu mai mult de 3 paragrafe cu titlu)',
 'tog-rememberpassword' => 'Autentificare automată de la acest navigator (expiră după $1 {{PLURAL:$1|zi|zile|de zile}})',
-'tog-watchcreations' => 'Adaugă paginile pe care le creez la lista mea de urmărire',
-'tog-watchdefault' => 'Adaugă paginile pe care le modific la lista mea de urmărire',
-'tog-watchmoves' => 'Adaugă paginile pe care le redenumesc la lista de pagini urmărite',
-'tog-watchdeletion' => 'Adaugă paginile pe care le șterg în lista de pagini urmărite',
+'tog-watchcreations' => 'Adaugă paginile pe care le creez și fișierele pe care le încarc la lista mea de urmărire',
+'tog-watchdefault' => 'Adaugă paginile și fișierele pe care le modific la lista mea de urmărire',
+'tog-watchmoves' => 'Adaugă paginile și fișierele pe care le redenumesc la lista mea de urmărire',
+'tog-watchdeletion' => 'Adaugă paginile și fișierele pe care le șterg la lista mea de urmărire',
 'tog-minordefault' => 'Marchează din oficiu toate modificările ca fiind minore',
 'tog-previewontop' => 'Arată previzualizarea deasupra căsuței de modificare',
 'tog-previewonfirst' => 'Arată previzualizarea la prima modificare',
 'tog-nocache' => 'Dezactivează opțiunea navigatorului de memorare în cache a paginilor',
-'tog-enotifwatchlistpages' => 'Trimite-mi un email la modificările paginilor',
+'tog-enotifwatchlistpages' => 'Trimite-mi un e-mail atunci când o pagină sau un fișier din lista mea de pagini urmărite suferă modificări',
 'tog-enotifusertalkpages' => 'Trimite-mi un email când pagina mea de discuții este modificată',
-'tog-enotifminoredits' => 'Trimite-mi un email de asemenea pentru modificările minore ale paginilor',
+'tog-enotifminoredits' => 'Trimite-mi, de asemenea, un e-mail în caz de modificări minore asupra paginilor și fișierelor',
 'tog-enotifrevealaddr' => 'Descoperă-mi adresa email în mesajele de notificare',
 'tog-shownumberswatching' => 'Arată numărul utilizatorilor care urmăresc',
 'tog-oldsig' => 'Semnătură actuală:',
index 6a8fff5..81c902d 100644 (file)
@@ -38,17 +38,17 @@ $messages = array(
 'tog-editsectiononrightclick' => "Abilite le cngiaminde d'a sezione ausanne 'u pulsande destre d'u mouse cazzanne sus a 'u titele (Javascript)",
 'tog-showtoc' => "Fa vedè 'a taggelle de le condenute (pe le pàggene cu cchiù de 3 testete)",
 'tog-rememberpassword' => "Arrencuerdete 'u nome mije sus a stu browser (pe 'nu massime de $1 {{PLURAL:$1|sciurne|sciurne}})",
-'tog-watchcreations' => "Mitte le pàggene ca je agghje ccrejete jndr'à le pàggene condrollete",
-'tog-watchdefault' => "Mitte le pàggene ca je agghje cangete jndr'à le pàggene condrolleteAdd pages I edit to my watchlist",
-'tog-watchmoves' => "Mitte le pàggene ca je agghje spustete jndr'à le pàggene condrollete",
-'tog-watchdeletion' => "Mitte le pàggene ca je agghje scangillete jndr'à le pàggene condrollete",
+'tog-watchcreations' => "Mitte le pàggene ca je agghie ccrejate jndr'à le pàggene condrollate",
+'tog-watchdefault' => "Mitte le pàggene ca je agghie cangiate jndr'à le pàggene condrollate",
+'tog-watchmoves' => "!Mitte le pàggene ca je agghie spustate jndr'à le pàggene condrollate",
+'tog-watchdeletion' => "Mitte le pàggene ca je agghie scangellate jndr'à le pàggene condrollate",
 'tog-minordefault' => 'Pe convenzione signe tutte le cangiaminde cumme a stuédeche',
 'tog-previewontop' => "Fa vedè l'andeprime apprime de 'a scatole de le cangiaminde",
 'tog-previewonfirst' => "Fà vedè l'andeprime sus a 'u prime cangiaminde",
 'tog-nocache' => "Disabbilete 'u caching d'a pàgene sfogliate",
-'tog-enotifwatchlistpages' => "Manneme 'na mail quanne 'a pàgene ca stoche a condrolle ha cangete",
+'tog-enotifwatchlistpages' => "Manneme 'na mail quanne 'a pàgene ca stoche a condrolle ha cangiate",
 'tog-enotifusertalkpages' => "Manneme 'na mail quanne 'a pàgene de le 'ngazzaminde ha cangete",
-'tog-enotifminoredits' => "Manneme 'na mail quanne onne state fatte cangiaminde stuèdeche sus 'a pàgene",
+'tog-enotifminoredits' => "Manneme 'na mail quanne onne state fatte cangiaminde stuèdeche sus a le pàggene",
 'tog-enotifrevealaddr' => "Fa vedè l'indirizze e-mail jndr'à le e-mail de notifiche",
 'tog-shownumberswatching' => "Fa vedè 'u numere de le utinde ca uardene",
 'tog-oldsig' => 'Firme esistende:',
index fabf34e..2f1dee6 100644 (file)
@@ -407,17 +407,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Править секции при правом щелчке мышью на заголовке (JavaScript)',
 'tog-showtoc' => 'Показывать оглавление (для страниц более чем с 3 заголовками)',
 'tog-rememberpassword' => 'Помнить мою учётную запись в этом браузере (не более $1 {{PLURAL:$1|дня|дней|дней}})',
-'tog-watchcreations' => 'Добавлять созданные мной страницы в список наблюдения',
-'tog-watchdefault' => 'Ð\94обавлÑ\8fÑ\82Ñ\8c Ð¸Ð·Ð¼ÐµÐ½Ñ\91ннÑ\8bе Ð¼Ð½Ð¾Ð¹ Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b Ð² Ñ\81пиÑ\81ок Ð½Ð°Ð±Ð»Ñ\8eдениÑ\8f',
-'tog-watchmoves' => 'Ð\94обавлÑ\8fÑ\82Ñ\8c Ð¿ÐµÑ\80еименованнÑ\8bе Ð¼Ð½Ð¾Ð¹ Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b Ð² Ñ\81пиÑ\81ок Ð½Ð°Ð±Ð»Ñ\8eдениÑ\8f',
-'tog-watchdeletion' => 'Добавлять удалённые мной страницы в список наблюдения',
+'tog-watchcreations' => 'Добавлять в список наблюдения созданные мной страницы и загруженные мной файлы',
+'tog-watchdefault' => 'Ð\94обавлÑ\8fÑ\82Ñ\8c Ð² Ñ\81пиÑ\81ок Ð½Ð°Ð±Ð»Ñ\8eдениÑ\8f Ð¸Ð·Ð¼ÐµÐ½Ñ\91ннÑ\8bе Ð¼Ð½Ð¾Ð¹ Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b Ð¸ Ð¾Ð¿Ð¸Ñ\81аниÑ\8f Ñ\84айлов',
+'tog-watchmoves' => 'Ð\94обавлÑ\8fÑ\82Ñ\8c Ð² Ñ\81пиÑ\81ок Ð½Ð°Ð±Ð»Ñ\8eдениÑ\8f Ð¿ÐµÑ\80еименованнÑ\8bе Ð¼Ð½Ð¾Ð¹ Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b Ð¸ Ñ\84айлÑ\8b',
+'tog-watchdeletion' => 'Добавлять в список наблюдения удалённые мной страницы и файлы',
 'tog-minordefault' => 'Помечать по умолчанию правки как малозначимые',
 'tog-previewontop' => 'Помещать предпросмотр перед окном редактирования',
 'tog-previewonfirst' => 'Показывать предпросмотр при переходе к редактированию',
 'tog-nocache' => 'Отключить кеширование страниц в браузере',
-'tog-enotifwatchlistpages' => 'Уведомлять по эл. почте об изменениях страниц из списка наблюдения',
+'tog-enotifwatchlistpages' => 'Уведомлять по эл. почте об изменениях страниц и файлов из списка наблюдения',
 'tog-enotifusertalkpages' => 'Уведомлять по эл. почте об изменении персональной страницы обсуждения',
-'tog-enotifminoredits' => 'УведомлÑ\8fÑ\82Ñ\8c Ð´Ð°Ð¶Ðµ Ð¿Ñ\80и Ð¼Ð°Ð»Ð¾Ð·Ð½Ð°Ñ\87иÑ\82елÑ\8cнÑ\8bÑ\85 Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8fÑ\85',
+'tog-enotifminoredits' => 'УведомлÑ\8fÑ\82Ñ\8c Ð´Ð°Ð¶Ðµ Ð¿Ñ\80и Ð½ÐµÐ·Ð½Ð°Ñ\87иÑ\82елÑ\8cнÑ\8bÑ\85 Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8fÑ\85 Ñ\81Ñ\82Ñ\80аниÑ\86 Ð¸ Ñ\84айлов',
 'tog-enotifrevealaddr' => 'Показывать мой почтовый адрес в сообщениях оповещения',
 'tog-shownumberswatching' => 'Показывать число участников, включивших страницу в свой список наблюдения',
 'tog-oldsig' => 'Текущая подпись:',
index cfc70f2..d06326a 100644 (file)
@@ -58,17 +58,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Сиэксийэ баһыгар уҥа тимэҕинэн<br />баттаан сиэксийэни көннөрүү (JavaScript)',
 'tog-showtoc' => 'Иһинээҕитин көрдөр (ыстатыйа үстэн ордук бас тыллаах буоллаҕына)',
 'tog-rememberpassword' => 'Миигин бу браузерга сигээ ($1 {{PLURAL:$1|күн|күнтэн ордуга суох}})',
-'tog-watchcreations' => 'Суруйбут ыстатыйаларбын кэтээн көрүүгэ киллэрэн ис',
-'tog-watchdefault' => 'Уларыппыт сирэйдэрбин кэтээн көрүү испииһэгэр киллэрэн ис',
-'tog-watchmoves' => 'Аатын уларыппыт сирэйдэрбин кэтээн көрүү испииһэгэр киллэрэн ис',
-'tog-watchdeletion' => 'Соппут сирэйдэрбин кэтээн көрүү испииһэгэр киллэрэн ис',
+'tog-watchcreations' => 'Суруйбут ыстатыйаларбын уонна укпут билэлэрбин кэтээн көрүүгэ киллэрэн ис',
+'tog-watchdefault' => 'Уларыппыт сирэйдэрбин уонна билэлэрбин кэтээн көрүү тиһигэр киллэрэн ис',
+'tog-watchmoves' => 'Аатын уларыппыт сирэйдэрбин уонна билэлэрбин кэтээн көрүү испииһэгэр киллэрэн ис',
+'tog-watchdeletion' => 'Соппут сирэйдэрбин уонна билэлэрбин кэтээн көрүү тиһигэр киллэрэн ис',
 'tog-minordefault' => 'Уларытыылары атын этиллибэтэҕинэ кыра уларытыы курдук бэлиэтээ',
 'tog-previewontop' => 'Хайдах буоларын уларытар түннүк үрдүнэн (иннигэр) көрдөр',
 'tog-previewonfirst' => 'Хайдах буоларын тута көрдөр',
 'tog-nocache' => 'Браузерга сирэйи кэштыыры араар',
-'tog-enotifwatchlistpages' => 'Кэтиир сирэйдэрим уларыйдахтарына e-mail көмөтүнэн биллэр',
+'tog-enotifwatchlistpages' => 'Ð\9aÑ\8dÑ\82ииÑ\80 Ñ\81иÑ\80Ñ\8dйдÑ\8dÑ\80им Ñ\83онна Ð±Ð¸Ð»Ñ\8dлÑ\8dÑ\80им Ñ\83лаÑ\80Ñ\8bйдаÑ\85Ñ\82аÑ\80Ñ\8bна e-mail ÐºÓ©Ð¼Ó©Ñ\82үнÑ\8dн Ð±Ð¸Ð»Ð»Ñ\8dÑ\80',
 'tog-enotifusertalkpages' => 'Ырытар сирэйим уларыйдаҕына эл. почтанан биллэр',
-'tog-enotifminoredits' => 'Кыра да уларытыы киирдэҕинэ эл. почтанан биллэр',
+'tog-enotifminoredits' => 'Кыра да уларытыы киирдэҕинэ эл. почтанан биллэрээр',
 'tog-enotifrevealaddr' => 'Мин почтам аадырыһын биллэриилэргэ көрдөр',
 'tog-shownumberswatching' => 'Сирэйи кэтээн көрөр дьон ахсаанын көрдөр',
 'tog-oldsig' => 'Баар илии баттааһын:',
@@ -397,6 +397,8 @@ $1',
 'cannotdelete' => '«$1» сирэй эбэтэр билэ сотуллар кыаҕа суох.
 Ким эрэ инники сотторбут буолуон сөп.',
 'cannotdelete-title' => '«$1» сирэйи сотор сатаммат',
+'delete-hook-aborted' => 'Көннөрүү төттөрү көннөрүллүбүт.
+Эбии туох да быһаарыллыбатах.',
 'badtitle' => 'Табыллыбат аат',
 'badtitletext' => 'Ыйытыллыбыт сирэй аата сыыһа, иччитэх, эбэтэр сыыһа ыйынньыктаах тыллар ыккардыларынааҕы дуу, биикилэр ыккардыларынааҕы дуу аат.',
 'perfcached' => 'Бу кээстэн ылыллыбыт онон бүтэһик уларыйыылары аахсымыан сөп. Кээскэ {{PLURAL:$1|соҕотох суруктан|$1 суруктан}} ордук хараллыбат.',
@@ -427,6 +429,9 @@ $2',
 
 Бу эрэсиими туруорбут дьаһабыл маннык быһаарыыны хаалларбыт: «''$3''».",
 'invalidtitle-knownnamespace' => '«$2» аат далыгар маннык тиэкистээх «$3» сатаммат аат',
+'invalidtitle-unknownnamespace' => 'Биллибэт аат дала $1 нүөмэрдээх, "$2" тиэкистээх сатаммат аат',
+'exception-nologin' => 'Ааккын билиһиннэрбэтэххин',
+'exception-nologin-text' => 'Маны көрөргө эбэтэр оҥорорго ааккын билиһиннэриэхтээххин.',
 
 # Virus scanner
 'virus-badscanner' => "Сатаммата. Вирус сканера биллибэтэ: ''$1''",
@@ -520,6 +525,7 @@ $2',
 'invalidemailaddress' => 'Киллэрбит аадырыһыҥ эл. почта аадырыһыгар майгыннаабат.
 Сөпкө суруй эбэтэр кураанах хааллар.',
 'cannotchangeemail' => 'Бу биикигэ бу аакка баайыллыбыт электроннай почта аадырыһа уларытыллар кыаҕа суох эбит.',
+'emaildisabled' => 'Бу ситим-сир сурук ыыппат эбит.',
 'accountcreated' => 'Саҥа аат иҥэрилиннэ',
 'accountcreatedtext' => 'Кыттааччы $1 диэн ааттанна.',
 'createaccount-title' => '{{SITENAME}} бырайыакка саҥа аат оҥоруу',
@@ -801,6 +807,7 @@ IP-аадырыһа эрэ көстөр.
 'edit-no-change' => 'Эн көннөрүүҥ киирбэтэ, тоҕо диэтэххэ тугу да уларыппатаххын.',
 'edit-already-exists' => 'Саҥа сирэйи оҥорор табыллыбат.
 Маннык сирэй баар эбит.',
+'defaultmessagetext' => 'Туспа этиллибэтэҕинэ суруллар тиэкис',
 
 # Parser/template warnings
 'expensive-parserfunction-warning' => 'Болҕой. Бу сирэй наһаа элбэх көмпүүтэри ноҕуруускалыыр ресурсаларга сигэнэр.
@@ -816,6 +823,10 @@ IP-аадырыһа эрэ көстөр.
 'parser-template-loop-warning' => 'Халыыптар бэйэ бэйлэригэр сигэниилэрэ (петля) булулунна: [[$1]]',
 'parser-template-recursion-depth-warning' => '($1) халыып рекурсиятын муҥура бүппүт (Превышен предел глубины рекурсии)',
 'language-converter-depth-warning' => 'Тыл конвертерын дириҥин хааччаҕа куоһарыллыбыт ($1)',
+'node-count-exceeded-category' => 'Түмүктэрин ахсаана аһара барбыт сирэйдэр',
+'node-count-exceeded-warning' => 'Сирэй түмүгүн ахсаана таһынан барбыт',
+'expansion-depth-exceeded-warning' => 'Сирэйгэ угуллубут билэлэр аһара элбээбиттэр',
+'parser-unstrip-recursion-limit' => 'Рекурсия ахсаана таһынан барбыт ($1)',
 
 # "Undo" feature
 'undo-success' => 'Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.',
@@ -995,6 +1006,8 @@ $1",
 
 # Diffs
 'history-title' => 'Көннөрүү сурунаала "$1"',
+'difference-title' => '$1 — барыллар ыккардыларынааҕы ураты',
+'difference-title-multipage' => '"$1" сирэйдэр "$2" уратылара',
 'difference-multipage' => '(Сирэйдэр ыккардыларынааҕы уратылар)',
 'lineno' => '$1 строка:',
 'compareselectedversions' => 'Талыллыбыт торумнары тэҥнээ',
@@ -1089,6 +1102,7 @@ $1",
 'prefs-beta' => 'Бета-туруоруулар',
 'prefs-datetime' => 'Күнэ-дьыла уонна кэмэ',
 'prefs-labs' => 'Тургутуллар туруоруулар',
+'prefs-user-pages' => 'Кыттааччы сирэйдэрэ',
 'prefs-personal' => 'Кыттааччы туруоруулара',
 'prefs-rc' => 'Кэлиҥҥи уларытыылар',
 'prefs-watchlist' => 'Кэтээһин',
@@ -1381,6 +1395,7 @@ $1 {{PLURAL:$1|бэлиэттэн|бэлиэттэн (буукубаттан)}}
 'newsectionsummary' => '/* $1 */ саҥа сиэксийэ',
 'rc-enhanced-expand' => 'Сиһилии көрдөр (JavaScript туһаныллар)',
 'rc-enhanced-hide' => 'Сиһилиитин көрдөрүмэ',
+'rc-old-title' => 'бастаан бу аатынан суруллубут "$1"',
 
 # Recent changes linked
 'recentchangeslinked' => 'Сигэнэр уларытыылар',
@@ -1677,6 +1692,8 @@ $1',
 Эбии информацияны [$2 туһунан сирэй]гэ булуохха сөп.',
 'sharedupload-desc-here' => '$1 бу билэтэ атын бырайыактарга эмиэ туттуллуон сөп.
 [$2 туһунан сирэй]тэн ылыллыбыт тиэкис аллара көрдөрүлүннэ.',
+'sharedupload-desc-edit' => 'Бу билэ манна сытар $1, хас да ситим-сиргэ туттуллар кыахтаах.
+[$2 билэ туһунан] сирэйи уларытыахха сөп.',
 'filepage-nofile' => 'Маннык ааттаах билэ суох.',
 'filepage-nofile-link' => 'Маннык ааттаах билэ суох. Ол гынан баран эн [$1 суруттарыаххын] сөп.',
 'uploadnewversion-linktext' => 'Бу билэ саҥа барылын суруттар',
@@ -2747,6 +2764,7 @@ $1',
 'spambot_username' => 'Спамы ыраастааһын',
 'spam_reverting' => 'Манна: $1 ыйынньыга суох бүтэһик торуму сөргүтүү (төннөрүү)',
 'spam_blanking' => 'Бары торумнар манна "$1" ыйынньыктаахтар, барытын суох оҥоруу',
+'spam_deleting' => 'Бары барыллар манна "$1" сигэнэллэр эит, сотуу бара турар',
 
 # Info page
 'pageinfo-title' => '"$1" туһунан',
@@ -3454,6 +3472,8 @@ MediaWiki туһалаах буоллун диэн тарҕатыллар, ол
 'version-software' => 'Туруоруллубут бырагырааммалар',
 'version-software-product' => 'Бородуукта',
 'version-software-version' => 'Барыл (торум)',
+'version-entrypoints-header-entrypoint' => 'Киирии сирэ',
+'version-entrypoints-header-url' => 'URL',
 
 # Special:FilePath
 'filepath' => 'Билэ суола',
@@ -3615,6 +3635,8 @@ MediaWiki туһалаах буоллун диэн тарҕатыллар, ол
 'api-error-empty-file' => 'Ыыппыт билэҥ кураанах.',
 'api-error-emptypage' => 'Саҥа кураанах сирэйи оҥорор табыллыбат.',
 'api-error-fetchfileerror' => 'Ис алҕас: билэни ыларга туох эрэ сатаммата.',
+'api-error-fileexists-forbidden' => 'Маннык "$1" ааттаах билэ хайыы үйэ баар уонна хат суруллар кыаҕа суох эбит.',
+'api-error-fileexists-shared-forbidden' => '«$1» диэн ааттаах билэ уопсай харайар сиргэ баар уонна хат суруллар кыаҕа суох эбит.',
 'api-error-file-too-large' => 'Ыыппыт билэҥ наһаа улахан эбит.',
 'api-error-filename-tooshort' => 'Билэҥ аата наһаа кылгас.',
 'api-error-filetype-banned' => 'Маннык көрүҥнээх билэлэр бобуулаахтар.',
@@ -3642,4 +3664,15 @@ MediaWiki туһалаах буоллун диэн тарҕатыллар, ол
 'api-error-uploaddisabled' => 'Бу биикигэ хачайдыыр араарыллыбыт эбит.',
 'api-error-verification-error' => 'Бу билэ алдьаммыт эбэтэр табыгаһа суох кэҥэтиилээх.',
 
+# Durations
+'duration-seconds' => '$1 сөкүүндэ',
+'duration-minutes' => '$1 мүнүүтэ',
+'duration-hours' => '$1 чаас',
+'duration-days' => '$1 хонук',
+'duration-weeks' => '$1 нэдиэлэ',
+'duration-years' => '$1 сыл',
+'duration-decades' => '$1 декаада',
+'duration-centuries' => '$1 үйэ',
+'duration-millennia' => '$1 тыһыынча сыл',
+
 );
index ddedea5..36b41e6 100644 (file)
@@ -364,7 +364,7 @@ Du geavaheaddjidovddaldat lea dál anus.
 'loginprompt' => 'Sisačállimii dárbbašuvvojit geavssat (cookies).',
 'userlogin' => 'Logge sisa dahje ráhkat dovddaldaga',
 'logout' => 'Čálligoađe olggos',
-'userlogout' => 'Čálligoađe olggos',
+'userlogout' => 'Logge olggos',
 'notloggedin' => 'It leat čálligoahttan sisa',
 'nologin' => "Jus dus ii vel leat geavaheaddjidovddaldat, sáhtát '''$1''' dakkára.",
 'nologinlink' => 'ráhkadit',
index 6cd98ab..cc05670 100644 (file)
@@ -269,8 +269,6 @@ $1',
 Ẓr [[Special:Version|ayyaw tasna]].',
 
 'ok' => 'Waxxa',
-'pagetitle' => '(MediaWiki)$1 - {{SITENAME}}',
-'pagetitle-view-mainpage' => '{{SITENAME}}',
 'retrievedfrom' => 'Yurrid z "$1"',
 'youhavenewmessages' => 'Illa dark $1 ($2).',
 'newmessageslink' => 'Tibratin timaynutin',
index 29c8f18..7feae4f 100644 (file)
@@ -296,17 +296,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Umožniť upravovať sekcie po kliknutí pravým tlačidlom na nadpisy sekcií (JavaScript)',
 'tog-showtoc' => 'Zobrazovať obsah (pre stránky s viac ako 3 nadpismi)',
 'tog-rememberpassword' => 'Zapamätať si prihlásenie na tomto počítači (najviac $1 {{PLURAL:$1|deň|dni|dní}})',
-'tog-watchcreations' => 'Pridávať stránky, ktoré vytvorím, automaticky medzi sledované',
-'tog-watchdefault' => 'Pridávať stránky, ktoré upravujem, automaticky medzi sledované',
-'tog-watchmoves' => 'Pridávať stránky, ktoré presuniem, do môjho zoznamu sledovaných',
-'tog-watchdeletion' => 'Pridávať stránky, ktoré zmažem, do môjho zoznamu sledovaných',
+'tog-watchcreations' => 'Pridávať stránky, ktoré vytvorím a súbory, ktoré nahrám medzi sledované',
+'tog-watchdefault' => 'Pridávať stránky a súbory, ktoré upravím medzi sledované',
+'tog-watchmoves' => 'Pridávať stránky a súbory, ktoré presuniem medzi sledované',
+'tog-watchdeletion' => 'Pridávať stránky a súbory, ktoré zmažem medzi sledované',
 'tog-minordefault' => 'Označovať všetky zmeny štandardne ako drobné',
 'tog-previewontop' => 'Zobrazovať náhľad pred textovým poľom úprav, nie až za ním',
 'tog-previewonfirst' => 'Zobraziť náhľad pred prvou úpravou',
 'tog-nocache' => 'Zakázať ukladanie stránok do vyrovnávacej pamäte prehliadača',
-'tog-enotifwatchlistpages' => 'Upozorniť ma emailom, keď sa zmení stránka z môjho zoznamu sledovaných',
+'tog-enotifwatchlistpages' => 'Upozorniť ma emailom, keď sa zmení stránka alebo súbor z môjho zoznamu sledovaných',
 'tog-enotifusertalkpages' => 'Upozorniť ma emailom po zmene mojej používateľskej diskusnej stránky',
-'tog-enotifminoredits' => 'Upozorniť ma emailom aj na drobné úpravy stránok',
+'tog-enotifminoredits' => 'Upozorniť ma emailom aj na drobné úpravy stránok a súborov',
 'tog-enotifrevealaddr' => 'Zobraziť moju emailovú adresu v emailoch s upozorneniami',
 'tog-shownumberswatching' => 'Zobraziť počet používateľov sledujúcich stránku',
 'tog-oldsig' => 'Súčasný podpis:',
index 2408a3b..5751f3c 100644 (file)
@@ -199,17 +199,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Omogoči urejanje razdelkov z desnim klikanjem njihovih naslovov (zahteva JavaScript)',
 'tog-showtoc' => 'Prikaži vsebino (strani z več kot tremi naslovi)',
 'tog-rememberpassword' => 'Zapomni si me v tem brskalniku (za največ $1 {{PLURAL:$1|dan|dneva|dni}})',
-'tog-watchcreations' => 'Vse ustvarjene strani dodaj na spisek nadzorov',
-'tog-watchdefault' => 'Dodaj na spisek nadzorov vse članke, ki sem jih ustvaril/-a ali spremenil/-a',
-'tog-watchmoves' => 'Dodaj strani, ki jih premaknem, na moj spisek nadzorov',
-'tog-watchdeletion' => 'Dodaj strani, ki jih izbrišem, na moj spisek nadzorov',
+'tog-watchcreations' => 'Vse ustvarjene strani in moje naložene datoteke dodaj na spisek nadzorov',
+'tog-watchdefault' => 'Dodaj na spisek nadzorov vse članke in datoteke, ki sem jih spremenil/-a',
+'tog-watchmoves' => 'Dodaj strani in datoteke, ki jih premaknem, na moj spisek nadzorov',
+'tog-watchdeletion' => 'Dodaj strani in datoteke, ki jih izbrišem, na moj spisek nadzorov',
 'tog-minordefault' => 'Vsa urejanja označi kot manjša',
 'tog-previewontop' => 'Prikaži predogled pred urejevalnim poljem in ne za njim',
 'tog-previewonfirst' => 'Ob začetku urejanja prikaži predogled',
 'tog-nocache' => 'Onemogoči predpomnenje strani v brskalniku',
-'tog-enotifwatchlistpages' => 'Ob spremembah strani mi pošlji e-pošto',
+'tog-enotifwatchlistpages' => 'Ob spremembah strani ali datotek mi pošlji e-pošto',
 'tog-enotifusertalkpages' => 'Pošlji e-pošto ob spremembah moje pogovorne strani',
-'tog-enotifminoredits' => 'Pošlji e-pošto tudi za manjše spremembe strani',
+'tog-enotifminoredits' => 'Pošlji e-pošto tudi za manjše spremembe strani in datotek',
 'tog-enotifrevealaddr' => 'V sporočilih z obvestili o spremembah razkrij moj e-poštni naslov',
 'tog-shownumberswatching' => 'Prikaži število uporabnikov, ki spremljajo temo',
 'tog-oldsig' => 'Obstoječi podpis:',
index 9868b9b..66ecaa6 100644 (file)
@@ -341,7 +341,7 @@ $messages = array(
 'tog-nocache' => 'Stäng av cachelagring för sidor',
 'tog-enotifwatchlistpages' => 'Skicka e-post till mig när en sida på min bevakningslista ändras',
 'tog-enotifusertalkpages' => 'Skicka e-post till mig när något händer på min diskussionssida',
-'tog-enotifminoredits' => 'Skicka mig e-post även för mindre ändringar',
+'tog-enotifminoredits' => 'Skicka mig e-post även för mindre ändringar av sidor och filer',
 'tog-enotifrevealaddr' => 'Visa min e-postadress i e-postmeddelanden om ändringar som skickas till andra',
 'tog-shownumberswatching' => 'Visa antalet användare som bevakar',
 'tog-oldsig' => 'Nuvarande signatur:',
@@ -675,6 +675,8 @@ Rapportera gärna problemet till någon [[Special:ListUsers/sysop|administratör
 'cannotdelete' => 'Sidan eller filen "$1" kunde inte raderas.
 Den kanske redan har raderats av någon annan.',
 'cannotdelete-title' => 'Sidan "$1" kan inte raderas',
+'delete-hook-aborted' => 'Borttagning avbruten av hook.
+Den gav ingen förklaring.',
 'badtitle' => 'Felaktig titel',
 'badtitletext' => 'Den begärda sidtiteln är antingen ogiltig eller tom, eller så är titeln felaktigt länkad från en annan wiki.
 Den kan innehålla ett eller flera tecken som inte får användas i sidtitlar.',
@@ -3952,6 +3954,8 @@ Annars kan du använda det enkla formuläret nedan. Din kommentar kommer att lä
 'api-error-empty-file' => 'Filen du skickade var tom.',
 'api-error-emptypage' => 'Det är inte tillåtet att skapa nya, tomma sidor.',
 'api-error-fetchfileerror' => 'Internt fel: något gick fel vid hämtningen av filen.',
+'api-error-fileexists-forbidden' => 'En fil med namnet "$1" finns redan och kan inte skrivas över.',
+'api-error-fileexists-shared-forbidden' => 'En fil med namnet "$1" finns redan i det delade filarkivet och kan inte skrivas över.',
 'api-error-file-too-large' => 'Filen du skickade var för stor.',
 'api-error-filename-tooshort' => 'Filnamnet är för kort.',
 'api-error-filetype-banned' => 'Denna typ av fil är förbjuden.',
index bed45fd..5e00b80 100644 (file)
@@ -726,6 +726,7 @@ or <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}}
 'updated' => '(இற்றைப்படுத்தப்பட்டது)',
 'note' => "'''குறிப்பு:'''",
 'previewnote' => "'''இது ஒரு முன்தோற்றம் மட்டுமே''', உங்கள் மாற்றங்கள் இன்னும் சேமிக்கப்படவில்லை!",
+'continue-editing' => 'தொகுத்தலைத் தொடரவும்',
 'previewconflict' => 'இந்த முன்தோற்றம் உரை தொகுப்புப் பகுதியின் மேற்பகுதியிலுள்ள உரையைப் பிரதிபலிக்கின்றது. நீங்கள் இப்பொழுது சேமித்தால் மேற்படி தோற்றமே கிடைக்கும்.',
 'session_fail_preview' => "'''உங்கள் அமர்வுத் தரவுகள் அழிந்துப்போனமையால் உங்கள் தொகுப்பை செயற்படுத்த முடியவில்லை. அருள் கூர்ந்து மீண்டும் முயலவும். அதுவும் பலனளிக்காவிட்டால் விடுபதிகைச் செய்து மீண்டும் புகுபதிகைச் செய்யவும்'''",
 'session_fail_preview_html' => "'''மன்னிக்கவும்! தங்கள் அமர்வுத் தரவுகள் அழிந்துப்போனமையால் தொகுப்பைச் செயற்படுத்த முடியவில்லை.'''
index 6c79c31..f0c4da6 100644 (file)
@@ -148,17 +148,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Payagan ang mga pagbabagong panseksyon sa pakanang pagpindot ng mga panseksyong pamagat (JavaScript)',
 'tog-showtoc' => 'Ipakita ang talaan ng mga nilalaman (sa mga pahinang may higit sa 3 punong pamagat)',
 'tog-rememberpassword' => 'Tandaan ang paglagda ko sa panghanaphanap na ito (pinakamarami na ang $1 {{PLURAL:$1|araw|mga araw}})',
-'tog-watchcreations' => 'Idagdag ang mga pahinang nilikha ko sa aking tala ng mga binabantayan',
-'tog-watchdefault' => 'Idagdag ang mga pahinang binago ko sa aking tala ng mga binabantayan',
-'tog-watchmoves' => 'Idagdag ang mga pahinang inilipat ko sa aking tala ng mga binabantayan',
-'tog-watchdeletion' => 'Idagdag mga pahinang ibinura ko sa aking tala ng mga binabantayan',
+'tog-watchcreations' => 'Idagdag sa aking tala ng mga binabantayan ang mga pahinang nilikha ko at mga talaksang ikinarga kong paitaas',
+'tog-watchdefault' => 'Idagdag sa aking tala ng mga binabantayan ang mga pahina at mga talaksang binago ko',
+'tog-watchmoves' => 'Idagdag sa aking tala ng mga binabantayan ang mga pahina at mga talaksang inilipat ko',
+'tog-watchdeletion' => 'Idagdag sa aking tala ng mga binabantayan ang mga pahina at mga talaksang binura ko',
 'tog-minordefault' => 'Markahan ang lahat ng pagbabago bilang maliit nang nakatakda',
 'tog-previewontop' => 'Ipakita ang paunang tingin bago ang kahon ng pagbabago',
 'tog-previewonfirst' => 'Ipakita ang paunang tingin sa unang pagbabago',
 'tog-nocache' => 'Huwag paganahin ang pagtatago ng pahinang pantingintingin',
-'tog-enotifwatchlistpages' => 'Padalhan ako ng e-liham kapag nabago ang isa sa mga pahinang binabantayan ko',
+'tog-enotifwatchlistpages' => 'Padalhan ako ng e-liham kapag nabago ang isa sa pahina o talaksang binabantayan ko',
 'tog-enotifusertalkpages' => 'Padalhan ako ng e-liham kapag binago ang aking pahina ng usapan',
-'tog-enotifminoredits' => 'Padalhan din ako ng e-liham para sa mga maliliit na pagbabago ng mga pahina',
+'tog-enotifminoredits' => 'Padalhan din ako ng e-liham para sa mga maliliit na mga pagbabago ng mga pahina at mga talaksan',
 'tog-enotifrevealaddr' => 'Ipakita ang aking direksiyong e-liham sa loob ng mga e-liham ng pagpapahayag',
 'tog-shownumberswatching' => 'Ipakita ang bilang ng mga nagbabantay na tagagamit',
 'tog-oldsig' => 'Umiiral na lagda:',
@@ -2261,10 +2261,10 @@ Tingnan ang [[Special:ProtectedPages|talaan ng pinuprutektahang mga pahina]] par
 'prot_1movedto2' => 'Inilipat ang [[$1]] patungo sa [[$2]]',
 'protect-badnamespace-title' => 'Hindi mapupruteksiyunang puwang ng pangalan',
 'protect-badnamespace-text' => 'Hindi mapupruteksiyunan ang mga pahinang nasa puwang na pampangalang ito.',
-'protect-legend' => 'Pagtibayin/tiyakin ang panananggalang',
+'protect-legend' => 'Tiyakin ang panananggalang',
 'protectcomment' => 'Dahilan:',
 'protectexpiry' => 'Magtatapos sa:',
-'protect_expiry_invalid' => 'Hindi tanggap/hindi tama ang oras ng pagtatapos.',
+'protect_expiry_invalid' => 'Hindi katanggap-tanggap ang oras ng pagtatapos.',
 'protect_expiry_old' => 'Nasa nakaraan ang oras ng pagtatapos.',
 'protect-unchain-permissions' => 'Huwag ikandado ang iba pang mga pagpipilian ng pagprutekta',
 'protect-text' => "Maaari mong tingnan at baguhin dito ang antas ng pananananggalang para sa pahinang '''$1'''.",
index 11a673d..98ff709 100644 (file)
@@ -39,14 +39,14 @@ $messages = array(
 'tog-hideminor' => 'Охоминә дәгишонәдә гәдә дәгишон нишо мәдә.',
 'tog-hidepatrolled' => 'Нујә дәгишон сијоһијәдә дәвинә кардә быә дәгишон нишо мәкә.',
 'tog-newpageshidepatrolled' => 'Нијони огәтеј ноғо доә быә сәһифон бә тожә сәһифон сијоһиәдә',
-'tog-usenewrc' => 'Охоминә дәгишон ән чокә сијоһи око дој (гәрәке JavaScript)',
+'tog-usenewrc' => 'Охоминә дәгишон сәһифәдә ијән ноғо доә сијоһијәдә дәгишон бә дәстон ҹо кардеј (гәрәке JavaScript)',
 'tog-numberheadings' => 'Автоматик башлығон нумрәләмиш быкә',
 'tog-showtoc' => 'Мындәриҹоти сијоһи нишо быдә (3 сәрловһәсә веј быә сәһифон)',
-'tog-watchcreations' => 'Зијод кардеј чымы офәјә быә сәһифон бә ноғо доә сијоһи',
-'tog-watchdefault' => 'Зијод кардеј демы дәгиш кардә быә сәһифон бә ноғо доә сијоһи',
-'tog-watchmoves' => 'Зијод кардеј ном дәгиш кардә быә сәһифон бә ноғо доә сијоһи',
-'tog-watchdeletion' => 'Ð\97иÑ\98од ÐºÐ°Ñ\80деÑ\98 Ñ\81Ó\99һиÑ\84он комон аз рәдд кардәме бә ноғо доә сијоһи',
-'tog-enotifwatchlistpages' => 'Ноғо доә сијоһиәдә кејнә сәһифон дәгиш бәбен бәмы е-номә бывығанд',
+'tog-watchcreations' => 'Ð\97иÑ\98од ÐºÐ°Ñ\80деÑ\98 Ñ\87Ñ\8bмÑ\8b Ð¾Ñ\84Ó\99Ñ\98Ó\99 Ð±Ñ\8bÓ\99 Ñ\81Ó\99һиÑ\84он Ð¸Ñ\98Ó\99н Ñ\84аÑ\98лон Ð±Ó\99 Ð½Ð¾Ò\93о Ð´Ð¾Ó\99 Ñ\81иÑ\98оһи',
+'tog-watchdefault' => 'Ð\97иÑ\98од ÐºÐ°Ñ\80деÑ\98 Ð´ÐµÐ¼Ñ\8b Ð´Ó\99гиÑ\88 ÐºÐ°Ñ\80дÓ\99 Ð±Ñ\8bÓ\99 Ñ\81Ó\99һиÑ\84он Ð¸Ñ\98Ó\99н Ñ\84аÑ\98лон Ð±Ó\99 Ð½Ð¾Ò\93о Ð´Ð¾Ó\99 Ñ\81иÑ\98оһи',
+'tog-watchmoves' => 'Зијод кардеј фајлон ијән ном дәгиш кардә быә сәһифон бә ноғо доә сијоһи',
+'tog-watchdeletion' => 'Ð\97иÑ\98од ÐºÐ°Ñ\80деÑ\98 Ñ\84аÑ\98лон Ð¸Ñ\98Ó\99н Ñ\81Ó\99һиÑ\84он, комон аз рәдд кардәме бә ноғо доә сијоһи',
+'tog-enotifwatchlistpages' => 'Ноғо доә сијоһиәдә кејнә сәһифон ја фајлон дәгиш бәбен бәмы е-номә бывығанд',
 'tog-watchlisthideown' => 'Чымы дәгишон ноғо доә сијһиәдә нијо кардеј',
 'tog-watchlisthidebots' => 'Нијо кардеј ботон дәгишон ноғо доә сијоһиәдә',
 'tog-watchlisthideminor' => 'Нијо кардеј гәдә дәгишон ноғо доә сијоһиәдә',
index 6dd36aa..a759d0d 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Tuvinian (Тыва дыл)
+/** Tuvinian (тыва дыл)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index ed20cdc..51e38fd 100644 (file)
@@ -362,17 +362,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Редагувати розділи при клацанні правою кнопкою мишки на заголовку (JavaScript)',
 'tog-showtoc' => 'Показувати зміст (для сторінок з більш ніж трьома заголовками)',
 'tog-rememberpassword' => "Запам'ятати мій обліковий запис для цього браузера (на строк не більше $1 {{PLURAL:$1|дня|днів}})",
-'tog-watchcreations' => 'Додавати створені мною сторінки до мого списку спостереження',
-'tog-watchdefault' => 'Додавати змінені мною сторінки до мого списку спостереження',
-'tog-watchmoves' => 'Додавати перейменовані мною сторінки до мого списку спостереження',
-'tog-watchdeletion' => 'Додавати вилучені мною сторінки до мого списку спостереження',
+'tog-watchcreations' => 'Додавати створені мною сторінки і завантажені мною файли до мого списку спостереження',
+'tog-watchdefault' => 'Додавати змінені мною сторінки та файли до мого списку спостереження',
+'tog-watchmoves' => 'Додавати перейменовані мною сторінки та файли до мого списку спостереження',
+'tog-watchdeletion' => 'Додавати вилучені мною сторінки та файли до мого списку спостереження',
 'tog-minordefault' => 'Спочатку позначати всі зміни незначними',
 'tog-previewontop' => 'Показувати попередній перегляд перед вікном редагування, а не після',
 'tog-previewonfirst' => 'Показувати попередній перегляд під час першого редагування',
 'tog-nocache' => 'Відключити кешування сторінок браузером',
-'tog-enotifwatchlistpages' => 'Повідомляти електронною поштою, коли сторінка з мого списку спостереження змінилася',
+'tog-enotifwatchlistpages' => 'Повідомляти електронною поштою при зміні сторінки або файлу з мого списку спостереження',
 'tog-enotifusertalkpages' => 'Повідомляти електронною поштою про зміну моєї сторінки обговорення',
-'tog-enotifminoredits' => 'Надсилати мені електронного листа навіть при незначних редагуваннях',
+'tog-enotifminoredits' => 'Надсилати мені електронного листа навіть при незначних редагуваннях сторінок та файлів',
 'tog-enotifrevealaddr' => 'Показувати мою поштову адресу в повідомленнях',
 'tog-shownumberswatching' => 'Показувати кількість користувачів, які додали сторінку до свого списку спостереження',
 'tog-oldsig' => 'Існуючий підпис:',
index 21ff98b..12d0b5e 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Uzbek (Oʻzbekcha)
+/** Uzbek (oʻzbekcha)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index 0ae2302..49dc5d8 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Veps (Vepsän kel’)
+/** Veps (vepsän kel’)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index 547bea9..7804bdb 100644 (file)
@@ -309,17 +309,17 @@ $messages = array(
 'tog-editsectiononrightclick' => 'Cho phép sửa đổi đề mục bằng cách bấm chuột phải trên tên đề mục (JavaScript)',
 'tog-showtoc' => 'Hiển thị mục lục (cho trang có trên 3 đề mục)',
 'tog-rememberpassword' => 'Nhớ thông tin đăng nhập của tôi trong trình duyệt này (cho đến $1 ngày)',
-'tog-watchcreations' => 'Tự động theo dõi trang tôi viết mới',
-'tog-watchdefault' => 'Tự động theo dõi trang tôi sửa',
-'tog-watchmoves' => 'Tự động theo dõi trang tôi di chuyển',
-'tog-watchdeletion' => 'Tự động theo dõi trang tôi xóa',
+'tog-watchcreations' => 'Tự động theo dõi các trang tôi viết mới và các tập tin tôi tải lên',
+'tog-watchdefault' => 'Tự động theo dõi các trang và tập tin tôi sửa',
+'tog-watchmoves' => 'Tự động theo dõi các trang và tập tin tôi di chuyển',
+'tog-watchdeletion' => 'Tự động theo dõi các trang và tập tin tôi xóa',
 'tog-minordefault' => 'Mặc định đánh dấu tất cả sửa đổi của tôi là sửa đổi nhỏ',
 'tog-previewontop' => 'Hiển thị phần xem thử nằm trên hộp sửa đổi',
 'tog-previewonfirst' => 'Hiện xem thử tại lần sửa đầu tiên',
 'tog-nocache' => 'Không lưu trang trong bộ nhớ đệm trình duyệt',
-'tog-enotifwatchlistpages' => 'Gửi thư cho tôi khi có thay đổi tại trang tôi theo dõi',
+'tog-enotifwatchlistpages' => 'Gửi thư cho tôi khi có thay đổi tại trang hoặc tập tin tôi theo dõi',
 'tog-enotifusertalkpages' => 'Gửi thư cho tôi khi có thay đổi tại trang thảo luận của tôi',
-'tog-enotifminoredits' => 'Gửi thư cho tôi cả những thay đổi nhỏ trong trang',
+'tog-enotifminoredits' => 'Gửi thư cho tôi cả những thay đổi nhỏ trong trang và tập tin',
 'tog-enotifrevealaddr' => 'Hiện địa chỉ thư điện tử của tôi trong thư thông báo',
 'tog-shownumberswatching' => 'Hiển thị số người đang xem',
 'tog-oldsig' => 'Chữ ký hiện tại:',
index 2aea831..8f77bc5 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Kalmyk (Хальмг)
+/** Kalmyk (хальмг)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
index e578859..02fd042 100644 (file)
@@ -196,15 +196,15 @@ $messages = array(
 'tog-editsectiononrightclick' => 'באמעגליך פאראגראף ענדערונגען דורכן קוועטשן אויפן רעכטן<br />אויף אפטייל קעפל (JavaScript)',
 'tog-showtoc' => 'ווייז דאס אינהאלט קעסטל<br />(פאר בלעטער מיט מער ווי 3 קעפלעך)',
 'tog-rememberpassword' => 'געדענק מיין אריינלאגירן אין דעם בלעטערער (ביז $1 {{PLURAL:$1|טאָג|טעג}})',
-'tog-watchcreations' => 'צולייגן בלעטער וואס איך באשאף צו מיין אכטונג ליסטע',
+'tog-watchcreations' => 'צ×\95×\9c×\99×\99×\92×\9f ×\91×\9c×¢×\98ער ×\95×\95×\90ס ×\90×\99×\9a ×\91×\90ש×\90×£ ×\90×\95×\9f ×\98עקעס ×\95×\95×\90ס ×\90×\99×\9a ×\9c×\90×\93 ×\90ר×\95×\99×£ ×¦×\95 ×\9e×\99×\99×\9f ×\90×\9b×\98×\95× ×\92 ×\9c×\99ס×\98×¢',
 'tog-watchdefault' => 'אויפפאסן אױטאָמאַטיש די ארטיקלען װאָס איך באַאַרבעט',
-'tog-watchmoves' => 'צולייגן בלעטער וואס איך באוועג צו מיין אכטונג ליסטע',
+'tog-watchmoves' => 'צ×\95×\9c×\99×\99×\92×\9f ×\91×\9c×¢×\98ער ×\95×\95×\90ס ×\90×\99×\9a ×\91×\90×\95×\95×¢×\92 ×\90×\95×\9f ×\98עקעס ×\95×\95×\90ס ×\90×\99×\9a ×\9c×\90×\93 ×\90ר×\95×\99×£ ×¦×\95 ×\9e×\99×\99×\9f ×\90×\9b×\98×\95× ×\92 ×\9c×\99ס×\98×¢',
 'tog-watchdeletion' => 'צולייגן בלעטער וואס איך מעק אויס צו מיין אויפפאסונג ליסטע',
 'tog-minordefault' => 'באגרענעצן אלע רעדאַקטירונגען גרונטלעך אלס מינערדיק',
 'tog-previewontop' => 'צײַג די "פֿאָרויסיגע װײַזונג" גלײַך בײַ דער ערשטער באַאַרבעטונג',
 'tog-previewonfirst' => 'ווייזן פֿאראויסדיגע ווייזונג בײַ דער ערשטער רעדאקטירונג',
 'tog-nocache' => 'מבטל זײַן האַלטן בלעטער אין זאַפאַס',
-'tog-enotifwatchlistpages' => 'שיק מיר א בליצבריוו ווען א בלאט וואס איך פאס אויף ווערט געענדערט',
+'tog-enotifwatchlistpages' => 'שיקט מיר א בליצבריוו ווען א בלאט וואס איך פאס אויף ווערט געענדערט',
 'tog-enotifusertalkpages' => 'שיקט מיר ע-פאסט ווען עס ווערט געענדערט מיין באניצער רעדן בלאט',
 'tog-enotifminoredits' => 'שיקט מיר ע-פאסט אויך פֿאַר מינערדיקע רעדאַקטירונגען פֿון בלעטער',
 'tog-enotifrevealaddr' => 'דעק אויף מיין בליצפאסט אדרעס אין פאסט מודעות',
@@ -1064,6 +1064,7 @@ $2
 'logdelete-selected' => "'''{{PLURAL:$1| אויסדערוויילטע לאג אקציע|אויסדערוויילטע לאג אקציעס}}:'''",
 'revdelete-text' => "'''אויסגעמעקטע רעוויזיעס און געשעענישן וועלן בלייבן אין דער בלאט היסטאריע און די לאגביכער, אבער טיילן פון זייער אינהאלט וועט ווערן אומגרייכלעך צום קהל. '''
 אנדערע סיסאפן אויף {{SITENAME}} וועלן נאך האבן צוטריט צום באהאלטענעם אינהאלט און קענען אים צוריקשטעלן דורך דעם זעלבן אייבערפלאך,  אחוץ ווען מען שטעלט נאך באגרענעצונגען.",
+'revdelete-confirm' => 'זייט אזוי גוט און באשטעטיקט אז דאס איז טאקע אייער כוונה, אז איר פארשטייט די קאנסעקווענצן, און אז איר טוט דאס לויט  [[{{MediaWiki:Policy-url}}|דער פאליסי]].',
 'revdelete-suppress-text' => "באהאלטן זאל בלויז גענוצט ווערן '''נאר''' אין די פאלגענדע פעלער:
 * אויפדעקונג פון פריוואטקייט אינפארמאציע
 * ''היים אדרעסן, טעלעפאן נומערן, אדער סאשעל סעקיורעטי, א.א.וו.:'''",
index 5f2566f..59045cd 100644 (file)
@@ -224,17 +224,17 @@ $messages = array(
 'tog-editsectiononrightclick' => '允許右擊標題編輯段落 (需要JavaScript)',
 'tog-showtoc' => '顯示目錄 (針對一頁超過3個標題的頁面)',
 'tog-rememberpassword' => '在這個瀏覽器上記住我的登入資訊(可維持 $1 {{PLURAL:$1|天|天}})',
-'tog-watchcreations' => '將我建立的頁面添加到我的監視列表中',
-'tog-watchdefault' => '將我更改的頁面添加到我的監視列表中',
-'tog-watchmoves' => 'å°\87æ\88\91移å\8b\95ç\9a\84é \81é\9d¢å\8a å\85¥我的監視列表',
-'tog-watchdeletion' => 'å°\87æ\88\91å\88ªé\99¤ç\9a\84é \81é\9d¢å\8a å\85¥我的監視列表',
+'tog-watchcreations' => '將我建立的頁面和檔案添加到我的監視列表中',
+'tog-watchdefault' => '將我更改的頁面和檔案添加到我的監視列表中',
+'tog-watchmoves' => 'å°\87æ\88\91移å\8b\95ç\9a\84é \81é\9d¢å\92\8cæª\94æ¡\88æ·»å\8a å\88°我的監視列表',
+'tog-watchdeletion' => 'å°\87æ\88\91å\88ªé\99¤ç\9a\84é \81é\9d¢å\92\8cæª\94æ¡\88æ·»å\8a å\88°我的監視列表',
 'tog-minordefault' => '預設將編輯設定為小編輯',
 'tog-previewontop' => '在編輯框上方顯示預覽',
 'tog-previewonfirst' => '第一次編輯時顯示原文內容的預覽',
 'tog-nocache' => '禁止瀏覽器頁面快取',
-'tog-enotifwatchlistpages' => '當在我的監視列表中的頁面改變時發電子郵件給我',
+'tog-enotifwatchlistpages' => 'ç\95¶å\9c¨æ\88\91ç\9a\84ç\9b£è¦\96å\88\97表中ç\9a\84é \81é\9d¢æ\88\96æª\94æ¡\88æ\94¹è®\8aæ\99\82ç\99¼é\9b»å­\90é\83µä»¶çµ¦æ\88\91',
 'tog-enotifusertalkpages' => '當我的對話頁發生改變時發電子郵件給我',
-'tog-enotifminoredits' => '即使是頁面的小修改也向我發電子郵件',
+'tog-enotifminoredits' => '即使是頁面和檔案的小修改也向我發電子郵件',
 'tog-enotifrevealaddr' => '在通知電子郵件中顯示我的電子郵件位址',
 'tog-shownumberswatching' => '顯示監視用戶的數目',
 'tog-oldsig' => '原有簽名:',
index 4a52d10..706f87f 100755 (executable)
@@ -1,12 +1,35 @@
 <?php\r
 /**\r
- * Description: This script takes $wgHiddenPrefs and removes their preference from the DB. [[bugzilla:30976]]\r
+ * Remove hidden preferences from the database.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
+ * http://www.gnu.org/copyleft/gpl.html\r
+ *\r
+ * @file\r
  * @author TyA <tya.wiki@gmail.com>\r
+ * @see [[bugzilla:30976]]\r
  * @ingroup Maintenance\r
  */\r
 \r
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );\r
 \r
+/**\r
+ * Maintenance script that removes hidden preferences from the database.\r
+ *\r
+ * @ingroup Maintenance\r
+ */\r
 class CleanupPreferences extends Maintenance {\r
        public function execute() {\r
                global $wgHiddenPrefs;\r
index 530b5ca..498bc6b 100644 (file)
@@ -42,6 +42,9 @@ class CopyFileBackend extends Maintenance {
                $this->addOption( 'dst', 'Backend where files should be copied to', true, true );
                $this->addOption( 'containers', 'Pipe separated list of containers', true, true );
                $this->addOption( 'subdir', 'Only do items in this child directory', false, true );
+               $this->addOption( 'ratefile', 'File to check periodically for batch size', false, true );
+               $this->addOption( 'skiphash', 'Skip SHA-1 sync checks for files' );
+               $this->addOption( 'missingonly', 'Only copy files missing from destination listing' );
                $this->setBatchSize( 50 );
        }
 
@@ -51,6 +54,8 @@ class CopyFileBackend extends Maintenance {
                $containers = explode( '|', $this->getOption( 'containers' ) );
                $subDir = $this->getOption( rtrim( 'subdir', '/' ), '' );
 
+               $rateFile = $this->getOption( 'ratefile' );
+
                $count = 0;
                foreach ( $containers as $container ) {
                        if ( $subDir != '' ) {
@@ -61,14 +66,41 @@ class CopyFileBackend extends Maintenance {
                                $this->output( "Doing container '$container'...\n" );
                        }
 
-                       $dir = $src->getRootStoragePath() . "/$backendRel";
-                       $srcPathsRel = $src->getFileList( array( 'dir' => $dir ) );
+                       $srcPathsRel = $src->getFileList( array(
+                               'dir' => $src->getRootStoragePath() . "/$backendRel" ) );
                        if ( $srcPathsRel === null ) {
                                $this->error( "Could not list files in $container.", 1 ); // die
                        }
 
+                       // Do a listing comparison if specified
+                       if ( $this->hasOption( 'missingonly' ) ) {
+                               $relFilesSrc = array();
+                               $relFilesDst = array();
+                               foreach ( $srcPathsRel as $srcPathRel ) {
+                                       $relFilesSrc[] = $srcPathRel;
+                               }
+                               $dstPathsRel = $dst->getFileList( array(
+                                       'dir' => $dst->getRootStoragePath() . "/$backendRel" ) );
+                               if ( $dstPathsRel === null ) {
+                                       $this->error( "Could not list files in $container.", 1 ); // die
+                               }
+                               foreach ( $dstPathsRel as $dstPathRel ) {
+                                       $relFilesDst[] = $dstPathRel;
+                               }
+                               // Only copy the missing files over in the next loop
+                               $srcPathsRel = array_diff( $relFilesSrc, $relFilesDst );
+                               $this->output( count( $srcPathsRel ) . " file(s) need to be copied.\n" );
+                               unset( $relFilesSrc );
+                               unset( $relFilesDst );
+                       }
+
                        $batchPaths = array();
                        foreach ( $srcPathsRel as $srcPathRel ) {
+                               // Check up on the rate file periodically to adjust the concurrency
+                               if ( $rateFile && ( !$count || ( $count % 500 ) == 0 ) ) {
+                                       $this->mBatchSize = max( 1, (int)file_get_contents( $rateFile ) );
+                                       $this->output( "Batch size is now {$this->mBatchSize}.\n" );
+                               }
                                $batchPaths[$srcPathRel] = 1; // remove duplicates
                                if ( count( $batchPaths ) >= $this->mBatchSize ) {
                                        $this->copyFileBatch( array_keys( $batchPaths ), $backendRel, $src, $dst );
@@ -96,6 +128,7 @@ class CopyFileBackend extends Maintenance {
        ) {
                $ops = array();
                $fsFiles = array();
+               $copiedRel = array(); // for output message
                foreach ( $srcPathsRel as $srcPathRel ) {
                        $srcPath = $src->getRootStoragePath() . "/$backendRel/$srcPathRel";
                        $dstPath = $dst->getRootStoragePath() . "/$backendRel/$srcPathRel";
@@ -110,33 +143,42 @@ class CopyFileBackend extends Maintenance {
                        }
                        $fsFiles[] = $fsFile; // keep TempFSFile objects alive as needed
                        // Note: prepare() is usually fast for key/value backends
-                       $status = $dst->prepare( array( 'dir' => dirname( $dstPath ) ) );
+                       $status = $dst->prepare( array( 'dir' => dirname( $dstPath ), 'bypassReadOnly' => 1 ) );
                        if ( !$status->isOK() ) {
                                $this->error( print_r( $status->getErrorsArray(), true ) );
                                $this->error( "Could not copy $srcPath to $dstPath.", 1 ); // die
                        }
                        $ops[] = array( 'op' => 'store',
                                'src' => $fsFile->getPath(), 'dst' => $dstPath, 'overwrite' => 1 );
+                       $copiedRel[] = $srcPathRel;
                }
 
-               $status = $dst->doOperations( $ops, array( 'nonJournaled' => 1 ) );
+               $t_start = microtime( true );
+               $status = $dst->doQuickOperations( $ops, array( 'bypassReadOnly' => 1 ) );
+               if ( !$status->isOK() ) {
+                       sleep( 10 ); // wait and retry copy again
+                       $status = $dst->doQuickOperations( $ops, array( 'bypassReadOnly' => 1 ) );
+               }
+               $ellapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
                if ( !$status->isOK() ) {
                        $this->error( print_r( $status->getErrorsArray(), true ) );
                        $this->error( "Could not copy file batch.", 1 ); // die
-               } else {
-                       $this->output( "Copied these file(s):\n" . implode( "\n", $srcPathsRel ) . "\n\n" );
+               } elseif ( count( $copiedRel ) ) {
+                       $this->output( "\nCopied these file(s) [{$ellapsed_ms}ms]:\n" .
+                               implode( "\n", $copiedRel ) . "\n\n" );
                }
        }
 
        protected function filesAreSame( FileBackend $src, FileBackend $dst, $sPath, $dPath ) {
+               $skipHash = $this->hasOption( 'skiphash' );
                return (
                        ( $src->fileExists( array( 'src' => $sPath, 'latest' => 1 ) )
                                === $dst->fileExists( array( 'src' => $dPath, 'latest' => 1 ) ) // short-circuit
                        ) && ( $src->getFileSize( array( 'src' => $sPath, 'latest' => 1 ) )
                                === $dst->getFileSize( array( 'src' => $dPath, 'latest' => 1 ) ) // short-circuit
-                       ) && ( $src->getFileSha1Base36( array( 'src' => $sPath, 'latest' => 1 ) )
+                       ) && ( $skipHash || ( $src->getFileSha1Base36( array( 'src' => $sPath, 'latest' => 1 ) )
                                === $dst->getFileSha1Base36( array( 'src' => $dPath, 'latest' => 1 ) )
-                       )
+                       ) )
                );
        }
 }
index 197ffab..414d41a 100644 (file)
@@ -1,7 +1,6 @@
 <?php
-
 /**
- * Delete archived (deleted from public) revisions from the database
+ * Helper methods for the deleteArchivedRevisions.php maintenance script.
  *
  * 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
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
+/**
+ * Helper methods for the deleteArchivedRevisions.php maintenance script.
+ *
+ * @ingroup Maintenance
+ */
 class DeleteArchivedRevisionsImplementation {
 
        /**
index 8963ae1..2a99fa1 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script to delete a batch of pages.
+ *
+ * @ingroup Maintenance
+ */
 class DeleteBatch extends Maintenance {
 
        public function __construct() {
index 4f5889b..540d225 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that deletes all pages in the MediaWiki namespace
+ * which were last edited by "MediaWiki default".
+ *
+ * @ingroup Maintenance
+ */
 class DeleteDefaultMessages extends Maintenance {
        public function __construct() {
                parent::__construct();
index 007f0d1..2029b57 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * This script delete image information from the cache.
+ * Delete image information from the object cache.
  *
  * Usage example:
  * php deleteImageMemcached.php --until "2005-09-05 00:00:00" --sleep 0
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that deletes image information from the object cache.
+ *
+ * @ingroup Maintenance
+ */
 class DeleteImageCache extends Maintenance {
        public function __construct() {
                parent::__construct();
index 8347a29..45a6b34 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 /**
  * Delete old (non-current) revisions from the database
  *
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  * @author Rob Church <robchur@gmail.com>
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that deletes old (non-current) revisions from the database.
+ *
+ * @ingroup Maintenance
+ */
 class DeleteOldRevisions extends Maintenance {
        public function __construct() {
                parent::__construct();
index 9fe5794..13b9c91 100644 (file)
@@ -1,8 +1,7 @@
 <?php
-
 /**
- * Maintenance script to delete revisions which refer to a nonexisting page
- * Sometimes manual deletion done in a rush leaves crap in the database
+ * Delete revisions which refer to a nonexisting page.
+ * Sometimes manual deletion done in a rush leaves crap in the database.
  *
  * 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
@@ -19,6 +18,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  * @author Rob Church <robchur@gmail.com>
  * @todo More efficient cleanup of text records
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that deletes revisions which refer to a nonexisting page.
+ *
+ * @ingroup Maintenance
+ */
 class DeleteOrphanedRevisions extends Maintenance {
        public function __construct() {
                parent::__construct();
index 5e8ecaa..fe3b515 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that deletes one or more revisions by moving them
+ * to the archive table.
+ *
+ * @ingroup Maintenance
+ */
 class DeleteRevision extends Maintenance {
 
        public function __construct() {
index 447d3bd..162dcb4 100644 (file)
@@ -1,10 +1,6 @@
 <?php
 /**
- * We want to make this whole thing as seamless as possible to the
- * end-user. Unfortunately, we can't do _all_ of the work in the class
- * because A) included files are not in global scope, but in the scope
- * of their caller, and B) MediaWiki has way too many globals. So instead
- * we'll kinda fake it, and do the requires() inline. <3 PHP
+ * Delete self-references to $wgServer from the externallinks table.
  *
  * 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
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
-
+/**
+ * Maintenance script that deletes self-references to $wgServer
+ * from the externallinks table.
+ *
+ * @ingroup Maintenance
+ */
 class DeleteSelfExternals extends Maintenance {
        public function __construct() {
                parent::__construct();
index 470bc56..4c04d86 100644 (file)
@@ -4,7 +4,8 @@
  * Used as a base class for CompareParsers and PreprocessDump.
  * We implement below the simple task of searching inside a dump.
  *
- * Copyright (C) 2011 Platonides - http://www.mediawiki.org/
+ * Copyright © 2011 Platonides
+ * http://www.mediawiki.org/
  *
  * 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
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Base class for interating over a dump.
+ *
+ * @ingroup Maintenance
+ */
 abstract class DumpIterator extends Maintenance {
 
        private $count = 0;
@@ -141,6 +147,11 @@ abstract class DumpIterator extends Maintenance {
        abstract public function processRevision( $rev );
 }
 
+/**
+ * Maintenance script that runs a regex in the revisions from a dump.
+ *
+ * @ingroup Maintenance
+ */
 class SearchDump extends DumpIterator {
 
        public function __construct() {
index 0101dc8..ad440e7 100644 (file)
@@ -1,8 +1,5 @@
 <?php
 /**
- * Copyright (C) 2005 Brion Vibber <brion@pobox.com>
- * http://www.mediawiki.org/
- *
  * Quick demo hack to generate a plaintext link dump,
  * per the proposed wiki link database standard:
  * http://www.usemod.com/cgi-bin/mb.pl?LinkDatabase
@@ -11,6 +8,9 @@
  * Does not include interwiki or URL links.
  * Dumps ASCII text to stdout; command-line.
  *
+ * Copyright © 2005 Brion Vibber <brion@pobox.com>
+ * http://www.mediawiki.org/
+ *
  * 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
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that generates a plaintext link dump.
+ *
+ * @ingroup Maintenance
+ */
 class DumpLinks extends Maintenance {
        public function __construct() {
                parent::__construct();
index f5abcd1..5dbb5e2 100644 (file)
@@ -3,7 +3,7 @@
  * Quickie page name dump script for SisterSites usage.
  * http://www.eekim.com/cgi-bin/wiki.pl?SisterSites
  *
- * Copyright (C) 2006 Brion Vibber <brion@pobox.com>
+ * Copyright © 2006 Brion Vibber <brion@pobox.com>
  * http://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that generates a page name dump for SisterSites usage.
+ *
+ * @ingroup Maintenance
+ */
 class DumpSisterSites extends Maintenance {
        public function __construct() {
                parent::__construct();
index 919bb4d..b16b0c2 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Dump a the list of files uploaded, for feeding to tar or similar
+ * Dump a the list of files uploaded, for feeding to tar or similar.
  *
  * 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
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script to dump a the list of files uploaded,
+ * for feeding to tar or similar.
+ *
+ * @ingroup Maintenance
+ */
 class UploadDumper extends Maintenance {
        public function __construct() {
                parent::__construct();
index 8857371..211dc4e 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Make an edit
+ * Make a page edit.
  *
  * 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
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script to make a page edit.
+ *
+ * @ingroup Maintenance
+ */
 class EditCLI extends Maintenance {
        public function __construct() {
                parent::__construct();
index 3b43bcd..2833081 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Communications protocol...
+ * Communications protocol.
  * This is used by dumpTextPass.php when the --spawn option is present.
  *
  * This program is free software; you can redistribute it and/or modify
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script used to fetch page text in a subprocess.
+ *
+ * @ingroup Maintenance
+ */
 class FetchText extends Maintenance {
        public function __construct() {
                parent::__construct();
index b16bd95..69a2a78 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Maintenance script to test fileop performance
+ * Test for fileop performance.
  *
  * 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
@@ -27,6 +27,11 @@ error_reporting( E_ALL );
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script to test fileop performance.
+ *
+ * @ingroup Maintenance
+ */
 class TestFileOpPerformance extends Maintenance {
        public function __construct() {
                parent::__construct();
index b8512e5..e4fa5de 100644 (file)
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that compares documented and actually present mismatches.
+ *
+ * @ingroup Maintenance
+ */
 class FindHooks extends Maintenance {
        public function __construct() {
                parent::__construct();
index c1d14dd..e52c6c3 100644 (file)
@@ -1,8 +1,8 @@
 <?php
 /**
- * Script to fix double redirects.
+ * Fix double redirects.
  *
- * Copyright (C) 2011 Ilmari Karonen <nospam@vyznev.net>
+ * Copyright © 2011 Ilmari Karonen <nospam@vyznev.net>
  * http://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that fixes double redirects.
+ *
+ * @ingroup Maintenance
+ */
 class FixDoubleRedirects extends Maintenance {
        public function __construct() {
                parent::__construct();
                $this->mDescription = "Script to fix double redirects";
                $this->addOption( 'async', 'Don\'t fix anything directly, just queue the jobs' );
                $this->addOption( 'title', 'Fix only redirects pointing to this page', false, true );
-               $this->addOption( 'dry-run', 'Perform a dry run, fix nothing' );                
+               $this->addOption( 'dry-run', 'Perform a dry run, fix nothing' );
        }
 
        public function execute() {
index 0cabe81..d247862 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that fixes any entriy for protocol-relative URLs
+ * in the externallinks table.
+ *
+ * @ingroup Maintenance
+ */
 class FixExtLinksProtocolRelative extends LoggedUpdateMaintenance {
        public function __construct() {
                parent::__construct();
index 7731d3e..de6f652 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 /**
+ * Fix erroneous page_latest values due to slave desynchronisation.
+ *
  * 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
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that fixes erroneous page_latest values
+ * due to slave desynchronisation.
+ *
+ * @ingroup Maintenance
+ */
 class FixSlaveDesync extends Maintenance {
        public function __construct() {
                parent::__construct();
index 3e3bd0a..3a95251 100644 (file)
@@ -1,10 +1,10 @@
 <?php
 /**
- * This script fixes timestamp corruption caused by one or more webservers
- * temporarily being set to the wrong time. The time offset must be known and
- * consistent. Start and end times (in 14-character format) restrict the search,
- * and must bracket the damage. There must be a majority of good timestamps in the
- * search period.
+ * Fixes timestamp corruption caused by one or more webservers temporarily
+ * being set to the wrong time.
+ * The time offset must be known and consistent. Start and end times
+ * (in 14-character format) restrict the search, and must bracket the damage.
+ * There must be a majority of good timestamps in the search period.
  *
  * 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
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that fixes timestamp corruption caused by one or
+ * more webservers temporarily being set to the wrong time.
+ *
+ * @ingroup Maintenance
+ */
 class FixTimestamps extends Maintenance {
        public function __construct() {
                parent::__construct();
index d4ff7c2..4eca396 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that fixes the user_registration field.
+ *
+ * @ingroup Maintenance
+ */
 class FixUserRegistration extends Maintenance {
        public function __construct() {
                parent::__construct();
index fa98813..474caab 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 /**
+ * Format RELEASE-NOTE file to wiki text or HTML markup.
+ *
  * 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
 
 require_once( dirname( __FILE__ ) .'/Maintenance.php' );
 
+/**
+ * Maintenance script that formats RELEASE-NOTE file to wiki text or HTML markup.
+ *
+ * @ingroup Maintenance
+ */
 class MaintenanceFormatInstallDoc extends Maintenance {
        function __construct() {
                parent::__construct();
index da6798e..2c38ed9 100644 (file)
@@ -35,7 +35,7 @@ class JSParseHelper extends Maintenance {
                if ( $this->hasArg() ) {
                        $files = $this->mArgs;
                } else {
-                       $this->maybeHelp( true ); // @fixme this is a lame API :)
+                       $this->maybeHelp( true ); // @todo fixme this is a lame API :)
                        exit( 1 ); // it should exit from the above first...
                }
 
index 2e381aa..126faaa 100644 (file)
@@ -1134,6 +1134,7 @@ $wgMessageStructure = array(
                'right-writeapi',
                'right-delete',
                'right-bigdelete',
+               'right-deletelogentry',
                'right-deleterevision',
                'right-deletedhistory',
                'right-deletedtext',
@@ -1414,6 +1415,7 @@ $wgMessageStructure = array(
                'lockmanager-fail-releaselock',
                'lockmanager-fail-db-bucket',
                'lockmanager-fail-db-release',
+               'lockmanager-fail-svr-acquire',
                'lockmanager-fail-svr-release'
        ),
 
index ba778f4..689c930 100644 (file)
@@ -239,7 +239,7 @@ class LockServerDaemon {
                        list( $session, $key, $command, $type, $values ) = $m;
                        if ( sha1( $session . $command . $type . $values . $this->authKey ) !== $key ) {
                                return 'BAD_KEY';
-                       } elseif ( strlen( $session ) !== 31 ) {
+                       } elseif ( strlen( $session ) !== 32 ) {
                                return 'BAD_SESSION';
                        }
                        $values = explode( '|', $values );
@@ -272,7 +272,7 @@ class LockServerDaemon {
        /**
         * Remove a socket's corresponding session from tracking and
         * store it in the dead session tracking if it still has locks.
-        * 
+        *
         * @param $socket resource
         * @return bool
         */
@@ -309,7 +309,7 @@ class LockServerDaemon {
 
        /**
         * Get the current timestamp and memory usage
-        * 
+        *
         * @return string
         */
        protected function stat() {
@@ -479,10 +479,10 @@ class LockHolder {
 
        /**
         * @param $session string
-        * @return bool 
+        * @return bool
         */
        public function sessionHasLocks( $session ) {
-               return isset( $this->sessionIndexSh[$session] ) 
+               return isset( $this->sessionIndexSh[$session] )
                        || isset( $this->sessionIndexEx[$session] );
        }
 
index 8669fe3..6a9baa8 100644 (file)
@@ -33,6 +33,7 @@ class MergeMessageFileList extends Maintenance {
        function __construct() {
                parent::__construct();
                $this->addOption( 'list-file', 'A file containing a list of extension setup files, one per line.', true, true );
+               $this->addOption( 'extensions-dir', 'Path where extensions can be found.', false, true );
                $this->addOption( 'output', 'Send output to this file (omit for stdout)', false, true );
                $this->mDescription = 'Merge $wgExtensionMessagesFiles from various extensions to produce a ' .
                        'single array containing all message files.';
@@ -41,11 +42,36 @@ class MergeMessageFileList extends Maintenance {
        public function execute() {
                global $mmfl;
 
+               # Add setup files contained in file passed to --list-file
                $lines = file( $this->getOption( 'list-file' ) );
                if ( $lines === false ) {
                        $this->error( 'Unable to open list file.' );
                }
                $mmfl = array( 'setupFiles' => array_map( 'trim', $lines ) );
+
+               # Now find out files in a directory
+               $hasError = false;
+               if ( $this->hasOption( 'extensions-dir' ) ) {
+                       $extdir = $this->getOption( 'extensions-dir' );
+                       $entries = scandir( $extdir );
+                       foreach( $entries as $extname ) {
+                               if ( $extname == '.' || $extname == '..' || !is_dir( "$extdir/$extname" ) ) {
+                                       continue;
+                               }
+                               $extfile = "{$extdir}/{$extname}/{$extname}.php";
+                               if ( file_exists( $extfile ) ) {
+                                       $mmfl['setupFiles'][] = $extfile;
+                               } else {
+                                       $hasError = true;
+                                       $this->error( "Extension {$extname} in {$extdir} lacks expected {$extname}.php" );
+                               }
+                       }
+               }
+
+               if ( $hasError ) {
+                       $this->error( "Some files are missing (see above). Giving up.", 1 );
+               }
+
                if ( $this->hasOption( 'output' ) ) {
                        $mmfl['output'] = $this->getOption( 'output' );
                }
index fe3b35c..7e3d8c4 100644 (file)
@@ -189,7 +189,7 @@ class ImageBuilder extends Maintenance {
                                $filename = $altname;
                                $this->output( "Estimating transcoding... $altname\n" );
                        } else {
-                               # @FIXME: create renameFile()
+                               # @todo FIXME: create renameFile()
                                $filename = $this->renameFile( $filename );
                        }
                }
index 0d5c9de..c4ba66e 100644 (file)
@@ -39,12 +39,11 @@ class SyncFileBackend extends Maintenance {
                $src = FileBackendGroup::singleton()->get( $this->getOption( 'src' ) );
                $dst = FileBackendGroup::singleton()->get( $this->getOption( 'dst' ) );
 
-               $posFile = $this->getOption( 'posdir' )
-                       ? $this->getOption( 'posdir' ) . '/' . wfWikiID()
-                       : false;
+               $posDir = $this->getOption( 'posdir' );
+               $posFile = $posDir ? $posDir . '/' . wfWikiID() : false;
 
                $start = $this->getOption( 'start', 0 );
-               if ( !$start && $posFile ) {
+               if ( !$start && $posFile && is_dir( $posDir ) ) {
                        $start = is_file( $posFile )
                                ? (int)trim( file_get_contents( $posFile ) )
                                : 0;
@@ -66,8 +65,11 @@ class SyncFileBackend extends Maintenance {
 
                // Update the sync position file
                if ( $startFromPosFile && $lastOKPos >= $start ) { // successfully advanced
-                       file_put_contents( $posFile, $lastOKPos, LOCK_EX );
-                       $this->output( "Updated journal position file.\n" );
+                       if ( file_put_contents( $posFile, $lastOKPos, LOCK_EX ) !== false ) {
+                               $this->output( "Updated journal position file.\n" );
+                       } else {
+                               $this->output( "Could not update journal position file.\n" );
+                       }
                }
 
                if ( $lastOKPos === false ) {
@@ -183,7 +185,8 @@ class SyncFileBackend extends Maintenance {
                                }
                                $fsFiles[] = $fsFile; // keep TempFSFile objects alive as needed
                                // Note: prepare() is usually fast for key/value backends
-                               $status->merge( $dst->prepare( array( 'dir' => dirname( $dPath ) ) ) );
+                               $status->merge( $dst->prepare( array(
+                                       'dir' => dirname( $dPath ), 'bypassReadOnly' => 1 ) ) );
                                if ( !$status->isOK() ) {
                                        return $status;
                                }
@@ -198,10 +201,12 @@ class SyncFileBackend extends Maintenance {
                        }
                }
 
-               $status->merge( $dst->doOperations( $ops,
-                       array( 'nonLocking' => 1, 'nonJournaled' => 1 ) ) );
+               $t_start = microtime( true );
+               $status->merge( $dst->doQuickOperations( $ops, array( 'bypassReadOnly' => 1 ) ) );
+               $ellapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
                if ( $status->isOK() && $this->getOption( 'verbose' ) ) {
-                       $this->output( "Synchronized these file(s):\n" . implode( "\n", $dPaths ) . "\n" );
+                       $this->output( "Synchronized these file(s) [{$ellapsed_ms}ms]:\n" .
+                               implode( "\n", $dPaths ) . "\n" );
                }
 
                return $status;
index fd425fe..2363665 100644 (file)
@@ -32,6 +32,8 @@ class UpdateCollation extends Maintenance {
        const BATCH_SIZE = 50; // Number of rows to process in one batch
        const SYNC_INTERVAL = 20; // Wait for slaves after this many batches
 
+       var $sizeHistogram = array();
+
        public function __construct() {
                parent::__construct();
 
@@ -50,6 +52,13 @@ TEXT;
                        'categorylinks table is large. This will only update rows with that ' .
                        'collation, though, so it may miss out-of-date rows with a different, ' .
                        'even older collation.', false, true );
+               $this->addOption( 'target-collation', 'Set this to the new collation type to ' .
+                       'use instead of $wgCategoryCollation. Usually you should not use this, ' . 
+                       'you should just update $wgCategoryCollation in LocalSettings.php.', 
+                       false, true );
+               $this->addOption( 'dry-run', 'Don\'t actually change the collations, just ' .
+                       'compile statistics.' );
+               $this->addOption( 'verbose-stats', 'Show more statistics.' );
        }
 
        public function execute() {
@@ -57,10 +66,19 @@ TEXT;
 
                $dbw = $this->getDB( DB_MASTER );
                $force = $this->getOption( 'force' );
+               $dryRun = $this->getOption( 'dry-run' );
+               $verboseStats = $this->getOption( 'verbose-stats' );
+               if ( $this->hasOption( 'target-collation' ) ) {
+                       $collationName = $this->getOption( 'target-collation' );
+                       $collation = Collation::factory( $collationName );
+               } else {
+                       $collationName = $wgCategoryCollation;
+                       $collation = Collation::singleton();
+               }
 
                $options = array( 'LIMIT' => self::BATCH_SIZE, 'STRAIGHT_JOIN' );
 
-               if ( $force ) {
+               if ( $force || $dryRun ) {
                        $options['ORDER BY'] = 'cl_from, cl_to';
                        $collationConds = array();
                } else {
@@ -68,7 +86,7 @@ TEXT;
                                $collationConds['cl_collation'] = $this->getOption( 'previous-collation' );
                        } else {
                                $collationConds = array( 0 =>
-                                       'cl_collation != ' . $dbw->addQuotes( $wgCategoryCollation )
+                                       'cl_collation != ' . $dbw->addQuotes( $collationName )
                                );
                        }
 
@@ -82,7 +100,7 @@ TEXT;
                        } else {
                                $count = $dbw->estimateRowCount(
                                        'categorylinks',
-                                       '',
+                                       '*',
                                        $collationConds,
                                        __METHOD__
                                );
@@ -110,7 +128,9 @@ TEXT;
                        );
                        $this->output( " processing..." );
 
-                       $dbw->begin( __METHOD__ );
+                       if ( !$dryRun ) {
+                               $dbw->begin( __METHOD__ );
+                       }
                        foreach ( $res as $row ) {
                                $title = Title::newFromRow( $row );
                                if ( !$row->cl_collation ) {
@@ -135,23 +155,32 @@ TEXT;
                                } else {
                                        $type = 'page';
                                }
-                               $dbw->update(
-                                       'categorylinks',
-                                       array(
-                                               'cl_sortkey' => Collation::singleton()->getSortKey(
-                                                       $title->getCategorySortkey( $prefix ) ),
-                                               'cl_sortkey_prefix' => $prefix,
-                                               'cl_collation' => $wgCategoryCollation,
-                                               'cl_type' => $type,
-                                               'cl_timestamp = cl_timestamp',
-                                       ),
-                                       array( 'cl_from' => $row->cl_from, 'cl_to' => $row->cl_to ),
-                                       __METHOD__
-                               );
+                               $newSortKey = $collation->getSortKey(
+                                       $title->getCategorySortkey( $prefix ) );
+                               if ( $verboseStats ) {
+                                       $this->updateSortKeySizeHistogram( $newSortKey );
+                               }
+
+                               if ( !$dryRun ) {
+                                       $dbw->update(
+                                               'categorylinks',
+                                               array(
+                                                       'cl_sortkey' => $newSortKey,
+                                                       'cl_sortkey_prefix' => $prefix,
+                                                       'cl_collation' => $collationName,
+                                                       'cl_type' => $type,
+                                                       'cl_timestamp = cl_timestamp',
+                                               ),
+                                               array( 'cl_from' => $row->cl_from, 'cl_to' => $row->cl_to ),
+                                               __METHOD__
+                                       );
+                               }
+                       }
+                       if ( !$dryRun ) {
+                               $dbw->commit( __METHOD__ );
                        }
-                       $dbw->commit( __METHOD__ );
 
-                       if ( $force && $row ) {
+                       if ( ( $force || $dryRun ) && $row ) {
                                $encFrom = $dbw->addQuotes( $row->cl_from );
                                $encTo = $dbw->addQuotes( $row->cl_to );
                                $batchConds = array(
@@ -162,12 +191,83 @@ TEXT;
                        $count += $res->numRows();
                        $this->output( "$count done.\n" );
 
-                       if ( ++$batchCount % self::SYNC_INTERVAL == 0 ) {
+                       if ( !$dryRun && ++$batchCount % self::SYNC_INTERVAL == 0 ) {
                                $this->output( "Waiting for slaves ... " );
                                wfWaitForSlaves();
                                $this->output( "done\n" );
                        }
                } while ( $res->numRows() == self::BATCH_SIZE );
+
+               $this->output( "$count rows processed\n" );
+
+               if ( $verboseStats ) {
+                       $this->output( "\n" );
+                       $this->showSortKeySizeHistogram();
+               }
+       }
+
+       function updateSortKeySizeHistogram( $key ) {
+               $length = strlen( $key );
+               if ( !isset( $this->sizeHistogram[$length] ) ) {
+                       $this->sizeHistogram[$length] = 0;
+               }
+               $this->sizeHistogram[$length]++;
+       }
+
+       function showSortKeySizeHistogram() {
+               $maxLength = max( array_keys( $this->sizeHistogram ) );
+               if ( $maxLength == 0 ) {
+                       return;
+               }
+               $numBins = 20;
+               $coarseHistogram = array_fill( 0, $numBins, 0 );
+               $coarseBoundaries = array();
+               $boundary = 0;
+               for ( $i = 0; $i < $numBins - 1; $i++ ) {
+                       $boundary += $maxLength / $numBins;
+                       $coarseBoundaries[$i] = round( $boundary );
+               }
+               $coarseBoundaries[$numBins - 1] = $maxLength + 1;
+               $raw = '';
+               for ( $i = 0; $i <= $maxLength; $i++ ) {
+                       if ( $raw !== '' ) {
+                               $raw .= ', ';
+                       }
+                       if ( !isset( $this->sizeHistogram[$i] ) ) {
+                               $val = 0;
+                       } else {
+                               $val = $this->sizeHistogram[$i];
+                       }
+                       for ( $coarseIndex = 0; $coarseIndex < $numBins - 1; $coarseIndex++ ) {
+                               if ( $coarseBoundaries[$coarseIndex] > $i ) {
+                                       $coarseHistogram[$coarseIndex] += $val;
+                                       break;
+                               }
+                       }
+                       if ( $coarseIndex == $numBins - 1 ) {
+                               $coarseHistogram[$coarseIndex] += $val;
+                       }
+                       $raw .= $val;
+               }
+
+               $this->output( "Sort key size histogram\nRaw data: $raw\n\n" );
+
+               $maxBinVal = max( $coarseHistogram );
+               $scale = 60 / $maxBinVal;
+               $prevBoundary = 0;
+               for ( $coarseIndex = 0; $coarseIndex < $numBins; $coarseIndex++ ) {
+                       if ( !isset( $coarseHistogram[$coarseIndex] ) ) {
+                               $val = 0;
+                       } else {
+                               $val = $coarseHistogram[$coarseIndex];
+                       }
+                       $boundary = $coarseBoundaries[$coarseIndex];
+                       $this->output( sprintf( "%-10s %-10d |%s\n",
+                               $prevBoundary . '-' . ( $boundary - 1 ) . ': ',
+                               $val,
+                               str_repeat( '*', $scale * $val ) ) );
+                       $prevBoundary = $boundary;
+               }
        }
 }
 
index 6046103..f8f6e95 100644 (file)
@@ -42,4 +42,4 @@
        /* TODO: eliminate duplication of jquery.arrowSteps.head.png embedding */
        /* @embed */
        background: url(images/jquery.arrowSteps.head-ltr.png) no-repeat right center;
-}
\ No newline at end of file
+}
index f963754..1b414dd 100644 (file)
@@ -1,21 +1,21 @@
 /**
  * jQuery arrowSteps plugin
  * Copyright Neil Kandalgaonkar, 2010
- * 
- * This work is licensed under the terms of the GNU General Public License, 
- * version 2 or later. 
- * (see http://www.fsf.org/licensing/licenses/gpl.html). 
- * Derivative works and later versions of the code must be free software 
+ *
+ * This work is licensed under the terms of the GNU General Public License,
+ * version 2 or later.
+ * (see http://www.fsf.org/licensing/licenses/gpl.html).
+ * Derivative works and later versions of the code must be free software
  * licensed under the same or a compatible license.
  *
  *
  * DESCRIPTION
  *
- * Show users their progress through a series of steps, via a row of items that fit 
+ * Show users their progress through a series of steps, via a row of items that fit
  * together like arrows. One item can be highlighted at a time.
  *
  *
- * SYNOPSIS 
+ * SYNOPSIS
  *
  * <ul id="robin-hood-daffy">
  *   <li id="guard"><div>Guard!</div></li>
  *   <li id="thrust"><div>Thrust!</div></li>
  * </ul>
  *
- * <script language="javascript"><!-- 
+ * <script>
  *   $( '#robin-hood-daffy' ).arrowSteps();
  *
  *   $( '#robin-hood-daffy' ).arrowStepsHighlight( '#guard' );
  *   // 'Guard!' is highlighted.
  *
  *   // ... user completes the 'guard' step ...
- * 
+ *
  *   $( '#robin-hood-daffy' ).arrowStepsHighlight( '#turn' );
  *   // 'Turn!' is highlighted.
- *
- *   //-->
  * </script>
  *
  */
 
-( function( $j ) { 
-       $j.fn.arrowSteps = function() {
+( function ( $ ) {
+       $.fn.arrowSteps = function () {
                this.addClass( 'arrowSteps' );
                var $steps = this.find( 'li' );
 
                var width = parseInt( 100 / $steps.length, 10 );
                $steps.css( 'width', width + '%' );
 
-               // every step except the last one has an arrow at the right hand side. Also add in the padding 
+               // every step except the last one has an arrow at the right hand side. Also add in the padding
                // for the calculated arrow width.
                var arrowWidth = parseInt( this.outerHeight(), 10 );
                $steps.filter( ':not(:last-child)' ).addClass( 'arrow' )
                this.data( 'arrowSteps', $steps );
                return this;
        };
-       
-       $j.fn.arrowStepsHighlight = function( selector ) {
+
+       $.fn.arrowStepsHighlight = function ( selector ) {
                var $steps = this.data( 'arrowSteps' );
                var $previous;
-               $j.each( $steps, function( i, step ) {
-                       var $step = $j( step );
+               $.each( $steps, function ( i, step ) {
+                       var $step = $( step );
                        if ( $step.is( selector ) ) {
                                if ($previous) {
                                        $previous.addClass( 'tail' );
@@ -75,7 +73,7 @@
                                $step.removeClass( 'head tail lasthead' );
                        }
                        $previous = $step;
-               } ); 
+               } );
        };
 
-} )( jQuery );
+}( jQuery ) );
index 9a5fcc9..23ba074 100644 (file)
@@ -1,23 +1,26 @@
 /**
  * Plugin that automatically truncates the plain text contents of an element and adds an ellipsis
  */
-( function( $ ) {
+( function ( $ ) {
 
 // Cache ellipsed substrings for every string-width-position combination
 var cache = { };
 // Use a separate cache when match highlighting is enabled
 var matchTextCache = { };
 
-$.fn.autoEllipsis = function( options ) {
+$.fn.autoEllipsis = function ( options ) {
        options = $.extend( {
-               'position': 'center',
-               'tooltip': false,
-               'restoreText': false,
-               'hasSpan': false,
-               'matchText': null
+               position: 'center',
+               tooltip: false,
+               restoreText: false,
+               hasSpan: false,
+               matchText: null
        }, options );
-       $(this).each( function() {
-               var $el = $(this);
+       $(this).each( function () {
+               var $container, $trimmableText,
+                       text, trimmableText, w, pw,
+                       l, r, i, side,
+                       $el = $(this);
                if ( options.restoreText ) {
                        if ( !$el.data( 'autoEllipsis.originalText' ) ) {
                                $el.data( 'autoEllipsis.originalText', $el.text() );
@@ -27,16 +30,13 @@ $.fn.autoEllipsis = function( options ) {
                }
 
                // container element - used for measuring against
-               var $container = $el;
-               // trimmable text element - only the text within this element will be trimmed
-               var $trimmableText = null;
-               // protected text element - the width of this element is counted, but next is never trimmed from it
-               var $protectedText = null;
+               $container = $el;
 
+               // trimmable text element - only the text within this element will be trimmed
                if ( options.hasSpan ) {
                        $trimmableText = $el.children( options.selector );
                } else {
-                       $trimmableText = $( '<span />' )
+                       $trimmableText = $( '<span>' )
                                .css( 'whiteSpace', 'nowrap' )
                                .text( $el.text() );
                        $el
@@ -44,10 +44,11 @@ $.fn.autoEllipsis = function( options ) {
                                .append( $trimmableText );
                }
 
-               var text = $container.text();
-               var trimmableText = $trimmableText.text();
-               var w = $container.width();
-               var pw = $protectedText ? $protectedText.width() : 0;
+               text = $container.text();
+               trimmableText = $trimmableText.text();
+               w = $container.width();
+               pw = 0;
+
                // Try cache
                if ( options.matchText ) {
                        if ( !( text in matchTextCache ) ) {
@@ -86,7 +87,8 @@ $.fn.autoEllipsis = function( options ) {
                        switch ( options.position ) {
                                case 'right':
                                        // Use binary search-like technique for efficiency
-                                       var l = 0, r = trimmableText.length;
+                                       l = 0;
+                                       r = trimmableText.length;
                                        do {
                                                var m = Math.ceil( ( l + r ) / 2 );
                                                $trimmableText.text( trimmableText.substr( 0, m ) + '...' );
@@ -101,9 +103,10 @@ $.fn.autoEllipsis = function( options ) {
                                        break;
                                case 'center':
                                        // TODO: Use binary search like for 'right'
-                                       var i = [Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 )];
-                                       var side = 1; // Begin with making the end shorter
-                                       while ( $trimmableText.outerWidth() + pw > w  && i[0] > 0 ) {
+                                       i = [Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 )];
+                                       // Begin with making the end shorter
+                                       side = 1;
+                                       while ( $trimmableText.outerWidth() + pw > w && i[0] > 0 ) {
                                                $trimmableText.text( trimmableText.substr( 0, i[0] ) + '...' + trimmableText.substr( i[1] ) );
                                                // Alternate between trimming the end and begining
                                                if ( side === 0 ) {
@@ -119,7 +122,7 @@ $.fn.autoEllipsis = function( options ) {
                                        break;
                                case 'left':
                                        // TODO: Use binary search like for 'right'
-                                       var r = 0;
+                                       r = 0;
                                        while ( $trimmableText.outerWidth() + pw > w && r < trimmableText.length ) {
                                                $trimmableText.text( '...' + trimmableText.substr( r ) );
                                                r++;
@@ -140,4 +143,4 @@ $.fn.autoEllipsis = function( options ) {
        } );
 };
 
-} )( jQuery );
\ No newline at end of file
+}( jQuery ) );
\ No newline at end of file
index 20fa5c8..3d5b720 100644 (file)
@@ -3,9 +3,9 @@
  *
  * Calculate the byte length of a string (accounting for UTF-8).
  *
- * @author Jan Paul Posma
+ * @author Jan Paul Posma, 2011
  */
-jQuery.byteLength = function( str ) {
+jQuery.byteLength = function ( str ) {
 
        // This basically figures out how many bytes a UTF-16 string (which is what js sees)
        // will take in UTF-8 by replacing a 2 byte character with 2 *'s, etc, and counting that.
@@ -16,4 +16,4 @@ jQuery.byteLength = function( str ) {
                .replace( /[\u0080-\u07FF\uD800-\uDFFF]/g, '**' )
                .replace( /[\u0800-\uD7FF\uE000-\uFFFF]/g, '***' )
                .length;
-}
+};
index d8f4bfc..484651e 100644 (file)
@@ -4,7 +4,7 @@
  * @author Jan Paul Posma, 2011
  * @author Timo Tijhof, 2011-2012
  */
-( function ( $, undefined ) {
+( function ( $ ) {
 
        /**
         * Enforces a byte limit to a textbox, so that UTF-8 entries are counted as well, when, for example,
@@ -61,7 +61,7 @@
                        }
        
                        // Save function for reference
-                       $el.data( 'byteLimit-callback', fn );
+                       $el.data( 'byteLimitCallback', fn );
        
                        // We've got something, go for it:
                        $el.keypress( function ( e ) {
index 0a1d7d7..3d7f94d 100644 (file)
@@ -7,22 +7,22 @@
  * @license GPL v2
  */
 ( function( $ ) {
-$.fn.checkboxShiftClick = function( text ) {
-       var prevCheckbox = null;
-       var $box = this;
-       // When our boxes are clicked..
-       $box.click( function( e ) {
-               // And one has been clicked before...
-               if ( prevCheckbox !== null && e.shiftKey ) {
-                       // Check or uncheck this one and all in-between checkboxes
-                       $box.slice(
-                               Math.min( $box.index( prevCheckbox ), $box.index( e.target ) ),
-                               Math.max( $box.index( prevCheckbox ), $box.index( e.target ) ) + 1
-                       ).prop( 'checked', e.target.checked ? true : false );
-               }
-               // Either way, update the prevCheckbox variable to the one clicked now
-               prevCheckbox = e.target;
-       } );
-       return $box;
-};
-} )( jQuery );
\ No newline at end of file
+       $.fn.checkboxShiftClick = function ( text ) {
+               var prevCheckbox = null;
+               var $box = this;
+               // When our boxes are clicked..
+               $box.click( function ( e ) {
+                       // And one has been clicked before...
+                       if ( prevCheckbox !== null && e.shiftKey ) {
+                               // Check or uncheck this one and all in-between checkboxes
+                               $box.slice(
+                                       Math.min( $box.index( prevCheckbox ), $box.index( e.target ) ),
+                                       Math.max( $box.index( prevCheckbox ), $box.index( e.target ) ) + 1
+                               ).prop( 'checked', e.target.checked ? true : false );
+                       }
+                       // Either way, update the prevCheckbox variable to the one clicked now
+                       prevCheckbox = e.target;
+               } );
+               return $box;
+       };
+}( jQuery ) );
index 2c6e257..26eea96 100644 (file)
@@ -32,6 +32,8 @@
                 *  }
                 */
                profile: function ( nav ) {
+                       /*jshint boss:true */
+
                        if ( nav === undefined ) {
                                nav = window.navigator;
                        }
                 * @return Boolean true if browser known or assumed to be supported, false if blacklisted
                 */
                test: function ( map, profile ) {
+                       /*jshint evil:true */
+
                        var conditions, dir, i, op, val;
                        profile = $.isPlainObject( profile ) ? profile : $.client.profile();
 
index 1784f86..cb25796 100644 (file)
@@ -1,30 +1,34 @@
-/*
+/**
  * Collapsible tabs jQuery Plugin
  */
-( function( $ ) {
-       $.fn.collapsibleTabs = function( options ) {
+( function ( $ ) {
+       $.fn.collapsibleTabs = function ( options ) {
                // return if the function is called on an empty jquery object
-               if( !this.length ) return this;
-               //merge options into the defaults
+               if ( !this.length ) {
+                       return this;
+               }
+               // Merge options into the defaults
                var $settings = $.extend( {}, $.collapsibleTabs.defaults, options );
 
-               this.each( function() {
-                       var $this = $( this );
+               this.each( function () {
+                       var $el = $( this );
                        // add the element to our array of collapsible managers
-                       $.collapsibleTabs.instances = ( $.collapsibleTabs.instances.length == 0 ?
-                               $this : $.collapsibleTabs.instances.add( $this ) );
+                       $.collapsibleTabs.instances = ( $.collapsibleTabs.instances.length === 0 ?
+                               $el : $.collapsibleTabs.instances.add( $el ) );
                        // attach the settings to the elements
-                       $this.data( 'collapsibleTabsSettings', $settings );
+                       $el.data( 'collapsibleTabsSettings', $settings );
                        // attach data to our collapsible elements
-                       $this.children( $settings.collapsible ).each( function() {
+                       $el.children( $settings.collapsible ).each( function () {
                                $.collapsibleTabs.addData( $( this ) );
                        } );
                } );
 
                // if we haven't already bound our resize hanlder, bind it now
-               if( !$.collapsibleTabs.boundEvent ) {
+               if ( !$.collapsibleTabs.boundEvent ) {
                        $( window )
-                               .delayedBind( '500', 'resize', function( ) { $.collapsibleTabs.handleResize(); } );
+                               .delayedBind( '500', 'resize', function ( ) {
+                                       $.collapsibleTabs.handleResize();
+                               } );
                }
                // call our resize handler to setup the page
                $.collapsibleTabs.handleResize();
                        collapsedContainer: '#p-cactions ul',
                        collapsible: 'li.collapsible',
                        shifting: false,
-                       expandCondition: function( eleWidth ) {
+                       expandCondition: function ( eleWidth ) {
                                return ( $( '#left-navigation' ).position().left + $( '#left-navigation' ).width() )
                                        < ( $( '#right-navigation' ).position().left - eleWidth );
                        },
-                       collapseCondition: function() {
+                       collapseCondition: function () {
                                return ( $( '#left-navigation' ).position().left + $( '#left-navigation' ).width() )
                                        > $( '#right-navigation' ).position().left;
                        }
                },
-               addData: function( $collapsible ) {
+               addData: function ( $collapsible ) {
                        var $settings = $collapsible.parent().data( 'collapsibleTabsSettings' );
-                       if ( $settings != null ) {
+                       if ( $settings !== null ) {
                                $collapsible.data( 'collapsibleTabsSettings', {
-                                       'expandedContainer': $settings.expandedContainer,
-                                       'collapsedContainer': $settings.collapsedContainer,
-                                       'expandedWidth': $collapsible.width(),
-                                       'prevElement': $collapsible.prev()
+                                       expandedContainer: $settings.expandedContainer,
+                                       collapsedContainer: $settings.collapsedContainer,
+                                       expandedWidth: $collapsible.width(),
+                                       prevElement: $collapsible.prev()
                                } );
                        }
                },
-               getSettings: function( $collapsible ) {
+               getSettings: function ( $collapsible ) {
                        var $settings = $collapsible.data( 'collapsibleTabsSettings' );
-                       if ( typeof $settings == 'undefined' ) {
+                       if ( $settings === undefined ) {
                                $.collapsibleTabs.addData( $collapsible );
                                $settings = $collapsible.data( 'collapsibleTabsSettings' );
                        }
                        return $settings;
                },
-               handleResize: function( e ){
-                       $.collapsibleTabs.instances.each( function() {
-                               var $this = $( this ), data = $.collapsibleTabs.getSettings( $this );
-                               if( data.shifting ) return;
+               handleResize: function ( e ) {
+                       $.collapsibleTabs.instances.each( function () {
+                               var $el = $( this ),
+                                       data = $.collapsibleTabs.getSettings( $el );
+
+                               if ( data.shifting ) {
+                                       return;
+                               }
 
                                // if the two navigations are colliding
-                               if( $this.children( data.collapsible ).length > 0 && data.collapseCondition() ) {
+                               if ( $el.children( data.collapsible ).length > 0 && data.collapseCondition() ) {
 
-                                       $this.trigger( "beforeTabCollapse" );
+                                       $el.trigger( 'beforeTabCollapse' );
                                        // move the element to the dropdown menu
-                                       $.collapsibleTabs.moveToCollapsed( $this.children( data.collapsible + ':last' ) );
+                                       $.collapsibleTabs.moveToCollapsed( $el.children( data.collapsible + ':last' ) );
                                }
 
                                // if there are still moveable items in the dropdown menu,
                                // and there is sufficient space to place them in the tab container
-                               if( $( data.collapsedContainer + ' ' + data.collapsible ).length > 0
+                               if ( $( data.collapsedContainer + ' ' + data.collapsible ).length > 0
                                                && data.expandCondition( $.collapsibleTabs.getSettings( $( data.collapsedContainer ).children(
-                                                               data.collapsible+":first" ) ).expandedWidth ) ) {
+                                                               data.collapsible + ':first' ) ).expandedWidth ) ) {
                                        //move the element from the dropdown to the tab
-                                       $this.trigger( "beforeTabExpand" );
+                                       $el.trigger( 'beforeTabExpand' );
                                        $.collapsibleTabs
-                                               .moveToExpanded( data.collapsedContainer + " " + data.collapsible + ':first' );
+                                               .moveToExpanded( data.collapsedContainer + ' ' + data.collapsible + ':first' );
                                }
                        });
                },
-               moveToCollapsed: function( ele ) {
-                       var $moving = $( ele );
-                       var data = $.collapsibleTabs.getSettings( $moving );
-                       var dataExp = $.collapsibleTabs.getSettings( data.expandedContainer );
+               moveToCollapsed: function ( ele ) {
+                       var $moving = $( ele ),
+                               data = $.collapsibleTabs.getSettings( $moving ),
+                               dataExp = $.collapsibleTabs.getSettings( data.expandedContainer );
                        dataExp.shifting = true;
                        $moving
                                .detach()
                        dataExp.shifting = false;
                        $.collapsibleTabs.handleResize();
                },
-               moveToExpanded: function( ele ) {
-                       var $moving = $( ele );
-                       var data = $.collapsibleTabs.getSettings( $moving );
-                       var dataExp = $.collapsibleTabs.getSettings( data.expandedContainer );
+               moveToExpanded: function ( ele ) {
+                       var $moving = $( ele ),
+                               data = $.collapsibleTabs.getSettings( $moving ),
+                               dataExp = $.collapsibleTabs.getSettings( data.expandedContainer );
                        dataExp.shifting = true;
                        // remove this element from where it's at and put it in the dropdown menu
                        $moving.detach().insertAfter( data.prevElement ).data( 'collapsibleTabsSettings', data );
                        $.collapsibleTabs.handleResize();
                }
        };
-} )( jQuery );
+
+}( jQuery ) );
index 8a619b5..8bc45c9 100644 (file)
@@ -1,44 +1,54 @@
 /**
  * jQuery Color Animations
- * Copyright 2007 John Resig
+ *
+ * @author John Resig, 2007
+ * @author Krinkle, 2011
  * Released under the MIT and GPL licenses.
  *
- * - 2011-01-05: Modified by Krinkle to use the jQuery.colorUtil plugin (which has to be loaded first!)
+ * - 2011-01-05: Forked for MediaWiki. See also jQuery.colorUtil plugin
  */
-(function( $ ) {
+( function ( $ ) {
 
-       // We override the animation for all of these color styles
-       $.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'],
-               function( i, attr ) {
-                       $.fx.step[attr] = function( fx ) {
-                               if ( fx.state == 0 ) {
-                                       fx.start = getColor( fx.elem, attr );
-                                       fx.end = $.colorUtil.getRGB( fx.end );
-                               }
-
-                               fx.elem.style[attr] = 'rgb(' + [
-                                       Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0),
-                                       Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0),
-                                       Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2]), 255), 0)
-                               ].join( ',' ) + ')';
-                       }
-               }
-       );
-
-       function getColor(elem, attr) {
+       function getColor( elem, attr ) {
+               /*jshint boss:true */
                var color;
 
                do {
-                       color = $.curCSS(elem, attr);
+                       color = $.curCSS( elem, attr );
 
                        // Keep going until we find an element that has color, or we hit the body
-                       if ( color != '' && color != 'transparent' || $.nodeName(elem, 'body') )
+                       if ( color !== '' && color !== 'transparent' || $.nodeName( elem, 'body' ) ) {
                                break;
+                       }
 
                        attr = 'backgroundColor';
                } while ( elem = elem.parentNode );
 
-               return $.colorUtil.getRGB(color);
-       };
+               return $.colorUtil.getRGB( color );
+       }
+
+       // We override the animation for all of these color styles
+       $.each([
+               'backgroundColor',
+               'borderBottomColor',
+               'borderLeftColor',
+               'borderRightColor',
+               'borderTopColor',
+               'color',
+               'outlineColor'
+       ], function ( i, attr ) {
+               $.fx.step[attr] = function ( fx ) {
+                       if ( fx.state === 0 ) {
+                               fx.start = getColor( fx.elem, attr );
+                               fx.end = $.colorUtil.getRGB( fx.end );
+                       }
+
+                       fx.elem.style[attr] = 'rgb(' + [
+                               Math.max( Math.min( parseInt( (fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10 ), 255 ), 0 ),
+                               Math.max( Math.min( parseInt( (fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10 ), 255 ), 0 ),
+                               Math.max( Math.min( parseInt( (fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10 ), 255 ), 0 )
+                       ].join( ',' ) + ')';
+               };
+       } );
 
-} )( jQuery );
+}( jQuery ) );
index 1116aec..c1fe7fe 100644 (file)
  * jQuery Color Utilities
  * Written by Krinkle in 2011
  * Released under the MIT and GPL licenses.
- * Mostly based on other plugins and functions (taken through JSLint and optimized a little).
- * Sources cited locally.
+ * Mostly based on other plugins and functions (linted and optimized a little).
+ * Sources cited inline.
  */
-( function( $ ) {
-$.colorUtil = {
-
-       // Color Conversion function from highlightFade
-       // By Blair Mitchelmore
-       // http://jquery.offput.ca/highlightFade/
-       // Parse strings looking for color tuples [255,255,255]
-       getRGB : function( color ) {
-               var result;
-
-               // Check if we're already dealing with an array of colors
-               if ( color && color.constructor == Array && color.length == 3 ){
-                       return color;
-               }
+( function ( $ ) {
+       $.colorUtil = {
 
-               // Look for rgb(num,num,num)
-               if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) {
-                       return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
-               }
+               // Color Conversion function from highlightFade
+               // By Blair Mitchelmore
+               // http://jquery.offput.ca/highlightFade/
+               // Parse strings looking for color tuples [255,255,255]
+               getRGB : function ( color ) {
+                       /*jshint boss:true */
+                       var result;
 
-               // Look for rgb(num%,num%,num%)
-               if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) {
-                       return [parseFloat(result[1],10)*2.55, parseFloat(result[2],10)*2.55, parseFloat(result[3])*2.55];
-               }
+                       // Check if we're already dealing with an array of colors
+                       if ( color && $.isArray( color ) && color.length === 3 ) {
+                               return color;
+                       }
 
-               // Look for #a0b1c2
-               if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) {
-                       return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
-               }
+                       // Look for rgb(num,num,num)
+                       if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) {
+                               return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
+                       }
 
-               // Look for #fff
-               if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) {
-                       return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
-               }
+                       // Look for rgb(num%,num%,num%)
+                       if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) {
+                               return [parseFloat(result[1],10)*2.55, parseFloat(result[2],10)*2.55, parseFloat(result[3])*2.55];
+                       }
 
-               // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
-               if (result = /rgba\(0, 0, 0, 0\)/.exec(color)) {
-                       return $.colorUtil.colors.transparent;
-               }
+                       // Look for #a0b1c2
+                       if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) {
+                               return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
+                       }
 
-               // Otherwise, we're most likely dealing with a named color
-               return $.colorUtil.colors[$.trim(color).toLowerCase()];
-       },
-
-       // Some named colors to work with
-       // From Interface by Stefan Petre
-       // http://interface.eyecon.ro/
-       colors: {
-               aqua:[0,255,255],
-               azure:[240,255,255],
-               beige:[245,245,220],
-               black:[0,0,0],
-               blue:[0,0,255],
-               brown:[165,42,42],
-               cyan:[0,255,255],
-               darkblue:[0,0,139],
-               darkcyan:[0,139,139],
-               darkgrey:[169,169,169],
-               darkgreen:[0,100,0],
-               darkkhaki:[189,183,107],
-               darkmagenta:[139,0,139],
-               darkolivegreen:[85,107,47],
-               darkorange:[255,140,0],
-               darkorchid:[153,50,204],
-               darkred:[139,0,0],
-               darksalmon:[233,150,122],
-               darkviolet:[148,0,211],
-               fuchsia:[255,0,255],
-               gold:[255,215,0],
-               green:[0,128,0],
-               indigo:[75,0,130],
-               khaki:[240,230,140],
-               lightblue:[173,216,230],
-               lightcyan:[224,255,255],
-               lightgreen:[144,238,144],
-               lightgrey:[211,211,211],
-               lightpink:[255,182,193],
-               lightyellow:[255,255,224],
-               lime:[0,255,0],
-               magenta:[255,0,255],
-               maroon:[128,0,0],
-               navy:[0,0,128],
-               olive:[128,128,0],
-               orange:[255,165,0],
-               pink:[255,192,203],
-               purple:[128,0,128],
-               violet:[128,0,128],
-               red:[255,0,0],
-               silver:[192,192,192],
-               white:[255,255,255],
-               yellow:[255,255,0],
-               transparent: [255,255,255]
-       },
-       /**
-        * http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
-        * Converts an RGB color value to HSL. Conversion formula
-        * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
-        * Assumes r, g, and b are contained in the set [0, 255] and
-        * returns h, s, and l in the set [0, 1].
-        *
-        * @param       Number  R               The red color value
-        * @param       Number  G               The green color value
-        * @param       Number  B               The blue color value
-        * @return      Array                   The HSL representation
-        */
-       rgbToHsl: function( R, G, B ) {
-               var     r = R / 255,
-                       g = G / 255,
-                       b = B / 255;
-               var max = Math.max(r, g, b), min = Math.min(r, g, b);
-               var h, s, l = (max + min) / 2;
-
-               if(max == min){
-                       h = s = 0; // achromatic
-               }else{
-                       var d = max - min;
-                       s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
-                       switch(max){
-                               case r: h = (g - b) / d + (g < b ? 6 : 0); break;
-                               case g: h = (b - r) / d + 2; break;
-                               case b: h = (r - g) / d + 4; break;
+                       // Look for #fff
+                       if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) {
+                               return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
+                       }
+
+                       // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
+                       if (result = /rgba\(0, 0, 0, 0\)/.exec(color)) {
+                               return $.colorUtil.colors.transparent;
                        }
-                       h /= 6;
-               }
 
-               return [h, s, l];
-       },
-       /**
-        * http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
-        * Converts an HSL color value to RGB. Conversion formula
-        * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
-        * Assumes h, s, and l are contained in the set [0, 1] and
-        * returns r, g, and b in the set [0, 255].
-        *
-        * @param       Number  h               The hue
-        * @param       Number  s               The saturation
-        * @param       Number  l               The lightness
-        * @return      Array                   The RGB representation
-        */
-       hslToRgb: function( h, s, l ) {
-               var r, g, b;
-
-               if(s === 0){
-                       r = g = b = l; // achromatic
-               }else{
-                       var hue2rgb = function(p, q, t){
-                               if(t < 0){ t += 1; }
-                               if(t > 1){ t -= 1; }
-                               if(t < 1/6){ return p + (q - p) * 6 * t; }
-                               if(t < 1/2){ return q; }
-                               if(t < 2/3){ return p + (q - p) * (2/3 - t) * 6; }
-                               return p;
-                       };
-
-                       var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
-                       var p = 2 * l - q;
-                       r = hue2rgb(p, q, h + 1/3);
-                       g = hue2rgb(p, q, h);
-                       b = hue2rgb(p, q, h - 1/3);
+                       // Otherwise, we're most likely dealing with a named color
+                       return $.colorUtil.colors[$.trim(color).toLowerCase()];
+               },
+
+               // Some named colors to work with
+               // From Interface by Stefan Petre
+               // http://interface.eyecon.ro/
+               colors: {
+                       aqua: [0,255,255],
+                       azure: [240,255,255],
+                       beige: [245,245,220],
+                       black: [0,0,0],
+                       blue: [0,0,255],
+                       brown: [165,42,42],
+                       cyan: [0,255,255],
+                       darkblue: [0,0,139],
+                       darkcyan: [0,139,139],
+                       darkgrey: [169,169,169],
+                       darkgreen: [0,100,0],
+                       darkkhaki: [189,183,107],
+                       darkmagenta: [139,0,139],
+                       darkolivegreen: [85,107,47],
+                       darkorange: [255,140,0],
+                       darkorchid: [153,50,204],
+                       darkred: [139,0,0],
+                       darksalmon: [233,150,122],
+                       darkviolet: [148,0,211],
+                       fuchsia: [255,0,255],
+                       gold: [255,215,0],
+                       green: [0,128,0],
+                       indigo: [75,0,130],
+                       khaki: [240,230,140],
+                       lightblue: [173,216,230],
+                       lightcyan: [224,255,255],
+                       lightgreen: [144,238,144],
+                       lightgrey: [211,211,211],
+                       lightpink: [255,182,193],
+                       lightyellow: [255,255,224],
+                       lime: [0,255,0],
+                       magenta: [255,0,255],
+                       maroon: [128,0,0],
+                       navy: [0,0,128],
+                       olive: [128,128,0],
+                       orange: [255,165,0],
+                       pink: [255,192,203],
+                       purple: [128,0,128],
+                       violet: [128,0,128],
+                       red: [255,0,0],
+                       silver: [192,192,192],
+                       white: [255,255,255],
+                       yellow: [255,255,0],
+                       transparent: [255,255,255]
+               },
+
+               /**
+                * http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
+                * Converts an RGB color value to HSL. Conversion formula
+                * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
+                * Assumes r, g, and b are contained in the set [0, 255] and
+                * returns h, s, and l in the set [0, 1].
+                *
+                * @param       Number  R               The red color value
+                * @param       Number  G               The green color value
+                * @param       Number  B               The blue color value
+                * @return      Array                   The HSL representation
+                */
+               rgbToHsl: function ( R, G, B ) {
+                       var r = R / 255,
+                               g = G / 255,
+                               b = B / 255;
+                       var max = Math.max( r, g, b ), min = Math.min( r, g, b );
+                       var h, s, l = (max + min) / 2;
+
+                       if ( max === min ) {
+                               // achromatic
+                               h = s = 0;
+                       } else {
+                               var d = max - min;
+                               s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+                               switch ( max ) {
+                                       case r:
+                                               h = (g - b) / d + (g < b ? 6 : 0);
+                                               break;
+                                       case g:
+                                               h = (b - r) / d + 2;
+                                               break;
+                                       case b:
+                                               h = (r - g) / d + 4;
+                                               break;
+                               }
+                               h /= 6;
+                       }
+
+                       return [h, s, l];
+               },
+
+               /**
+                * http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
+                * Converts an HSL color value to RGB. Conversion formula
+                * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
+                * Assumes h, s, and l are contained in the set [0, 1] and
+                * returns r, g, and b in the set [0, 255].
+                *
+                * @param       Number  h               The hue
+                * @param       Number  s               The saturation
+                * @param       Number  l               The lightness
+                * @return      Array                   The RGB representation
+                */
+               hslToRgb: function ( h, s, l ) {
+                       var r, g, b;
+
+                       if ( s === 0 ) {
+                               r = g = b = l; // achromatic
+                       } else {
+                               var hue2rgb = function ( p, q, t ) {
+                                       if ( t < 0 ) {
+                                               t += 1;
+                                       }
+                                       if ( t > 1 ) {
+                                               t -= 1;
+                                       }
+                                       if ( t < 1/6 ) {
+                                               return p + (q - p) * 6 * t;
+                                       }
+                                       if ( t < 1/2 ) {
+                                               return q;
+                                       }
+                                       if ( t < 2/3 ) {
+                                               return p + (q - p) * (2/3 - t) * 6;
+                                       }
+                                       return p;
+                               };
+
+                               var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+                               var p = 2 * l - q;
+                               r = hue2rgb( p, q, h + 1/3 );
+                               g = hue2rgb( p, q, h );
+                               b = hue2rgb( p, q, h - 1/3 );
+                       }
+
+                       return [r * 255, g * 255, b * 255];
+               },
+
+               /**
+                * Get's a brighter or darker rgb() value string.
+                *
+                * @author Krinkle
+                *
+                * @example     getCSSColorMod( 'red', +0.1 )
+                * @example     getCSSColorMod( 'rgb(200,50,50)', -0.2 )
+                *
+                * @param       Mixed   currentColor current value in css
+                * @param       Number  mod wanted brightness modification between -1 and 1
+                * @return      String 'rgb(r,g,b)'
+                */
+               getColorBrightness: function ( currentColor, mod ) {
+                       var rgbArr = $.colorUtil.getRGB( currentColor ),
+                               hslArr = $.colorUtil.rgbToHsl(rgbArr[0], rgbArr[1], rgbArr[2] );
+                       rgbArr = $.colorUtil.hslToRgb(hslArr[0], hslArr[1], hslArr[2]+mod);
+
+                       return 'rgb(' +
+                               [parseInt( rgbArr[0], 10), parseInt( rgbArr[1], 10 ), parseInt( rgbArr[2], 10 )].join( ',' ) +
+                               ')';
                }
 
-               return [r * 255, g * 255, b * 255];
-       },
-       /**
-        * Get's a brighter or darker rgb() value string.
-        *
-        * @author Krinkle
-        *
-        * @example     getCSSColorMod( 'red', +0.1 )
-        * @example     getCSSColorMod( 'rgb(200,50,50)', -0.2 )
-        *
-        * @param       Mixed   currentColor current value in css
-        * @param       Number  mod wanted brightness modification between -1 and 1
-        * @return      String 'rgb(r,g,b)'
-        */
-       getColorBrightness: function( currentColor, mod ) {
-               var     rgbArr = $.colorUtil.getRGB( currentColor ),
-                       hslArr = $.colorUtil.rgbToHsl(rgbArr[0], rgbArr[1], rgbArr[2] );
-               rgbArr = $.colorUtil.hslToRgb(hslArr[0], hslArr[1], hslArr[2]+mod);
-               return 'rgb(' +
-                       [parseInt( rgbArr[0], 10), parseInt( rgbArr[1], 10 ), parseInt( rgbArr[2], 10 )].join( ',' ) +
-                       ')';
-       }
-
-};
-} )( jQuery );
\ No newline at end of file
+       };
+
+}( jQuery ) );
index d84ee26..5d32b6b 100644 (file)
@@ -1,4 +1,4 @@
-(function( $ ) {
+( function ( $ ) {
 /**
  * Function that escapes spaces in event names. This is needed because
  * "_delayedBind-foo bar-1000" refers to two events
@@ -18,25 +18,26 @@ $.fn.extend( {
         * @param data Data to pass to the event handler (optional)
         * @param callback Function to call
         */
-       delayedBind: function( timeout, event, data, callback ) {
-               if ( arguments.length == 3 ) {
+       delayedBind: function ( timeout, event, data, callback ) {
+               if ( arguments.length === 3 ) {
                        // Shift optional parameter down
                        callback = data;
                        data = undefined;
                }
                var encEvent = encodeEvent( event );
-               return this.each( function() {
+               return this.each( function () {
                        var that = this;
                        // Bind the top half
                        // Do this only once for every (event, timeout) pair
                        if (  !( $(this).data( '_delayedBindBound-' + encEvent + '-' + timeout ) ) ) {
                                $(this).data( '_delayedBindBound-' + encEvent + '-' + timeout, true );
-                               $(this).bind( event, function() {
+                               $(this).bind( event, function () {
                                        var timerID = $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout );
                                        // Cancel the running timer
-                                       if ( typeof timerID != 'undefined' )
+                                       if ( timerID !== null ) {
                                                clearTimeout( timerID );
-                                       timerID = setTimeout( function() {
+                                       }
+                                       timerID = setTimeout( function () {
                                                $(that).trigger( '_delayedBind-' + encEvent + '-' + timeout );
                                        }, timeout );
                                        $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout, timerID );
@@ -51,23 +52,25 @@ $.fn.extend( {
        /**
         * Cancel the timers for delayed events on the selected elements.
         */
-       delayedBindCancel: function( timeout, event ) {
+       delayedBindCancel: function ( timeout, event ) {
                var encEvent = encodeEvent( event );
-               return this.each( function() {
+               return this.each( function () {
                        var timerID = $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout );
-                       if ( typeof timerID != 'undefined' )
+                       if ( timerID !== null ) {
                                clearTimeout( timerID );
+                       }
                } );
        },
        
        /**
         * Unbind an event bound with delayedBind()
         */
-       delayedBindUnbind: function( timeout, event, callback ) {
+       delayedBindUnbind: function ( timeout, event, callback ) {
                var encEvent = encodeEvent( event );
-               return this.each( function() {
+               return this.each( function () {
                        $(this).unbind( '_delayedBind-' + encEvent + '-' + timeout, callback );
                } );
        }
 } );
-} )( jQuery );
\ No newline at end of file
+
+}( jQuery ) );
index 3c65d01..a3396a2 100644 (file)
  * Options:
  *
  */
-( function( $ ) {
-
-$.expandableField = {
-       /**
-        * Expand the field, make the callback
-        */
-       expandField: function( e, context ) {
-               context.config.beforeExpand.call( context.data.$field, context );
-               context.data.$field
-                       .animate( { 'width': context.data.expandedWidth }, 'fast', function() {
-                               context.config.afterExpand.call( this, context );
-                       } );
-       },
-       /**
-        * Condense the field, make the callback
-        */
-       condenseField: function( e, context ) {
-               context.config.beforeCondense.call( context.data.$field, context );
-               context.data.$field
-                       .animate( { 'width': context.data.condensedWidth }, 'fast', function() {
-                               context.config.afterCondense.call( this, context );
-                       } );
-       },
-       /**
-        * Sets the value of a property, and updates the widget accordingly
-        * @param property String Name of property
-        * @param value Mixed Value to set property with
-        */
-       configure: function( context, property, value ) {
-               // Validate creation using fallback values
-               switch( property ) {
-                       default:
-                               context.config[property] = value;
-                               break;
-               }
-       }
-
-};
-$.fn.expandableField = function() {
-       
-       // Multi-context fields
-       var returnValue = null;
-       var args = arguments;
-       
-       $( this ).each( function() {
-
-               /* Construction / Loading */
-               
-               var context = $( this ).data( 'expandableField-context' );
-               if ( context == null ) {
-                       context = {
-                               config: {
-                                       // callback function for before collapse
-                                       'beforeCondense': function( context ) {},
-                                       // callback function for before expand
-                                       'beforeExpand': function( context ) {},
-                                       // callback function for after collapse
-                                       'afterCondense': function( context ) {},
-                                       // callback function for after expand
-                                       'afterExpand': function( context ) {},
-                                       // Whether the field should expand to the left or the right -- defaults to left
-                                       'expandToLeft': true
-                               }
-                       };
+( function ( $ ) {
+
+       $.expandableField = {
+               /**
+                * Expand the field, make the callback
+                */
+               expandField: function ( e, context ) {
+                       context.config.beforeExpand.call( context.data.$field, context );
+                       context.data.$field
+                               .animate( { 'width': context.data.expandedWidth }, 'fast', function () {
+                                       context.config.afterExpand.call( this, context );
+                               } );
+               },
+               /**
+                * Condense the field, make the callback
+                */
+               condenseField: function ( e, context ) {
+                       context.config.beforeCondense.call( context.data.$field, context );
+                       context.data.$field
+                               .animate( { 'width': context.data.condensedWidth }, 'fast', function () {
+                                       context.config.afterCondense.call( this, context );
+                               } );
+               },
+               /**
+                * Sets the value of a property, and updates the widget accordingly
+                * @param property String Name of property
+                * @param value Mixed Value to set property with
+                */
+               configure: function ( context, property, value ) {
+                       // TODO: Validate creation using fallback values
+                       context.config[property] = value;
                }
-               
-               /* API */
-               // Handle various calling styles
-               if ( args.length > 0 ) {
-                       if ( typeof args[0] == 'object' ) {
-                               // Apply set of properties
-                               for ( var key in args[0] ) {
-                                       $.expandableField.configure( context, key, args[0][key] );
-                               }
-                       } else if ( typeof args[0] == 'string' ) {
-                               if ( args.length > 1 ) {
-                                       // Set property values
-                                       $.expandableField.configure( context, args[0], args[1] );
-                               } else if ( returnValue == null ) {
-                                       // Get property values, but don't give access to internal data - returns only the first
-                                       returnValue = ( args[0] in context.config ? undefined : context.config[args[0]] );
+
+       };
+
+       $.fn.expandableField = function () {
+
+               // Multi-context fields
+               var returnValue;
+               var args = arguments;
+
+               $( this ).each( function () {
+                       var key;
+
+                       /* Construction / Loading */
+
+                       var context = $( this ).data( 'expandableField-context' );
+
+                       // TODO: Do we need to check both null and undefined?
+                       if ( context === undefined || context === null ) {
+                               context = {
+                                       config: {
+                                               // callback function for before collapse
+                                               beforeCondense: function ( context ) {},
+
+                                               // callback function for before expand
+                                               beforeExpand: function ( context ) {},
+
+                                               // callback function for after collapse
+                                               afterCondense: function ( context ) {},
+
+                                               // callback function for after expand
+                                               afterExpand: function ( context ) {},
+
+                                               // Whether the field should expand to the left or the right -- defaults to left
+                                               expandToLeft: true
+                                       }
+                               };
+                       }
+
+                       /* API */
+                       // Handle various calling styles
+                       if ( args.length > 0 ) {
+                               if ( typeof args[0] === 'object' ) {
+                                       // Apply set of properties
+                                       for ( key in args[0] ) {
+                                               $.expandableField.configure( context, key, args[0][key] );
+                                       }
+                               } else if ( typeof args[0] === 'string' ) {
+                                       if ( args.length > 1 ) {
+                                               // Set property values
+                                               $.expandableField.configure( context, args[0], args[1] );
+
+                                       // TODO: Do we need to check both null and undefined?
+                                       } else if ( returnValue === null || returnValue === undefined ) {
+                                               // Get property values, but don't give access to internal data - returns only the first
+                                               returnValue = ( args[0] in context.config ? undefined : context.config[args[0]] );
+                                       }
                                }
                        }
-               }
-               
-               /* Initialization */
-               
-               if ( typeof context.data == 'undefined' ) {
-                       context.data = {
-                               // The width of the field in it's condensed state
-                               'condensedWidth': $( this ).width(),
-                               // The width of the field in it's expanded state
-                               'expandedWidth': $( this ).width() * 2,
-                               // Reference to the field
-                               '$field': $( this )
-                       };
-                       
-                       $( this )
-                               .addClass( 'expandableField' )
-                               .focus( function( e ) {
-                                       $.expandableField.expandField( e, context );
-                               } )
-                               .delayedBind( 250, 'blur', function( e ) {
-                                       $.expandableField.condenseField( e, context );
-                               } );
-               }
-               // Store the context for next time
-               $( this ).data( 'expandableField-context', context );
-       } );
-       return returnValue !== null ? returnValue : $(this);
-};
 
-} )( jQuery );
+                       /* Initialization */
+
+                       if ( context.data === undefined ) {
+                               context.data = {
+                                       // The width of the field in it's condensed state
+                                       condensedWidth: $( this ).width(),
+
+                                       // The width of the field in it's expanded state
+                                       expandedWidth: $( this ).width() * 2,
+
+                                       // Reference to the field
+                                       $field: $( this )
+                               };
+
+                               $( this )
+                                       .addClass( 'expandableField' )
+                                       .focus( function ( e ) {
+                                               $.expandableField.expandField( e, context );
+                                       } )
+                                       .delayedBind( 250, 'blur', function ( e ) {
+                                               $.expandableField.condenseField( e, context );
+                                       } );
+                       }
+                       // Store the context for next time
+                       $( this ).data( 'expandableField-context', context );
+               } );
+               return returnValue !== undefined ? returnValue : $(this);
+       };
+
+}( jQuery ) );
index c05012d..25b806b 100644 (file)
@@ -3,8 +3,8 @@
  *
  * @author Timo Tijhof, 2011
  */
-jQuery.fn.getAttrs = function( all ) {
-       var     map = this[0].attributes,
+jQuery.fn.getAttrs = function ( all ) {
+       var map = this[0].attributes,
                attrs = {},
                len = map.length,
                i, v;
index 42b1194..fa4416c 100644 (file)
@@ -1,64 +1,67 @@
 /**
- * Plugin that highlights matched word partials in a given element
- * TODO: add a function for restoring the previous text
- * TODO: accept mappings for converting shortcuts like WP: to Wikipedia: 
+ * Plugin that highlights matched word partials in a given element.
+ * TODO: Add a function for restoring the previous text.
+ * TODO: Accept mappings for converting shortcuts like WP: to Wikipedia:.
  */
-( function( $ ) {
+( function ( $ ) {
 
-$.highlightText = {
-       
-       // Split our pattern string at spaces and run our highlight function on the results
-       splitAndHighlight: function( node, pat ) {
-               var patArray = pat.split(" ");
-               for ( var i = 0; i < patArray.length; i++ ) {
-                       if ( patArray[i].length == 0 ) continue;
-                       $.highlightText.innerHighlight( node, patArray[i] );
-               }
-               return node;
-       },
-       // scans a node looking for the pattern and wraps a span around each match 
-       innerHighlight: function( node, pat ) {
-               // if this is a text node
-               if ( node.nodeType == 3 ) {
-                       // TODO - need to be smarter about the character matching here. 
-                       // non latin characters can make regex think a new word has begun: do not use \b
-                       // http://stackoverflow.com/questions/3787072/regex-wordwrap-with-utf8-characters-in-js
-                       // look for an occurrence of our pattern and store the starting position
-                       var match = node.data.match( new RegExp( "(^|\\s)" + $.escapeRE( pat ), "i" ) );
-                       if ( match ) {
-                               var pos = match.index + match[1].length; // include length of any matched spaces
-                               // create the span wrapper for the matched text
-                               var spannode = document.createElement( 'span' );
-                               spannode.className = 'highlight';
-                               // shave off the characters preceding the matched text
-                               var middlebit = node.splitText( pos );
-                               // shave off any unmatched text off the end
-                               middlebit.splitText( pat.length );
-                               // clone for appending to our span
-                               var middleclone = middlebit.cloneNode( true );
-                               // append the matched text node to the span
-                               spannode.appendChild( middleclone );
-                               // replace the matched node, with our span-wrapped clone of the matched node
-                               middlebit.parentNode.replaceChild( spannode, middlebit );
+       $.highlightText = {
+
+               // Split our pattern string at spaces and run our highlight function on the results
+               splitAndHighlight: function ( node, pat ) {
+                       var patArray = pat.split( ' ' );
+                       for ( var i = 0; i < patArray.length; i++ ) {
+                               if ( patArray[i].length === 0 ) {
+                                       continue;
+                               }
+                               $.highlightText.innerHighlight( node, patArray[i] );
                        }
-               // if this is an element with childnodes, and not a script, style or an element we created
-               } else if ( node.nodeType == 1 && node.childNodes && !/(script|style)/i.test( node.tagName )
-                               && !( node.tagName.toLowerCase() == 'span' && node.className.match( /\bhighlight/ ) ) ) {
-                       for ( var i = 0; i < node.childNodes.length; ++i ) {
-                               // call the highlight function for each child node
-                               $.highlightText.innerHighlight( node.childNodes[i], pat );
+                       return node;
+               },
+
+               // scans a node looking for the pattern and wraps a span around each match
+               innerHighlight: function ( node, pat ) {
+                       // if this is a text node
+                       if ( node.nodeType === 3 ) {
+                               // TODO - need to be smarter about the character matching here.
+                               // non latin characters can make regex think a new word has begun: do not use \b
+                               // http://stackoverflow.com/questions/3787072/regex-wordwrap-with-utf8-characters-in-js
+                               // look for an occurrence of our pattern and store the starting position
+                               var match = node.data.match( new RegExp( "(^|\\s)" + $.escapeRE( pat ), "i" ) );
+                               if ( match ) {
+                                       var pos = match.index + match[1].length; // include length of any matched spaces
+                                       // create the span wrapper for the matched text
+                                       var spannode = document.createElement( 'span' );
+                                       spannode.className = 'highlight';
+                                       // shave off the characters preceding the matched text
+                                       var middlebit = node.splitText( pos );
+                                       // shave off any unmatched text off the end
+                                       middlebit.splitText( pat.length );
+                                       // clone for appending to our span
+                                       var middleclone = middlebit.cloneNode( true );
+                                       // append the matched text node to the span
+                                       spannode.appendChild( middleclone );
+                                       // replace the matched node, with our span-wrapped clone of the matched node
+                                       middlebit.parentNode.replaceChild( spannode, middlebit );
+                               }
+                       // if this is an element with childnodes, and not a script, style or an element we created
+                       } else if ( node.nodeType === 1 && node.childNodes && !/(script|style)/i.test( node.tagName )
+                                       && !( node.tagName.toLowerCase() === 'span' && node.className.match( /\bhighlight/ ) ) ) {
+                               for ( var i = 0; i < node.childNodes.length; ++i ) {
+                                       // call the highlight function for each child node
+                                       $.highlightText.innerHighlight( node.childNodes[i], pat );
+                               }
                        }
                }
-       }
-};
+       };
 
-$.fn.highlightText = function( matchString ) {
-       return $( this ).each( function() {
-               var $this = $( this );
-               $this.data( 'highlightText', { originalText: $this.text() } );
-               $.highlightText.splitAndHighlight( this, matchString );
-       } );
-};
+       $.fn.highlightText = function ( matchString ) {
+               return $( this ).each( function () {
+                       var $el = $( this );
+                       $el.data( 'highlightText', { originalText: $el.text() } );
+                       $.highlightText.splitAndHighlight( this, matchString );
+               } );
+       };
 
-} )( jQuery );
+}( jQuery ) );
 
index 42554e0..27cae29 100644 (file)
@@ -18,7 +18,7 @@
  *                     <img src="something.jpg" title="My Title Message" alt="My Alt Message" />
  *             </p>
  */
-( function( $ ) {
+( function ( $, mw ) {
 /**
  * Localizes a DOM selection by replacing <html:msg /> elements with localized text and adding
  * localized title and alt attributes to elements with title-msg and alt-msg attributes
  *  * prefix: Message prefix to use when localizing elements and attributes
  */
 
-$.fn.localize = function( options ) {
-       options = $.extend( { 'prefix': '', 'keys': {}, 'params': {} }, options );
+$.fn.localize = function ( options ) {
+       options = $.extend( {
+               prefix: '',
+               keys: {},
+               params: {}
+       }, options );
+
        function msg( key ) {
                var args = key in options.params ? options.params[key] : [];
                // Format: mw.msg( key [, p1, p2, ...] )
                args.unshift( options.prefix + ( key in options.keys ? options.keys[key] : key ) );
                return mw.msg.apply( mw, args );
-       };
+       }
+
        return $(this)
                // Ok, so here's the story on this selector.
                // In IE 6/7, searching for 'msg' turns up the 'html:msg', but searching for 'html:msg' does not.
@@ -43,11 +49,11 @@ $.fn.localize = function( options ) {
                // So searching for both 'msg' and 'html:msg' seems to get the job done.
                // This feels pretty icky, though.
                .find( 'msg,html\\:msg' )
-                       .each( function() {
+                       .each( function () {
                                var $el = $(this);
                                var msgText = msg( $el.attr( 'key' ) );
 
-                               if ( $el.attr('raw') ) {
+                               if ( $el.attr( 'raw' ) ) {
                                        $el.html(msgText);
                                } else {
                                        $el.text(msgText);
@@ -58,7 +64,7 @@ $.fn.localize = function( options ) {
                        } )
                        .end()
                .find( '[title-msg]' )
-                       .each( function() {
+                       .each( function () {
                                var $el = $(this);
                                $el
                                        .attr( 'title', msg( $el.attr( 'title-msg' ) ) )
@@ -66,7 +72,7 @@ $.fn.localize = function( options ) {
                        } )
                        .end()
                .find( '[alt-msg]' )
-                       .each( function() {
+                       .each( function () {
                                var $el = $(this);
                                $el
                                        .attr( 'alt', msg( $el.attr( 'alt-msg' ) ) )
@@ -77,4 +83,5 @@ $.fn.localize = function( options ) {
 
 // Let IE know about the msg tag before it's used...
 document.createElement( 'msg' );
-} )( jQuery );
+
+}( jQuery, mediaWiki ) );
index 7a1897c..77f2639 100644 (file)
  * @license CC-BY 3.0 <http://creativecommons.org/licenses/by/3.0>
  * @license GPL2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
  */
-( function( $, mw ) {
+( function ( $, mw ) {
 
-$.fn.makeCollapsible = function() {
+$.fn.makeCollapsible = function () {
 
-       return this.each(function() {
+       return this.each(function () {
                var _fn = 'jquery.makeCollapsible> ';
 
                // Define reused variables and functions
-               var     $that = $(this).addClass( 'mw-collapsible' ), // case: $( '#myAJAXelement' ).makeCollapsible()
+               var $toggle,
+                       $that = $(this).addClass( 'mw-collapsible' ), // case: $( '#myAJAXelement' ).makeCollapsible()
                        that = this,
                        collapsetext = $(this).attr( 'data-collapsetext' ),
                        expandtext = $(this).attr( 'data-expandtext' ),
-                       toggleElement = function( $collapsible, action, $defaultToggle, instantHide ) {
+                       toggleElement = function ( $collapsible, action, $defaultToggle, instantHide ) {
+                               var $collapsibleContent, $containers;
+
                                // Validate parameters
                                if ( !$collapsible.jquery ) { // $collapsible must be an instance of jQuery
                                        return;
                                }
-                               if ( action != 'expand' && action != 'collapse' ) {
+                               if ( action !== 'expand' && action !== 'collapse' ) {
                                        // action must be string with 'expand' or 'collapse'
                                        return;
                                }
-                               if ( typeof $defaultToggle == 'undefined' ) {
+                               if ( $defaultToggle === undefined ) {
                                        $defaultToggle = null;
                                }
                                if ( $defaultToggle !== null && !($defaultToggle instanceof $) ) {
@@ -45,9 +48,7 @@ $.fn.makeCollapsible = function() {
                                        return;
                                }
 
-                               var $containers = null;
-
-                               if ( action == 'collapse' ) {
+                               if ( action === 'collapse' ) {
 
                                        // Collapse the element
                                        if ( $collapsible.is( 'table' ) ) {
@@ -80,7 +81,7 @@ $.fn.makeCollapsible = function() {
                                                }
 
                                        } else { // <div>, <p> etc.
-                                               var $collapsibleContent = $collapsible.find( '> .mw-collapsible-content' );
+                                               $collapsibleContent = $collapsible.find( '> .mw-collapsible-content' );
 
                                                // If a collapsible-content is defined, collapse it
                                                if ( $collapsibleContent.length ) {
@@ -123,7 +124,7 @@ $.fn.makeCollapsible = function() {
                                                }
 
                                        } else { // <div>, <p> etc.
-                                               var $collapsibleContent = $collapsible.find( '> .mw-collapsible-content' );
+                                               $collapsibleContent = $collapsible.find( '> .mw-collapsible-content' );
 
                                                // If a collapsible-content is defined, collapse it
                                                if ( $collapsibleContent.length ) {
@@ -142,8 +143,8 @@ $.fn.makeCollapsible = function() {
                                }
                        },
                        // Toggles collapsible and togglelink class and updates text label
-                       toggleLinkDefault = function( that, e ) {
-                               var     $that = $(that),
+                       toggleLinkDefault = function ( that, e ) {
+                               var $that = $(that),
                                        $collapsible = $that.closest( '.mw-collapsible.mw-made-collapsible' ).toggleClass( 'mw-collapsed' );
                                e.preventDefault();
                                e.stopPropagation();
@@ -175,9 +176,9 @@ $.fn.makeCollapsible = function() {
                                return;
                        },
                        // Toggles collapsible and togglelink class
-                       toggleLinkPremade = function( $that, e ) {
-                               var     $collapsible = $that.eq(0).closest( '.mw-collapsible.mw-made-collapsible' ).toggleClass( 'mw-collapsed' );
-                               if ( $(e.target).is('a') ) {
+                       toggleLinkPremade = function ( $that, e ) {
+                               var $collapsible = $that.eq(0).closest( '.mw-collapsible.mw-made-collapsible' ).toggleClass( 'mw-collapsed' );
+                               if ( $(e.target).is( 'a' ) ) {
                                        return true;
                                }
                                e.preventDefault();
@@ -200,11 +201,11 @@ $.fn.makeCollapsible = function() {
                                return;
                        },
                        // Toggles customcollapsible
-                       toggleLinkCustom = function( $that, e, $collapsible ) {
+                       toggleLinkCustom = function ( $that, e, $collapsible ) {
                                // For the initial state call of customtogglers there is no event passed
                                if (e) {
                                        e.preventDefault();
-                               e.stopPropagation();
+                                       e.stopPropagation();
                                }
                                // Get current state and toggle to the opposite
                                var action = $collapsible.hasClass( 'mw-collapsed' ) ? 'expand' : 'collapse';
@@ -214,7 +215,7 @@ $.fn.makeCollapsible = function() {
                        };
 
                // Use custom text or default ?
-               if( !collapsetext ) {
+               if ( !collapsetext ) {
                        collapsetext = mw.msg( 'collapsible-collapse' );
                }
                if ( !expandtext ) {
@@ -229,7 +230,7 @@ $.fn.makeCollapsible = function() {
                                .parent()
                                .prepend( '&nbsp;[' )
                                .append( ']&nbsp;' )
-                               .bind( 'click.mw-collapse', function(e) {
+                               .bind( 'click.mw-collapse', function (e) {
                                        toggleLinkDefault( this, e );
                                } );
 
@@ -251,7 +252,7 @@ $.fn.makeCollapsible = function() {
 
                        // Double check that there is actually a customtoggle link
                        if ( $customTogglers.length ) {
-                               $customTogglers.bind( 'click.mw-collapse', function( e ) {
+                               $customTogglers.bind( 'click.mw-collapse', function ( e ) {
                                        toggleLinkCustom( $(this), e, $that );
                                } );
                        } else {
@@ -271,22 +272,22 @@ $.fn.makeCollapsible = function() {
                        // Elements are treated differently
                        if ( $that.is( 'table' ) ) {
                                // The toggle-link will be in one the the cells (td or th) of the first row
-                               var     $firstRowCells = $( 'tr:first th, tr:first td', that ),
-                                       $toggle = $firstRowCells.find( '> .mw-collapsible-toggle' );
+                               var $firstRowCells = $that.find( 'tr:first th, tr:first td' );
+                               $toggle = $firstRowCells.find( '> .mw-collapsible-toggle' );
 
                                // If theres no toggle link, add it to the last cell
                                if ( !$toggle.length ) {
                                        $firstRowCells.eq(-1).prepend( $toggleLink );
                                } else {
-                                       $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function( e ) {
+                                       $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function ( e ) {
                                                toggleLinkPremade( $toggle, e );
                                        } );
                                }
 
                        } else if ( $that.is( 'ul' ) || $that.is( 'ol' ) ) {
                                // The toggle-link will be in the first list-item
-                               var     $firstItem = $( 'li:first', $that),
-                                       $toggle = $firstItem.find( '> .mw-collapsible-toggle' );
+                               var $firstItem = $that.find( 'li:first' );
+                               $toggle = $firstItem.find( '> .mw-collapsible-toggle' );
 
                                // If theres no toggle link, add it
                                if ( !$toggle.length ) {
@@ -294,12 +295,12 @@ $.fn.makeCollapsible = function() {
                                        // to be "1". Except if the value-attribute is already used.
                                        // If no value was set WebKit returns "", Mozilla returns '-1', others return null or undefined.
                                        var firstval = $firstItem.attr( 'value' );
-                                       if ( firstval === undefined || !firstval || firstval == '-1' ) {
+                                       if ( firstval === undefined || !firstval || firstval === '-1' || firstval === -1 ) {
                                                $firstItem.attr( 'value', '1' );
                                        }
                                        $that.prepend( $toggleLink.wrap( '<li class="mw-collapsible-toggle-li"></li>' ).parent() );
                                } else {
-                                       $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function( e ) {
+                                       $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function ( e ) {
                                                toggleLinkPremade( $toggle, e );
                                        } );
                                }
@@ -307,7 +308,7 @@ $.fn.makeCollapsible = function() {
                        } else { // <div>, <p> etc.
 
                                // The toggle-link will be the first child of the element
-                               var $toggle = $that.find( '> .mw-collapsible-toggle' );
+                               $toggle = $that.find( '> .mw-collapsible-toggle' );
 
                                // If a direct child .content-wrapper does not exists, create it
                                if ( !$that.find( '> .mw-collapsible-content' ).length ) {
@@ -318,7 +319,7 @@ $.fn.makeCollapsible = function() {
                                if ( !$toggle.length ) {
                                        $that.prepend( $toggleLink );
                                } else {
-                                       $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function( e ) {
+                                       $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function ( e ) {
                                                toggleLinkPremade( $toggle, e );
                                        } );
                                }
@@ -336,4 +337,5 @@ $.fn.makeCollapsible = function() {
                }
        } );
 };
-} )( jQuery, mediaWiki );
+
+}( jQuery, mediaWiki ) );
index 690fedd..c088bc4 100644 (file)
  * @license CC-BY 3.0 <http://creativecommons.org/licenses/by/3.0>
  * @license GPL2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
  */
-( function( $ ) {
-// @return jQuery object of the message box
-$.messageBoxNew = function( options ) {
+( function ( $ ) {
+
+/** @return jQuery object of the message box */
+$.messageBoxNew = function ( options ) {
        options = $.extend( {
-               'id': 'js-messagebox', // unique identifier for this message box
-               'parent': 'body', // jQuery/CSS selector
-               'insert': 'prepend' // 'prepend' or 'append'
+               // unique identifier for this message box
+               id: 'js-messagebox',
+
+               // jQuery/CSS selector
+               parent: 'body',
+
+               // 'prepend' or 'append'
+               insert: 'prepend'
        }, options );
        var $curBox = $( '#' + options.id );
        // Only create a new box if it doesn't exist already
@@ -47,17 +53,21 @@ $.messageBoxNew = function( options ) {
                }
        }
 };
-// Calling with no message or message set to empty string or null will hide the group,
-// setting 'replace' to true as well will reset and hide the group entirely.
-// If there are no visible groups the main message box is hidden automatically,
-// and shown again once there are messages
-// @return jQuery object of message group
-$.messageBox = function( options ) {
+
+/**
+ * Calling with no message or message set to empty string or null will hide the group,
+ * setting 'replace' to true as well will reset and hide the group entirely.
+ * If there are no visible groups the main message box is hidden automatically,
+ * and shown again once there are messages
+ * @return {jQuery}: jQuery object of message group.
+ */
+$.messageBox = function ( options ) {
        options = $.extend( {
-               'message': '',
-               'group': 'default',
-               'replace': false, // if true replaces any previous message in this group
-               'target': 'js-messagebox'
+               message: '',
+               group: 'default',
+               // if replace=true, it replaces any previous message in this group
+               replace: false,
+               target: 'js-messagebox'
        }, options );
        var $target = $.messageBoxNew( { id: options.target } );
        var groupID = options.target + '-' + options.group;
@@ -95,4 +105,5 @@ $.messageBox = function( options ) {
        }
        return $group;
 };
-} )( jQuery );
+
+}( jQuery ) );
index 023ea7f..3757393 100644 (file)
@@ -1,26 +1,26 @@
 /*
  * JavaScript backwards-compatibility alternatives and other convenience functions
  */
-( function( $ ) {
+( function ( $ ) {
 
        $.extend({
-               trimLeft: function( str ) {
+               trimLeft: function ( str ) {
                        return str === null ? '' : str.toString().replace( /^\s+/, '' );
                },
-               trimRight: function( str ) {
+               trimRight: function ( str ) {
                        return str === null ?
                                        '' : str.toString().replace( /\s+$/, '' );
                },
-               ucFirst: function( str ) {
+               ucFirst: function ( str ) {
                        return str.charAt( 0 ).toUpperCase() + str.substr( 1 );
                },
-               escapeRE: function( str ) {
-                       return str.replace ( /([\\{}()|.?*+\-^$\[\]])/g, "\\$1" );
+               escapeRE: function ( str ) {
+                       return str.replace ( /([\\{}()|.?*+\-\^$\[\]])/g, "\\$1" );
                },
-               isDomElement: function( el ) {
+               isDomElement: function ( el ) {
                        return !!el && !!el.nodeType;
                },
-               isEmpty: function( v ) {
+               isEmpty: function ( v ) {
                        if ( v === '' || v === 0 || v === '0' || v === null
                                || v === false || v === undefined )
                        {
@@ -39,8 +39,8 @@
                        }
                        return false;
                },
-               compareArray: function( arrThis, arrAgainst ) {
-                       if ( arrThis.length != arrAgainst.length ) {
+               compareArray: function ( arrThis, arrAgainst ) {
+                       if ( arrThis.length !== arrAgainst.length ) {
                                return false;
                        }
                        for ( var i = 0; i < arrThis.length; i++ ) {
                        }
                        return true;
                },
-               compareObject: function( objectA, objectB ) {
+               compareObject: function ( objectA, objectB ) {
 
                        // Do a simple check if the types match
-                       if ( typeof objectA == typeof objectB ) {
+                       if ( typeof objectA === typeof objectB ) {
 
                                // Only loop over the contents if it really is an object
-                               if ( typeof objectA == 'object' ) {
+                               if ( typeof objectA === 'object' ) {
                                        // If they are aliases of the same object (ie. mw and mediaWiki) return now
                                        if ( objectA === objectB ) {
                                                return true;
@@ -72,7 +72,7 @@
                                                        if ( prop in objectB ) {
                                                                // Compare the types of the properties
                                                                var type = typeof objectA[prop];
-                                                               if ( type == typeof objectB[prop] ) {
+                                                               if ( type === typeof objectB[prop] ) {
                                                                        // Recursively check objects inside this one
                                                                        switch ( type ) {
                                                                                case 'object' :
                }
        });
 
-} )( jQuery );
+}( jQuery ) );
index 8310cef..5ec05f2 100644 (file)
@@ -3,85 +3,86 @@
  *
  * This will automatically use the HTML5 placeholder attribute if supported, or emulate this behavior if not.
  *
- * @author Trevor Parscal <tparscal@wikimedia.org>
- * @author Krinkle <krinklemail@gmail.com>
+ * @author Trevor Parscal <tparscal@wikimedia.org>, 2012
+ * @author Krinkle <krinklemail@gmail.com>, 2012
  * @version 0.2.0
  * @license GPL v2
  */
-( function( $ ) {
+( function ( $ ) {
 
-$.fn.placeholder = function() {
+       $.fn.placeholder = function () {
 
-       return this.each( function() {
+               return this.each( function () {
 
-               // If the HTML5 placeholder attribute is supported, use it
-               if ( this.placeholder && 'placeholder' in document.createElement( this.tagName ) ) {
-                       return;
-               }
+                       // If the HTML5 placeholder attribute is supported, use it
+                       if ( this.placeholder && 'placeholder' in document.createElement( this.tagName ) ) {
+                               return;
+                       }
 
-               var placeholder = this.getAttribute( 'placeholder' );
-               var $input = $(this);
+                       var placeholder = this.getAttribute( 'placeholder' );
+                       var $input = $(this);
 
-               // Show initially, if empty
-               if ( this.value === '' || this.value === placeholder ) {
-                       $input.addClass( 'placeholder' ).val( placeholder );
-               }
+                       // Show initially, if empty
+                       if ( this.value === '' || this.value === placeholder ) {
+                               $input.addClass( 'placeholder' ).val( placeholder );
+                       }
 
-               $input
-                       // Show on blur if empty
-                       .blur( function() {
-                               if ( this.value === '' ) {
-                                       this.value = placeholder;
-                                       $input.addClass( 'placeholder' );
-                               }
-                       } )
+                       $input
+                               // Show on blur if empty
+                               .blur( function () {
+                                       if ( this.value === '' ) {
+                                               this.value = placeholder;
+                                               $input.addClass( 'placeholder' );
+                                       }
+                               } )
+
+                               // Hide on focus
+                               // Also listen for other events in case $input was
+                               // already focused when the events were bound
+                               .bind( 'focus drop keydown paste', function ( e ) {
+                                       if ( $input.hasClass( 'placeholder' ) ) {
+                                               if ( e.type === 'drop' && e.originalEvent.dataTransfer ) {
+                                                       // Support for drag&drop. Instead of inserting the dropped
+                                                       // text somewhere in the middle of the placeholder string,
+                                                       // we want to set the contents of the search box to the
+                                                       // dropped text.
 
-                       // Hide on focus
-                       // Also listen for other events in case $input was
-                       // already focused when the events were bound
-                       .bind( 'focus drop keydown paste', function( e ) {
-                               if ( $input.hasClass( 'placeholder' ) ) {
-                                       if ( e.type == 'drop' && e.originalEvent.dataTransfer ) {
-                                               // Support for drag&drop. Instead of inserting the dropped
-                                               // text somewhere in the middle of the placeholder string,
-                                               // we want to set the contents of the search box to the
-                                               // dropped text.
+                                                       // IE wants getData( 'text' ) but Firefox wants getData( 'text/plain' )
+                                                       // Firefox fails gracefully with an empty string, IE barfs with an error
+                                                       try {
+                                                               // Try the Firefox way
+                                                               this.value = e.originalEvent.dataTransfer.getData( 'text/plain' );
+                                                       } catch ( exception ) {
+                                                               // Got an exception, so use the IE way
+                                                               this.value = e.originalEvent.dataTransfer.getData( 'text' );
+                                                       }
 
-                                               // IE wants getData( 'text' ) but Firefox wants getData( 'text/plain' )
-                                               // Firefox fails gracefully with an empty string, IE barfs with an error
-                                               try {
-                                                       // Try the Firefox way
-                                                       this.value = e.originalEvent.dataTransfer.getData( 'text/plain' );
-                                               } catch ( exception ) {
-                                                       // Got an exception, so use the IE way
-                                                       this.value = e.originalEvent.dataTransfer.getData( 'text' );
+                                                       // On Firefox, drop fires after the dropped text has been inserted,
+                                                       // but on IE it fires before. If we don't prevent the default action,
+                                                       // IE will insert the dropped text twice.
+                                                       e.preventDefault();
+                                               } else {
+                                                       this.value = '';
                                                }
+                                               $input.removeClass( 'placeholder' );
+                                       }
+                               } );
 
-                                               // On Firefox, drop fires after the dropped text has been inserted,
-                                               // but on IE it fires before. If we don't prevent the default action,
-                                               // IE will insert the dropped text twice.
-                                               e.preventDefault();
-                                       } else {
-                                               this.value = '';
+                       // Blank on submit -- prevents submitting with unintended value
+                       if ( this.form ) {
+                               $( this.form ).submit( function () {
+                                       // $input.trigger( 'focus' ); would be problematic
+                                       // because it actually focuses $input, leading
+                                       // to nasty behavior in mobile browsers
+                                       if ( $input.hasClass( 'placeholder' ) ) {
+                                               $input
+                                                       .val( '' )
+                                                       .removeClass( 'placeholder' );
                                        }
-                                       $input.removeClass( 'placeholder' );
-                               }
-                       } );
+                               });
+                       }
 
-               // Blank on submit -- prevents submitting with unintended value
-               if ( this.form ) {
-                       $( this.form ).submit( function() {
-                               // $input.trigger( 'focus' ); would be problematic
-                               // because it actually focuses $input, leading
-                               // to nasty behavior in mobile browsers
-                               if ( $input.hasClass( 'placeholder' ) ) {
-                                       $input
-                                               .val( '' )
-                                               .removeClass( 'placeholder' );
-                               }
-                       });
-               }
+               });
+       };
 
-       });
-};
-} )( jQuery );
+}( jQuery ) );
index 2db0629..d54e66c 100644 (file)
@@ -43,8 +43,9 @@
                                length = arguments.length;
 
                        for ( ; i < length; i++ ) {
+                               options = arguments[ i ];
                                // Only deal with non-null/undefined values
-                               if ( (options = arguments[ i ]) != null ) {
+                               if ( options !== null && options !== undefined ) {
                                        // Extend the base object
                                        for ( name in options ) {
                                                src = target[ name ];
        };
 
 
-/**
- * CompletenessTest
- * @constructor
- *
- * @example
- *  var myTester = new CompletenessTest( myLib );
- * @param masterVariable {Object} The root variable that contains all object
- *  members. CompletenessTest will recursively traverse objects and keep track
- *  of all methods.
- * @param ignoreFn {Function} Optionally pass a function to filter out certain
- *  methods. Example: You may want to filter out instances of jQuery or some
- *  other constructor. Otherwise "missingTests" will include all methods that
- *  were not called from that instance.
- */
-var CompletenessTest = function ( masterVariable, ignoreFn ) {
-
-       // Keep track in these objects. Keyed by strings with the
-       // method names (ie. 'my.foo', 'my.bar', etc.) values are boolean true.
-       this.injectionTracker = {};
-       this.methodCallTracker = {};
-       this.missingTests = {};
-
-       this.ignoreFn = undefined === ignoreFn ? function () { return false; } : ignoreFn;
+       /**
+        * CompletenessTest
+        * @constructor
+        *
+        * @example
+        *  var myTester = new CompletenessTest( myLib );
+        * @param masterVariable {Object} The root variable that contains all object
+        *  members. CompletenessTest will recursively traverse objects and keep track
+        *  of all methods.
+        * @param ignoreFn {Function} Optionally pass a function to filter out certain
+        *  methods. Example: You may want to filter out instances of jQuery or some
+        *  other constructor. Otherwise "missingTests" will include all methods that
+        *  were not called from that instance.
+        */
+       var CompletenessTest = function ( masterVariable, ignoreFn ) {
 
-       // Lazy limit in case something weird happends (like recurse (part of) ourself).
-       this.lazyLimit = 2000;
-       this.lazyCounter = 0;
+               // Keep track in these objects. Keyed by strings with the
+               // method names (ie. 'my.foo', 'my.bar', etc.) values are boolean true.
+               this.injectionTracker = {};
+               this.methodCallTracker = {};
+               this.missingTests = {};
 
-       var that = this;
+               this.ignoreFn = undefined === ignoreFn ? function () { return false; } : ignoreFn;
 
-       // Bind begin and end to QUnit.
-       QUnit.begin( function () {
-               that.walkTheObject( null, masterVariable, masterVariable, [], CompletenessTest.ACTION_INJECT );
-               log( 'CompletenessTest/walkTheObject/ACTION_INJECT', that );
-       });
+               // Lazy limit in case something weird happends (like recurse (part of) ourself).
+               this.lazyLimit = 2000;
+               this.lazyCounter = 0;
 
-       QUnit.done( function () {
-               that.populateMissingTests();
-               log( 'CompletenessTest/populateMissingTests', that );
+               var that = this;
 
-               var toolbar, testResults, cntTotal, cntCalled, cntMissing;
+               // Bind begin and end to QUnit.
+               QUnit.begin( function () {
+                       that.walkTheObject( null, masterVariable, masterVariable, [], CompletenessTest.ACTION_INJECT );
+                       log( 'CompletenessTest/walkTheObject/ACTION_INJECT', that );
+               });
 
-               cntTotal = util.keys( that.injectionTracker ).length;
-               cntCalled = util.keys( that.methodCallTracker ).length;
-               cntMissing = util.keys( that.missingTests ).length;
+               QUnit.done( function () {
+                       that.populateMissingTests();
+                       log( 'CompletenessTest/populateMissingTests', that );
 
-               function makeTestResults( blob, title, style ) {
-                       var elOutputWrapper, elTitle, elContainer, elList, elFoot;
+                       var toolbar, testResults, cntTotal, cntCalled, cntMissing;
 
-                       elTitle = document.createElement( 'strong' );
-                       elTitle.textContent = title || 'Values';
+                       cntTotal = util.keys( that.injectionTracker ).length;
+                       cntCalled = util.keys( that.methodCallTracker ).length;
+                       cntMissing = util.keys( that.missingTests ).length;
 
-                       elList = document.createElement( 'ul' );
-                       util.each( blob, function ( key ) {
-                               var elItem = document.createElement( 'li' );
-                               elItem.textContent = key;
-                               elList.appendChild( elItem );
-                       });
+                       function makeTestResults( blob, title, style ) {
+                               var elOutputWrapper, elTitle, elContainer, elList, elFoot;
 
-                       elFoot = document.createElement( 'p' );
-                       elFoot.innerHTML = '<em>&mdash; CompletenessTest</em>';
+                               elTitle = document.createElement( 'strong' );
+                               elTitle.textContent = title || 'Values';
 
-                       elContainer = document.createElement( 'div' );
-                       elContainer.appendChild( elTitle );
-                       elContainer.appendChild( elList );
-                       elContainer.appendChild( elFoot );
+                               elList = document.createElement( 'ul' );
+                               util.each( blob, function ( key ) {
+                                       var elItem = document.createElement( 'li' );
+                                       elItem.textContent = key;
+                                       elList.appendChild( elItem );
+                               });
 
-                       elOutputWrapper = document.getElementById( 'qunit-completenesstest' );
-                       if ( !elOutputWrapper ) {
-                               elOutputWrapper = document.createElement( 'div' );
-                               elOutputWrapper.id = 'qunit-completenesstest';
-                       }
-                       elOutputWrapper.appendChild( elContainer );
+                               elFoot = document.createElement( 'p' );
+                               elFoot.innerHTML = '<em>&mdash; CompletenessTest</em>';
 
-                       util.each( style, function ( key, value ) {
-                               elOutputWrapper.style[key] = value;
-                       });
-                       return elOutputWrapper;
-               }
+                               elContainer = document.createElement( 'div' );
+                               elContainer.appendChild( elTitle );
+                               elContainer.appendChild( elList );
+                               elContainer.appendChild( elFoot );
 
-               if ( cntMissing === 0 ) {
-                       // Good
-                       testResults = makeTestResults(
-                               {},
-                               'Detected calls to ' + cntCalled + '/' + cntTotal + ' methods. No missing tests!',
-                               {
-                                       backgroundColor: '#D2E0E6',
-                                       color: '#366097',
-                                       paddingTop: '1em',
-                                       paddingRight: '1em',
-                                       paddingBottom: '1em',
-                                       paddingLeft: '1em'
-                               }
-                       );
-               } else {
-                       // Bad
-                       testResults = makeTestResults(
-                               that.missingTests,
-                               'Detected calls to ' + cntCalled + '/' + cntTotal + ' methods. ' + cntMissing + ' methods not covered:',
-                               {
-                                       backgroundColor: '#EE5757',
-                                       color: 'black',
-                                       paddingTop: '1em',
-                                       paddingRight: '1em',
-                                       paddingBottom: '1em',
-                                       paddingLeft: '1em'
+                               elOutputWrapper = document.getElementById( 'qunit-completenesstest' );
+                               if ( !elOutputWrapper ) {
+                                       elOutputWrapper = document.createElement( 'div' );
+                                       elOutputWrapper.id = 'qunit-completenesstest';
                                }
-                       );
-               }
+                               elOutputWrapper.appendChild( elContainer );
 
-               toolbar = document.getElementById( 'qunit-testrunner-toolbar' );
-               if ( toolbar ) {
-                       toolbar.insertBefore( testResults, toolbar.firstChild );
-               }
-       });
+                               util.each( style, function ( key, value ) {
+                                       elOutputWrapper.style[key] = value;
+                               });
+                               return elOutputWrapper;
+                       }
 
-       return this;
-};
+                       if ( cntMissing === 0 ) {
+                               // Good
+                               testResults = makeTestResults(
+                                       {},
+                                       'Detected calls to ' + cntCalled + '/' + cntTotal + ' methods. No missing tests!',
+                                       {
+                                               backgroundColor: '#D2E0E6',
+                                               color: '#366097',
+                                               paddingTop: '1em',
+                                               paddingRight: '1em',
+                                               paddingBottom: '1em',
+                                               paddingLeft: '1em'
+                                       }
+                               );
+                       } else {
+                               // Bad
+                               testResults = makeTestResults(
+                                       that.missingTests,
+                                       'Detected calls to ' + cntCalled + '/' + cntTotal + ' methods. ' + cntMissing + ' methods not covered:',
+                                       {
+                                               backgroundColor: '#EE5757',
+                                               color: 'black',
+                                               paddingTop: '1em',
+                                               paddingRight: '1em',
+                                               paddingBottom: '1em',
+                                               paddingLeft: '1em'
+                                       }
+                               );
+                       }
 
-/* Static members */
-CompletenessTest.ACTION_INJECT = 500;
-CompletenessTest.ACTION_CHECK = 501;
+                       toolbar = document.getElementById( 'qunit-testrunner-toolbar' );
+                       if ( toolbar ) {
+                               toolbar.insertBefore( testResults, toolbar.firstChild );
+                       }
+               });
 
-/* Public methods */
-CompletenessTest.fn = CompletenessTest.prototype = {
+               return this;
+       };
 
-       /**
-        * CompletenessTest.fn.walkTheObject
-        *
-        * This function recursively walks through the given object, calling itself as it goes.
-        * Depending on the action it either injects our listener into the methods, or
-        * reads from our tracker and records which methods have not been called by the test suite.
-        *
-        * @param currName {String|Null} Name of the given object member (Initially this is null).
-        * @param currVar {mixed} The variable to check (initially an object,
-        *  further down it could be anything).
-        * @param masterVariable {Object} Throughout our interation, always keep track of the master/root.
-        *  Initially this is the same as currVar.
-        * @param parentPathArray {Array} Array of names that indicate our breadcrumb path starting at
-        *  masterVariable. Not including currName.
-        * @param action {Number} What is this function supposed to do (ACTION_INJECT or ACTION_CHECK)
-        */
-       walkTheObject: function ( currName, currVar, masterVariable, parentPathArray, action ) {
+       /* Static members */
+       CompletenessTest.ACTION_INJECT = 500;
+       CompletenessTest.ACTION_CHECK = 501;
+
+       /* Public methods */
+       CompletenessTest.fn = CompletenessTest.prototype = {
+
+               /**
+                * CompletenessTest.fn.walkTheObject
+                *
+                * This function recursively walks through the given object, calling itself as it goes.
+                * Depending on the action it either injects our listener into the methods, or
+                * reads from our tracker and records which methods have not been called by the test suite.
+                *
+                * @param currName {String|Null} Name of the given object member (Initially this is null).
+                * @param currVar {mixed} The variable to check (initially an object,
+                *  further down it could be anything).
+                * @param masterVariable {Object} Throughout our interation, always keep track of the master/root.
+                *  Initially this is the same as currVar.
+                * @param parentPathArray {Array} Array of names that indicate our breadcrumb path starting at
+                *  masterVariable. Not including currName.
+                * @param action {Number} What is this function supposed to do (ACTION_INJECT or ACTION_CHECK)
+                */
+               walkTheObject: function ( currName, currVar, masterVariable, parentPathArray, action ) {
+
+                       var key, value, tmpPathArray,
+                               type = util.type( currVar ),
+                               that = this;
+
+                       // Hard ignores
+                       if ( this.ignoreFn( currVar, that, parentPathArray ) ) {
+                               return null;
+                       }
 
-               var key, value, tmpPathArray,
-                       type = util.type( currVar ),
-                       that = this;
+                       // Handle the lazy limit
+                       this.lazyCounter++;
+                       if ( this.lazyCounter > this.lazyLimit ) {
+                               log( 'CompletenessTest.fn.walkTheObject> Limit reached: ' + this.lazyCounter, parentPathArray );
+                               return null;
+                       }
 
-               // Hard ignores
-               if ( this.ignoreFn( currVar, that, parentPathArray ) ) {
-                       return null;
-               }
+                       // Functions
+                       if ( type === 'function' ) {
 
-               // Handle the lazy limit
-               this.lazyCounter++;
-               if ( this.lazyCounter > this.lazyLimit ) {
-                       log( 'CompletenessTest.fn.walkTheObject> Limit reached: ' + this.lazyCounter, parentPathArray );
-                       return null;
-               }
+                               if ( !currVar.prototype || util.isEmptyObject( currVar.prototype ) ) {
 
-               // Functions
-               if ( type === 'function' ) {
+                                       if ( action === CompletenessTest.ACTION_INJECT ) {
 
-                       if ( !currVar.prototype || util.isEmptyObject( currVar.prototype ) ) {
+                                               that.injectionTracker[ parentPathArray.join( '.' ) ] = true;
+                                               that.injectCheck( masterVariable, parentPathArray, function () {
+                                                       that.methodCallTracker[ parentPathArray.join( '.' ) ] = true;
+                                               } );
+                                       }
 
-                               if ( action === CompletenessTest.ACTION_INJECT ) {
+                               // We don't support checking object constructors yet...
+                               // ...we can check the prototypes fine, though.
+                               } else {
+                                       if ( action === CompletenessTest.ACTION_INJECT ) {
 
-                                       that.injectionTracker[ parentPathArray.join( '.' ) ] = true;
-                                       that.injectCheck( masterVariable, parentPathArray, function () {
-                                               that.methodCallTracker[ parentPathArray.join( '.' ) ] = true;
-                                       } );
-                               }
+                                               for ( key in currVar.prototype ) {
+                                                       if ( hasOwn.call( currVar.prototype, key ) ) {
+                                                               value = currVar.prototype[key];
+                                                               if ( key === 'constructor' ) {
+                                                                       continue;
+                                                               }
 
-                       // We don't support checking object constructors yet...
-                       // ...we can check the prototypes fine, though.
-                       } else {
-                               if ( action === CompletenessTest.ACTION_INJECT ) {
+                                                               // Clone and break reference to parentPathArray
+                                                               tmpPathArray = util.extend( [], parentPathArray );
+                                                               tmpPathArray.push( 'prototype' );
+                                                               tmpPathArray.push( key );
 
-                                       for ( key in currVar.prototype ) {
-                                               if ( hasOwn.call( currVar.prototype, key ) ) {
-                                                       value = currVar.prototype[key];
-                                                       if ( key === 'constructor' ) {
-                                                               continue;
+                                                               that.walkTheObject( key, value, masterVariable, tmpPathArray, action );
                                                        }
-
-                                                       // Clone and break reference to parentPathArray
-                                                       tmpPathArray = util.extend( [], parentPathArray );
-                                                       tmpPathArray.push( 'prototype' );
-                                                       tmpPathArray.push( key );
-
-                                                       that.walkTheObject( key, value, masterVariable, tmpPathArray, action );
                                                }
-                                       }
 
+                                       }
                                }
-                       }
 
-               }
+                       }
 
-               // Recursively. After all, this is the *completeness* test
-               if ( type === 'function' || type === 'object' ) {
-                       for ( key in currVar ) {
-                               if ( hasOwn.call( currVar, key ) ) {
-                                       value = currVar[key];
+                       // Recursively. After all, this is the *completeness* test
+                       if ( type === 'function' || type === 'object' ) {
+                               for ( key in currVar ) {
+                                       if ( hasOwn.call( currVar, key ) ) {
+                                               value = currVar[key];
 
-                                       // Clone and break reference to parentPathArray
-                                       tmpPathArray = util.extend( [], parentPathArray );
-                                       tmpPathArray.push( key );
+                                               // Clone and break reference to parentPathArray
+                                               tmpPathArray = util.extend( [], parentPathArray );
+                                               tmpPathArray.push( key );
 
-                                       that.walkTheObject( key, value, masterVariable, tmpPathArray, action );
+                                               that.walkTheObject( key, value, masterVariable, tmpPathArray, action );
+                                       }
                                }
                        }
-               }
-       },
+               },
 
-       populateMissingTests: function () {
-               var ct = this;
-               util.each( ct.injectionTracker, function ( key ) {
-                       ct.hasTest( key );
-               });
-       },
+               populateMissingTests: function () {
+                       var ct = this;
+                       util.each( ct.injectionTracker, function ( key ) {
+                               ct.hasTest( key );
+                       });
+               },
 
-       /**
-        * CompletenessTest.fn.hasTest
-        *
-        * Checks if the given method name (ie. 'my.foo.bar')
-        * was called during the test suite (as far as the tracker knows).
-        * If not it adds it to missingTests.
-        *
-        * @param fnName {String}
-        * @return {Boolean}
-        */
-       hasTest: function ( fnName ) {
-               if ( !( fnName in this.methodCallTracker ) ) {
-                       this.missingTests[fnName] = true;
-                       return false;
-               }
-               return true;
-       },
+               /**
+                * CompletenessTest.fn.hasTest
+                *
+                * Checks if the given method name (ie. 'my.foo.bar')
+                * was called during the test suite (as far as the tracker knows).
+                * If not it adds it to missingTests.
+                *
+                * @param fnName {String}
+                * @return {Boolean}
+                */
+               hasTest: function ( fnName ) {
+                       if ( !( fnName in this.methodCallTracker ) ) {
+                               this.missingTests[fnName] = true;
+                               return false;
+                       }
+                       return true;
+               },
 
-       /**
-        * CompletenessTest.fn.injectCheck
-        *
-        * Injects a function (such as a spy that updates methodCallTracker when
-        * it's called) inside another function.
-        *
-        * @param masterVariable {Object}
-        * @param objectPathArray {Array}
-        * @param injectFn {Function}
-        */
-       injectCheck: function ( masterVariable, objectPathArray, injectFn ) {
-               var i, len, prev, memberName, lastMember,
-                       curr = masterVariable;
-
-               // Get the object in question through the path from the master variable,
-               // We can't pass the value directly because we need to re-define the object
-               // member and keep references to the parent object, member name and member
-               // value at all times.
-               for ( i = 0, len = objectPathArray.length; i < len; i++ ) {
-                       memberName = objectPathArray[i];
-
-                       prev = curr;
-                       curr = prev[memberName];
-                       lastMember = memberName;
-               }
+               /**
+                * CompletenessTest.fn.injectCheck
+                *
+                * Injects a function (such as a spy that updates methodCallTracker when
+                * it's called) inside another function.
+                *
+                * @param masterVariable {Object}
+                * @param objectPathArray {Array}
+                * @param injectFn {Function}
+                */
+               injectCheck: function ( masterVariable, objectPathArray, injectFn ) {
+                       var i, len, prev, memberName, lastMember,
+                               curr = masterVariable;
+
+                       // Get the object in question through the path from the master variable,
+                       // We can't pass the value directly because we need to re-define the object
+                       // member and keep references to the parent object, member name and member
+                       // value at all times.
+                       for ( i = 0, len = objectPathArray.length; i < len; i++ ) {
+                               memberName = objectPathArray[i];
+
+                               prev = curr;
+                               curr = prev[memberName];
+                               lastMember = memberName;
+                       }
 
-               // Objects are by reference, members (unless objects) are not.
-               prev[lastMember] = function () {
-                       injectFn();
-                       return curr.apply( this, arguments );
-               };
-       }
-};
+                       // Objects are by reference, members (unless objects) are not.
+                       prev[lastMember] = function () {
+                               injectFn();
+                               return curr.apply( this, arguments );
+                       };
+               }
+       };
 
-window.CompletenessTest = CompletenessTest;
+       /* Expose */
+       window.CompletenessTest = CompletenessTest;
 
-} )( jQuery );
+}( jQuery ) );
index 87e4538..e8b683e 100644 (file)
@@ -3,42 +3,42 @@
  *
  * Simple jQuery plugin to create, inject and remove spinners.
  */
-( function( $ ) {
+( function ( $ ) {
 
-$.extend( {
-       /**
-        * Creates a spinner element.
-        *
-        * @param id {String} id of the spinner
-        * @return {jQuery} spinner
-        */
-       createSpinner: function( id ) {
-               return $( '<div>' ).attr( {
-                       id: 'mw-spinner-' + id,
-                       'class': 'mw-spinner',
-                       title: '...'
-               } );
-       },
+       $.extend( {
+               /**
+                * Creates a spinner element.
+                *
+                * @param id {String} id of the spinner
+                * @return {jQuery} spinner
+                */
+               createSpinner: function ( id ) {
+                       return $( '<div>' ).attr( {
+                               id: 'mw-spinner-' + id,
+                               'class': 'mw-spinner',
+                               title: '...'
+                       } );
+               },
+
+               /**
+                * Removes a spinner element.
+                *
+                * @param id {String}
+                * @return {jQuery} spinner
+                */
+               removeSpinner: function ( id ) {
+                       return $( '#mw-spinner-' + id ).remove();
+               }
+       } );
 
        /**
-        * Removes a spinner element.
+        * Injects a spinner after the elements in the jQuery collection.
         *
-        * @param id {String}
-        * @return {jQuery} spinner
+        * @param id String id of the spinner
+        * @return {jQuery}
         */
-       removeSpinner: function( id ) {
-               return $( '#mw-spinner-' + id ).remove();
-       }
-} );
-
-/**
- * Injects a spinner after the elements in the jQuery collection.
- *
- * @param id String id of the spinner
- * @return {jQuery}
- */
-$.fn.injectSpinner = function( id ) {
-       return this.after( $.createSpinner( id ) );
-};
+       $.fn.injectSpinner = function ( id ) {
+               return this.after( $.createSpinner( id ) );
+       };
 
-} )( jQuery );
+}( jQuery ) );
index 466c551..dff5535 100644 (file)
  * highlightInput: Whether to hightlight matched portions of the input or not
  *             Type: Boolean, Default: false
  */
-( function( $ ) {
+( function ( $ ) {
 
 $.suggestions = {
        /**
         * Cancel any delayed updateSuggestions() call and inform the user so
         * they can cancel their result fetching if they use AJAX or something
         */
-       cancel: function( context ) {
-               if ( context.data.timerID != null ) {
+       cancel: function ( context ) {
+               if ( context.data.timerID !== null ) {
                        clearTimeout( context.data.timerID );
                }
                if ( $.isFunction( context.config.cancel ) ) {
@@ -61,7 +61,7 @@ $.suggestions = {
         * restores the value the currently displayed suggestions are based on, rather than the value just before
         * highlight() overwrote it; the former is arguably slightly more sensible.
         */
-       restore: function( context ) {
+       restore: function ( context ) {
                context.data.$textbox.val( context.data.prevText );
        },
        /**
@@ -70,7 +70,7 @@ $.suggestions = {
         * function does nothing.
         * @param {Boolean} delayed Whether or not to delay this by the currently configured amount of time
         */
-       update: function( context, delayed ) {
+       update: function ( context, delayed ) {
                // Only fetch if the value in the textbox changed and is not empty
                // if the textbox is empty then clear the result div, but leave other settings intouched
                function maybeFetch() {
@@ -86,7 +86,7 @@ $.suggestions = {
                }
 
                // Cancel previous call
-               if ( context.data.timerID != null ) {
+               if ( context.data.timerID !== null ) {
                        clearTimeout( context.data.timerID );
                }
                if ( delayed ) {
@@ -97,11 +97,11 @@ $.suggestions = {
                }
                $.suggestions.special( context );
        },
-       special: function( context ) {
+       special: function ( context ) {
                // Allow custom rendering - but otherwise don't do any rendering
                if ( typeof context.config.special.render === 'function' ) {
                        // Wait for the browser to update the value
-                       setTimeout( function() {
+                       setTimeout( function () {
                                // Render special
                                var $special = context.data.$container.find( '.suggestions-special' );
                                context.config.special.render.call( $special, context.data.$textbox.val() );
@@ -113,7 +113,7 @@ $.suggestions = {
         * @param property String Name of property
         * @param value Mixed Value to set property with
         */
-       configure: function( context, property, value ) {
+       configure: function ( context, property, value ) {
                // Validate creation using fallback values
                switch( property ) {
                        case 'fetch':
@@ -154,12 +154,13 @@ $.suggestions = {
                                                var $autoEllipseMe = $( [] );
                                                var matchedText = null;
                                                for ( var i = 0; i < context.config.suggestions.length; i++ ) {
+                                                       /*jshint loopfunc:true */
                                                        var text = context.config.suggestions[i];
                                                        var $result = $( '<div>' )
                                                                .addClass( 'suggestions-result' )
                                                                .attr( 'rel', i )
                                                                .data( 'text', context.config.suggestions[i] )
-                                                               .mousemove( function( e ) {
+                                                               .mousemove( function ( e ) {
                                                                        context.data.selectedWithMouse = true;
                                                                        $.suggestions.highlight(
                                                                                context, $(this).closest( '.suggestions-results div' ), false
@@ -220,9 +221,9 @@ $.suggestions = {
         * @param result <tr> to highlight: jQuery object, or 'prev' or 'next'
         * @param updateTextbox If true, put the suggestion in the textbox
         */
-       highlight: function( context, result, updateTextbox ) {
+       highlight: function ( context, result, updateTextbox ) {
                var selected = context.data.$container.find( '.suggestions-result-current' );
-               if ( !result.get || selected.get( 0 ) != result.get( 0 ) ) {
+               if ( !result.get || selected.get( 0 ) !== result.get( 0 ) ) {
                        if ( result === 'prev' ) {
                                if( selected.is( '.suggestions-special' ) ) {
                                        result = context.data.$container.find( '.suggestions-result:last' );
@@ -277,8 +278,8 @@ $.suggestions = {
         * Respond to keypress event
         * @param key Integer Code of key pressed
         */
-       keypress: function( e, context, key ) {
-               var     wasVisible = context.data.$container.is( ':visible' ),
+       keypress: function ( e, context, key ) {
+               var wasVisible = context.data.$container.is( ':visible' ),
                        preventDefault = false;
                switch ( key ) {
                        // Arrow down
@@ -340,13 +341,13 @@ $.suggestions = {
                }
        }
 };
-$.fn.suggestions = function() {
+$.fn.suggestions = function () {
 
        // Multi-context fields
-       var returnValue = null;
+       var returnValue;
        var args = arguments;
 
-       $(this).each( function() {
+       $(this).each( function () {
 
                /* Construction / Loading */
 
@@ -354,8 +355,8 @@ $.fn.suggestions = function() {
                if ( context === undefined || context === null ) {
                        context = {
                                config: {
-                                       'fetch' : function() {},
-                                       'cancel': function() {},
+                                       'fetch' : function () {},
+                                       'cancel': function () {},
                                        'special': {},
                                        'result': {},
                                        '$region': $(this),
@@ -383,7 +384,7 @@ $.fn.suggestions = function() {
                                if ( args.length > 1 ) {
                                        // Set property values
                                        $.suggestions.configure( context, args[0], args[1] );
-                               } else if ( returnValue == null ) {
+                               } else if ( returnValue === null || returnValue === undefined ) {
                                        // Get property values, but don't give access to internal data - returns only the first
                                        returnValue = ( args[0] in context.config ? undefined : context.config[args[0]] );
                                }
@@ -395,15 +396,18 @@ $.fn.suggestions = function() {
                if ( context.data === undefined ) {
                        context.data = {
                                // ID of running timer
-                               'timerID': null,
+                               timerID: null,
+
                                // Text in textbox when suggestions were last fetched
-                               'prevText': null,
+                               prevText: null,
+
                                // Number of results visible without scrolling
-                               'visibleResults': 0,
+                               visibleResults: 0,
+
                                // Suggestion the last mousedown event occured on
-                               'mouseDownOn': $( [] ),
-                               '$textbox': $(this),
-                               'selectedWithMouse': false
+                               mouseDownOn: $( [] ),
+                               $textbox: $(this),
+                               selectedWithMouse: false
                        };
                        // Setup the css for positioning the results box
                        var newCSS = {
@@ -426,14 +430,14 @@ $.fn.suggestions = function() {
                                        $( '<div>' ).addClass( 'suggestions-results' )
                                                // Can't use click() because the container div is hidden when the textbox loses focus. Instead,
                                                // listen for a mousedown followed by a mouseup on the same div
-                                               .mousedown( function( e ) {
+                                               .mousedown( function ( e ) {
                                                        context.data.mouseDownOn = $( e.target ).closest( '.suggestions-results div' );
                                                } )
-                                               .mouseup( function( e ) {
+                                               .mouseup( function ( e ) {
                                                        var $result = $( e.target ).closest( '.suggestions-results div' );
                                                        var $other = context.data.mouseDownOn;
                                                        context.data.mouseDownOn = $( [] );
-                                                       if ( $result.get( 0 ) != $other.get( 0 ) ) {
+                                                       if ( $result.get( 0 ) !== $other.get( 0 ) ) {
                                                                return;
                                                        }
                                                        $.suggestions.highlight( context, $result, true );
@@ -448,14 +452,14 @@ $.fn.suggestions = function() {
                                        $( '<div>' ).addClass( 'suggestions-special' )
                                                // Can't use click() because the container div is hidden when the textbox loses focus. Instead,
                                                // listen for a mousedown followed by a mouseup on the same div
-                                               .mousedown( function( e ) {
+                                               .mousedown( function ( e ) {
                                                        context.data.mouseDownOn = $( e.target ).closest( '.suggestions-special' );
                                                } )
-                                               .mouseup( function( e ) {
+                                               .mouseup( function ( e ) {
                                                        var $special = $( e.target ).closest( '.suggestions-special' );
                                                        var $other = context.data.mouseDownOn;
                                                        context.data.mouseDownOn = $( [] );
-                                                       if ( $special.get( 0 ) != $other.get( 0 ) ) {
+                                                       if ( $special.get( 0 ) !== $other.get( 0 ) ) {
                                                                return;
                                                        }
                                                        context.data.$container.hide();
@@ -464,7 +468,7 @@ $.fn.suggestions = function() {
                                                        }
                                                        context.data.$textbox.focus();
                                                } )
-                                               .mousemove( function( e ) {
+                                               .mousemove( function ( e ) {
                                                        context.data.selectedWithMouse = true;
                                                        $.suggestions.highlight(
                                                                context, $( e.target ).closest( '.suggestions-special' ), false
@@ -475,9 +479,9 @@ $.fn.suggestions = function() {
                        $(this)
                                // Stop browser autocomplete from interfering
                                .attr( 'autocomplete', 'off')
-                               .keydown( function( e ) {
+                               .keydown( function ( e ) {
                                        // Store key pressed to handle later
-                                       context.data.keypressed = ( e.keyCode === undefined ) ? e.which : e.keyCode;
+                                       context.data.keypressed = e.which;
                                        context.data.keypressedCount = 0;
 
                                        switch ( context.data.keypressed ) {
@@ -496,18 +500,18 @@ $.fn.suggestions = function() {
                                                        }
                                        }
                                } )
-                               .keypress( function( e ) {
+                               .keypress( function ( e ) {
                                        context.data.keypressedCount++;
                                        $.suggestions.keypress( e, context, context.data.keypressed );
                                } )
-                               .keyup( function( e ) {
+                               .keyup( function ( e ) {
                                        // Some browsers won't throw keypress() for arrow keys. If we got a keydown and a keyup without a
                                        // keypress in between, solve it
                                        if ( context.data.keypressedCount === 0 ) {
                                                $.suggestions.keypress( e, context, context.data.keypressed );
                                        }
                                } )
-                               .blur( function() {
+                               .blur( function () {
                                        // When losing focus because of a mousedown
                                        // on a suggestion, don't hide the suggestions
                                        if ( context.data.mouseDownOn.length > 0 ) {
@@ -520,6 +524,7 @@ $.fn.suggestions = function() {
                // Store the context for next time
                $(this).data( 'suggestions-context', context );
        } );
-       return returnValue !== null ? returnValue : $(this);
+       return returnValue !== undefined ? returnValue : $(this);
 };
-} )( jQuery );
\ No newline at end of file
+
+}( jQuery ) );
index 75731d7..cdae0ba 100644 (file)
@@ -1,50 +1,52 @@
 /**
  * jQuery tabIndex
  */
-( function( $ ) {
-/**
- * Finds the lowerst tabindex in use within a selection
- *
- * @return number Lowest tabindex on the page
- */
-$.fn.firstTabIndex = function() {
-       var minTabIndex = null;
-       $(this).find( '[tabindex]' ).each( function() {
-               var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 );
-               // In IE6/IE7 the above jQuery selector returns all elements,
-               // becuase it has a default value for tabIndex in IE6/IE7 of 0
-               // (rather than null/undefined). Therefore check "> 0" as well.
-               // Under IE7 under Windows NT 5.2 is also capable of returning NaN.
-               if ( tabIndex > 0 && !isNaN( tabIndex ) ) {
-                       // Initial value
-                       if ( minTabIndex === null ) {
-                               minTabIndex = tabIndex;
-                       } else if ( tabIndex < minTabIndex ) {
-                               minTabIndex = tabIndex;
+( function ( $ ) {
+
+       /**
+        * Finds the lowerst tabindex in use within a selection
+        *
+        * @return number Lowest tabindex on the page
+        */
+       $.fn.firstTabIndex = function () {
+               var minTabIndex = null;
+               $(this).find( '[tabindex]' ).each( function () {
+                       var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 );
+                       // In IE6/IE7 the above jQuery selector returns all elements,
+                       // becuase it has a default value for tabIndex in IE6/IE7 of 0
+                       // (rather than null/undefined). Therefore check "> 0" as well.
+                       // Under IE7 under Windows NT 5.2 is also capable of returning NaN.
+                       if ( tabIndex > 0 && !isNaN( tabIndex ) ) {
+                               // Initial value
+                               if ( minTabIndex === null ) {
+                                       minTabIndex = tabIndex;
+                               } else if ( tabIndex < minTabIndex ) {
+                                       minTabIndex = tabIndex;
+                               }
                        }
-               }
-       } );
-       return minTabIndex;
-};
+               } );
+               return minTabIndex;
+       };
 
-/**
- * Finds the highest tabindex in use within a selection
- *
- * @return number Highest tabindex on the page
- */
-$.fn.lastTabIndex = function() {
-       var maxTabIndex = null;
-       $(this).find( '[tabindex]' ).each( function() {
-               var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 );
-               if ( tabIndex > 0 && !isNaN( tabIndex ) ) {
-                       // Initial value
-                       if ( maxTabIndex === null ) {
-                               maxTabIndex = tabIndex;
-                       } else if ( tabIndex > maxTabIndex ) {
-                               maxTabIndex = tabIndex;
+       /**
+        * Finds the highest tabindex in use within a selection
+        *
+        * @return number Highest tabindex on the page
+        */
+       $.fn.lastTabIndex = function () {
+               var maxTabIndex = null;
+               $(this).find( '[tabindex]' ).each( function () {
+                       var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 );
+                       if ( tabIndex > 0 && !isNaN( tabIndex ) ) {
+                               // Initial value
+                               if ( maxTabIndex === null ) {
+                                       maxTabIndex = tabIndex;
+                               } else if ( tabIndex > maxTabIndex ) {
+                                       maxTabIndex = tabIndex;
+                               }
                        }
-               }
-       } );
-       return maxTabIndex;
-};
-} )( jQuery );
+               } );
+               return maxTabIndex;
+       };
+
+}( jQuery ) );
index 08272a5..0edc8ee 100644 (file)
@@ -56,7 +56,7 @@
  * @author Christian Bach/christian.bach@polyester.se
  */
 
-( function( $ ) {
+( function ( $, mw ) {
 
        /* Local scope */
 
        }
 
        function detectParserForColumn( table, rows, cellIndex ) {
-               var     l = parsers.length,
+               var l = parsers.length,
                        nodeValue,
                        // Start with 1 because 0 is the fallback parser
                        i = 1,
        }
 
        function buildParserCache( table, $headers ) {
-               var     rows = table.tBodies[0].rows,
+               var rows = table.tBodies[0].rows,
                        sortType,
                        parsers = [];
 
                if ( rows[0] ) {
 
-                       var     cells = rows[0].cells,
+                       var cells = rows[0].cells,
                                len = cells.length,
                                i, parser;
 
        /* Other utility functions */
 
        function buildCache( table ) {
-               var     totalRows = ( table.tBodies[0] && table.tBodies[0].rows.length ) || 0,
+               var totalRows = ( table.tBodies[0] && table.tBodies[0].rows.length ) || 0,
                        totalCells = ( table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length ) || 0,
                        parsers = table.config.parsers,
                        cache = {
                for ( var i = 0; i < totalRows; ++i ) {
 
                        // Add the table data to main data array
-                       var     $row = $( table.tBodies[0].rows[i] ),
+                       var $row = $( table.tBodies[0].rows[i] ),
                                cols = [];
 
                        // if this is a child row, add it to the last row's children and
        }
 
        function appendToTable( table, cache ) {
-               var     row = cache.row,
+               var row = cache.row,
                        normalized = cache.normalized,
                        totalRows = normalized.length,
                        checkCell = ( normalized[0].length - 1 ),
                }
                table.tBodies[0].appendChild( fragment );
        }
-       
+
        /**
         * Find all header rows in a thead-less table and put them in a <thead> tag.
         * This only treats a row as a header row if it contains only <th>s (no <td>s)
         * and if it is preceded entirely by header rows. The algorithm stops when
         * it encounters the first non-header row.
-        * 
+        *
         * After this, it will look at all rows at the bottom for footer rows
         * And place these in a tfoot using similar rules.
         * @param $table jQuery object for a <table>
-        */ 
+        */
        function emulateTHeadAndFoot( $table ) {
                var $rows = $table.find( '> tbody > tr' );
                if( !$table.get(0).tHead ) {
                        var $thead = $( '<thead>' );
-                       $rows.each( function() {
+                       $rows.each( function () {
                                if ( $(this).children( 'td' ).length > 0 ) {
                                        // This row contains a <td>, so it's not a header row
                                        // Stop here
                                        break;
                                }
                                $tfoot.prepend( $( $rows[i] ));
-                       } 
+                       }
                        $table.append( $tfoot );
                }
        }
 
        function buildHeaders( table, msg ) {
-               var     maxSeen = 0,
+               var maxSeen = 0,
                        longest,
                        realCellIndex = 0,
                        $tableHeaders = $( 'thead:eq(0) > tr', table );
                if ( $tableHeaders.length > 1 ) {
-                       $tableHeaders.each( function() {
+                       $tableHeaders.each( function () {
                                if ( this.cells.length > maxSeen ) {
                                        maxSeen = this.cells.length;
                                        longest = this;
                        });
                        $tableHeaders = $( longest );
                }
-               $tableHeaders = $tableHeaders.children( 'th' ).each( function( index ) {
+               $tableHeaders = $tableHeaders.children( 'th' ).each( function ( index ) {
                        this.column = realCellIndex;
 
                        var colspan = this.colspan;
        function isValueInArray( v, a ) {
                var l = a.length;
                for ( var i = 0; i < l; i++ ) {
-                       if ( a[i][0] == v ) {
+                       if ( a[i][0] === v ) {
                                return true;
                        }
                }
                $headers.removeClass( css[0] ).removeClass( css[1] );
 
                var h = [];
-               $headers.each( function( offset ) {
+               $headers.each( function ( offset ) {
                        if ( !this.sortDisabled ) {
                                h[this.column] = $( this );
                        }
 
        function explodeRowspans( $table ) {
                // Split multi row cells into multiple cells with the same content
-               $table.find( '> tbody > tr > [rowspan]' ).each(function() {
+               $table.find( '> tbody > tr > [rowspan]' ).each(function () {
                        var rowSpan = this.rowSpan;
                        this.rowSpan = 1;
                        var cell = $( this );
                         * @param $tables {jQuery}
                         * @param settings {Object} (optional)
                         */
-                       construct: function( $tables, settings ) {
-                               return $tables.each( function( i, table ) {
+                       construct: function ( $tables, settings ) {
+                               return $tables.each( function ( i, table ) {
                                        // Declare and cache.
-                                       var     $document, $headers, cache, config, sortOrder,
+                                       var $document, $headers, cache, config, sortOrder,
                                                $table = $( table ),
                                                shiftDown = 0,
                                                firstTime = true;
                                                // No thead found. Look for rows with <th>s and
                                                // move them into a <thead> tag or a <tfoot> tag
                                                emulateTHeadAndFoot( $table );
-                                               
+
                                                // Still no thead? Then quit
                                                if ( !table.tHead ) {
                                                        return;
 
                                        // Apply event handling to headers
                                        // this is too big, perhaps break it out?
-                                       $headers.click( function( e ) {
-                                               if ( e.target.nodeName.toLowerCase() == 'a' ) {
+                                       $headers.click( function ( e ) {
+                                               if ( e.target.nodeName.toLowerCase() === 'a' ) {
                                                        // The user clicked on a link inside a table header
                                                        // Do nothing and let the default link click action continue
                                                        return true;
                                                                        for ( var j = 0; j < config.sortList.length; j++ ) {
                                                                                var s = config.sortList[j],
                                                                                        o = config.headerList[s[0]];
-                                                                               if ( s[0] == i ) {
+                                                                               if ( s[0] === i ) {
                                                                                        o.count = s[1];
                                                                                        o.count++;
                                                                                        s[1] = o.count % 2;
                                                }
 
                                        // Cancel selection
-                                       } ).mousedown( function() {
+                                       } ).mousedown( function () {
                                                if ( config.cancelSelection ) {
-                                                       this.onselectstart = function() {
+                                                       this.onselectstart = function () {
                                                                return false;
                                                        };
                                                        return false;
                                } );
                        },
 
-                       addParser: function( parser ) {
-                               var     l = parsers.length,
+                       addParser: function ( parser ) {
+                               var l = parsers.length,
                                        a = true;
                                for ( var i = 0; i < l; i++ ) {
-                                       if ( parsers[i].id.toLowerCase() == parser.id.toLowerCase() ) {
+                                       if ( parsers[i].id.toLowerCase() === parser.id.toLowerCase() ) {
                                                a = false;
                                        }
                                }
                                }
                        },
 
-                       formatDigit: function( s ) {
+                       formatDigit: function ( s ) {
                                if ( ts.transformTable !== false ) {
-                                       var     out = '',
+                                       var out = '',
                                                c;
                                        for ( var p = 0; p < s.length; p++ ) {
                                                c = s.charAt(p);
                                return ( isNaN(i)) ? 0 : i;
                        },
 
-                       formatFloat: function( s ) {
+                       formatFloat: function ( s ) {
                                var i = parseFloat(s);
                                return ( isNaN(i)) ? 0 : i;
                        },
 
-                       formatInt: function( s ) {
+                       formatInt: function ( s ) {
                                var i = parseInt( s, 10 );
                                return ( isNaN(i)) ? 0 : i;
                        },
 
-                       clearTableBody: function( table ) {
+                       clearTableBody: function ( table ) {
                                if ( $.browser.msie ) {
-                                       var empty = function( el ) {
+                                       var empty = function ( el ) {
                                                while ( el.firstChild ) {
                                                        el.removeChild( el.firstChild );
                                                }
        ts = $.tablesorter;
 
        // Register as jQuery prototype method
-       $.fn.tablesorter = function( settings ) {
+       $.fn.tablesorter = function ( settings ) {
                return ts.construct( this, settings );
        };
 
        // Add default parsers
        ts.addParser( {
                id: 'text',
-               is: function( s ) {
+               is: function ( s ) {
                        return true;
                },
-               format: function( s ) {
+               format: function ( s ) {
                        s = $.trim( s.toLowerCase() );
                        if ( ts.collationRegex ) {
                                var tsc = ts.collationTable;
-                               s = s.replace( ts.collationRegex, function( match ) {
+                               s = s.replace( ts.collationRegex, function ( match ) {
                                        var r = tsc[match] ? tsc[match] : tsc[match.toUpperCase()];
                                        return r.toLowerCase();
                                } );
 
        ts.addParser( {
                id: 'IPAddress',
-               is: function( s ) {
+               is: function ( s ) {
                        return ts.rgx.IPAddress[0].test(s);
                },
-               format: function( s ) {
-                       var     a = s.split( '.' ),
+               format: function ( s ) {
+                       var a = s.split( '.' ),
                                r = '',
                                l = a.length;
                        for ( var i = 0; i < l; i++ ) {
                                var item = a[i];
-                               if ( item.length == 1 ) {
+                               if ( item.length === 1 ) {
                                        r += '00' + item;
-                               } else if ( item.length == 2 ) {
+                               } else if ( item.length === 2 ) {
                                        r += '0' + item;
                                } else {
                                        r += item;
 
        ts.addParser( {
                id: 'currency',
-               is: function( s ) {
+               is: function ( s ) {
                        return ts.rgx.currency[0].test(s);
                },
-               format: function( s ) {
+               format: function ( s ) {
                        return $.tablesorter.formatDigit( s.replace( ts.rgx.currency[1], '' ) );
                },
                type: 'numeric'
 
        ts.addParser( {
                id: 'url',
-               is: function( s ) {
+               is: function ( s ) {
                        return ts.rgx.url[0].test(s);
                },
-               format: function( s ) {
+               format: function ( s ) {
                        return $.trim( s.replace( ts.rgx.url[1], '' ) );
                },
                type: 'text'
 
        ts.addParser( {
                id: 'isoDate',
-               is: function( s ) {
+               is: function ( s ) {
                        return ts.rgx.isoDate[0].test(s);
                },
-               format: function( s ) {
+               format: function ( s ) {
                        return $.tablesorter.formatFloat((s !== '') ? new Date(s.replace(
                        new RegExp( /-/g), '/')).getTime() : '0' );
                },
 
        ts.addParser( {
                id: 'usLongDate',
-               is: function( s ) {
+               is: function ( s ) {
                        return ts.rgx.usLongDate[0].test(s);
                },
-               format: function( s ) {
+               format: function ( s ) {
                        return $.tablesorter.formatFloat( new Date(s).getTime() );
                },
                type: 'numeric'
 
        ts.addParser( {
                id: 'date',
-               is: function( s ) {
+               is: function ( s ) {
                        return ( ts.dateRegex[0].test(s) || ts.dateRegex[1].test(s) || ts.dateRegex[2].test(s ));
                },
-               format: function( s, table ) {
+               format: function ( s, table ) {
+                       var match;
                        s = $.trim( s.toLowerCase() );
 
-                       var match;
                        if ( ( match = s.match( ts.dateRegex[0] ) ) !== null ) {
-                               if ( mw.config.get( 'wgDefaultDateFormat' ) == 'mdy' || mw.config.get( 'wgContentLanguage' ) == 'en' ) {
+                               if ( mw.config.get( 'wgDefaultDateFormat' ) === 'mdy' || mw.config.get( 'wgContentLanguage' ) === 'en' ) {
                                        s = [ match[3], match[1], match[2] ];
-                               } else if ( mw.config.get( 'wgDefaultDateFormat' ) == 'dmy' ) {
+                               } else if ( mw.config.get( 'wgDefaultDateFormat' ) === 'dmy' ) {
                                        s = [ match[3], match[2], match[1] ];
                                }
                        } else if ( ( match = s.match( ts.dateRegex[1] ) ) !== null ) {
                        }
 
                        // Pad Month and Day
-                       if ( s[1].length == 1 ) {
+                       if ( s[1].length === 1 ) {
                                s[1] = '0' + s[1];
                        }
-                       if ( s[2].length == 1 ) {
+                       if ( s[2].length === 1 ) {
                                s[2] = '0' + s[2];
                        }
 
 
        ts.addParser( {
                id: 'time',
-               is: function( s ) {
+               is: function ( s ) {
                        return ts.rgx.time[0].test(s);
                },
-               format: function( s ) {
+               format: function ( s ) {
                        return $.tablesorter.formatFloat( new Date( '2000/01/01 ' + s ).getTime() );
                },
                type: 'numeric'
 
        ts.addParser( {
                id: 'number',
-               is: function( s, table ) {
+               is: function ( s, table ) {
                        return $.tablesorter.numberRegex.test( $.trim( s ));
                },
-               format: function( s ) {
+               format: function ( s ) {
                        return $.tablesorter.formatDigit(s);
                },
                type: 'numeric'
        } );
 
-} )( jQuery );
+}( jQuery, mediaWiki ) );
index 91b6e75..9f1f3a4 100644 (file)
 /**
  * These plugins provide extra functionality for interaction with textareas.
  */
-( function( $ ) {
+( function ( $ ) {
+       /*jshint noempty:false */
 
-if (document.selection && document.selection.createRange) {
-       // On IE, patch the focus() method to restore the windows' scroll position
-       // (bug 32241)
-       $.fn.extend({
-               focus : (function ( _focus ) {
-                       return function () {
-                               if ( arguments.length == 0 ) {
-                                       var $w = $( window );
-                                       var state = {top: $w.scrollTop(), left: $w.scrollLeft()};
-                                       var result = _focus.apply( this, arguments );
-                                       window.scrollTo( state.top, state.left );
-                                       return result;
-                               }
-                               return _focus.apply( this, arguments );
-                       };
-               })( $.fn.focus )
-       });
-}
-
-$.fn.textSelection = function( command, options ) {
-
-/**
- * Helper function to get an IE TextRange object for an element
- */
-function rangeForElementIE( e ) {
-       if ( e.nodeName.toLowerCase() == 'input' ) {
-               return e.createTextRange();
-       } else {
-               var sel = document.body.createTextRange();
-               sel.moveToElementText( e );
-               return sel;
+       if ( document.selection && document.selection.createRange ) {
+               // On IE, patch the focus() method to restore the windows' scroll position
+               // (bug 32241)
+               $.fn.extend({
+                       focus : ( function ( _focus ) {
+                               return function () {
+                                       if ( arguments.length === 0 ) {
+                                               var $w = $( window );
+                                               var state = {top: $w.scrollTop(), left: $w.scrollLeft()};
+                                               var result = _focus.apply( this, arguments );
+                                               window.scrollTo( state.top, state.left );
+                                               return result;
+                                       }
+                                       return _focus.apply( this, arguments );
+                               };
+                       }( $.fn.focus ) )
+               });
        }
-}
 
-/**
- * Helper function for IE for activating the textarea. Called only in the
- * IE-specific code paths below; makes use of IE-specific non-standard
- * function setActive() if possible to avoid screen flicker.
- */
-function activateElementOnIE( element ) {
-       if ( element.setActive ) {
-               element.setActive(); // bug 32241: doesn't scroll
-       } else {
-               $( element ).focus(); // may scroll (but we patched it above)
-       }
-}
-
-var fn = {
-/**
- * Get the contents of the textarea
- */
-getContents: function() {
-       return this.val();
-},
-/**
- * Get the currently selected text in this textarea. Will focus the textarea
- * in some browsers (IE/Opera)
- */
-getSelection: function() {
-       var e = this.get( 0 );
-       var retval = '';
-       if ( $(e).is( ':hidden' ) ) {
-               // Do nothing
-       } else if ( document.selection && document.selection.createRange ) {
-               activateElementOnIE( e );
-               var range = document.selection.createRange();
-               retval = range.text;
-       } else if ( e.selectionStart || e.selectionStart == '0' ) {
-               retval = e.value.substring( e.selectionStart, e.selectionEnd );
-       }
-       return retval;
-},
-/**
- * Ported from skins/common/edit.js by Trevor Parscal
- * (c) 2009 Wikimedia Foundation (GPLv2) - http://www.wikimedia.org
- *
- * Inserts text at the begining and end of a text selection, optionally
- * inserting text at the caret when selection is empty.
- *
- * @fixme document the options parameters
- */
-encapsulateSelection: function( options ) {
-       return this.each( function() {
-               var pre = options.pre, post = options.post;
+       $.fn.textSelection = function ( command, options ) {
 
                /**
-                * Check if the selected text is the same as the insert text
+                * Helper function to get an IE TextRange object for an element
                 */
-               function checkSelectedText() {
-                       if ( !selText ) {
-                               selText = options.peri;
-                               isSample = true;
-                       } else if ( options.replace ) {
-                               selText = options.peri;
+               function rangeForElementIE( e ) {
+                       if ( e.nodeName.toLowerCase() === 'input' ) {
+                               return e.createTextRange();
                        } else {
-                               while ( selText.charAt( selText.length - 1 ) == ' ' ) {
-                                       // Exclude ending space char
-                                       selText = selText.substring( 0, selText.length - 1 );
-                                       post += ' ';
-                               }
-                               while ( selText.charAt( 0 ) == ' ' ) {
-                                       // Exclude prepending space char
-                                       selText = selText.substring( 1, selText.length );
-                                       pre = ' ' + pre;
-                               }
+                               var sel = document.body.createTextRange();
+                               sel.moveToElementText( e );
+                               return sel;
                        }
                }
 
                /**
-                * Do the splitlines stuff.
-                *
-                * Wrap each line of the selected text with pre and post
+                * Helper function for IE for activating the textarea. Called only in the
+                * IE-specific code paths below; makes use of IE-specific non-standard
+                * function setActive() if possible to avoid screen flicker.
                 */
-               function doSplitLines( selText, pre, post ) {
-                       var insertText = '';
-                       var selTextArr = selText.split( '\n' );
-                       for ( var i = 0; i < selTextArr.length; i++ ) {
-                               insertText += pre + selTextArr[i] + post;
-                               if ( i != selTextArr.length - 1 ) {
-                                       insertText += '\n';
-                               }
+               function activateElementOnIE( element ) {
+                       if ( element.setActive ) {
+                               element.setActive(); // bug 32241: doesn't scroll
+                       } else {
+                               $( element ).focus(); // may scroll (but we patched it above)
                        }
-                       return insertText;
                }
 
-               var isSample = false;
-               if ( this.style.display == 'none' ) {
-                       // Do nothing
-               } else if ( document.selection && document.selection.createRange ) {
-                       // IE
+               var fn = {
+                       /**
+                        * Get the contents of the textarea
+                        */
+                       getContents: function () {
+                               return this.val();
+                       },
+                       /**
+                        * Get the currently selected text in this textarea. Will focus the textarea
+                        * in some browsers (IE/Opera)
+                        */
+                       getSelection: function () {
+                               var e = this.get( 0 );
+                               var retval = '';
+                               if ( $(e).is( ':hidden' ) ) {
+                                       // Do nothing
+                               } else if ( document.selection && document.selection.createRange ) {
+                                       activateElementOnIE( e );
+                                       var range = document.selection.createRange();
+                                       retval = range.text;
+                               } else if ( e.selectionStart || e.selectionStart === 0 ) {
+                                       retval = e.value.substring( e.selectionStart, e.selectionEnd );
+                               }
+                               return retval;
+                       },
+                       /**
+                        * Ported from skins/common/edit.js by Trevor Parscal
+                        * (c) 2009 Wikimedia Foundation (GPLv2) - http://www.wikimedia.org
+                        *
+                        * Inserts text at the begining and end of a text selection, optionally
+                        * inserting text at the caret when selection is empty.
+                        *
+                        * @fixme document the options parameters
+                        */
+                       encapsulateSelection: function ( options ) {
+                               return this.each( function () {
+                                       var selText, scrollTop, insertText,
+                                               pre = options.pre,
+                                               post = options.post;
 
-                       // Note that IE9 will trigger the next section unless we check this first.
-                       // See bug 35201.
+                                       /**
+                                        * Check if the selected text is the same as the insert text
+                                        */
+                                       function checkSelectedText() {
+                                               if ( !selText ) {
+                                                       selText = options.peri;
+                                                       isSample = true;
+                                               } else if ( options.replace ) {
+                                                       selText = options.peri;
+                                               } else {
+                                                       while ( selText.charAt( selText.length - 1 ) === ' ' ) {
+                                                               // Exclude ending space char
+                                                               selText = selText.substring( 0, selText.length - 1 );
+                                                               post += ' ';
+                                                       }
+                                                       while ( selText.charAt( 0 ) === ' ' ) {
+                                                               // Exclude prepending space char
+                                                               selText = selText.substring( 1, selText.length );
+                                                               pre = ' ' + pre;
+                                                       }
+                                               }
+                                       }
 
-                       activateElementOnIE( this );
-                       if ( context ) {
-                               context.fn.restoreCursorAndScrollTop();
-                       }
-                       if ( options.selectionStart !== undefined ) {
-                               $(this).textSelection( 'setSelection', { 'start': options.selectionStart, 'end': options.selectionEnd } );
-                       }
+                                       /**
+                                        * Do the splitlines stuff.
+                                        *
+                                        * Wrap each line of the selected text with pre and post
+                                        */
+                                       function doSplitLines( selText, pre, post ) {
+                                               var insertText = '';
+                                               var selTextArr = selText.split( '\n' );
+                                               for ( var i = 0; i < selTextArr.length; i++ ) {
+                                                       insertText += pre + selTextArr[i] + post;
+                                                       if ( i !== selTextArr.length - 1 ) {
+                                                               insertText += '\n';
+                                                       }
+                                               }
+                                               return insertText;
+                                       }
 
-                       var selText = $(this).textSelection( 'getSelection' );
-                       var scrollTop = this.scrollTop;
-                       var range = document.selection.createRange();
+                                       var isSample = false;
+                                       if ( this.style.display === 'none' ) {
+                                               // Do nothing
+                                       } else if ( document.selection && document.selection.createRange ) {
+                                               // IE
 
-                       checkSelectedText();
-                       var insertText = pre + selText + post;
-                       if ( options.splitlines ) {
-                               insertText = doSplitLines( selText, pre, post );
-                       }
-                       if ( options.ownline && range.moveStart ) {
-                               var range2 = document.selection.createRange();
-                               range2.collapse();
-                               range2.moveStart( 'character', -1 );
-                               // FIXME: Which check is correct?
-                               if ( range2.text != "\r" && range2.text != "\n" && range2.text != "" ) {
-                                       insertText = "\n" + insertText;
-                                       pre += "\n";
-                               }
-                               var range3 = document.selection.createRange();
-                               range3.collapse( false );
-                               range3.moveEnd( 'character', 1 );
-                               if ( range3.text != "\r" && range3.text != "\n" && range3.text != "" ) {
-                                       insertText += "\n";
-                                       post += "\n";
-                               }
-                       }
+                                               // Note that IE9 will trigger the next section unless we check this first.
+                                               // See bug 35201.
 
-                       range.text = insertText;
-                       if ( isSample && options.selectPeri && range.moveStart ) {
-                               range.moveStart( 'character', - post.length - selText.length );
-                               range.moveEnd( 'character', - post.length );
-                       }
-                       range.select();
-                       // Restore the scroll position
-                       this.scrollTop = scrollTop;
-               } else if ( this.selectionStart || this.selectionStart == '0' ) {
-                       // Mozilla/Opera
+                                               activateElementOnIE( this );
+                                               if ( context ) {
+                                                       context.fn.restoreCursorAndScrollTop();
+                                               }
+                                               if ( options.selectionStart !== undefined ) {
+                                                       $(this).textSelection( 'setSelection', { 'start': options.selectionStart, 'end': options.selectionEnd } );
+                                               }
 
-                       $(this).focus();
-                       if ( options.selectionStart !== undefined ) {
-                               $(this).textSelection( 'setSelection', { 'start': options.selectionStart, 'end': options.selectionEnd } );
-                       }
-                       
-                       var selText = $(this).textSelection( 'getSelection' );
-                       var startPos = this.selectionStart;
-                       var endPos = this.selectionEnd;
-                       var scrollTop = this.scrollTop;
-                       checkSelectedText();
-                       if ( options.selectionStart !== undefined
-                                       && endPos - startPos != options.selectionEnd - options.selectionStart )
-                       {
-                               // This means there is a difference in the selection range returned by browser and what we passed.
-                               // This happens for Chrome in the case of composite characters. Ref bug #30130
-                               // Set the startPos to the correct position.
-                               startPos = options.selectionStart;
-                       }
+                                               selText = $(this).textSelection( 'getSelection' );
+                                               scrollTop = this.scrollTop;
+                                               var range = document.selection.createRange();
 
-                       var insertText = pre + selText + post;
-                       if ( options.splitlines ) {
-                               insertText = doSplitLines( selText, pre, post );
-                       }
-                       if ( options.ownline ) {
-                               if ( startPos != 0 && this.value.charAt( startPos - 1 ) != "\n" && this.value.charAt( startPos - 1 ) != "\r" ) {
-                                       insertText = "\n" + insertText;
-                                       pre += "\n";
-                               }
-                               if ( this.value.charAt( endPos ) != "\n" && this.value.charAt( endPos ) != "\r" ) {
-                                       insertText += "\n";
-                                       post += "\n";
-                               }
-                       }
-                       this.value = this.value.substring( 0, startPos ) + insertText +
-                               this.value.substring( endPos, this.value.length );
-                       // Setting this.value scrolls the textarea to the top, restore the scroll position
-                       this.scrollTop = scrollTop;
-                       if ( window.opera ) {
-                               pre = pre.replace( /\r?\n/g, "\r\n" );
-                               selText = selText.replace( /\r?\n/g, "\r\n" );
-                               post = post.replace( /\r?\n/g, "\r\n" );
-                       }
-                       if ( isSample && options.selectPeri && !options.splitlines ) {
-                               this.selectionStart = startPos + pre.length;
-                               this.selectionEnd = startPos + pre.length + selText.length;
-                       } else {
-                               this.selectionStart = startPos + insertText.length;
-                               this.selectionEnd = this.selectionStart;
-                       }
-               }
-               $(this).trigger( 'encapsulateSelection', [ options.pre, options.peri, options.post, options.ownline,
-                       options.replace, options.spitlines ] );
-       });
-},
-/**
- * Ported from Wikia's LinkSuggest extension
- * https://svn.wikia-code.com/wikia/trunk/extensions/wikia/LinkSuggest
- * Some code copied from
- * http://www.dedestruct.com/2008/03/22/howto-cross-browser-cursor-position-in-textareas/
- *
- * Get the position (in resolution of bytes not nessecarily characters)
- * in a textarea
- *
- * Will focus the textarea in some browsers (IE/Opera)
- *
- * @fixme document the options parameters
- */
- getCaretPosition: function( options ) {
-       function getCaret( e ) {
-               var caretPos = 0, endPos = 0;
-               if ( document.selection && document.selection.createRange ) {
-                       // IE doesn't properly report non-selected caret position through
-                       // the selection ranges when textarea isn't focused. This can
-                       // lead to saving a bogus empty selection, which then screws up
-                       // whatever we do later (bug 31847).
-                       activateElementOnIE( e );
+                                               checkSelectedText();
+                                               insertText = pre + selText + post;
+                                               if ( options.splitlines ) {
+                                                       insertText = doSplitLines( selText, pre, post );
+                                               }
+                                               if ( options.ownline && range.moveStart ) {
+                                                       var range2 = document.selection.createRange();
+                                                       range2.collapse();
+                                                       range2.moveStart( 'character', -1 );
+                                                       // FIXME: Which check is correct?
+                                                       if ( range2.text !== "\r" && range2.text !== "\n" && range2.text !== "" ) {
+                                                               insertText = "\n" + insertText;
+                                                               pre += "\n";
+                                                       }
+                                                       var range3 = document.selection.createRange();
+                                                       range3.collapse( false );
+                                                       range3.moveEnd( 'character', 1 );
+                                                       if ( range3.text !== "\r" && range3.text !== "\n" && range3.text !== "" ) {
+                                                               insertText += "\n";
+                                                               post += "\n";
+                                                       }
+                                               }
 
-                       // IE Support
-                       var preFinished = false;
-                       var periFinished = false;
-                       var postFinished = false;
-                       var preText, rawPreText, periText;
-                       var rawPeriText, postText, rawPostText;
-                       // Create range containing text in the selection
-                       var periRange = document.selection.createRange().duplicate();
-                       // Create range containing text before the selection
-                       var preRange = rangeForElementIE( e );
-                       // Move the end where we need it
-                       preRange.setEndPoint("EndToStart", periRange);
-                       // Create range containing text after the selection
-                       var postRange = rangeForElementIE( e );
-                       // Move the start where we need it
-                       postRange.setEndPoint("StartToEnd", periRange);
-                       // Load the text values we need to compare
-                       preText = rawPreText = preRange.text;
-                       periText = rawPeriText = periRange.text;
-                       postText = rawPostText = postRange.text;
-                       /*
-                        * Check each range for trimmed newlines by shrinking the range by 1
-                        * character and seeing if the text property has changed. If it has
-                        * not changed then we know that IE has trimmed a \r\n from the end.
-                        */
-                       do {
-                               if ( !preFinished ) {
-                                       if ( preRange.compareEndPoints( "StartToEnd", preRange ) == 0 ) {
-                                               preFinished = true;
-                                       } else {
-                                               preRange.moveEnd( "character", -1 );
-                                               if ( preRange.text == preText ) {
-                                                       rawPreText += "\r\n";
+                                               range.text = insertText;
+                                               if ( isSample && options.selectPeri && range.moveStart ) {
+                                                       range.moveStart( 'character', - post.length - selText.length );
+                                                       range.moveEnd( 'character', - post.length );
+                                               }
+                                               range.select();
+                                               // Restore the scroll position
+                                               this.scrollTop = scrollTop;
+                                       } else if ( this.selectionStart || this.selectionStart === 0 ) {
+                                               // Mozilla/Opera
+
+                                               $(this).focus();
+                                               if ( options.selectionStart !== undefined ) {
+                                                       $(this).textSelection( 'setSelection', { 'start': options.selectionStart, 'end': options.selectionEnd } );
+                                               }
+
+                                               selText = $(this).textSelection( 'getSelection' );
+                                               var startPos = this.selectionStart;
+                                               var endPos = this.selectionEnd;
+                                               scrollTop = this.scrollTop;
+                                               checkSelectedText();
+                                               if ( options.selectionStart !== undefined
+                                                               && endPos - startPos !== options.selectionEnd - options.selectionStart )
+                                               {
+                                                       // This means there is a difference in the selection range returned by browser and what we passed.
+                                                       // This happens for Chrome in the case of composite characters. Ref bug #30130
+                                                       // Set the startPos to the correct position.
+                                                       startPos = options.selectionStart;
+                                               }
+
+                                               insertText = pre + selText + post;
+                                               if ( options.splitlines ) {
+                                                       insertText = doSplitLines( selText, pre, post );
+                                               }
+                                               if ( options.ownline ) {
+                                                       if ( startPos !== 0 && this.value.charAt( startPos - 1 ) !== "\n" && this.value.charAt( startPos - 1 ) !== "\r" ) {
+                                                               insertText = "\n" + insertText;
+                                                               pre += "\n";
+                                                       }
+                                                       if ( this.value.charAt( endPos ) !== "\n" && this.value.charAt( endPos ) !== "\r" ) {
+                                                               insertText += "\n";
+                                                               post += "\n";
+                                                       }
+                                               }
+                                               this.value = this.value.substring( 0, startPos ) + insertText +
+                                                       this.value.substring( endPos, this.value.length );
+                                               // Setting this.value scrolls the textarea to the top, restore the scroll position
+                                               this.scrollTop = scrollTop;
+                                               if ( window.opera ) {
+                                                       pre = pre.replace( /\r?\n/g, "\r\n" );
+                                                       selText = selText.replace( /\r?\n/g, "\r\n" );
+                                                       post = post.replace( /\r?\n/g, "\r\n" );
+                                               }
+                                               if ( isSample && options.selectPeri && !options.splitlines ) {
+                                                       this.selectionStart = startPos + pre.length;
+                                                       this.selectionEnd = startPos + pre.length + selText.length;
                                                } else {
-                                                       preFinished = true;
+                                                       this.selectionStart = startPos + insertText.length;
+                                                       this.selectionEnd = this.selectionStart;
                                                }
                                        }
+                                       $(this).trigger( 'encapsulateSelection', [ options.pre, options.peri, options.post, options.ownline,
+                                               options.replace, options.spitlines ] );
+                               });
+                       },
+                       /**
+                        * Ported from Wikia's LinkSuggest extension
+                        * https://svn.wikia-code.com/wikia/trunk/extensions/wikia/LinkSuggest
+                        * Some code copied from
+                        * http://www.dedestruct.com/2008/03/22/howto-cross-browser-cursor-position-in-textareas/
+                        *
+                        * Get the position (in resolution of bytes not nessecarily characters)
+                        * in a textarea
+                        *
+                        * Will focus the textarea in some browsers (IE/Opera)
+                        *
+                        * @fixme document the options parameters
+                        */
+                        getCaretPosition: function ( options ) {
+                               function getCaret( e ) {
+                                       var caretPos = 0, endPos = 0;
+                                       if ( document.selection && document.selection.createRange ) {
+                                               // IE doesn't properly report non-selected caret position through
+                                               // the selection ranges when textarea isn't focused. This can
+                                               // lead to saving a bogus empty selection, which then screws up
+                                               // whatever we do later (bug 31847).
+                                               activateElementOnIE( e );
+
+                                               // IE Support
+                                               var preFinished = false;
+                                               var periFinished = false;
+                                               var postFinished = false;
+                                               var preText, rawPreText, periText;
+                                               var rawPeriText, postText, rawPostText;
+                                               // Create range containing text in the selection
+                                               var periRange = document.selection.createRange().duplicate();
+                                               // Create range containing text before the selection
+                                               var preRange = rangeForElementIE( e );
+                                               // Move the end where we need it
+                                               preRange.setEndPoint( 'EndToStart', periRange );
+                                               // Create range containing text after the selection
+                                               var postRange = rangeForElementIE( e );
+                                               // Move the start where we need it
+                                               postRange.setEndPoint( 'StartToEnd', periRange );
+                                               // Load the text values we need to compare
+                                               preText = rawPreText = preRange.text;
+                                               periText = rawPeriText = periRange.text;
+                                               postText = rawPostText = postRange.text;
+                                               /*
+                                                * Check each range for trimmed newlines by shrinking the range by 1
+                                                * character and seeing if the text property has changed. If it has
+                                                * not changed then we know that IE has trimmed a \r\n from the end.
+                                                */
+                                               do {
+                                                       if ( !preFinished ) {
+                                                               if ( preRange.compareEndPoints( 'StartToEnd', preRange ) === 0 ) {
+                                                                       preFinished = true;
+                                                               } else {
+                                                                       preRange.moveEnd( 'character', -1 );
+                                                                       if ( preRange.text === preText ) {
+                                                                               rawPreText += "\r\n";
+                                                                       } else {
+                                                                               preFinished = true;
+                                                                       }
+                                                               }
+                                                       }
+                                                       if ( !periFinished ) {
+                                                               if ( periRange.compareEndPoints( 'StartToEnd', periRange ) === 0 ) {
+                                                                       periFinished = true;
+                                                               } else {
+                                                                       periRange.moveEnd( 'character', -1 );
+                                                                       if ( periRange.text === periText ) {
+                                                                               rawPeriText += "\r\n";
+                                                                       } else {
+                                                                               periFinished = true;
+                                                                       }
+                                                               }
+                                                       }
+                                                       if ( !postFinished ) {
+                                                               if ( postRange.compareEndPoints( 'StartToEnd', postRange ) === 0 ) {
+                                                                       postFinished = true;
+                                                               } else {
+                                                                       postRange.moveEnd( 'character', -1 );
+                                                                       if ( postRange.text === postText ) {
+                                                                               rawPostText += "\r\n";
+                                                                       } else {
+                                                                               postFinished = true;
+                                                                       }
+                                                               }
+                                                       }
+                                               } while ( ( !preFinished || !periFinished || !postFinished ) );
+                                               caretPos = rawPreText.replace( /\r\n/g, "\n" ).length;
+                                               endPos = caretPos + rawPeriText.replace( /\r\n/g, "\n" ).length;
+                                       } else if ( e.selectionStart || e.selectionStart === 0 ) {
+                                               // Firefox support
+                                               caretPos = e.selectionStart;
+                                               endPos = e.selectionEnd;
+                                       }
+                                       return options.startAndEnd ? [ caretPos, endPos ] : caretPos;
                                }
-                               if ( !periFinished ) {
-                                       if ( periRange.compareEndPoints( "StartToEnd", periRange ) == 0 ) {
-                                               periFinished = true;
-                                       } else {
-                                               periRange.moveEnd( "character", -1 );
-                                               if ( periRange.text == periText ) {
-                                                       rawPeriText += "\r\n";
+                               return getCaret( this.get( 0 ) );
+                       },
+                       /**
+                        * @fixme document the options parameters
+                        */
+                       setSelection: function ( options ) {
+                               return this.each( function () {
+                                       if ( $(this).is( ':hidden' ) ) {
+                                               // Do nothing
+                                       } else if ( this.selectionStart || this.selectionStart === 0 ) {
+                                               // Opera 9.0 doesn't allow setting selectionStart past
+                                               // selectionEnd; any attempts to do that will be ignored
+                                               // Make sure to set them in the right order
+                                               if ( options.start > this.selectionEnd ) {
+                                                       this.selectionEnd = options.end;
+                                                       this.selectionStart = options.start;
                                                } else {
-                                                       periFinished = true;
+                                                       this.selectionStart = options.start;
+                                                       this.selectionEnd = options.end;
+                                               }
+                                       } else if ( document.body.createTextRange ) {
+                                               var selection = rangeForElementIE( this );
+                                               var length = this.value.length;
+                                               // IE doesn't count \n when computing the offset, so we won't either
+                                               var newLines = this.value.match( /\n/g );
+                                               if ( newLines ) {
+                                                       length = length - newLines.length;
                                                }
+                                               selection.moveStart( 'character', options.start );
+                                               selection.moveEnd( 'character', -length + options.end );
+
+                                               // This line can cause an error under certain circumstances (textarea empty, no selection)
+                                               // Silence that error
+                                               try {
+                                                       selection.select();
+                                               } catch( e ) { }
                                        }
+                               });
+                       },
+                       /**
+                        * Ported from Wikia's LinkSuggest extension
+                        * https://svn.wikia-code.com/wikia/trunk/extensions/wikia/LinkSuggest
+                        *
+                        * Scroll a textarea to the current cursor position. You can set the cursor
+                        * position with setSelection()
+                        * @param options boolean Whether to force a scroll even if the caret position
+                        *  is already visible. Defaults to false
+                        *
+                        * @fixme document the options parameters (function body suggests options.force is a boolean, not options itself)
+                        */
+                       scrollToCaretPosition: function ( options ) {
+                               function getLineLength( e ) {
+                                       return Math.floor( e.scrollWidth / ( $.client.profile().platform === 'linux' ? 7 : 8 ) );
                                }
-                               if ( !postFinished ) {
-                                       if ( postRange.compareEndPoints("StartToEnd", postRange) == 0 ) {
-                                               postFinished = true;
-                                       } else {
-                                               postRange.moveEnd( "character", -1 );
-                                               if ( postRange.text == postText ) {
-                                                       rawPostText += "\r\n";
-                                               } else {
-                                                       postFinished = true;
+                               function getCaretScrollPosition( e ) {
+                                       var i, j;
+                                       // FIXME: This functions sucks and is off by a few lines most
+                                       // of the time. It should be replaced by something decent.
+                                       var text = e.value.replace( /\r/g, '' );
+                                       var caret = $( e ).textSelection( 'getCaretPosition' );
+                                       var lineLength = getLineLength( e );
+                                       var row = 0;
+                                       var charInLine = 0;
+                                       var lastSpaceInLine = 0;
+                                       for ( i = 0; i < caret; i++ ) {
+                                               charInLine++;
+                                               if ( text.charAt( i ) === ' ' ) {
+                                                       lastSpaceInLine = charInLine;
+                                               } else if ( text.charAt( i ) === "\n" ) {
+                                                       lastSpaceInLine = 0;
+                                                       charInLine = 0;
+                                                       row++;
+                                               }
+                                               if ( charInLine > lineLength ) {
+                                                       if ( lastSpaceInLine > 0 ) {
+                                                               charInLine = charInLine - lastSpaceInLine;
+                                                               lastSpaceInLine = 0;
+                                                               row++;
+                                                       }
                                                }
                                        }
+                                       var nextSpace = 0;
+                                       for ( j = caret; j < caret + lineLength; j++ ) {
+                                               if (
+                                                       text.charAt( j ) === ' ' ||
+                                                       text.charAt( j ) === "\n" ||
+                                                       caret === text.length
+                                               ) {
+                                                       nextSpace = j;
+                                                       break;
+                                               }
+                                       }
+                                       if ( nextSpace > lineLength && caret <= lineLength ) {
+                                               charInLine = caret - lastSpaceInLine;
+                                               row++;
+                                       }
+                                       return ( $.client.profile().platform === 'mac' ? 13 : ( $.client.profile().platform === 'linux' ? 15 : 16 ) ) * row;
                                }
-                       } while ( ( !preFinished || !periFinished || !postFinished ) );
-                       caretPos = rawPreText.replace( /\r\n/g, "\n" ).length;
-                       endPos = caretPos + rawPeriText.replace( /\r\n/g, "\n" ).length;
-               } else if ( e.selectionStart || e.selectionStart == '0' ) {
-                       // Firefox support
-                       caretPos = e.selectionStart;
-                       endPos = e.selectionEnd;
-               }
-               return options.startAndEnd ? [ caretPos, endPos ] : caretPos;
-       }
-       return getCaret( this.get( 0 ) );
-},
-/**
- * @fixme document the options parameters
- */
-setSelection: function( options ) {
-       return this.each( function() {
-               if ( $(this).is( ':hidden' ) ) {
-                       // Do nothing
-               } else if ( this.selectionStart || this.selectionStart == '0' ) {
-                       // Opera 9.0 doesn't allow setting selectionStart past
-                       // selectionEnd; any attempts to do that will be ignored
-                       // Make sure to set them in the right order
-                       if ( options.start > this.selectionEnd ) {
-                               this.selectionEnd = options.end;
-                               this.selectionStart = options.start;
-                       } else {
-                               this.selectionStart = options.start;
-                               this.selectionEnd = options.end;
+                               return this.each(function () {
+                                       if ( $(this).is( ':hidden' ) ) {
+                                               // Do nothing
+                                       } else if ( this.selectionStart || this.selectionStart === 0 ) {
+                                               // Mozilla
+                                               var scroll = getCaretScrollPosition( this );
+                                               if ( options.force || scroll < $(this).scrollTop() ||
+                                                               scroll > $(this).scrollTop() + $(this).height() ) {
+                                                       $(this).scrollTop( scroll );
+                                               }
+                                       } else if ( document.selection && document.selection.createRange ) {
+                                               // IE / Opera
+                                               /*
+                                                * IE automatically scrolls the selected text to the
+                                                * bottom of the textarea at range.select() time, except
+                                                * if it was already in view and the cursor position
+                                                * wasn't changed, in which case it does nothing. To
+                                                * cover that case, we'll force it to act by moving one
+                                                * character back and forth.
+                                                */
+                                               var range = document.body.createTextRange();
+                                               var savedRange = document.selection.createRange();
+                                               var pos = $(this).textSelection( 'getCaretPosition' );
+                                               var oldScrollTop = this.scrollTop;
+                                               range.moveToElementText( this );
+                                               range.collapse();
+                                               range.move( 'character', pos + 1);
+                                               range.select();
+                                               if ( this.scrollTop !== oldScrollTop ) {
+                                                       this.scrollTop += range.offsetTop;
+                                               } else if ( options.force ) {
+                                                       range.move( 'character', -1 );
+                                                       range.select();
+                                               }
+                                               savedRange.select();
+                                       }
+                                       $(this).trigger( 'scrollToPosition' );
+                               } );
                        }
-               } else if ( document.body.createTextRange ) {
-                       var selection = rangeForElementIE( this );
-                       var length = this.value.length;
-                       // IE doesn't count \n when computing the offset, so we won't either
-                       var newLines = this.value.match( /\n/g );
-                       if ( newLines ) length = length - newLines.length;
-                       selection.moveStart( 'character', options.start );
-                       selection.moveEnd( 'character', -length + options.end );
+               };
 
-                       // This line can cause an error under certain circumstances (textarea empty, no selection)
-                       // Silence that error
-                       try {
-                               selection.select();
-                       } catch( e ) { }
-               }
-       });
-},
-/**
- * Ported from Wikia's LinkSuggest extension
- * https://svn.wikia-code.com/wikia/trunk/extensions/wikia/LinkSuggest
- *
- * Scroll a textarea to the current cursor position. You can set the cursor
- * position with setSelection()
- * @param options boolean Whether to force a scroll even if the caret position
- *  is already visible. Defaults to false
- *
- * @fixme document the options parameters (function body suggests options.force is a boolean, not options itself)
- */
-scrollToCaretPosition: function( options ) {
-       function getLineLength( e ) {
-               return Math.floor( e.scrollWidth / ( $.client.profile().platform == 'linux' ? 7 : 8 ) );
-       }
-       function getCaretScrollPosition( e ) {
-               // FIXME: This functions sucks and is off by a few lines most
-               // of the time. It should be replaced by something decent.
-               var text = e.value.replace( /\r/g, "" );
-               var caret = $( e ).textSelection( 'getCaretPosition' );
-               var lineLength = getLineLength( e );
-               var row = 0;
-               var charInLine = 0;
-               var lastSpaceInLine = 0;
-               for ( i = 0; i < caret; i++ ) {
-                       charInLine++;
-                       if ( text.charAt( i ) == " " ) {
-                               lastSpaceInLine = charInLine;
-                       } else if ( text.charAt( i ) == "\n" ) {
-                               lastSpaceInLine = 0;
-                               charInLine = 0;
-                               row++;
-                       }
-                       if ( charInLine > lineLength ) {
-                               if ( lastSpaceInLine > 0 ) {
-                                       charInLine = charInLine - lastSpaceInLine;
-                                       lastSpaceInLine = 0;
-                                       row++;
+               // Apply defaults
+               switch ( command ) {
+                       //case 'getContents': // no params
+                       //case 'setContents': // no params with defaults
+                       //case 'getSelection': // no params
+                       case 'encapsulateSelection':
+                               options = $.extend( {
+                                       pre: '', // Text to insert before the cursor/selection
+                                       peri: '', // Text to insert between pre and post and select afterwards
+                                       post: '', // Text to insert after the cursor/selection
+                                       ownline: false, // Put the inserted text on a line of its own
+                                       replace: false, // If there is a selection, replace it with peri instead of leaving it alone
+                                       selectPeri: true, // Select the peri text if it was inserted (but not if there was a selection and replace==false, or if splitlines==true)
+                                       splitlines: false, // If multiple lines are selected, encapsulate each line individually
+                                       selectionStart: undefined, // Position to start selection at
+                                       selectionEnd: undefined // Position to end selection at. Defaults to start
+                               }, options );
+                               break;
+                       case 'getCaretPosition':
+                               options = $.extend( {
+                                       // Return [start, end] instead of just start
+                                       startAndEnd: false
+                               }, options );
+                               // FIXME: We may not need character position-based functions if we insert markers in the right places
+                               break;
+                       case 'setSelection':
+                               options = $.extend( {
+                                       // Position to start selection at
+                                       start: undefined,
+                                       // Position to end selection at. Defaults to start
+                                       end: undefined,
+                                       // Element to start selection in (iframe only)
+                                       startContainer: undefined,
+                                       // Element to end selection in (iframe only). Defaults to startContainer
+                                       endContainer: undefined
+                               }, options );
+
+                               if ( options.end === undefined ) {
+                                       options.end = options.start;
                                }
-                       }
-               }
-               var nextSpace = 0;
-               for ( j = caret; j < caret + lineLength; j++ ) {
-                       if (
-                               text.charAt( j ) == " " ||
-                               text.charAt( j ) == "\n" ||
-                               caret == text.length
-                       ) {
-                               nextSpace = j;
+                               if ( options.endContainer === undefined ) {
+                                       options.endContainer = options.startContainer;
+                               }
+                               // FIXME: We may not need character position-based functions if we insert markers in the right places
+                               break;
+                       case 'scrollToCaretPosition':
+                               options = $.extend( {
+                                       force: false // Force a scroll even if the caret position is already visible
+                               }, options );
                                break;
-                       }
                }
-               if ( nextSpace > lineLength && caret <= lineLength ) {
-                       charInLine = caret - lastSpaceInLine;
-                       row++;
+
+               var context = $(this).data( 'wikiEditor-context' );
+               var hasIframe = typeof context !== 'undefined' && context && typeof context.$iframe !== 'undefined';
+
+               // IE selection restore voodoo
+               var needSave = false;
+               if ( hasIframe && context.savedSelection !== null ) {
+                       context.fn.restoreSelection();
+                       needSave = true;
                }
-               return ( $.client.profile().platform == 'mac' ? 13 : ( $.client.profile().platform == 'linux' ? 15 : 16 ) ) * row;
-       }
-       return this.each(function() {
-               if ( $(this).is( ':hidden' ) ) {
-                       // Do nothing
-               } else if ( this.selectionStart || this.selectionStart == '0' ) {
-                       // Mozilla
-                       var scroll = getCaretScrollPosition( this );
-                       if ( options.force || scroll < $(this).scrollTop() ||
-                                       scroll > $(this).scrollTop() + $(this).height() )
-                               $(this).scrollTop( scroll );
-               } else if ( document.selection && document.selection.createRange ) {
-                       // IE / Opera
-                       /*
-                        * IE automatically scrolls the selected text to the
-                        * bottom of the textarea at range.select() time, except
-                        * if it was already in view and the cursor position
-                        * wasn't changed, in which case it does nothing. To
-                        * cover that case, we'll force it to act by moving one
-                        * character back and forth.
-                        */
-                       var range = document.body.createTextRange();
-                       var savedRange = document.selection.createRange();
-                       var pos = $(this).textSelection( 'getCaretPosition' );
-                       var oldScrollTop = this.scrollTop;
-                       range.moveToElementText( this );
-                       range.collapse();
-                       range.move( 'character', pos + 1);
-                       range.select();
-                       if ( this.scrollTop != oldScrollTop )
-                               this.scrollTop += range.offsetTop;
-                       else if ( options.force ) {
-                               range.move( 'character', -1 );
-                               range.select();
-                       }
-                       savedRange.select();
+               var retval = ( hasIframe ? context.fn : fn )[command].call( this, options );
+               if ( hasIframe && needSave ) {
+                       context.fn.saveSelection();
                }
-               $(this).trigger( 'scrollToPosition' );
-       } );
-}
-};
-       // Apply defaults
-       switch ( command ) {
-               //case 'getContents': // no params
-               //case 'setContents': // no params with defaults
-               //case 'getSelection': // no params
-               case 'encapsulateSelection':
-                       options = $.extend( {
-                               'pre': '', // Text to insert before the cursor/selection
-                               'peri': '', // Text to insert between pre and post and select afterwards
-                               'post': '', // Text to insert after the cursor/selection
-                               'ownline': false, // Put the inserted text on a line of its own
-                               'replace': false, // If there is a selection, replace it with peri instead of leaving it alone
-                               'selectPeri': true, // Select the peri text if it was inserted (but not if there was a selection and replace==false, or if splitlines==true)
-                               'splitlines': false, // If multiple lines are selected, encapsulate each line individually
-                               'selectionStart': undefined, // Position to start selection at
-                               'selectionEnd': undefined // Position to end selection at. Defaults to start
-                       }, options );
-                       break;
-               case 'getCaretPosition':
-                       options = $.extend( {
-                               'startAndEnd': false // Return [start, end] instead of just start
-                       }, options );
-                       // FIXME: We may not need character position-based functions if we insert markers in the right places
-                       break;
-               case 'setSelection':
-                       options = $.extend( {
-                               'start': undefined, // Position to start selection at
-                               'end': undefined, // Position to end selection at. Defaults to start
-                               'startContainer': undefined, // Element to start selection in (iframe only)
-                               'endContainer': undefined // Element to end selection in (iframe only). Defaults to startContainer
-                       }, options );
-                       if ( options.end === undefined )
-                               options.end = options.start;
-                       if ( options.endContainer == undefined )
-                               options.endContainer = options.startContainer;
-                       // FIXME: We may not need character position-based functions if we insert markers in the right places
-                       break;
-               case 'scrollToCaretPosition':
-                       options = $.extend( {
-                               'force': false // Force a scroll even if the caret position is already visible
-                       }, options );
-                       break;
-       }
-       var context = $(this).data( 'wikiEditor-context' );
-       var hasIframe = typeof context !== 'undefined' && context && typeof context.$iframe !== 'undefined';
 
-       // IE selection restore voodoo
-       var needSave = false;
-       if ( hasIframe && context.savedSelection !== null ) {
-               context.fn.restoreSelection();
-               needSave = true;
-       }
-       var retval = ( hasIframe ? context.fn : fn )[command].call( this, options );
-       if ( hasIframe && needSave ) {
-               context.fn.saveSelection();
-       }
-       return retval;
-};
-} )( jQuery );
+               return retval;
+       };
+
+}( jQuery ) );
index e685ca9..14b845d 100644 (file)
@@ -92,7 +92,7 @@
                isReady = true;
 
                // Make sure edit summary does not exceed byte limit
-               $( '#wpSummary' ).byteLimit( 250 );
+               $( '#wpSummary' ).byteLimit( 255 );
 
                /**
                 * Restore the edit box scroll state following a preview operation,
index a9d488a..5197396 100644 (file)
                 */
                getEditToken: function( tokenCallback, err ) {
                        var parameters = {
-                                       prop: 'info',
-                                       intoken: 'edit',
-                                       // we need some kind of dummy page to get a token from. This will return a response
-                                       // complaining that the page is missing, but we should also get an edit token
-                                       titles: 'DummyPageForEditToken'
+                                       action: 'tokens',
+                                       type: 'edit'
                                },
                                ok = function( data ) {
                                        var token;
-                                       $.each( data.query.pages, function( i, page ) {
-                                               if ( page.edittoken ) {
-                                                       token = page.edittoken;
-                                                       return false;
-                                               }
-                                       } );
-                                       if ( token !== undefined ) {
+                                       // If token type is not available for this user,
+                                       // key 'edittoken' is missing or can contain Boolean false
+                                       if ( data.tokens && data.tokens.edittoken ) {
+                                               token = data.tokens.edittoken;
                                                cachedToken = token;
                                                tokenCallback( token );
                                        } else {
index 74306d5..88b92ee 100644 (file)
@@ -1,5 +1,6 @@
-/* mw.Api objects represent the API of a particular MediaWiki server. */
-
+/**
+ * mw.Api objects represent the API of a particular MediaWiki server.
+ */
 ( function( $, mw, undefined ) {
 
        /**
                        ajax: {
                                url: mw.util.wikiScript( 'api' ),
 
-                               ok: function() {},
-
-                               // caller can supply handlers for http transport error or api errors
-                               err: function( code, result ) {
-                                       mw.log( 'mw.Api error: ' + code, 'debug' );
-                               },
-
-                               timeout: 30000, // 30 seconds
+                               timeout: 30 * 1000, // 30 seconds
 
                                dataType: 'json'
                        }
        mw.Api.prototype = {
 
                /**
-                * For api queries, in simple cases the caller just passes a success callback.
-                * In complex cases they pass an object with a success property as callback and
-                * probably other options.
-                * Normalize the argument so that it's always the latter case.
+                * Normalize the ajax options for compatibility and/or convenience methods.
                 *
-                * @param {Object|Function} An object contaning one or more of options.ajax,
+                * @param {undefined|Object|Function} An object contaning one or more of options.ajax,
                 * or just a success function (options.ajax.ok).
                 * @return {Object} Normalized ajax options.
                 */
-               normalizeAjaxOptions: function( arg ) {
-                       var opt = arg;
+               normalizeAjaxOptions: function ( arg ) {
+                       // Arg argument is usually empty
+                       // (before MW 1.20 it was often used to pass ok/err callbacks)
+                       var opts = arg || {};
+                       // Options can also be a success callback handler
                        if ( typeof arg === 'function' ) {
-                               opt = { 'ok': arg };
+                               opts = { ok: arg };
                        }
-                       if ( !opt.ok ) {
-                               throw new Error( 'ajax options must include ok callback' );
-                       }
-                       return opt;
+                       return opts;
                },
 
                /**
                 * Perform API get request
                 *
                 * @param {Object} request parameters
-                * @param {Object|Function} ajax options, or just a success function
-                * @return {jqXHR}
+                * @param {Object|Function} [optional] ajax options
+                * @return {jQuery.Promise}
                 */
                get: function( parameters, ajaxOptions ) {
                        ajaxOptions = this.normalizeAjaxOptions( ajaxOptions );
                 * @todo Post actions for nonlocal will need proxy
                 *
                 * @param {Object} request parameters
-                * @param {Object|Function} ajax options, or just a success function
-                * @return {jqXHR}
+                * @param {Object|Function} [optional] ajax options
+                * @return {jQuery.Promise}
                 */
                post: function( parameters, ajaxOptions ) {
                        ajaxOptions = this.normalizeAjaxOptions( ajaxOptions );
                 *
                 * @param {Object} request parameters
                 * @param {Object} ajax options
-                * @return {jqXHR}
+                * @return {jQuery.Promise}
+                * - done: API response data as first argument
+                * - fail: errorcode as first arg, details (string or object) as second arg.
                 */
                ajax: function( parameters, ajaxOptions ) {
-                       var token;
+                       var token,
+                               apiDeferred = $.Deferred();
+
                        parameters = $.extend( {}, this.defaults.parameters, parameters );
                        ajaxOptions = $.extend( {}, this.defaults.ajax, ajaxOptions );
 
                        // So let's escape them here. See bug #28235
                        // This works because jQuery accepts data as a query string or as an Object
                        ajaxOptions.data = $.param( parameters ).replace( /\./g, '%2E' );
+
                        // If we extracted a token parameter, add it back in.
                        if ( token ) {
                                ajaxOptions.data += '&token=' + encodeURIComponent( token );
                        }
-                       ajaxOptions.error = function( xhr, textStatus, exception ) {
-                               ajaxOptions.err( 'http', {
-                                       xhr: xhr,
-                                       textStatus: textStatus,
-                                       exception: exception
+
+                       // Backwards compatibility: Before MediaWiki 1.20,
+                       // callbacks were done with the 'ok' and 'err' property in ajaxOptions.
+                       if ( ajaxOptions.ok ) {
+                               apiDeferred.done( ajaxOptions.ok );
+                               delete ajaxOptions.ok;
+                       }
+                       if ( ajaxOptions.err ) {
+                               apiDeferred.fail( ajaxOptions.err );
+                               delete ajaxOptions.err;
+                       }
+
+                       // Make the AJAX request
+                       $.ajax( ajaxOptions )
+                               // If AJAX fails, reject API call with error code 'http'
+                               // and details in second argument.
+                               .fail( function ( xhr, textStatus, exception ) {
+                                       apiDeferred.reject( 'http', {
+                                               xhr: xhr,
+                                               textStatus: textStatus,
+                                               exception: exception
+                                       } );
+                               } )
+                               // AJAX success just means "200 OK" response, also check API error codes
+                               .done( function ( result ) {
+                                       if ( result === undefined || result === null || result === '' ) {
+                                               apiDeferred.reject( 'ok-but-empty',
+                                                       'OK response but empty result (check HTTP headers?)'
+                                               );
+                                       } else if ( result.error ) {
+                                               var code = result.error.code === undefined ? 'unknown' : result.error.code;
+                                               apiDeferred.reject( code, result );
+                                       } else {
+                                               apiDeferred.resolve( result );
+                                       }
                                } );
-                       };
-
-                       // Success just means 200 OK; also check for output and API errors
-                       ajaxOptions.success = function( result ) {
-                               if ( result === undefined || result === null || result === '' ) {
-                                       ajaxOptions.err( 'ok-but-empty',
-                                               'OK response but empty result (check HTTP headers?)' );
-                               } else if ( result.error ) {
-                                       var code = result.error.code === undefined ? 'unknown' : result.error.code;
-                                       ajaxOptions.err( code, result );
-                               } else {
-                                       ajaxOptions.ok( result );
-                               }
-                       };
-
-                       return $.ajax( ajaxOptions );
+
+                       // Return the Promise
+                       return apiDeferred.promise().fail( function ( code, details ) {
+                               mw.log( 'mw.Api error: ', code, details );
+                       });
                }
 
        };
index 1cc68f2..e784ef7 100644 (file)
@@ -1,31 +1,42 @@
 /**
- * Additional mw.Api methods to assist with API calls related to parsing wikitext.
+ * mw.Api methods for parsing wikitext.
  */
-
-( function( $, mw ) {
+( function ( mw, $ ) {
 
        $.extend( mw.Api.prototype, {
                /**
                 * Convinience method for 'action=parse'. Parses wikitext into HTML.
                 *
                 * @param wikiText {String}
-                * @param success {Function} callback to which to pass success HTML
-                * @param err {Function} callback if error (optional)
-                * @return {jqXHR}
+                * @param ok {Function} [optional] deprecated (success callback)
+                * @param err {Function} [optional] deprecated (error callback)
+                * @return {jQuery.Promise}
                 */
-               parse: function( wikiText, success, err ) {
-                       var params = {
-                                       text: wikiText,
-                                       action: 'parse'
-                               },
-                               ok = function( data ) {
+               parse: function( wikiText, ok, err ) {
+                       var apiDeferred = $.Deferred();
+
+                       // Backwards compatibility (< MW 1.20)
+                       if ( ok ) {
+                               apiDeferred.done( ok );
+                       }
+                       if ( err ) {
+                               apiDeferred.fail( err );
+                       }
+
+                       this.get( {
+                                       action: 'parse',
+                                       text: wikiText
+                               } )
+                               .done( function ( data ) {
                                        if ( data.parse && data.parse.text && data.parse.text['*'] ) {
-                                               success( data.parse.text['*'] );
+                                               apiDeferred.resolve( data.parse.text['*'] );
                                        }
-                               };
-                       return this.get( params, { ok: ok, err: err } );
-               }
+                               } )
+                               .fail( apiDeferred.reject );
 
+                       // Return the promise
+                       return apiDeferred.promise();
+               }
        } );
 
-} )( jQuery, mediaWiki );
+} )( mediaWiki, jQuery );
index 02fd29f..903a4f7 100644 (file)
@@ -198,6 +198,12 @@ pre, .mw-code {
        border: 1px dashed #2f6fab;
        color: black;
        background-color: #f9f9f9;
+       /* Handle overflow (bug 260) */
+       overflow: auto;
+       /* IE 7 is the first IE to support overflow but got it wrong */
+       /* IE 8+ is fine */
+       *padding-bottom: 21px;
+       *overflow-y: hidden;
 }
 
 /* Tables */
index 783f031..6dd8ea3 100644 (file)
@@ -10,6 +10,8 @@ abstract class MediaWikiLangTestCase extends MediaWikiTestCase {
        public function setUp() {
                global $wgLanguageCode, $wgLang, $wgContLang;
 
+               parent::setUp();
+
                self::$oldLang = $wgLang;
                self::$oldContLang = $wgContLang;
 
@@ -23,6 +25,7 @@ abstract class MediaWikiLangTestCase extends MediaWikiTestCase {
 
                $wgContLang = $wgLang = Language::factory( $wgLanguageCode );
                MessageCache::singleton()->disable();
+
        }
 
        public function tearDown() {
@@ -32,6 +35,8 @@ abstract class MediaWikiLangTestCase extends MediaWikiTestCase {
                $wgContLang = self::$oldContLang;
                $wgLanguageCode = $wgContLang->getCode();
                self::$oldContLang = self::$oldLang = null;
+
+               parent::tearDown();
        }
 
 }
diff --git a/tests/phpunit/docs/ExportDemoTest.php b/tests/phpunit/docs/ExportDemoTest.php
new file mode 100644 (file)
index 0000000..ce65d49
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Test for the demo xml
+ *
+ * @group Dump
+ */
+class ExportDemoTest extends DumpTestCase {
+
+       /**
+        * @group large
+        */
+       function testExportDemo() {
+               $this->validateXmlFileAgainstXsd( "../../docs/export-demo.xml" );
+       }
+
+       /**
+        * Validates a xml file against the xsd.
+        *
+        * The validation is slow, because php has to read the xsd on each call.
+        *
+        * @param $fname string: name of file to validate
+        */
+       protected function validateXmlFileAgainstXsd( $fname ) {
+               $version = WikiExporter::schemaVersion();
+
+               $dom = new DomDocument();
+               $dom->load( $fname );
+
+               try {
+                       $this->assertTrue( $dom->schemaValidate( "../../docs/export-" . $version . ".xsd" ),
+                               "schemaValidate has found an error" );
+               } catch( Exception $e ) {
+                       $this->fail( "xml not valid against xsd: " . $e->getMessage() );
+               }
+       }
+}
index f197da8..0c95b8d 100644 (file)
@@ -123,6 +123,67 @@ class BlockTest extends MediaWikiLangTestCase {
                );
        }
 
+       function testBlockedUserCanNotCreateAccount() {
+               $username = 'BlockedUserToCreateAccountWith';
+               $u = User::newFromName( $username );
+               $u->setPassword( 'NotRandomPass' );
+               $u->addToDatabase();
+               unset( $u );
+
+
+               // Sanity check
+               $this->assertNull(
+                       Block::newFromTarget( $username ),
+                       "$username should not be blocked"
+               );
+
+               // Reload user
+               $u = User::newFromName( $username );
+               $this->assertFalse(
+                       $u->isBlockedFromCreateAccount(),
+                       "Our sandbox user should be able to create account before being blocked"
+               );
+
+               // Foreign perspective (blockee not on current wiki)...
+               $block = new Block(
+                       /* $address */ $username,
+                       /* $user */ 14146,
+                       /* $by */ 0,
+                       /* $reason */ 'crosswiki block...',
+                       /* $timestamp */ wfTimestampNow(),
+                       /* $auto */ false,
+                       /* $expiry */ $this->db->getInfinity(),
+                       /* anonOnly */ false,
+                       /* $createAccount */ true,
+                       /* $enableAutoblock */ true,
+                       /* $hideName (ipb_deleted) */ true,
+                       /* $blockEmail */ true,
+                       /* $allowUsertalk */ false,
+                       /* $byName */ 'MetaWikiUser'
+               );
+               $block->insert();
+
+               // Reload block from DB
+               $userBlock = Block::newFromTarget( $username );
+               $this->assertTrue(
+                       (bool) $block->prevents( 'createaccount' ),
+                       "Block object in DB should prevents 'createaccount'"
+               );
+
+               $this->assertInstanceOf(
+                       'Block',
+                       $userBlock,
+                       "'$username' block block object should be existent"
+               );
+
+               // Reload user
+               $u = User::newFromName( $username );
+               $this->assertTrue(
+                       (bool) $u->isBlockedFromCreateAccount(),
+                       "Our sandbox user '$username' should NOT be able to create account"
+               );
+       }
+
        function testCrappyCrossWikiBlocks() {
                // Delete the last round's block if it's still there
                $oldBlock = Block::newFromTarget( 'UserOnForeignWiki' );
index b72a5cd..2949a3a 100644 (file)
@@ -478,7 +478,7 @@ more stuff
                $this->assertEquals( $expected, $text );
        }
 
-       /* @FIXME: fix this!
+       /* @todo FIXME: fix this!
        public function testGetUndoText() {
                global $wgDiff3;
 
index 1d4a36f..93ed3dc 100644 (file)
@@ -251,6 +251,15 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
+       function testLanguageSelector() {
+               $select = Xml::languageSelector( 'en', true, null,
+                       array( 'id' => 'testlang' ), wfMessage( 'yourlanguage' ) );
+               $this->assertEquals(
+                       '<label for="testlang">Language:</label>',
+                       $select[0]
+               );
+       }
+
        #
        # JS
        #
diff --git a/tests/phpunit/includes/cache/ProcessCacheLRUTest.php b/tests/phpunit/includes/cache/ProcessCacheLRUTest.php
new file mode 100644 (file)
index 0000000..30bfb12
--- /dev/null
@@ -0,0 +1,239 @@
+<?php
+
+/**
+ * Test for ProcessCacheLRU class.
+ *
+ * Note that it uses the ProcessCacheLRUTestable class which extends some
+ * properties and methods visibility. That class is defined at the end of the
+ * file containing this class.
+ *
+ * @group Cache
+ */
+class ProcessCacheLRUTest extends MediaWikiTestCase {
+
+       /**
+        * Helper to verify emptiness of a cache object.
+        * Compare against an array so we get the cache content difference.
+        */
+       function assertCacheEmpty( $cache, $msg = 'Cache should be empty' ) {
+               $this->assertAttributeEquals( array(), 'cache', $cache, $msg );
+       }
+
+       /**
+        * Helper to fill a cache object passed by reference
+        */
+       function fillCache( &$cache, $numEntries ) {
+               // Fill cache with three values
+               for( $i=1; $i<=$numEntries; $i++) {
+                       $cache->set( "cache-key-$i", "prop-$i", "value-$i" );
+               }
+       }
+
+       /**
+        * Generates an array of what would be expected in cache for a given cache
+        * size and a number of entries filled in sequentially
+        */
+       function getExpectedCache( $cacheMaxEntries, $entryToFill ) {
+               $expected = array();
+
+               if( $entryToFill === 0 ) {
+                       # The cache is empty!
+                       return array();
+               } elseif( $entryToFill <= $cacheMaxEntries ) {
+                       # Cache is not fully filled
+                       $firstKey = 1;
+               } else {
+                       # Cache overflowed
+                       $firstKey = 1 + $entryToFill - $cacheMaxEntries;
+               }
+
+               $lastKey  = $entryToFill;
+
+               for( $i=$firstKey; $i<=$lastKey; $i++ ) {
+                       $expected["cache-key-$i"] = array( "prop-$i" => "value-$i" );
+               }
+               return $expected;
+       }
+
+       /**
+        * Highlight diff between assertEquals and assertNotSame
+        */
+       function testPhpUnitArrayEquality() {
+               $one = array( 'A' => 1, 'B' => 2 );
+               $two = array( 'B' => 2, 'A' => 1 );
+               $this->assertEquals( $one, $two );  // ==
+               $this->assertNotSame( $one, $two ); // ===
+       }
+
+       /**
+        * @dataProvider provideInvalidConstructorArg
+        * @expectedException MWException
+        */
+       function testConstructorGivenInvalidValue( $maxSize ) {
+               $c = new ProcessCacheLRUTestable( $maxSize );
+       }
+
+       /**
+        * Value which are forbidden by the constructor
+        */
+       function provideInvalidConstructorArg() {
+               return array(
+                       array( null ),
+                       array( array() ),
+                       array( new stdClass() ),
+                       array( 0 ),
+                       array( '5' ),
+                       array( -1 ),
+               );
+       }
+
+       function testAddAndGetAKey() {
+               $oneCache = new ProcessCacheLRUTestable( 1 );
+               $this->assertCacheEmpty( $oneCache );
+
+               // First set just one value
+               $oneCache->set( 'cache-key', 'prop1', 'value1' );
+               $this->assertEquals( 1, $oneCache->getEntriesCount() );
+               $this->assertTrue( $oneCache->has( 'cache-key', 'prop1' ) );
+               $this->assertEquals( 'value1', $oneCache->get( 'cache-key', 'prop1' ) );
+       }
+
+       function testDeleteOldKey() {
+               $oneCache = new ProcessCacheLRUTestable( 1 );
+               $this->assertCacheEmpty( $oneCache );
+
+               $oneCache->set( 'cache-key', 'prop1', 'value1' );
+               $oneCache->set( 'cache-key', 'prop1', 'value2' );
+               $this->assertEquals( 'value2', $oneCache->get( 'cache-key', 'prop1' ) );
+       }
+
+       /**
+        * This test that we properly overflow when filling a cache with
+        * a sequence of always different cache-keys. Meant to verify we correclty
+        * delete the older key.
+        *
+        * @dataProvider provideCacheFilling
+        * @param $cacheMaxEntries Maximum entry the created cache will hold
+        * @param $entryToFill Number of entries to insert in the created cache.
+        */
+       function testFillingCache( $cacheMaxEntries, $entryToFill, $msg = '' ) {
+               $cache = new ProcessCacheLRUTestable( $cacheMaxEntries );
+               $this->fillCache( $cache, $entryToFill);
+
+               $this->assertSame(
+                       $this->getExpectedCache( $cacheMaxEntries, $entryToFill ),
+                       $cache->getCache(),
+                       "Filling a $cacheMaxEntries entries cache with $entryToFill entries"
+               );
+
+       }
+
+       /**
+        * Provider for testFillingCache
+        */
+       function provideCacheFilling() {
+               // ($cacheMaxEntries, $entryToFill, $msg='')
+               return array(
+                       array( 1,  0 ),
+                       array( 1,  1 ),
+                       array( 1,  2 ), # overflow
+                       array( 5, 33 ), # overflow
+               );
+
+       }
+
+       /**
+        * Create a cache with only one remaining entry then update
+        * the first inserted entry. Should bump it to the top.
+        */
+       function testReplaceExistingKeyShouldBumpEntryToTop() {
+               $maxEntries = 3;
+
+               $cache = new ProcessCacheLRUTestable( $maxEntries );
+               // Fill cache leaving just one remaining slot
+               $this->fillCache( $cache, $maxEntries - 1 );
+
+               // Set an existing cache key
+               $cache->set( "cache-key-1", "prop-1", "new-value-for-1" );
+
+               $this->assertSame(
+                       array(
+                               'cache-key-2' => array( 'prop-2' => 'value-2' ),
+                               'cache-key-1' => array( 'prop-1' => 'new-value-for-1' ),
+                       ),
+                       $cache->getCache()
+               );
+       }
+
+       function testRecentlyAccessedKeyStickIn() {
+               $cache = new ProcessCacheLRUTestable( 2 );
+               $cache->set( 'first' , 'prop1', 'value1' );
+               $cache->set( 'second', 'prop2', 'value2' );
+
+               // Get first
+               $cache->get( 'first', 'prop1' );
+               // Cache a third value, should invalidate the least used one
+               $cache->set( 'third', 'prop3', 'value3' );
+
+               $this->assertFalse( $cache->has( 'second', 'prop2' ) );
+       }
+
+       /**
+        * This first create a full cache then update the value for the 2nd
+        * filled entry.
+        * Given a cache having 1,2,3 as key, updating 2 should bump 2 to
+        * the top of the queue with the new value: 1,3,2* (* = updated).
+        */
+       function testReplaceExistingKeyInAFullCacheShouldBumpToTop() {
+               $maxEntries = 3;
+
+               $cache = new ProcessCacheLRUTestable( $maxEntries );
+               $this->fillCache( $cache, $maxEntries );
+
+               // Set an existing cache key
+               $cache->set( "cache-key-2", "prop-2", "new-value-for-2" );
+               $this->assertSame(
+                       array(
+                               'cache-key-1' => array( 'prop-1' => 'value-1' ),
+                               'cache-key-3' => array( 'prop-3' => 'value-3' ),
+                               'cache-key-2' => array( 'prop-2' => 'new-value-for-2' ),
+                       ),
+                       $cache->getCache()
+               );
+               $this->assertEquals( 'new-value-for-2',
+                       $cache->get( 'cache-key-2', 'prop-2' )
+               );
+       }
+
+       function testBumpExistingKeyToTop() {
+               $cache = new ProcessCacheLRUTestable( 3 );
+               $this->fillCache( $cache, 3 );
+
+               // Set the very first cache key to a new value
+               $cache->set( "cache-key-1", "prop-1", "new value for 1" );
+               $this->assertEquals(
+                       array(
+                               'cache-key-2' => array( 'prop-2' => 'value-2' ),
+                               'cache-key-3' => array( 'prop-3' => 'value-3' ),
+                               'cache-key-1' => array( 'prop-1' => 'new value for 1' ),
+                       ),
+                       $cache->getCache()
+               );
+
+       }
+
+}
+
+/**
+ * Overrides some ProcessCacheLRU methods and properties accessibility.
+ */
+class ProcessCacheLRUTestable extends ProcessCacheLRU {
+       public $cache = array();
+
+       public function getCache() {
+               return $this->cache;
+       }
+       public function getEntriesCount() {
+               return count( $this->cache );
+       }
+}
index 61507f5..6fb7ace 100644 (file)
@@ -243,6 +243,8 @@ class FileBackendTest extends MediaWikiTestCase {
                $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
                $this->assertEquals( $props1, $props2,
                        "Source and destination have the same props ($backendName)." );
+
+               $this->assertBackendPathsConsistent( array( $dest ) );
        }
 
        public function provider_testStore() {
@@ -330,6 +332,8 @@ class FileBackendTest extends MediaWikiTestCase {
                $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
                $this->assertEquals( $props1, $props2,
                        "Source and destination have the same props ($backendName)." );
+
+               $this->assertBackendPathsConsistent( array( $source, $dest ) );
        }
 
        public function provider_testCopy() {
@@ -419,6 +423,8 @@ class FileBackendTest extends MediaWikiTestCase {
                        "Source file does not exist accourding to props ($backendName)." );
                $this->assertEquals( true, $props2['fileExists'],
                        "Destination file exists accourding to props ($backendName)." );
+
+               $this->assertBackendPathsConsistent( array( $source, $dest ) );
        }
 
        public function provider_testMove() {
@@ -504,6 +510,8 @@ class FileBackendTest extends MediaWikiTestCase {
                $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
                $this->assertFalse( $props1['fileExists'],
                        "Source file $source does not exist according to props ($backendName)." );
+
+               $this->assertBackendPathsConsistent( array( $source ) );
        }
 
        public function provider_testDelete() {
@@ -595,6 +603,8 @@ class FileBackendTest extends MediaWikiTestCase {
                                $this->backend->getFileSize( array( 'src' => $dest ) ),
                                "Destination file $dest has original size according to props ($backendName)." );
                }
+
+               $this->assertBackendPathsConsistent( array( $dest ) );
        }
 
        /**
@@ -1822,6 +1832,13 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->backend->clean( array( 'dir' => "$base/$container", 'recursive' => 1 ) );
        }
 
+       function assertBackendPathsConsistent( array $paths ) {
+               if ( $this->backend instanceof FileBackendMultiWrite ) {
+                       $status = $this->backend->consistencyCheck( $paths );
+                       $this->assertGoodStatus( $status, "Files synced: " . implode( ',', $paths ) );
+               }
+       }
+
        function assertGoodStatus( $status, $msg ) {
                $this->assertEquals( print_r( array(), 1 ), print_r( $status->errors, 1 ), $msg );
        }
index d9b1671..11f9471 100644 (file)
@@ -596,7 +596,7 @@ class NewParserTest extends MediaWikiTestCase {
         * Run a fuzz test series
         * Draw input from a set of test files
         *
-        * @todo @fixme Needs some work to not eat memory until the world explodes
+        * @todo fixme Needs some work to not eat memory until the world explodes
         *
         * @group ParserFuzz
         */
index a1a6d59..bef7677 100644 (file)
@@ -24,43 +24,195 @@ class LanguageTest extends MediaWikiTestCase {
                );
        }
 
-       /** @dataProvider provideFormattableTimes */
+       /**
+        * @dataProvider provideFormattableTimes
+        */
        function testFormatTimePeriod( $seconds, $format, $expected, $desc ) {
                $this->assertEquals( $expected, $this->lang->formatTimePeriod( $seconds, $format ), $desc );
        }
 
        function provideFormattableTimes() {
                return array(
-                       array( 9.45, array(), '9.5s', 'formatTimePeriod() rounding (<10s)' ),
-                       array( 9.45, array( 'noabbrevs' => true ), '9.5 seconds', 'formatTimePeriod() rounding (<10s)' ),
-                       array( 9.95, array(), '10s', 'formatTimePeriod() rounding (<10s)' ),
-                       array( 9.95, array( 'noabbrevs' => true ), '10 seconds', 'formatTimePeriod() rounding (<10s)' ),
-                       array( 59.55, array(), '1m 0s', 'formatTimePeriod() rounding (<60s)' ),
-                       array( 59.55, array( 'noabbrevs' => true ), '1 minute 0 seconds', 'formatTimePeriod() rounding (<60s)' ),
-                       array( 119.55, array(), '2m 0s', 'formatTimePeriod() rounding (<1h)' ),
-                       array( 119.55, array( 'noabbrevs' => true ), '2 minutes 0 seconds', 'formatTimePeriod() rounding (<1h)' ),
-                       array( 3599.55, array(), '1h 0m 0s', 'formatTimePeriod() rounding (<1h)' ),
-                       array( 3599.55, array( 'noabbrevs' => true ), '1 hour 0 minutes 0 seconds', 'formatTimePeriod() rounding (<1h)' ),
-                       array( 7199.55, array(), '2h 0m 0s', 'formatTimePeriod() rounding (>=1h)' ),
-                       array( 7199.55, array( 'noabbrevs' => true ), '2 hours 0 minutes 0 seconds', 'formatTimePeriod() rounding (>=1h)' ),
-                       array( 7199.55, 'avoidseconds', '2h 0m', 'formatTimePeriod() rounding (>=1h), avoidseconds' ),
-                       array( 7199.55, array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ), '2 hours 0 minutes', 'formatTimePeriod() rounding (>=1h), avoidseconds' ),
-                       array( 7199.55, 'avoidminutes', '2h 0m', 'formatTimePeriod() rounding (>=1h), avoidminutes' ),
-                       array( 7199.55, array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ), '2 hours 0 minutes', 'formatTimePeriod() rounding (>=1h), avoidminutes' ),
-                       array( 172799.55, 'avoidseconds', '48h 0m', 'formatTimePeriod() rounding (=48h), avoidseconds' ),
-                       array( 172799.55, array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ), '48 hours 0 minutes', 'formatTimePeriod() rounding (=48h), avoidseconds' ),
-                       array( 259199.55, 'avoidminutes', '3d 0h', 'formatTimePeriod() rounding (>48h), avoidminutes' ),
-                       array( 259199.55, array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ), '3 days 0 hours', 'formatTimePeriod() rounding (>48h), avoidminutes' ),
-                       array( 176399.55, 'avoidseconds', '2d 1h 0m', 'formatTimePeriod() rounding (>48h), avoidseconds' ),
-                       array( 176399.55, array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ), '2 days 1 hour 0 minutes', 'formatTimePeriod() rounding (>48h), avoidseconds' ),
-                       array( 176399.55, 'avoidminutes', '2d 1h', 'formatTimePeriod() rounding (>48h), avoidminutes' ),
-                       array( 176399.55, array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ), '2 days 1 hour', 'formatTimePeriod() rounding (>48h), avoidminutes' ),
-                       array( 259199.55, 'avoidseconds', '3d 0h 0m', 'formatTimePeriod() rounding (>48h), avoidseconds' ),
-                       array( 259199.55, array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ), '3 days 0 hours 0 minutes', 'formatTimePeriod() rounding (>48h), avoidseconds' ),
-                       array( 172801.55, 'avoidseconds', '2d 0h 0m', 'formatTimePeriod() rounding, (>48h), avoidseconds' ),
-                       array( 172801.55, array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ), '2 days 0 hours 0 minutes', 'formatTimePeriod() rounding, (>48h), avoidseconds' ),
-                       array( 176460.55, array(), '2d 1h 1m 1s', 'formatTimePeriod() rounding, recursion, (>48h)' ),
-                       array( 176460.55, array( 'noabbrevs' => true ), '2 days 1 hour 1 minute 1 second', 'formatTimePeriod() rounding, recursion, (>48h)' ),
+                       array(
+                               9.45,
+                               array(),
+                               '9.5s',
+                               'formatTimePeriod() rounding (<10s)'
+                       ),
+                       array(
+                               9.45,
+                               array( 'noabbrevs' => true ),
+                               '9.5 seconds',
+                               'formatTimePeriod() rounding (<10s)'
+                       ),
+                       array(
+                               9.95,
+                               array(),
+                               '10s',
+                               'formatTimePeriod() rounding (<10s)'
+                       ),
+                       array(
+                               9.95,
+                               array( 'noabbrevs' => true ),
+                               '10 seconds',
+                               'formatTimePeriod() rounding (<10s)'
+                       ),
+                       array(
+                               59.55,
+                               array(),
+                               '1m 0s',
+                               'formatTimePeriod() rounding (<60s)'
+                       ),
+                       array(
+                               59.55,
+                               array( 'noabbrevs' => true ),
+                               '1 minute 0 seconds',
+                               'formatTimePeriod() rounding (<60s)'
+                       ),
+                       array(
+                               119.55,
+                               array(),
+                               '2m 0s',
+                               'formatTimePeriod() rounding (<1h)'
+                       ),
+                       array(
+                               119.55,
+                               array( 'noabbrevs' => true ),
+                               '2 minutes 0 seconds',
+                               'formatTimePeriod() rounding (<1h)'
+                       ),
+                       array(
+                               3599.55,
+                               array(),
+                               '1h 0m 0s',
+                               'formatTimePeriod() rounding (<1h)'
+                       ),
+                       array(
+                               3599.55,
+                               array( 'noabbrevs' => true ),
+                               '1 hour 0 minutes 0 seconds',
+                               'formatTimePeriod() rounding (<1h)'
+                       ),
+                       array(
+                               7199.55,
+                               array(),
+                               '2h 0m 0s',
+                               'formatTimePeriod() rounding (>=1h)'
+                       ),
+                       array(
+                               7199.55,
+                               array( 'noabbrevs' => true ),
+                               '2 hours 0 minutes 0 seconds',
+                               'formatTimePeriod() rounding (>=1h)'
+                       ),
+                       array(
+                               7199.55,
+                               'avoidseconds',
+                               '2h 0m',
+                               'formatTimePeriod() rounding (>=1h), avoidseconds'
+                       ),
+                       array(
+                               7199.55,
+                               array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
+                               '2 hours 0 minutes',
+                               'formatTimePeriod() rounding (>=1h), avoidseconds'
+                       ),
+                       array(
+                               7199.55,
+                               'avoidminutes',
+                               '2h 0m',
+                               'formatTimePeriod() rounding (>=1h), avoidminutes'
+                       ),
+                       array(
+                               7199.55,
+                               array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
+                               '2 hours 0 minutes',
+                               'formatTimePeriod() rounding (>=1h), avoidminutes'
+                       ),
+                       array(
+                               172799.55,
+                               'avoidseconds',
+                               '48h 0m',
+                               'formatTimePeriod() rounding (=48h), avoidseconds'
+                       ),
+                       array(
+                               172799.55,
+                               array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
+                               '48 hours 0 minutes',
+                               'formatTimePeriod() rounding (=48h), avoidseconds'
+                       ),
+                       array(
+                               259199.55,
+                               'avoidminutes',
+                               '3d 0h',
+                               'formatTimePeriod() rounding (>48h), avoidminutes'
+                       ),
+                       array(
+                               259199.55,
+                               array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
+                               '3 days 0 hours',
+                               'formatTimePeriod() rounding (>48h), avoidminutes'
+                       ),
+                       array(
+                               176399.55,
+                               'avoidseconds',
+                               '2d 1h 0m',
+                               'formatTimePeriod() rounding (>48h), avoidseconds'
+                       ),
+                       array(
+                               176399.55,
+                               array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
+                               '2 days 1 hour 0 minutes',
+                               'formatTimePeriod() rounding (>48h), avoidseconds'
+                       ),
+                       array(
+                               176399.55,
+                               'avoidminutes',
+                               '2d 1h',
+                               'formatTimePeriod() rounding (>48h), avoidminutes'
+                       ),
+                       array(
+                               176399.55,
+                               array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
+                               '2 days 1 hour',
+                               'formatTimePeriod() rounding (>48h), avoidminutes'
+                       ),
+                       array(
+                               259199.55,
+                               'avoidseconds',
+                               '3d 0h 0m',
+                               'formatTimePeriod() rounding (>48h), avoidseconds'
+                       ),
+                       array(
+                               259199.55,
+                               array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
+                               '3 days 0 hours 0 minutes',
+                               'formatTimePeriod() rounding (>48h), avoidseconds'
+                       ),
+                       array(
+                               172801.55,
+                               'avoidseconds',
+                               '2d 0h 0m',
+                               'formatTimePeriod() rounding, (>48h), avoidseconds'
+                       ),
+                       array(
+                               172801.55,
+                               array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
+                               '2 days 0 hours 0 minutes',
+                               'formatTimePeriod() rounding, (>48h), avoidseconds'
+                       ),
+                       array(
+                               176460.55,
+                               array(),
+                               '2d 1h 1m 1s',
+                               'formatTimePeriod() rounding, recursion, (>48h)'
+                       ),
+                       array(
+                               176460.55,
+                               array( 'noabbrevs' => true ),
+                               '2 days 1 hour 1 minute 1 second',
+                               'formatTimePeriod() rounding, recursion, (>48h)'
+                       ),
                );
 
        }
@@ -98,8 +250,8 @@ class LanguageTest extends MediaWikiTestCase {
        }
 
        /**
-       * @dataProvider provideHTMLTruncateData()
-       */
+        * @dataProvider provideHTMLTruncateData()
+        */
        function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
                // Actual HTML...
                $this->assertEquals(
@@ -792,16 +944,16 @@ class LanguageTest extends MediaWikiTestCase {
                );
        }
 
-       /**\r
-        * @dataProvider provideCheckTitleEncodingData\r
-        */\r
-       function testCheckTitleEncoding( $s ) {\r
-               $this->assertEquals(\r
-                       $s,\r
-                       $this->lang->checkTitleEncoding($s),\r
-                       "checkTitleEncoding('$s')"\r
-               );\r
-       }\r
+       /**
+        * @dataProvider provideCheckTitleEncodingData
+        */
+       function testCheckTitleEncoding( $s ) {
+               $this->assertEquals(
+                       $s,
+                       $this->lang->checkTitleEncoding($s),
+                       "checkTitleEncoding('$s')"
+               );
+       }
 
        function provideCheckTitleEncodingData() {
                return array (
@@ -815,42 +967,42 @@ class LanguageTest extends MediaWikiTestCase {
                        ),
                        // The following two data sets come from bug 36839. They fail if checkTitleEncoding uses a regexp to test for
                        // valid UTF-8 encoding and the pcre.recursion_limit is low (like, say, 1024). They succeed if checkTitleEncoding
-                   // uses mb_check_encoding for its test.
+                       // uses mb_check_encoding for its test.
                        array(
                                rawurldecode(
                                        "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn%7C"
-                                       . "Catherine%20Willows%7CDavid%20Hodges%7CDavid%20Phillips%7CGil%20Grissom%7CGreg%20Sanders%7CHodges%7C"
-                                       . "Internet%20Movie%20Database%7CJim%20Brass%7CLady%20Heather%7C"
-                                       . "Les%20Experts%20(s%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e)%7CLes%20Experts%20:%20Manhattan%7C"
-                                       . "Les%20Experts%20:%20Miami%7CListe%20des%20personnages%20des%20Experts%7C"
-                                       . "Liste%20des%20%C3%A9pisodes%20des%20Experts%7CMod%C3%A8le%20discussion:Palette%20Les%20Experts%7C"
-                                       . "Nick%20Stokes%7CPersonnage%20de%20fiction%7CPersonnage%20fictif%7CPersonnage%20de%20fiction%7C"
-                                       . "Personnages%20r%C3%A9currents%20dans%20Les%20Experts%7CRaymond%20Langston%7CRiley%20Adams%7C"
-                                       . "Saison%201%20des%20Experts%7CSaison%2010%20des%20Experts%7CSaison%2011%20des%20Experts%7C"
-                                       . "Saison%2012%20des%20Experts%7CSaison%202%20des%20Experts%7CSaison%203%20des%20Experts%7C"
-                                       . "Saison%204%20des%20Experts%7CSaison%205%20des%20Experts%7CSaison%206%20des%20Experts%7C"
-                                       . "Saison%207%20des%20Experts%7CSaison%208%20des%20Experts%7CSaison%209%20des%20Experts%7C"
-                                       . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C"
-                                       . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis"
+                                               . "Catherine%20Willows%7CDavid%20Hodges%7CDavid%20Phillips%7CGil%20Grissom%7CGreg%20Sanders%7CHodges%7C"
+                                               . "Internet%20Movie%20Database%7CJim%20Brass%7CLady%20Heather%7C"
+                                               . "Les%20Experts%20(s%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e)%7CLes%20Experts%20:%20Manhattan%7C"
+                                               . "Les%20Experts%20:%20Miami%7CListe%20des%20personnages%20des%20Experts%7C"
+                                               . "Liste%20des%20%C3%A9pisodes%20des%20Experts%7CMod%C3%A8le%20discussion:Palette%20Les%20Experts%7C"
+                                               . "Nick%20Stokes%7CPersonnage%20de%20fiction%7CPersonnage%20fictif%7CPersonnage%20de%20fiction%7C"
+                                               . "Personnages%20r%C3%A9currents%20dans%20Les%20Experts%7CRaymond%20Langston%7CRiley%20Adams%7C"
+                                               . "Saison%201%20des%20Experts%7CSaison%2010%20des%20Experts%7CSaison%2011%20des%20Experts%7C"
+                                               . "Saison%2012%20des%20Experts%7CSaison%202%20des%20Experts%7CSaison%203%20des%20Experts%7C"
+                                               . "Saison%204%20des%20Experts%7CSaison%205%20des%20Experts%7CSaison%206%20des%20Experts%7C"
+                                               . "Saison%207%20des%20Experts%7CSaison%208%20des%20Experts%7CSaison%209%20des%20Experts%7C"
+                                               . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C"
+                                               . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis"
                                ),
                        ),
                        array(
                                rawurldecode(
                                        "Mod%C3%A8le%3AArrondissements%20homonymes%7CMod%C3%A8le%3ABandeau%20standard%20pour%20page%20d'homonymie%7C"
-                                       . "Mod%C3%A8le%3ABatailles%20homonymes%7CMod%C3%A8le%3ACantons%20homonymes%7C"
-                                       . "Mod%C3%A8le%3ACommunes%20fran%C3%A7aises%20homonymes%7CMod%C3%A8le%3AFilms%20homonymes%7C"
-                                       . "Mod%C3%A8le%3AGouvernements%20homonymes%7CMod%C3%A8le%3AGuerres%20homonymes%7CMod%C3%A8le%3AHomonymie%7C"
-                                       . "Mod%C3%A8le%3AHomonymie%20bateau%7CMod%C3%A8le%3AHomonymie%20d'%C3%A9tablissements%20scolaires%20ou"
-                                       . "%20universitaires%7CMod%C3%A8le%3AHomonymie%20d'%C3%AEles%7CMod%C3%A8le%3AHomonymie%20de%20clubs%20sportifs%7C"
-                                       . "Mod%C3%A8le%3AHomonymie%20de%20comt%C3%A9s%7CMod%C3%A8le%3AHomonymie%20de%20monument%7C"
-                                       . "Mod%C3%A8le%3AHomonymie%20de%20nom%20romain%7CMod%C3%A8le%3AHomonymie%20de%20parti%20politique%7C"
-                                       . "Mod%C3%A8le%3AHomonymie%20de%20route%7CMod%C3%A8le%3AHomonymie%20dynastique%7C"
-                                       . "Mod%C3%A8le%3AHomonymie%20vid%C3%A9oludique%7CMod%C3%A8le%3AHomonymie%20%C3%A9difice%20religieux%7C"
-                                       . "Mod%C3%A8le%3AInternationalisation%7CMod%C3%A8le%3AIsom%C3%A9rie%7CMod%C3%A8le%3AParonymie%7C"
-                                       . "Mod%C3%A8le%3APatronyme%7CMod%C3%A8le%3APatronyme%20basque%7CMod%C3%A8le%3APatronyme%20italien%7C"
-                                       . "Mod%C3%A8le%3APatronymie%7CMod%C3%A8le%3APersonnes%20homonymes%7CMod%C3%A8le%3ASaints%20homonymes%7C"
-                                       . "Mod%C3%A8le%3ATitres%20homonymes%7CMod%C3%A8le%3AToponymie%7CMod%C3%A8le%3AUnit%C3%A9s%20homonymes%7C"
-                                       . "Mod%C3%A8le%3AVilles%20homonymes%7CMod%C3%A8le%3A%C3%89difices%20religieux%20homonymes"
+                                               . "Mod%C3%A8le%3ABatailles%20homonymes%7CMod%C3%A8le%3ACantons%20homonymes%7C"
+                                               . "Mod%C3%A8le%3ACommunes%20fran%C3%A7aises%20homonymes%7CMod%C3%A8le%3AFilms%20homonymes%7C"
+                                               . "Mod%C3%A8le%3AGouvernements%20homonymes%7CMod%C3%A8le%3AGuerres%20homonymes%7CMod%C3%A8le%3AHomonymie%7C"
+                                               . "Mod%C3%A8le%3AHomonymie%20bateau%7CMod%C3%A8le%3AHomonymie%20d'%C3%A9tablissements%20scolaires%20ou"
+                                               . "%20universitaires%7CMod%C3%A8le%3AHomonymie%20d'%C3%AEles%7CMod%C3%A8le%3AHomonymie%20de%20clubs%20sportifs%7C"
+                                               . "Mod%C3%A8le%3AHomonymie%20de%20comt%C3%A9s%7CMod%C3%A8le%3AHomonymie%20de%20monument%7C"
+                                               . "Mod%C3%A8le%3AHomonymie%20de%20nom%20romain%7CMod%C3%A8le%3AHomonymie%20de%20parti%20politique%7C"
+                                               . "Mod%C3%A8le%3AHomonymie%20de%20route%7CMod%C3%A8le%3AHomonymie%20dynastique%7C"
+                                               . "Mod%C3%A8le%3AHomonymie%20vid%C3%A9oludique%7CMod%C3%A8le%3AHomonymie%20%C3%A9difice%20religieux%7C"
+                                               . "Mod%C3%A8le%3AInternationalisation%7CMod%C3%A8le%3AIsom%C3%A9rie%7CMod%C3%A8le%3AParonymie%7C"
+                                               . "Mod%C3%A8le%3APatronyme%7CMod%C3%A8le%3APatronyme%20basque%7CMod%C3%A8le%3APatronyme%20italien%7C"
+                                               . "Mod%C3%A8le%3APatronymie%7CMod%C3%A8le%3APersonnes%20homonymes%7CMod%C3%A8le%3ASaints%20homonymes%7C"
+                                               . "Mod%C3%A8le%3ATitres%20homonymes%7CMod%C3%A8le%3AToponymie%7CMod%C3%A8le%3AUnit%C3%A9s%20homonymes%7C"
+                                               . "Mod%C3%A8le%3AVilles%20homonymes%7CMod%C3%A8le%3A%C3%89difices%20religieux%20homonymes"
                                )
                        )
                );
index 35e7e32..24186fb 100644 (file)
@@ -420,10 +420,10 @@ class TextPassDumperTest extends DumpTestCase {
         *           file is generated that is automatically removed upon
         *           tearDown.
         * @param $iterations integer: (Optional) specifies how often the block
-        *           of 3 pages should go into the stub file. The page id
-        *           increase further and further, while the revision and text
-        *           ids of the first iteration are reused. The pages of
-        *           iteration > 1 have no corresponding representation in the
+        *           of 3 pages should go into the stub file. The page and
+        *           revision id increase further and further, while the text
+        *           id of the first iteration is reused. The pages and revision
+        *           of iteration > 1 have no corresponding representation in the
         *           database.
         * @return string absolute filename of the stub
         */
index adfd111..1cd085f 100644 (file)
@@ -19,14 +19,16 @@ return array(
                        'tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.textSelection.test.js',
+                       'tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js',
+                       'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js',
+                       'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.parse.test.js',
                        'tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js',
-                       'tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js',
                ),
                'dependencies' => array(
@@ -44,13 +46,15 @@ return array(
                        'jquery.tablesorter',
                        'jquery.textSelection',
                        'mediawiki',
+                       'mediawiki.api',
+                       'mediawiki.api.parse',
+                       'mediawiki.jqueryMsg',
                        'mediawiki.Title',
                        'mediawiki.Uri',
                        'mediawiki.user',
                        'mediawiki.util',
                        'mediawiki.special.recentchanges',
-                       'mediawiki.jqueryMsg',
-                       'mediawiki.language'
+                       'mediawiki.language',
                ),
                'position' => 'top',
        )
index f3176ab..848384b 100644 (file)
@@ -147,6 +147,24 @@ QUnit.newMwEnvironment = ( function () {
        };
 }() );
 
+// $.when stops as soon as one fails, which makes sense in most
+// practical scenarios, but not in a unit test where we really do
+// need to wait until all of them are finished.
+QUnit.whenPromisesComplete = function () {
+       var altPromises = [];
+
+       $.each( arguments, function ( i, arg ) {
+               var alt = $.Deferred();
+               altPromises.push( alt );
+
+               // Whether this one fails or not, forwards it to
+               // the 'done' (resolve) callback of the alternative promise.
+               arg.always( alt.resolve );
+       });
+
+       return $.when.apply( $, altPromises );
+};
+
 /**
  * Add-on assertion helpers
  */
diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.parse.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.parse.test.js
new file mode 100644 (file)
index 0000000..246b74a
--- /dev/null
@@ -0,0 +1,18 @@
+QUnit.module( 'mediawiki.api.parse', QUnit.newMwEnvironment() );
+
+QUnit.asyncTest( 'Simple', function ( assert ) {
+       var api;
+       QUnit.expect( 1 );
+
+       api = new mw.Api();
+
+       api.parse( "'''Hello world'''" )
+               .done( function ( html ) {
+                       // Html also contains "NewPP report", so only check the first part
+                       assert.equal( html.substr( 0, 26 ), '<p><b>Hello world</b>\n</p>',
+                               'Wikitext to html parsing works.'
+                       );
+
+                       QUnit.start();
+               });
+});
diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js
new file mode 100644 (file)
index 0000000..79bd730
--- /dev/null
@@ -0,0 +1,59 @@
+QUnit.module( 'mediawiki.api', QUnit.newMwEnvironment() );
+
+QUnit.asyncTest( 'Basic functionality', function ( assert ) {
+       var api, d1, d2, d3;
+       QUnit.expect( 3 );
+
+       api = new mw.Api();
+
+       d1 = api.get( {} )
+               .done( function ( data ) {
+                       assert.deepEqual( data, [], 'If request succeeds without errors, resolve deferred' );
+               });
+
+       d2 = api.get({
+                       action: 'doesntexist'
+               })
+               .fail( function ( errorCode, details ) {
+                       assert.equal( errorCode, 'unknown_action', 'API error (e.g. "unknown_action") should reject the deferred' );
+               });
+
+       d3 = api.post( {} )
+               .done( function ( data ) {
+                       assert.deepEqual( data, [], 'Simple POST request' );
+               });
+
+       // After all are completed, continue the test suite.
+       QUnit.whenPromisesComplete( d1, d2, d3 ).always( function () {
+               QUnit.start();
+       });
+});
+
+QUnit.asyncTest( 'Deprecated callback methods', function ( assert ) {
+       var api, d1, d2, d3;
+       QUnit.expect( 3 );
+
+       api = new mw.Api();
+
+       d1 = api.get( {}, function () {
+               assert.ok( true, 'Function argument treated as success callback.' );
+       });
+
+       d2 = api.get( {}, {
+               ok: function ( data ) {
+                       assert.ok( true, '"ok" property treated as success callback.' );
+               }
+       });
+
+       d3 = api.get({
+                       action: 'doesntexist'
+               }, {
+               err: function ( data ) {
+                       assert.ok( true, '"err" property treated as error callback.' );
+               }
+       });
+
+       QUnit.whenPromisesComplete( d1, d2, d3 ).always( function () {
+               QUnit.start();
+       });
+});
index 86beb5f..0e700ef 100644 (file)
@@ -141,7 +141,7 @@ test( '$content', function() {
  * one element can have a given id. 
  */
 test( 'addPortletLink', function () {
-       var pTestTb, vectorTabs, tbRL, cuQuux, $cuQuux, tbMW, $tbMW, tbRLDM, caFoo;
+       var pTestTb, pCustom, vectorTabs, tbRL, cuQuux, $cuQuux, tbMW, $tbMW, tbRLDM, caFoo;
        expect( 8 );
 
        pTestTb = '\