Merge "Use Context in Article::delete for messages"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 18 Dec 2014 19:19:01 +0000 (19:19 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 18 Dec 2014 19:19:01 +0000 (19:19 +0000)
143 files changed:
HISTORY
RELEASE-NOTES-1.25
api.php
autoload.php
composer.json
docs/hooks.txt
docs/skin.txt
includes/DefaultSettings.php
includes/EditPage.php
includes/GlobalFunctions.php
includes/Html.php
includes/OutputPage.php
includes/Preferences.php
includes/StubObject.php
includes/User.php
includes/WikiMap.php
includes/api/ApiMain.php
includes/api/ApiQueryLogEvents.php
includes/api/i18n/ar.json
includes/api/i18n/be-tarask.json
includes/api/i18n/en.json
includes/api/i18n/ja.json
includes/api/i18n/zh-hans.json
includes/content/JsonContent.php
includes/db/IORMTable.php
includes/db/ORMTable.php
includes/debug/logger/Logger.php
includes/debug/logger/legacy/Logger.php
includes/debug/logger/monolog/LegacyFormatter.php
includes/diff/DairikiDiff.php
includes/filebackend/FileBackendStore.php
includes/htmlform/HTMLForm.php
includes/htmlform/HTMLFormField.php
includes/htmlform/HTMLFormFieldCloner.php
includes/installer/DatabaseUpdater.php
includes/installer/i18n/bs.json
includes/installer/i18n/diq.json
includes/jobqueue/JobQueueDB.php
includes/jobqueue/jobs/HTMLCacheUpdateJob.php
includes/jobqueue/jobs/RefreshLinksJob.php
includes/libs/RunningStat.php
includes/libs/ScopedCallback.php
includes/libs/UDPTransport.php [new file with mode: 0644]
includes/normal/README
includes/page/ImagePage.php
includes/page/WikiPage.php
includes/parser/CoreParserFunctions.php
includes/parser/Parser.php
includes/parser/ParserOutput.php
includes/profiler/ProfilerStub.php
includes/profiler/ProfilerXhprof.php
includes/profiler/SectionProfiler.php
includes/rcfeed/UDPRCFeedEngine.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/revisiondelete/RevisionDeleter.php
includes/specials/SpecialJavaScriptTest.php
includes/specials/SpecialMostimages.php
includes/specials/SpecialSearch.php
languages/i18n/ar.json
languages/i18n/be-tarask.json
languages/i18n/bs.json
languages/i18n/ckb.json
languages/i18n/cs.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/egl.json
languages/i18n/en.json
languages/i18n/es.json
languages/i18n/fa.json
languages/i18n/he.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/kk-cyrl.json
languages/i18n/mk.json
languages/i18n/nap.json
languages/i18n/pl.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/ro.json
languages/i18n/ru.json
languages/i18n/sl.json
languages/i18n/sv.json
languages/i18n/zh-hans.json
maintenance/Maintenance.php
maintenance/fetchText.php
maintenance/populateParentId.php
maintenance/storage/recompressTracked.php
resources/assets/file-type-icons/COPYING
resources/lib/oojs-ui/i18n/nds-nl.json
resources/lib/oojs-ui/i18n/sl.json
resources/lib/oojs-ui/i18n/uk.json
resources/lib/oojs-ui/oojs-ui-apex.css
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-apex.svg.css
resources/lib/oojs-ui/oojs-ui-mediawiki.css
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui-mediawiki.svg.css
resources/lib/oojs-ui/oojs-ui.js
resources/src/jquery/jquery.makeCollapsible.js
resources/src/jquery/jquery.tablesorter.js
resources/src/mediawiki.legacy/shared.css
resources/src/mediawiki.toolbar/toolbar.js
resources/src/mediawiki/mediawiki.content.json.css
resources/src/mediawiki/mediawiki.js
tests/phpunit/includes/ArrayUtilsTest.php [deleted file]
tests/phpunit/includes/ArticleTablesTest.php [deleted file]
tests/phpunit/includes/ArticleTest.php [deleted file]
tests/phpunit/includes/ExternalStoreTest.php [deleted file]
tests/phpunit/includes/ImagePage404Test.php [deleted file]
tests/phpunit/includes/ImagePageTest.php [deleted file]
tests/phpunit/includes/LinksUpdateTest.php [deleted file]
tests/phpunit/includes/LocalFileTest.php [deleted file]
tests/phpunit/includes/MWFunctionTest.php [deleted file]
tests/phpunit/includes/OutputPageTest.php
tests/phpunit/includes/PasswordTest.php [deleted file]
tests/phpunit/includes/RequestContextTest.php [deleted file]
tests/phpunit/includes/SpecialPageTest.php [deleted file]
tests/phpunit/includes/WikiPageTest.php [deleted file]
tests/phpunit/includes/WikiPageTestContentHandlerUseDB.php [deleted file]
tests/phpunit/includes/XmlTypeCheckTest.php [deleted file]
tests/phpunit/includes/api/ApiTestCase.php
tests/phpunit/includes/api/format/ApiFormatWddxTest.php
tests/phpunit/includes/content/JsonContentTest.php
tests/phpunit/includes/context/RequestContextTest.php [new file with mode: 0644]
tests/phpunit/includes/deferred/LinksUpdateTest.php [new file with mode: 0644]
tests/phpunit/includes/deferred/SearchUpdateTest.php [new file with mode: 0644]
tests/phpunit/includes/externalstore/ExternalStoreTest.php [new file with mode: 0644]
tests/phpunit/includes/filerepo/file/LocalFileTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/ArrayUtilsTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/XmlTypeCheckTest.php [new file with mode: 0644]
tests/phpunit/includes/page/ArticleTablesTest.php [new file with mode: 0644]
tests/phpunit/includes/page/ArticleTest.php [new file with mode: 0644]
tests/phpunit/includes/page/ImagePage404Test.php [new file with mode: 0644]
tests/phpunit/includes/page/ImagePageTest.php [new file with mode: 0644]
tests/phpunit/includes/page/WikiPageTest.php [new file with mode: 0644]
tests/phpunit/includes/page/WikiPageTestContentHandlerUseDB.php [new file with mode: 0644]
tests/phpunit/includes/password/PasswordTest.php [new file with mode: 0644]
tests/phpunit/includes/search/SearchUpdateTest.php [deleted file]
tests/phpunit/includes/specialpage/SpecialPageTest.php [new file with mode: 0644]
tests/phpunit/includes/utils/MWFunctionTest.php [new file with mode: 0644]
tests/phpunit/suite.xml
tests/qunit/data/testrunner.js
thumb.php

diff --git a/HISTORY b/HISTORY
index 4343c5d..e5864fd 100644 (file)
--- a/HISTORY
+++ b/HISTORY
@@ -809,7 +809,7 @@ of files that are no longer available follows.
 * HTMLForm 'select', 'selectandother', 'selectorother', 'multiselect', and
   'radio' fields can now use message keys as labels via the 'options-messages'
   parameter, which overrides the 'options' parameter.
-* Admins can expire users users passwords manually, or on a schedule using the
+* Admins can expire users passwords manually, or on a schedule using the
   $wgPasswordExpirationDays configuration setting.
 * Add new hook SendWatchlistEmailNotification, this will be used to determine
   whether to send a watchlist email notification.
@@ -1899,7 +1899,7 @@ This is a maintenance release of the MediaWiki 1.21 branch.
 
 === New features in 1.21 ===
 * (bug 38110) Schema changes (adding or dropping tables, indices and
-  fields) can be now be done separately from from other changes that
+  fields) can be now be done separately from other changes that
   update.php makes.  This is useful in environments that use database
   permissions to restrict schema changes but allow the DB user that
   MediaWiki normally runs as to perform other changes that update.php
@@ -4300,7 +4300,7 @@ Selected changes since MediaWiki 1.16 that may be of interest:
 * (bug 20186) Allow filtering Special:Contributions for RevisionDeleted edits.
 * ajaxwatch now uses the API and JQuery, and can be used to animate arbitrary
   watch links, not just to watch the page the link is on.
-* (bug 20976) "searchmenu-new-nocreate" message now displayed when when there
+* (bug 20976) "searchmenu-new-nocreate" message now displayed when there
   is no title match in search and the user has no rights to create pages.
 * (bug 23429) Added new hook WatchlistEditorBuildRemoveLine.
 * (bug 22844) Added support for WinCache object caching (for IIS).
@@ -5270,7 +5270,7 @@ comment from another wiki.
 ** Note that this change will break some extensions which have not been adapted
    for it.
 * (bug 17020) Adding fallback encodings for Traditional and Simplified Chinese
-  languages while the the text is typed as URLs.
+  languages while the text is typed as URLs.
 * (bug 17614) Prev / Next links are not shown if all results are shown
 * (bug 18207) Strange spacing before [[irc:...]] links
 * Removed float from the user login form in RTL interface - caused display
@@ -5441,7 +5441,7 @@ comment from another wiki.
   enabled
 * (bug 19857) maintenance/deleteRevision.php on last revision no longer breaks
   target page
-* (bug 20365) Page name with with c/g/h/j/s/u + x are now correctly handled in
+* (bug 20365) Page name with c/g/h/j/s/u + x are now correctly handled in
   Special:MovePage with Esperanto as content language
 * (bug 20364) Fixed regression in GIF metadata loading
 * (bug 20299) MediaWiki:Move-subpages and MediaWiki:Move-talk-subpages can now
@@ -5451,7 +5451,7 @@ comment from another wiki.
 * (bug 19966) MediaWiki:License-header is now used for the licensing header in
   the file description page instead of MediaWiki:License
 * (bug 20380) Links to history/deleted edits at the top of
-  Special:RevisionDelete are no more displayed when when doing log suppression
+  Special:RevisionDelete are no more displayed when doing log suppression
 * (bug 8143) Localised parser function names are now correctly case insensitive
   if they contain non-ASCII characters
 * (bug 19055) maintenance/rebuildrecentchanges.php now purges
@@ -6236,7 +6236,7 @@ The following extensions are migrated into MediaWiki 1.14:
 * Extensions can use the SkinBuildSidebar hook to modify the content of the
   sidebar and add custom portlets to it
 * Added 'MakeGlobalVariablesScript' hook for extensions to be able to add vari-
-  ables into into the output of Skin::makeVariablesScript
+  ables into the output of Skin::makeVariablesScript
 * (bug 13846) Added $wgAddGroups and $wgRemoveGroups display on
   Special:ListGroupRights
 * (bug 14377) Add a date selector to history pages
@@ -9840,7 +9840,7 @@ they will be run along with the main tests by maintenance/parserTests.php
 * Fix formatting of titles on Special:Undelete
 * (bug 7026) Fix action=raw&templates=expand
 * (bug 6976) Add namespace and direction classes to classic skins
-* (bug 7144) Don't "return to main" from OutputPage::loginToUse() if the the user can't
+* (bug 7144) Don't "return to main" from OutputPage::loginToUse() if the user can't
   read the main page in the first place
 * (bug 7188) Fix minor borkage in HTMLForm
 * (bug 6675) Replaced message 'watchthis' with new message 'watchthisupload in Special:Upload
index d98d7bb..e8b1162 100644 (file)
@@ -12,6 +12,7 @@ production.
 * $wgPageShowWatchingUsers was removed.
 * $wgLocalVirtualHosts has been added to replace $wgConf->localVHosts.
 * $wgAntiLockFlags was removed.
+* $wgJavaScriptTestConfig was removed.
 * Edit tokens returned from User::getEditToken may change on every call. Token
   validity must be checked by passing the user-supplied token to
   User::matchEditToken rather than by testing for equality with a
@@ -61,6 +62,7 @@ production.
 * The debug logging internals have been overhauled, and are now using the
   PSR-3 interfaces.
 * Update CSSJanus to v1.1.1.
+* Update lessphp to v0.5.0.
 * Added a hook, "ApiOpenSearchSuggest", to allow extensions to provide extracts
   and images for ApiOpenSearch output. The semantics are identical to the
   "OpenSearchXml" hook provided by the OpenSearchXml extension.
@@ -71,28 +73,29 @@ production.
 
 ==== External libraries ====
 * MediaWiki now requires certain external libraries to be installed. In the past
-  these were bundled inside the git repository of MediaWiki core, but now they
+  these were bundled inside the Git repository of MediaWiki core, but now they
   need to be installed separately. For users using the tarball, this will be taken
-  care of and no action will be required. Users using git will either need to use
+  care of and no action will be required. Users using Git will either need to use
   composer to fetch dependencies or use the mediawiki/vendor repository which includes
   all dependencies for MediaWiki core and ones used in Wikimedia deployment. Detailed
-  instructions can be found at <https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries>.
+  instructions can be found at:
+  https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries
 * The following libraries are now required:
-** psr/log 1.0.0
-*** This library provides the interfaces set by the PSR-3 standard (<http://www.php-fig.org/psr/psr-3/>)
-    which are used by MediaWiki interally by the MWLogger class.
-*** See the structured logging RfC (<https://www.mediawiki.org/wiki/Requests_for_comment/Structured_logging>)
-    for more background information.
-** cssjanus/cssjanus 1.1.1
-*** This library was formerly bundled with MediaWiki core and has now been removed. It automatically
-    flips CSS for RTL support.
-** leafo/lessphp 0.5.0
-*** This library was formerly bundled with MediaWiki core and has now been removed. It compiles LESS
-    files into CSS.
-** cdb/cdb 1.0.0
-*** This library was formerly a part of MediaWiki core, and has now been split out into a separate library.
-    It provides CDB functions which are used in the Interwiki and Localization caches. More information
-    about the library can be found at <https://www.mediawiki.org/wiki/CDB>.
+** psr/log
+   This library provides the interfaces set by the PSR-3 standard (http://www.php-fig.org/psr/psr-3/)
+   which are used by MediaWiki interally by the MWLogger class.
+   See the structured logging RfC (https://www.mediawiki.org/wiki/Requests_for_comment/Structured_logging)
+   for more background information.
+** cssjanus/cssjanus
+   This library was formerly bundled with MediaWiki core and has been removed.
+   It automatically flips CSS for RTL support.
+** leafo/lessphp
+   This library was formerly bundled with MediaWiki core and has been removed.
+   It compiles LESS files into CSS.
+** wikimedia/cdb
+   This library was formerly a part of MediaWiki core, and has been moved into a separate library.
+   It provides CDB functions which are used in the Interwiki and Localization caches.
+   More information about the library can be found at https://www.mediawiki.org/wiki/CDB.
 
 === Bug fixes in 1.25 ===
 * (T73003) No additional code will be generated to try to load CSS-embedded
@@ -243,8 +246,8 @@ changes to languages because of Bugzilla reports.
 * BREAKING CHANGE: In the XML dump format used by Special:Export and
   dumpBackup.php, the <model> and <format> tags now apprear before the <text>
   tag, instead of after the <text> and <sha1> tags.
-  The new schema version is 0.10, the new schema URI is
-  <https://www.mediawiki.org/xml/export-0.10.xsd>.
+  The new schema version is 0.10, the new schema URI is:
+  https://www.mediawiki.org/xml/export-0.10.xsd
 * MWFunction::call() and MWFunction::callArray() were removed, having being
   deprecated in 1.22.
 * Deprecated the getInternalLinkAttributes, getInternalLinkAttributesObj,
diff --git a/api.php b/api.php
index cc589b0..92e6704 100644 (file)
--- a/api.php
+++ b/api.php
@@ -122,7 +122,7 @@ if ( $wgAPIRequestLog ) {
        } else {
                $items[] = "failed in ApiBeforeMain";
        }
-       wfErrorLog( implode( ',', $items ) . "\n", $wgAPIRequestLog );
+       MWLoggerLegacyLogger::emit( implode( ',', $items ) . "\n", $wgAPIRequestLog );
        wfDebug( "Logged API request to $wgAPIRequestLog\n" );
 }
 
index 107ab81..d4a152a 100644 (file)
@@ -1018,6 +1018,7 @@ $wgAutoloadLocalClasses = array(
        'SearchResultSet' => __DIR__ . '/includes/search/SearchResultSet.php',
        'SearchSqlite' => __DIR__ . '/includes/search/SearchSqlite.php',
        'SearchUpdate' => __DIR__ . '/includes/deferred/SearchUpdate.php',
+       'SectionProfileCallback' => __DIR__ . '/includes/profiler/SectionProfiler.php',
        'SectionProfiler' => __DIR__ . '/includes/profiler/SectionProfiler.php',
        'SevenZipStream' => __DIR__ . '/maintenance/7zip.inc',
        'ShiConverter' => __DIR__ . '/languages/classes/LanguageShi.php',
@@ -1182,6 +1183,7 @@ $wgAutoloadLocalClasses = array(
        'TransformTooBigImageAreaError' => __DIR__ . '/includes/media/MediaTransformOutput.php',
        'TransformationalImageHandler' => __DIR__ . '/includes/media/TransformationalImageHandler.php',
        'UDPRCFeedEngine' => __DIR__ . '/includes/rcfeed/UDPRCFeedEngine.php',
+       'UDPTransport' => __DIR__ . '/includes/libs/UDPTransport.php',
        'UIDGenerator' => __DIR__ . '/includes/utils/UIDGenerator.php',
        'UcdXmlReader' => __DIR__ . '/maintenance/language/generateCollationData.php',
        'UncategorizedCategoriesPage' => __DIR__ . '/includes/specials/SpecialUncategorizedcategories.php',
index d7a7f04..8aeb792 100644 (file)
@@ -21,7 +21,7 @@
                "psr/log": "1.0.0",
                "cssjanus/cssjanus": "1.1.1",
                "wikimedia/cdb": "1.0.1",
-               "oojs/oojs-ui": "0.5.0"
+               "oojs/oojs-ui": "0.6.0"
        },
        "require-dev": {
                "phpunit/phpunit": "*"
index 369ad9f..f83a6c3 100644 (file)
@@ -1461,7 +1461,7 @@ $page: ImagePage object
 'ImgAuthBeforeStream': executed before file is streamed to user, but only when
 using img_auth.php.
 &$title: the Title object of the file as it would appear for the upload page
-&$path: the original file and path name when img_auth was invoked by the the web
+&$path: the original file and path name when img_auth was invoked by the web
   server
 &$name: the name only component of the file
 &$result: The location to pass back results of the hook routine (only used if
index e998ebd..a3c8c33 100644 (file)
@@ -7,7 +7,7 @@ MediaWiki includes four core skins:
   Monobook.
 
 * Monobook: Named after the black-and-white photo of a book in the page
-  background. Introduced in the 2004 release of 1.3, it had been been the
+  background. Introduced in the 2004 release of 1.3, it had been the
   default skin since then, before being replaced by Vector.
 
 * Modern: An attractive blue/grey theme with sidebar and top bar. Derived from
index 4a6aa0b..4261c68 100644 (file)
@@ -5481,25 +5481,6 @@ $wgParserTestFiles = array(
  */
 $wgEnableJavaScriptTest = false;
 
-/**
- * Configuration for javascript testing.
- */
-$wgJavaScriptTestConfig = array(
-       'qunit' => array(
-               // Page where documentation can be found relevant to the QUnit test suite being ran.
-               // Used in the intro paragraph on [[Special:JavaScriptTest/qunit]] for the
-               // documentation link in the "javascripttest-qunit-intro" message.
-               'documentation' => '//www.mediawiki.org/wiki/Manual:JavaScript_unit_testing',
-               // If you are submitting the QUnit test suite to a TestSwarm instance,
-               // point this to the "inject.js" script of that instance. This is was registers
-               // the QUnit hooks to extract the test results and push them back up into the
-               // TestSwarm database.
-               // @example 'http://localhost/testswarm/js/inject.js'
-               // @example '//integration.mediawiki.org/testswarm/js/inject.js'
-               'testswarm-injectjs' => false,
-       ),
-);
-
 /**
  * Overwrite the caching key prefix with custom value.
  * @since 1.19
index 2155c1b..d05b996 100644 (file)
@@ -1928,11 +1928,15 @@ class EditPage {
                        && $content->isRedirect()
                        && $content->getRedirectTarget()->equals( $this->getTitle() )
                ) {
-                       $this->selfRedirect = true;
-                       $status->fatal( 'selfredirect' );
-                       $status->value = self::AS_SELF_REDIRECT;
-                       wfProfileOut( __METHOD__ );
-                       return $status;
+                       // If the page already redirects to itself, don't warn.
+                       $currentTarget = $this->getCurrentContent()->getRedirectTarget();
+                       if ( !$currentTarget || !$currentTarget->equals( $this->getTitle() ) ) {
+                               $this->selfRedirect = true;
+                               $status->fatal( 'selfredirect' );
+                               $status->value = self::AS_SELF_REDIRECT;
+                               wfProfileOut( __METHOD__ );
+                               return $status;
+                       }
                }
 
                // Check for length errors again now that the section is merged in
index 859b421..403566e 100644 (file)
@@ -1067,14 +1067,18 @@ function wfDebugMem( $exact = false ) {
 }
 
 /**
- * Send a line to a supplementary debug log file, if configured, or main debug log if not.
- * To configure a supplementary log file, set $wgDebugLogGroups[$logGroup] to a string
- * filename or an associative array mapping 'destination' to the desired filename. The
- * associative array may also contain a 'sample' key with an integer value, specifying
- * a sampling factor.
+ * Send a line to a supplementary debug log file, if configured, or main debug
+ * log if not.
+ *
+ * To configure a supplementary log file, set $wgDebugLogGroups[$logGroup] to
+ * a string filename or an associative array mapping 'destination' to the
+ * desired filename. The associative array may also contain a 'sample' key
+ * with an integer value, specifying a sampling factor. Sampled log events
+ * will be emitted with a 1 in N random chance.
  *
  * @since 1.23 support for sampling log messages via $wgDebugLogGroups.
  * @since 1.25 support for additional context data
+ * @since 1.25 sample behavior dependent on configured $wgMWLoggerDefaultSpi
  *
  * @param string $logGroup
  * @param string $text
@@ -1106,7 +1110,7 @@ function wfDebugLog(
 
        $logger = MWLogger::getInstance( $logGroup );
        $context['private'] = ( $dest === 'private' );
-       $logger->debug( $text, $context );
+       $logger->info( $text, $context );
 }
 
 /**
@@ -1177,8 +1181,10 @@ function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
  * @param string $file Filename
  * @param array $context Additional logging context data
  * @throws MWException
+ * @deprecated since 1.25 Use MWLoggerLegacyLogger::emit or UDPTransport
  */
 function wfErrorLog( $text, $file, array $context = array() ) {
+       wfDeprecated( __METHOD__, '1.25' );
        $logger = MWLogger::getInstance( 'wfErrorLog' );
        $context['destination'] = $file;
        $logger->info( trim( $text ), $context );
@@ -3891,7 +3897,7 @@ function wfBCP47( $code ) {
 /**
  * Get a cache object.
  *
- * @param int $inputType Cache type, one the the CACHE_* constants.
+ * @param int $inputType Cache type, one of the CACHE_* constants.
  * @return BagOStuff
  */
 function wfGetCache( $inputType ) {
index b3437d3..e033746 100644 (file)
@@ -893,7 +893,7 @@ class Html {
                                continue;
                        }
                        if ( $nsId === NS_MAIN ) {
-                               // For other namespaces use use the namespace prefix as label, but for
+                               // For other namespaces use the namespace prefix as label, but for
                                // main we don't use "" but the user message describing it (e.g. "(Main)" or "(Article)")
                                $nsName = wfMessage( 'blanknamespace' )->text();
                        } elseif ( is_int( $nsId ) ) {
index 24b9e46..411bb2e 100644 (file)
@@ -1075,7 +1075,7 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Set the page as printable, i.e. it'll be displayed with with all
+        * Set the page as printable, i.e. it'll be displayed with all
         * print styles included
         */
        public function setPrintable() {
index 44995ac..aca6dcb 100644 (file)
@@ -130,8 +130,7 @@ class Preferences {
                        if ( $disable && !in_array( $name, self::$saveBlacklist ) ) {
                                $info['disabled'] = 'disabled';
                        }
-                       $field = HTMLForm::loadInputFromParameters( $name, $info ); // For validation
-                       $field->mParent = $dummyForm;
+                       $field = HTMLForm::loadInputFromParameters( $name, $info, $dummyForm ); // For validation
                        $defaultOptions = User::getDefaultOptions();
                        $globalDefault = isset( $defaultOptions[$name] )
                                ? $defaultOptions[$name]
index 7f12c16..adca862 100644 (file)
@@ -182,6 +182,24 @@ class StubUserLang extends StubObject {
                return $this->_call( $name, $args );
        }
 
+       /**
+        * Call Language::findVariantLink after unstubbing $wgLang.
+        *
+        * This method is implemented with a full signature rather than relying on
+        * __call so that the pass-by-reference signature of the proxied method is
+        * honored.
+        *
+        * @param string &$link The name of the link
+        * @param Title &$nt The title object of the link
+        * @param bool $ignoreOtherCond To disable other conditions when
+        *   we need to transclude a template or update a category's link
+        */
+       public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
+               global $wgLang;
+               $this->_unstub( 'findVariantLink', 3 );
+               return $wgLang->findVariantLink( $link, $nt, $ignoreOtherCond );
+       }
+
        /**
         * @return Language
         */
index bda825f..34af4c5 100644 (file)
@@ -2489,7 +2489,7 @@ class User implements IDBAccessObject {
                        $type = $oldaddr != '' ? 'changed' : 'set';
                        $result = $this->sendConfirmationMail( $type );
                        if ( $result->isGood() ) {
-                               // Say the the caller that a confirmation mail has been sent
+                               // Say to the caller that a confirmation mail has been sent
                                $result->value = 'eauth';
                        }
                } else {
index 34cd48d..f16f5aa 100644 (file)
@@ -161,7 +161,7 @@ class WikiReference {
        }
 
        /**
-        * Get the the URL in a way to de displayed to the user
+        * Get the URL in a way to be displayed to the user
         * More or less Wikimedia specific
         *
         * @return string
index 81353f6..c03e513 100644 (file)
@@ -575,7 +575,7 @@ class ApiMain extends ApiBase {
                        $wildcard
                );
 
-               return "/https?:\/\/$wildcard/";
+               return "/^https?:\/\/$wildcard$/";
        }
 
        protected function sendCacheHeaders() {
index 5f9fae4..7d79680 100644 (file)
@@ -36,7 +36,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
        }
 
        private $fld_ids = false, $fld_title = false, $fld_type = false,
-               $fld_action = false, $fld_user = false, $fld_userid = false,
+               $fld_user = false, $fld_userid = false,
                $fld_timestamp = false, $fld_comment = false, $fld_parsedcomment = false,
                $fld_details = false, $fld_tags = false;
 
@@ -50,7 +50,6 @@ class ApiQueryLogEvents extends ApiQueryBase {
                $this->fld_ids = isset( $prop['ids'] );
                $this->fld_title = isset( $prop['title'] );
                $this->fld_type = isset( $prop['type'] );
-               $this->fld_action = isset( $prop['action'] );
                $this->fld_user = isset( $prop['user'] );
                $this->fld_userid = isset( $prop['userid'] );
                $this->fld_timestamp = isset( $prop['timestamp'] );
@@ -200,8 +199,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
                }
 
                // Paranoia: avoid brute force searches (bug 17342)
-               $hideActions = $params['namespace'] !== null || !is_null( $title ) || !is_null( $params['action'] );
-               if ( $hideActions || !is_null( $user ) ) {
+               if ( $params['namespace'] !== null || !is_null( $title ) || !is_null( $user ) ) {
                        if ( !$this->getUser()->isAllowed( 'deletedhistory' ) ) {
                                $titleBits = LogPage::DELETED_ACTION;
                                $userBits = LogPage::DELETED_USER;
@@ -212,7 +210,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
                                $titleBits = 0;
                                $userBits = 0;
                        }
-                       if ( $hideActions && $titleBits ) {
+                       if ( ( $params['namespace'] !== null || !is_null( $title ) ) && $titleBits ) {
                                $this->addWhere( $db->bitAnd( 'log_deleted', $titleBits ) . " != $titleBits" );
                        }
                        if ( !is_null( $user ) && $userBits ) {
@@ -373,18 +371,12 @@ class ApiQueryLogEvents extends ApiQueryBase {
                        $title = Title::makeTitle( $row->log_namespace, $row->log_title );
                }
 
-               if ( $this->fld_title || $this->fld_ids || $this->fld_type
-                       || $this->fld_details && $row->log_params !== ''
-               ) {
+               if ( $this->fld_title || $this->fld_ids || $this->fld_details && $row->log_params !== '' ) {
                        if ( LogEventsList::isDeleted( $row, LogPage::DELETED_ACTION ) ) {
                                $vals['actionhidden'] = '';
                                $anyHidden = true;
                        }
                        if ( LogEventsList::userCan( $row, LogPage::DELETED_ACTION, $user ) ) {
-
-                               if ( $this->fld_type ) {
-                                       $vals['action'] = $row->log_action;
-                               }
                                if ( $this->fld_title ) {
                                        ApiQueryBase::addTitleInfo( $vals, $title );
                                }
@@ -408,6 +400,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
 
                if ( $this->fld_type ) {
                        $vals['type'] = $row->log_type;
+                       $vals['action'] = $row->log_action;
                }
 
                if ( $this->fld_user || $this->fld_userid ) {
index d82b0d8..ce0943c 100644 (file)
@@ -2,7 +2,8 @@
        "@metadata": {
                "authors": [
                        "Meno25",
-                       "أحمد المحمودي"
+                       "أحمد المحمودي",
+                       "Khaled"
                ]
        },
        "apihelp-main-param-format": "صيغة الخرج.",
@@ -19,5 +20,6 @@
        "apihelp-delete-description": "حذف صفحة.",
        "apihelp-delete-param-unwatch": "أزل الصفحة من قائمة مراقبتك.",
        "apihelp-edit-description": "إنشاء وتعديل الصفحات.",
+       "apihelp-emailuser-description": "مراسلة المستخدم",
        "apihelp-query+prefixsearch-param-offset": "عدد النتائج المراد تخطيها."
 }
index ef58bd5..754c9a3 100644 (file)
@@ -27,5 +27,6 @@
        "apihelp-block-param-hidename": "Схаваць імя ўдзельніка з журналу блякаваньняў (патрабуе права «hideuser»).",
        "apihelp-block-param-allowusertalk": "Дазволіць удзельніку рэдагаваць уласную старонку гутарак (залежыць ад $wgBlockAllowsUTEdit).",
        "apihelp-block-param-reblock": "Калі ўдзельнік ужо заблякаваны, перапісаць дзейнае блякаваньне.",
-       "apihelp-block-param-watchuser": "Назіраць за старонкай удзельніка або старонкай IP-адрасу, а таксама старонкай гутарак."
+       "apihelp-block-param-watchuser": "Назіраць за старонкай удзельніка або старонкай IP-адрасу, а таксама старонкай гутарак.",
+       "apihelp-block-example-ip-simple": "Заблякаваць IP-адрас 192.0.2.5 на тры дні з прычынай «First strike»"
 }
index 21b546c..d0eb230 100644 (file)
@@ -5,7 +5,7 @@
                ]
        },
 
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [https://www.mediawiki.org/wiki/API:Main_page Documentation]\n* [https://www.mediawiki.org/wiki/API:FAQ FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API Announcements]\n* [https://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts Bugs & requests]\n</div>\n<strong>Status:</strong> All features shown on this page should be working, but the API is still in active development, and may change at any time. Subscribe to [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce mailing list] for notice of updates.\n\n<strong>Erroneous requests:</strong> When erroneous requests are sent to the API, a HTTP header will be sent with the key \"MediaWiki-API-Error\" and then both the value of the header and the error code sent back will be set to the same value. For more information see https://www.mediawiki.org/wiki/API:Errors_and_warnings.",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [https://www.mediawiki.org/wiki/API:Main_page Documentation]\n* [https://www.mediawiki.org/wiki/API:FAQ FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API Announcements]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bugs & requests]\n</div>\n<strong>Status:</strong> All features shown on this page should be working, but the API is still in active development, and may change at any time. Subscribe to [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce mailing list] for notice of updates.\n\n<strong>Erroneous requests:</strong> When erroneous requests are sent to the API, a HTTP header will be sent with the key \"MediaWiki-API-Error\" and then both the value of the header and the error code sent back will be set to the same value. For more information see https://www.mediawiki.org/wiki/API:Errors_and_warnings.",
        "apihelp-main-param-action": "Which action to perform.",
        "apihelp-main-param-format": "The format of the output.",
        "apihelp-main-param-maxlag": "Maximum lag can be used when MediaWiki is installed on a database replicated cluster. To save actions causing any more site replication lag, this parameter can make the client wait until the replication lag is less than the specified value. In case of excessive lag, error code \"maxlag\" is returned with a message like \"Waiting for $host: $lag seconds lagged\".<br />See https://www.mediawiki.org/wiki/Manual:Maxlag_parameter for more information.",
        "apihelp-query+revisions-example-last5": "Get last 5 revisions of the \"Main Page\"",
        "apihelp-query+revisions-example-first5": "Get first 5 revisions of the \"Main Page\"",
        "apihelp-query+revisions-example-first5-after": "Get first 5 revisions of the \"Main Page\" made after 2006-05-01",
-       "apihelp-query+revisions-example-first5-not-localhost": "Get first 5 revisions of the \"Main Page\" that were not made made by anonymous user \"127.0.0.1\"",
+       "apihelp-query+revisions-example-first5-not-localhost": "Get first 5 revisions of the \"Main Page\" that were not made by anonymous user \"127.0.0.1\"",
        "apihelp-query+revisions-example-first5-user": "Get first 5 revisions of the \"Main Page\" that were made by the user \"MediaWiki default\"",
 
        "apihelp-query+revisions+base-param-prop": "Which properties to get for each revision:\n;ids:The ID of the revision.\n;flags:Revision flags (minor).\n;timestamp:The timestamp of the revision.\n;user:User that made the revision.\n;userid:User ID of the revision creator.\n;size:Length (bytes) of the revision.\n;sha1:SHA-1 (base 16) of the revision.\n;contentmodel:Content model ID of the revision.\n;comment:Comment by the user for the revision.\n;parsedcomment:Parsed comment by the user for the revision.\n;content:Text of the revision.\n;tags:Tags for the revision.",
        "api-help-right-apihighlimits": "Use higher limits in API queries (slow queries: $1; fast queries: $2). The limits for slow queries also apply to multivalue parameters.",
 
        "api-credits-header": "Credits",
-       "api-credits": "API developers:\n* Roan Kattouw (lead developer Sep 2007–2009)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (creator, lead developer Sep 2006–Sep 2007)\n* Brad Jorsch (lead developer 2013–present)\n\nPlease send your comments, suggestions and questions to mediawiki-api@lists.wikimedia.org\nor file a bug report at https://bugzilla.wikimedia.org/."
+       "api-credits": "API developers:\n* Roan Kattouw (lead developer Sep 2007–2009)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (creator, lead developer Sep 2006–Sep 2007)\n* Brad Jorsch (lead developer 2013–present)\n\nPlease send your comments, suggestions and questions to mediawiki-api@lists.wikimedia.org\nor file a bug report at https://phabricator.wikimedia.org/."
 }
index a06db94..08f6ed5 100644 (file)
@@ -2,7 +2,8 @@
        "@metadata": {
                "authors": [
                        "Shirayuki",
-                       "2nd-player"
+                       "2nd-player",
+                       "Los688"
                ]
        },
        "apihelp-main-param-action": "実行する操作です。",
        "apihelp-main-param-curtimestamp": "現在のタイムスタンプを結果に含めます。",
        "apihelp-main-param-uselang": "メッセージの翻訳に使用する言語です。コードの一覧は [[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]] に siprop=languages を付けることで取得できます。\"user\" を指定することで現在の利用者の個人設定の言語を、\"content\" を指定することでこのウィキの本文の言語を使用することもできます。",
        "apihelp-block-description": "利用者をブロックします。",
+       "apihelp-block-param-reason": "ブロックの理由:",
        "apihelp-block-param-nocreate": "アカウントの作成を禁止します。",
        "apihelp-createaccount-description": "新しい利用者アカウントを作成します。",
+       "apihelp-createaccount-param-name": "利用者名:",
        "apihelp-createaccount-param-password": "パスワード ($1mailpassword が設定されると無視されます)。",
        "apihelp-createaccount-param-token": "最初のリクエストで得られたアカウント作成用トークンです。",
        "apihelp-createaccount-param-email": "利用者の電子メールアドレス (任意)。",
        "apihelp-edit-param-title": "編集するページ名です。$1pageid とは同時に使用できません。",
        "apihelp-edit-param-pageid": "編集するページIDです。$1title とは同時に使用できません。",
        "apihelp-edit-param-text": "ページの本文。",
+       "apihelp-edit-param-minor": "細部の編集",
        "apihelp-edit-param-createonly": "すでにそのページが存在する場合は編集を行いません。",
        "apihelp-edit-param-nocreate": "そのページが存在しない場合にエラーを返します。",
        "apihelp-edit-param-watch": "そのページをウォッチリストに追加します。",
        "apihelp-edit-param-unwatch": "そのページをウォッチリストから除去します。",
        "apihelp-edit-param-token": "このトークンは常に最後のパラメーターとして、または少なくとも $1text パラメーターより後に送信されるべきです。",
+       "apihelp-edit-example-edit": "ページを編集",
        "apihelp-emailuser-description": "利用者に電子メールを送信します。",
        "apihelp-emailuser-param-target": "送信先の利用者名。",
        "apihelp-emailuser-param-text": "電子メールの本文。",
@@ -53,6 +58,7 @@
        "apihelp-help-example-query": "2つの下位モジュールのヘルプ",
        "apihelp-login-param-name": "利用者名。",
        "apihelp-login-param-password": "パスワード。",
+       "apihelp-login-example-login": "ログイン",
        "apihelp-move-description": "ページを移動します。",
        "apihelp-move-param-from": "移動するページのページ名です。 $1fromid とは同時に使用できません。",
        "apihelp-move-param-fromid": "移動するページのページIDです。 $1from とは同時に使用できません。",
index fe8d76c..bb6b7f4 100644 (file)
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [https://www.mediawiki.org/wiki/API:Main_page/zh 文档]\n* [https://www.mediawiki.org/wiki/API:FAQ/zh 常见问题]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 邮件列表]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts 程序错误与功能请求]\n</div>\n<strong>状态信息:</strong> 本页所展示的所有特性都应正常工作,但是API仍在开发当中,将会随时变化。请订阅[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 邮件列表]以便获得更新通知。\n\n<strong>错误请求:</strong> 当API收到错误请求时,HTTP header将会返回一个包含\"MediaWiki-API-Error\"的值,随后header的值与error code将会送回并设置为相同的值。详细信息请参阅 https://www.mediawiki.org/wiki/API:Errors_and_warnings 。",
        "apihelp-main-param-action": "要执行的操作。",
        "apihelp-main-param-format": "输出的格式。",
+       "apihelp-main-param-maxlag": "最大延迟可被用于MediaWiki安装于数据库复制集中。要保存导致更多网站复制延迟的操作,此参数可使客户端等待直到复制延迟少于指定值时。万一发生过多延迟,错误代码“maxlag”会返回消息,例如“等待$host中:延迟$lag秒”。<br />参见https://www.mediawiki.org/wiki/Manual:Maxlag_parameter以获取更多信息。",
+       "apihelp-main-param-smaxage": "设置s-maxage页顶至这些秒。错误不会缓存。",
+       "apihelp-main-param-maxage": "设置max-age页顶至这些秒。错误不会缓存。",
        "apihelp-main-param-assert": "如果设置为“user”就验证用户是否登录,或如果设置为“bot”就验证是否有机器人用户权限。",
+       "apihelp-main-param-requestid": "任何在此提供的值将包含在响应中。可能可以用以区别请求。",
        "apihelp-main-param-servedby": "包含保存结果请求的主机名。",
        "apihelp-main-param-curtimestamp": "在结果中包括当前时间戳。",
        "apihelp-main-param-origin": "当通过跨域名AJAX请求(CORS)访问API时,设置此作为起始域名。这必须包括在任何pre-flight请求中,并因此必须是请求的URI的一部分(而不是POST正文)。这必须匹配Origin中的一个起点:从头到底,因此它已经设置为像http://zh.wikipedia.org或https://meta.wikimedia.org的东西。如果此参数不匹配Origin: header,就返回403错误响应。如果此参数匹配Origin: header并且起点被白名单,将设置一个Access-Control-Allow-Origin开头。",
        "apihelp-block-param-reason": "封禁的原因",
        "apihelp-block-param-anononly": "只封禁匿名用户(也就是说禁止此IP的匿名编辑)。",
        "apihelp-block-param-nocreate": "防止创建帐户。",
+       "apihelp-block-param-autoblock": "自动封禁最近使用的IP地址,以及以后他们尝试登陆使用的IP地址。",
        "apihelp-block-param-noemail": "阻止用户通过 wiki发送电子邮件。(要求\"blockemail\"权限)。",
        "apihelp-block-param-hidename": "从封禁日志中隐藏用户名。(需要“隐藏用户”权限)。",
        "apihelp-block-param-allowusertalk": "允许用户编辑自己的讨论页 (取决于 $wgBlockAllowsUTEdit)。",
        "apihelp-block-param-reblock": "如果该用户已被封禁,则覆盖已有的封禁。",
        "apihelp-block-param-watchuser": "监视该用户或该 IP 的用户页和讨论页。",
-       "apihelp-block-example-ip-simple": "封禁IP地址192.0.2.5三天,原因“首次罢工”",
+       "apihelp-block-example-ip-simple": "封禁IP地址192.0.2.5三天,原因“首次处理”",
+       "apihelp-block-example-user-complex": "无限期封禁破坏用户,原因“纯破坏用户”,并阻止新账户创建和电子邮件",
        "apihelp-clearhasmsg-description": "清除当前用户的 hasmsg 标志。",
        "apihelp-clearhasmsg-example-1": "清除当前用户的 hasmsg 标志",
        "apihelp-compare-description": "获取2个页面之间的差别。\n\n您必须为\"from\"和\"to\"传递特定的修订版本号、 页面标题或页面ID。",
@@ -56,7 +62,9 @@
        "apihelp-delete-param-pageid": "你所希望删除的页面的页面ID。不能与$1title一起使用。",
        "apihelp-delete-param-reason": "删除原因。如果未设置,将使用一个自动生成的原因。",
        "apihelp-delete-param-watch": "将该页面加入您的监视列表。",
+       "apihelp-delete-param-watchlist": "无条件地将页面加入至您的监视列表或将其移除,使用设置或不更改监视。",
        "apihelp-delete-param-unwatch": "将该页面从您的监视列表删除。",
+       "apihelp-delete-param-oldimage": "由[[Special:ApiHelp/query+imageinfo|action=query&prop=imageinfo&iiprop=archivename]]提供的要删除的旧图片名称。",
        "apihelp-delete-example-simple": "删除首页",
        "apihelp-delete-example-reason": "删除首页,原因“准备移动”",
        "apihelp-disabled-description": "此模块已禁用。",
        "apihelp-imagerotate-example-generator": "将[[:Category:Flip]]之中的所有图像旋转180度",
        "apihelp-import-param-summary": "导入摘要。",
        "apihelp-import-param-xml": "上传的XML文件。",
+       "apihelp-import-param-interwikisource": "用于跨wiki导入:导入的来源wiki。",
        "apihelp-import-param-interwikipage": "用于跨wiki导入:导入的页面。",
+       "apihelp-import-param-fullhistory": "用于跨wiki导入:完整导入历史,而不只是最新版本。",
+       "apihelp-import-param-templates": "用于跨wiki导入:连带导入所有包含的模板。",
+       "apihelp-import-param-namespace": "用于跨wiki导入:导入到此名字空间。",
        "apihelp-import-param-rootpage": "导入作为此页面的子页面。",
        "apihelp-import-example-import": "将页面[[meta:Help:Parserfunctions]]连带完整历史导入至100名字空间。",
        "apihelp-login-param-name": "用户名。",
index b36827c..ff3b25b 100644 (file)
@@ -2,6 +2,8 @@
 /**
  * JSON Content Model
  *
+ * This class requires the root structure to be an object (not primitives or arrays).
+ *
  * @file
  *
  * @author Ori Livneh <ori@wikimedia.org>
  */
 class JsonContent extends TextContent {
 
-       public function __construct( $text, $modelId = CONTENT_MODEL_JSON ) {
-               parent::__construct( $text, $modelId );
+       /**
+        * @since 1.25
+        * @var Status
+        */
+       protected $jsonParse;
+
+       /**
+        * @param string $text JSON
+        */
+       public function __construct( $text ) {
+               parent::__construct( $text, CONTENT_MODEL_JSON );
        }
 
        /**
         * Decodes the JSON into a PHP associative array.
-        * @return array
+        *
+        * @deprecated since 1.25 Use getData instead.
+        * @return array|null
         */
        public function getJsonData() {
+               wfDeprecated( __METHOD__, '1.25' );
                return FormatJson::decode( $this->getNativeData(), true );
        }
 
        /**
-        * @return bool Whether content is valid JSON.
+        * Decodes the JSON string into a PHP object.
+        *
+        * @return Status
+        */
+       public function getData() {
+               if ( $this->jsonParse === null ) {
+                       $this->jsonParse = FormatJson::parse( $this->getNativeData() );
+               }
+               return $this->jsonParse;
+       }
+
+       /**
+        * @return bool Whether content is valid.
         */
        public function isValid() {
-               return $this->getJsonData() !== null;
+               return $this->getData()->isGood() && is_object( $this->getData()->getValue() );
        }
 
        /**
-        * Pretty-print JSON
+        * Pretty-print JSON.
+        *
+        * If called before validation, it may return JSON "null".
         *
-        * @return bool|null|string
+        * @return string
         */
        public function beautifyJSON() {
-               $decoded = FormatJson::decode( $this->getNativeData(), true );
-               if ( !is_array( $decoded ) ) {
-                       return null;
-               }
-               return FormatJson::encode( $decoded, true );
-
+               return FormatJson::encode( $this->getData()->getValue(), true );
        }
 
        /**
         * Beautifies JSON prior to save.
+        *
         * @param Title $title Title
         * @param User $user User
         * @param ParserOptions $popts
         * @return JsonContent
         */
        public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
+               // FIXME: WikiPage::doEditContent invokes PST before validation. As such, native data
+               // may be invalid (though PST result is discarded later in that case).
+               if ( !$this->isValid() ) {
+                       return $this;
+               }
+
                return new static( $this->beautifyJSON() );
        }
 
        /**
-        * Set the HTML and add the appropriate styles
-        *
+        * Set the HTML and add the appropriate styles.
         *
         * @param Title $title
         * @param int $revId
@@ -71,50 +100,112 @@ class JsonContent extends TextContent {
        protected function fillParserOutput( Title $title, $revId,
                ParserOptions $options, $generateHtml, ParserOutput &$output
        ) {
-               if ( $generateHtml ) {
-                       $output->setText( $this->objectTable( $this->getJsonData() ) );
+               // FIXME: WikiPage::doEditContent generates parser output before validation.
+               // As such, native data may be invalid (though output is discarded later in that case).
+               if ( $generateHtml && $this->isValid() ) {
+                       $output->setText( $this->objectTable( $this->getData()->getValue() ) );
                        $output->addModuleStyles( 'mediawiki.content.json' );
                } else {
                        $output->setText( '' );
                }
        }
+
        /**
-        * Constructs an HTML representation of a JSON object.
-        * @param array $mapping
+        * Construct an HTML representation of a JSON object.
+        *
+        * Called recursively via valueCell().
+        *
+        * @param stdClass $mapping
         * @return string HTML
         */
        protected function objectTable( $mapping ) {
                $rows = array();
+               $empty = true;
 
                foreach ( $mapping as $key => $val ) {
                        $rows[] = $this->objectRow( $key, $val );
+                       $empty = false;
                }
-               return Xml::tags( 'table', array( 'class' => 'mw-json' ),
-                       Xml::tags( 'tbody', array(), join( "\n", $rows ) )
+               if ( $empty ) {
+                       $rows[] = Html::rawElement( 'tr', array(),
+                               Html::element( 'td', array( 'class' => 'mw-json-empty' ),
+                                       wfMessage( 'content-json-empty-object' )->text()
+                               )
+                       );
+               }
+               return Html::rawElement( 'table', array( 'class' => 'mw-json' ),
+                       Html::rawElement( 'tbody', array(), join( "\n", $rows ) )
                );
        }
 
        /**
-        * Constructs HTML representation of a single key-value pair.
+        * Construct HTML representation of a single key-value pair.
         * @param string $key
         * @param mixed $val
         * @return string HTML.
         */
        protected function objectRow( $key, $val ) {
                $th = Xml::elementClean( 'th', array(), $key );
-               if ( is_array( $val ) ) {
-                       $td = Xml::tags( 'td', array(), self::objectTable( $val ) );
-               } else {
-                       if ( is_string( $val ) ) {
-                               $val = '"' . $val . '"';
-                       } else {
-                               $val = FormatJson::encode( $val );
-                       }
+               $td = self::valueCell( $val );
+               return Html::rawElement( 'tr', array(), $th . $td );
+       }
+
+       /**
+        * Constructs an HTML representation of a JSON array.
+        *
+        * Called recursively via valueCell().
+        *
+        * @param array $mapping
+        * @return string HTML
+        */
+       protected function arrayTable( $mapping ) {
+               $rows = array();
+               $empty = true;
 
-                       $td = Xml::elementClean( 'td', array( 'class' => 'value' ), $val );
+               foreach ( $mapping as $val ) {
+                       $rows[] = $this->arrayRow( $val );
+                       $empty = false;
                }
+               if ( $empty ) {
+                       $rows[] = Html::rawElement( 'tr', array(),
+                               Html::element( 'td', array( 'class' => 'mw-json-empty' ),
+                                       wfMessage( 'content-json-empty-array' )->text()
+                               )
+                       );
+               }
+               return Html::rawElement( 'table', array( 'class' => 'mw-json' ),
+                       Html::rawElement( 'tbody', array(), join( "\n", $rows ) )
+               );
+       }
 
-               return Xml::tags( 'tr', array(), $th . $td );
+       /**
+        * Construct HTML representation of a single array value.
+        * @param mixed $val
+        * @return string HTML.
+        */
+       protected function arrayRow( $val ) {
+               $td = self::valueCell( $val );
+               return Html::rawElement( 'tr', array(), $td );
        }
 
+       /**
+        * Construct HTML representation of a single value.
+        * @param mixed $val
+        * @return string HTML.
+        */
+       protected function valueCell( $val ) {
+               if ( is_object( $val ) ) {
+                       return Html::rawElement( 'td', array(), self::objectTable( $val ) );
+               }
+               if ( is_array( $val ) ) {
+                       return Html::rawElement( 'td', array(), self::arrayTable( $val ) );
+               }
+               if ( is_string( $val ) ) {
+                       $val = '"' . $val . '"';
+               } else {
+                       $val = FormatJson::encode( $val );
+               }
+
+               return Xml::elementClean( 'td', array( 'class' => 'value' ), $val );
+       }
 }
index 6e262e8..b2527f9 100644 (file)
@@ -94,7 +94,7 @@ interface IORMTable {
        public function getSummaryFields();
 
        /**
-        * Selects the the specified fields of the records matching the provided
+        * Selects the specified fields of the records matching the provided
         * conditions and returns them as DBDataObject. Field names get prefixed.
         *
         * @see DatabaseBase::select()
@@ -113,7 +113,7 @@ interface IORMTable {
                array $options = array(), $functionName = null );
 
        /**
-        * Selects the the specified fields of the records matching the provided
+        * Selects the specified fields of the records matching the provided
         * conditions and returns them as DBDataObject. Field names get prefixed.
         *
         * @since 1.20
@@ -145,7 +145,7 @@ interface IORMTable {
                array $options = array(), $functionName = null );
 
        /**
-        * Selects the the specified fields of the records matching the provided
+        * Selects the specified fields of the records matching the provided
         * conditions and returns them as associative arrays.
         * Provided field names get prefixed.
         * Returned field names will not have a prefix.
@@ -170,7 +170,7 @@ interface IORMTable {
                array $options = array(), $collapse = true, $functionName = null );
 
        /**
-        * Selects the the specified fields of the first matching record.
+        * Selects the specified fields of the first matching record.
         * Field names get prefixed.
         *
         * @since 1.20
@@ -186,7 +186,7 @@ interface IORMTable {
                array $options = array(), $functionName = null );
 
        /**
-        * Selects the the specified fields of the records matching the provided
+        * Selects the specified fields of the records matching the provided
         * conditions. Field names do NOT get prefixed.
         *
         * @since 1.20
@@ -202,7 +202,7 @@ interface IORMTable {
                array $options = array(), $functionName = null );
 
        /**
-        * Selects the the specified fields of the first record matching the provided
+        * Selects the specified fields of the first record matching the provided
         * conditions and returns it as an associative array, or false when nothing matches.
         * This method makes use of selectFields and expects the same parameters and
         * returns the same results (if there are any, if there are none, this method returns false).
index b22df39..cada298 100644 (file)
@@ -190,7 +190,7 @@ class ORMTable extends DBAccessBase implements IORMTable {
        }
 
        /**
-        * Selects the the specified fields of the records matching the provided
+        * Selects the specified fields of the records matching the provided
         * conditions and returns them as DBDataObject. Field names get prefixed.
         *
         * @since 1.20
@@ -211,7 +211,7 @@ class ORMTable extends DBAccessBase implements IORMTable {
        }
 
        /**
-        * Selects the the specified fields of the records matching the provided
+        * Selects the specified fields of the records matching the provided
         * conditions and returns them as DBDataObject. Field names get prefixed.
         *
         * @since 1.20
@@ -296,7 +296,7 @@ class ORMTable extends DBAccessBase implements IORMTable {
        }
 
        /**
-        * Selects the the specified fields of the records matching the provided
+        * Selects the specified fields of the records matching the provided
         * conditions and returns them as associative arrays.
         * Provided field names get prefixed.
         * Returned field names will not have a prefix.
@@ -346,7 +346,7 @@ class ORMTable extends DBAccessBase implements IORMTable {
        }
 
        /**
-        * Selects the the specified fields of the first matching record.
+        * Selects the specified fields of the first matching record.
         * Field names get prefixed.
         *
         * @since 1.20
@@ -369,7 +369,7 @@ class ORMTable extends DBAccessBase implements IORMTable {
        }
 
        /**
-        * Selects the the specified fields of the records matching the provided
+        * Selects the specified fields of the records matching the provided
         * conditions. Field names do NOT get prefixed.
         *
         * @since 1.20
@@ -400,7 +400,7 @@ class ORMTable extends DBAccessBase implements IORMTable {
        }
 
        /**
-        * Selects the the specified fields of the first record matching the provided
+        * Selects the specified fields of the first record matching the provided
         * conditions and returns it as an associative array, or false when nothing matches.
         * This method makes use of selectFields and expects the same parameters and
         * returns the same results (if there are any, if there are none, this method returns false).
index 7417c6b..c54d241 100644 (file)
@@ -44,9 +44,8 @@ TXT;
  * change the service provider. If MWLogger::getInstance() is called before
  * any service provider has been registered, it will attempt to use the
  * $wgMWLoggerDefaultSpi global to bootstrap MWLoggerSpi registration.
- * $wgMWLoggerDefaultSpi can either be the name of a class implementing the
- * MWLoggerSpi interface with a zero argument constructor or a callable that
- * will return an MWLoggerSpi instance.
+ * $wgMWLoggerDefaultSpi is expected to be an array usable by
+ * ObjectFactory::getObjectFromSpec() to create a class.
  *
  * @see MWLoggerSpi
  * @since 1.25
index e7c69b8..a682504 100644 (file)
@@ -107,7 +107,7 @@ class MWLoggerLegacyLogger extends \Psr\Log\AbstractLogger {
                        // and no explicit wgDebugLogGroups configuration.
                        $shouldEmit = false;
                } else {
-                       // Default return value is the the same as the historic wfDebug
+                       // Default return value is the same as the historic wfDebug
                        // method: emit if $wgDebugLogFile has been set.
                        $shouldEmit = $wgDebugLogFile != '';
                }
@@ -324,48 +324,8 @@ class MWLoggerLegacyLogger extends \Psr\Log\AbstractLogger {
        */
        public static function emit( $text, $file ) {
                if ( substr( $file, 0, 4 ) == 'udp:' ) {
-                       # Needs the sockets extension
-                       if ( preg_match( '!^udp:(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $file, $m ) ) {
-                               // IPv6 bracketed host
-                               $host = $m[1];
-                               $port = intval( $m[2] );
-                               $prefix = isset( $m[3] ) ? $m[3] : false;
-                               $domain = AF_INET6;
-                       } elseif ( preg_match( '!^udp:(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $file, $m ) ) {
-                               $host = $m[1];
-                               if ( !IP::isIPv4( $host ) ) {
-                                       $host = gethostbyname( $host );
-                               }
-                               $port = intval( $m[2] );
-                               $prefix = isset( $m[3] ) ? $m[3] : false;
-                               $domain = AF_INET;
-                       } else {
-                               throw new MWException( __METHOD__ . ': Invalid UDP specification' );
-                       }
-
-                       // Clean it up for the multiplexer
-                       if ( strval( $prefix ) !== '' ) {
-                               $text = preg_replace( '/^/m', $prefix . ' ', $text );
-
-                               // Limit to 64KB
-                               if ( strlen( $text ) > 65506 ) {
-                                       $text = substr( $text, 0, 65506 );
-                               }
-
-                               if ( substr( $text, -1 ) != "\n" ) {
-                                       $text .= "\n";
-                               }
-                       } elseif ( strlen( $text ) > 65507 ) {
-                               $text = substr( $text, 0, 65507 );
-                       }
-
-                       $sock = socket_create( $domain, SOCK_DGRAM, SOL_UDP );
-                       if ( !$sock ) {
-                               return;
-                       }
-
-                       socket_sendto( $sock, $text, strlen( $text ), 0, $host, $port );
-                       socket_close( $sock );
+                       $transport = UDPTransport::newFromString( $file );
+                       $transport->emit( $text );
                } else {
                        wfSuppressWarnings();
                        $exists = file_exists( $file );
index c9545fa..67acf57 100644 (file)
@@ -21,7 +21,7 @@
 /**
  * Log message formatter that mimics the legacy log message formatting of
  * `wfDebug`, `wfDebugLog`, `wfLogDBError` and `wfErrorLog` global functions by
- * deligating the formatting to MWLoggerLegacyLogger.
+ * delegating the formatting to MWLoggerLegacyLogger.
  *
  * @since 1.25
  * @author Bryan Davis <bd808@wikimedia.org>
index a4c0168..30534f0 100644 (file)
@@ -189,7 +189,7 @@ class DiffOpChange extends DiffOp {
  * More ideas are taken from:
  *     http://www.ics.uci.edu/~eppstein/161/960229.html
  *
- * Some ideas are (and a bit of code) are from from analyze.c, from GNU
+ * Some ideas (and a bit of code) are from analyze.c, from GNU
  * diffutils-2.7, which can be found at:
  *     ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz
  *
index 495ac3c..06fb2c6 100644 (file)
@@ -1372,7 +1372,7 @@ abstract class FileBackendStore extends FileBackend {
 
        /**
         * Check if a container name is valid.
-        * This checks for for length and illegal characters.
+        * This checks for length and illegal characters.
         *
         * @param string $container
         * @return bool
index 62345b8..df805aa 100644 (file)
@@ -246,10 +246,7 @@ class HTMLForm extends ContextSource {
                                $this->mUseMultipart = true;
                        }
 
-                       $field = self::loadInputFromParameters( $fieldname, $info );
-                       // FIXME During field's construct, the parent form isn't available!
-                       // could add a 'parent' name-value to $info, could add a third parameter.
-                       $field->mParent = $this;
+                       $field = self::loadInputFromParameters( $fieldname, $info, $this );
 
                        // vform gets too much space if empty labels generate HTML.
                        if ( $this->isVForm() ) {
@@ -359,14 +356,18 @@ class HTMLForm extends ContextSource {
         *
         * @param string $fieldname Name of the field
         * @param array $descriptor Input Descriptor, as described above
+        * @param HTMLForm|null $parent Parent instance of HTMLForm
         *
         * @throws MWException
         * @return HTMLFormField Instance of a subclass of HTMLFormField
         */
-       public static function loadInputFromParameters( $fieldname, $descriptor ) {
+       public static function loadInputFromParameters( $fieldname, $descriptor, HTMLForm $parent = null ) {
                $class = self::getClassFromDescriptor( $fieldname, $descriptor );
 
                $descriptor['fieldname'] = $fieldname;
+               if ( $parent ) {
+                       $descriptor['parent'] = $parent;
+               }
 
                # @todo This will throw a fatal error whenever someone try to use
                # 'class' to feed a CSS class instead of 'cssclass'. Would be
index 4cf2394..861ae4c 100644 (file)
@@ -343,6 +343,10 @@ abstract class HTMLFormField {
        function __construct( $params ) {
                $this->mParams = $params;
 
+               if ( isset( $params['parent'] ) && $params['parent'] instanceof HTMLForm ) {
+                       $this->mParent = $params['parent'];
+               }
+
                # Generate the label from a message, if possible
                if ( isset( $params['label-message'] ) ) {
                        $msgInfo = $params['label-message'];
@@ -354,7 +358,7 @@ abstract class HTMLFormField {
                                $msgInfo = array();
                        }
 
-                       $this->mLabel = wfMessage( $msg, $msgInfo )->parse();
+                       $this->mLabel = $this->msg( $msg, $msgInfo )->parse();
                } elseif ( isset( $params['label'] ) ) {
                        if ( $params['label'] === '&#160;' ) {
                                // Apparently some things set &nbsp directly and in an odd format
index 5dadaf8..d1b7746 100644 (file)
@@ -96,8 +96,7 @@ class HTMLFormFieldCloner extends HTMLFormField {
                        } else {
                                $info['id'] = Sanitizer::escapeId( "{$this->mID}--$key--$fieldname" );
                        }
-                       $field = HTMLForm::loadInputFromParameters( $name, $info );
-                       $field->mParent = $this->mParent;
+                       $field = HTMLForm::loadInputFromParameters( $name, $info, $this->mParent );
                        $fields[$fieldname] = $field;
                }
                return $fields;
@@ -310,8 +309,7 @@ class HTMLFormFieldCloner extends HTMLFormField {
                                'id' => Sanitizer::escapeId( "{$this->mID}--$key--delete" ),
                                'cssclass' => 'mw-htmlform-cloner-delete-button',
                                'default' => $this->msg( $label )->text(),
-                       ) );
-                       $field->mParent = $this->mParent;
+                       ), $this->mParent );
                        $v = $field->getDefault();
 
                        if ( $displayFormat === 'table' ) {
@@ -383,8 +381,7 @@ class HTMLFormFieldCloner extends HTMLFormField {
                        'id' => Sanitizer::escapeId( "{$this->mID}--create" ),
                        'cssclass' => 'mw-htmlform-cloner-create-button',
                        'default' => $this->msg( $label )->text(),
-               ) );
-               $field->mParent = $this->mParent;
+               ), $this->mParent );
                $html .= $field->getInputHTML( $field->getDefault() );
 
                return $html;
index ea1213c..b676f45 100644 (file)
@@ -612,7 +612,7 @@ abstract class DatabaseUpdater {
         * Append a line to the open filehandle.  The line is assumed to
         * be a complete SQL statement.
         *
-        * This is used as a callback for for sourceLine().
+        * This is used as a callback for sourceLine().
         *
         * @param string $line Text to append to the file
         * @return bool False to skip actually executing the file
index 28b14fa..cb97c31 100644 (file)
@@ -1,7 +1,8 @@
 {
        "@metadata": {
                "authors": [
-                       "CERminator"
+                       "CERminator",
+                       "Palapa"
                ]
        },
        "config-desc": "Instalacija za MediaWiki",
@@ -36,7 +37,7 @@
        "config-sidebar": "* [//www.mediawiki.org MediaWiki Početna strana]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Vodič za korisnike]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Vodič za administratore]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ NPP]\n----\n* <doclink href=Readme>Pročitaj me</doclink>\n* <doclink href=ReleaseNotes>Napomene izdanja</doclink>\n* <doclink href=Copying>Kopiranje</doclink>\n* <doclink href=UpgradeDoc>Poboljšavanje</doclink>",
        "config-env-good": "Okruženje je provjereno.\nMožete instalirati MediaWiki.",
        "config-env-php": "PHP $1 je instaliran.",
-       "config-no-db": "Nije mogao biti pronađen pogodan driver za bazu podataka! Morate instalirati driver baze podataka za PHP.\nSlijedeće vrste baza podataka su podržane: $1.\n\nAko se na dijeljenom serveru, tražite od vašeg pružaoca usluga da instalira pogodan driver za bazu podataka.\nAko se sami kompajlirali PHP, podesite ga sa omogućenim klijentom baze podataka, koristeći naprimjer <code>./configure --with-mysql</code>.\nAko ste instalirali PHP iz Debian ili Ubuntu paketa, možda morate instalirati i modul php5-mysql.",
+       "config-no-db": "Nije mogao biti pronađen pogodan driver za bazu podataka! Morate instalirati driver baze podataka za PHP.\nSljedeće vrste baza podataka su podržane: $1.\n\nAko se sami kompajlirali PHP, podesite ga sa omogućenim klijentom baze podataka, koristeći naprimjer, <code>./configure --with-mysqli</code>.\nAko ste instalirali PHP iz Debian ili Ubuntu paketa, tada morate instalirati, naprimjer, i paket <code>php5-mysql</code>.",
        "config-xcache": "[http://xcache.lighttpd.net/ XCache] je instaliran",
        "config-apc": "[http://www.php.net/apc APC] je instaliran",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] je instaliran",
@@ -52,7 +53,7 @@
        "config-header-oracle": "Postavke Oracle",
        "config-invalid-db-type": "Nevaljana vrsta baze podataka",
        "config-upgrade-done": "Nadogradnja završena.\n\nSada možete [$1 početi koristiti vašu wiki].\n\nAko želite regenerisati vašu datoteku <code>LocalSettings.php</code>, kliknite na dugme ispod.\nOvo '''nije preporučeno''' osim ako nemate problema s vašom wiki.",
-       "config-admin-name": "Vaše ime:",
+       "config-admin-name": "Vaše korisničko ime:",
        "config-admin-password": "Šifra:",
        "mainpagetext": "'''MediaViki softver is uspješno instaliran.'''",
        "mainpagedocfooter": "Kontaktirajte [//meta.wikimedia.org/wiki/Help:Contents uputstva za korisnike] za informacije o upotrebi wiki programa.\n\n== Početak ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista postavki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki najčešće postavljana pitanja]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Lista E-Mail adresa MediaWiki]"
index 843fe2f..2cc85ce 100644 (file)
@@ -42,7 +42,7 @@
        "config-ns-other": "Zewbi (keyfiyo)",
        "config-ns-other-default": "MyWiki",
        "config-admin-box": "Hesabê Administratori",
-       "config-admin-name": "Namey  karberdé to:",
+       "config-admin-name": "Nameyê şımayê karberi:",
        "config-admin-password": "Parola:",
        "config-admin-password-confirm": "Fına parola:",
        "config-admin-email": "Adresa e-postey:",
index 056e5a8..5e8399c 100644 (file)
@@ -221,7 +221,7 @@ class JobQueueDB extends JobQueue {
                }
 
                $rowSet = array(); // (sha1 => job) map for jobs that are de-duplicated
-               $rowList = array(); // list of jobs for jobs that are are not de-duplicated
+               $rowList = array(); // list of jobs for jobs that are not de-duplicated
                foreach ( $jobs as $job ) {
                        $row = $this->insertFields( $job );
                        if ( $job->ignoreDuplicates() ) {
index 4d1e72c..b4ddd11 100644 (file)
@@ -26,9 +26,9 @@
  *
  * This job comes in a few variants:
  *   - a) Recursive jobs to purge caches for backlink pages for a given title.
- *        These jobs have have (recursive:true,table:<table>) set.
+ *        These jobs have (recursive:true,table:<table>) set.
  *   - b) Jobs to purge caches for a set of titles (the job title is ignored).
- *           These jobs have have (pages:(<page ID>:(<namespace>,<title>),...) set.
+ *           These jobs have (pages:(<page ID>:(<namespace>,<title>),...) set.
  *
  * @ingroup JobQueue
  */
@@ -67,7 +67,7 @@ class HTMLCacheUpdateJob extends Job {
                                array( 'params' => $this->getRootJobParams() )
                        );
                        JobQueueGroup::singleton()->push( $jobs );
-               // Job to purge pages for for a set of titles
+               // Job to purge pages for a set of titles
                } elseif ( isset( $this->params['pages'] ) ) {
                        $this->invalidateTitles( $this->params['pages'] );
                // B/C for job to purge a range of backlink pages for a given page
index f82af27..5d95792 100644 (file)
@@ -26,9 +26,9 @@
  *
  * This job comes in a few variants:
  *   - a) Recursive jobs to update links for backlink pages for a given title.
- *        These jobs have have (recursive:true,table:<table>) set.
+ *        These jobs have (recursive:true,table:<table>) set.
  *   - b) Jobs to update links for a set of pages (the job title is ignored).
- *           These jobs have have (pages:(<page ID>:(<namespace>,<title>),...) set.
+ *           These jobs have (pages:(<page ID>:(<namespace>,<title>),...) set.
  *   - c) Jobs to update links for a single page (the job title)
  *        These jobs need no extra fields set.
  *
@@ -86,7 +86,7 @@ class RefreshLinksJob extends Job {
                                array( 'params' => $extraParams )
                        );
                        JobQueueGroup::singleton()->push( $jobs );
-               // Job to update link tables for for a set of titles
+               // Job to update link tables for a set of titles
                } elseif ( isset( $this->params['pages'] ) ) {
                        foreach ( $this->params['pages'] as $pageId => $nsAndKey ) {
                                list( $ns, $dbKey ) = $nsAndKey;
@@ -157,7 +157,7 @@ class RefreshLinksJob extends Job {
                        $ellapsed = microtime( true ) - $start;
                        // If it took a long time to render, then save this back to the cache to avoid
                        // wasted CPU by other apaches or job runners. We don't want to always save to
-                       // cache as this cause cause high cache I/O and LRU churn when a template changes.
+                       // cache as this can cause high cache I/O and LRU churn when a template changes.
                        if ( $ellapsed >= self::PARSE_THRESHOLD_SEC
                                && $page->isParserCacheUsed( $parserOptions, $revision->getId() )
                                && $parserOutput->isCacheable()
index f09d101..8bd4656 100644 (file)
@@ -60,10 +60,10 @@ class RunningStat implements Countable {
        /** @var float The second central moment (or variance). **/
        public $m2 = 0.0;
 
-       /** @var float The least value in the the set. **/
+       /** @var float The least value in the set. **/
        public $min = INF;
 
-       /** @var float The most value in the set. **/
+       /** @var float The greatest value in the set. **/
        public $max = NEGATIVE_INF;
 
        /**
@@ -129,7 +129,7 @@ class RunningStat implements Countable {
         * Get the estimated standard deviation.
         *
         * The standard deviation of a statistical population is the square root of
-        * its variance. It shows shows how much variation from the mean exists. In
+        * its variance. It shows how much variation from the mean exists. In
         * addition to expressing the variability of a population, the standard
         * deviation is commonly used to measure confidence in statistical conclusions.
         *
index 629c269..1ec9eaa 100644 (file)
@@ -32,12 +32,12 @@ class ScopedCallback {
        protected $params;
 
        /**
-        * @param callable $callback
+        * @param callable|null $callback
         * @param array $params Callback arguments (since 1.25)
         * @throws Exception
         */
        public function __construct( $callback, array $params = array() ) {
-               if ( !is_callable( $callback ) ) {
+               if ( $callback !== null && !is_callable( $callback ) ) {
                        throw new InvalidArgumentException( "Provided callback is not valid." );
                }
                $this->callback = $callback;
diff --git a/includes/libs/UDPTransport.php b/includes/libs/UDPTransport.php
new file mode 100644 (file)
index 0000000..7fad882
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * A generic class to send a message over UDP
+ *
+ * If a message prefix is provided to the constructor or via
+ * UDPTransport::newFromString(), the payload of the UDP datagrams emitted
+ * will be formatted with the prefix and a single space at the start of each
+ * line. This is the payload format expected by the udp2log service.
+ *
+ * @since 1.25
+ */
+class UDPTransport {
+       private $host, $port, $prefix, $domain;
+
+       /**
+        * @param string $host IP address to send to
+        * @param int $port port number
+        * @param int $domain AF_INET or AF_INET6 constant
+        * @param string|bool $prefix Prefix to use, false for no prefix
+        */
+       public function __construct( $host, $port, $domain, $prefix = false ) {
+               $this->host = $host;
+               $this->port = $port;
+               $this->domain = $domain;
+               $this->prefix = $prefix;
+       }
+
+       /**
+        * @param string $info In the format of "udp://host:port/prefix"
+        * @return UDPTransport
+        * @throws InvalidArgumentException
+        */
+       public static function newFromString( $info ) {
+               if ( preg_match( '!^udp:(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $info, $m ) ) {
+                       // IPv6 bracketed host
+                       $host = $m[1];
+                       $port = intval( $m[2] );
+                       $prefix = isset( $m[3] ) ? $m[3] : false;
+                       $domain = AF_INET6;
+               } elseif ( preg_match( '!^udp:(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $info, $m ) ) {
+                       $host = $m[1];
+                       if ( !IP::isIPv4( $host ) ) {
+                               $host = gethostbyname( $host );
+                       }
+                       $port = intval( $m[2] );
+                       $prefix = isset( $m[3] ) ? $m[3] : false;
+                       $domain = AF_INET;
+               } else {
+                       throw new InvalidArgumentException( __METHOD__ . ': Invalid UDP specification' );
+               }
+
+               return new self( $host, $port, $domain, $prefix );
+       }
+
+       /**
+        * @param string $text
+        */
+       public function emit( $text ) {
+               // Clean it up for the multiplexer
+               if ( $this->prefix !== false ) {
+                       $text = preg_replace( '/^/m', $this->prefix . ' ', $text );
+
+                       // Limit to 64KB
+                       if ( strlen( $text ) > 65506 ) {
+                               $text = substr( $text, 0, 65506 );
+                       }
+
+                       if ( substr( $text, -1 ) != "\n" ) {
+                               $text .= "\n";
+                       }
+               } elseif ( strlen( $text ) > 65507 ) {
+                       $text = substr( $text, 0, 65507 );
+               }
+
+               $sock = socket_create( $this->domain, SOCK_DGRAM, SOL_UDP );
+               if ( !$sock ) { // @todo should this throw an exception?
+                       return;
+               }
+
+               socket_sendto( $sock, $text, strlen( $text ), 0, $this->host, $this->port );
+               socket_close( $sock );
+       }
+}
index 0f718d2..fa70c63 100644 (file)
@@ -29,7 +29,7 @@ have been changed or you remove it.
 == Testing ==
 
 'make test' will run the conformance test (UtfNormalTest.php), fetching the
-data from from the net if necessary. If it reports failure, something is
+data from the net if necessary. If it reports failure, something is
 going wrong!
 
 You may have to set up PHPUnit first.
index b9f99c8..b4409c3 100644 (file)
@@ -175,7 +175,7 @@ class ImagePage extends Article {
 
                # Show shared description, if needed
                if ( $this->mExtraDescription ) {
-                       $fol = wfMessage( 'shareddescriptionfollows' );
+                       $fol = $this->getContext()->msg( 'shareddescriptionfollows' );
                        if ( !$fol->isDisabled() ) {
                                $out->addWikiText( $fol->plain() );
                        }
@@ -188,7 +188,7 @@ class ImagePage extends Article {
 
                $out->addHTML( Xml::element( 'h2',
                        array( 'id' => 'filelinks' ),
-                       wfMessage( 'imagelinks' )->text() ) . "\n" );
+                               $this->getContext()->msg( 'imagelinks' )->text() ) . "\n" );
                $this->imageDupes();
                # @todo FIXME: For some freaky reason, we can't redirect to foreign images.
                # Yet we return metadata about the target. Definitely an issue in the FileRepo
@@ -205,7 +205,7 @@ class ImagePage extends Article {
                        $out->addHTML( Xml::element(
                                'h2',
                                array( 'id' => 'metadata' ),
-                               wfMessage( 'metadata' )->text() ) . "\n" );
+                                       $this->getContext()->msg( 'metadata' )->text() ) . "\n" );
                        $out->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
                        $out->addModules( array( 'mediawiki.action.view.metadata' ) );
                }
@@ -237,12 +237,12 @@ class ImagePage extends Article {
         */
        protected function showTOC( $metadata ) {
                $r = array(
-                       '<li><a href="#file">' . wfMessage( 'file-anchor-link' )->escaped() . '</a></li>',
-                       '<li><a href="#filehistory">' . wfMessage( 'filehist' )->escaped() . '</a></li>',
-                       '<li><a href="#filelinks">' . wfMessage( 'imagelinks' )->escaped() . '</a></li>',
+                       '<li><a href="#file">' . $this->getContext()->msg( 'file-anchor-link' )->escaped() . '</a></li>',
+                       '<li><a href="#filehistory">' . $this->getContext()->msg( 'filehist' )->escaped() . '</a></li>',
+                       '<li><a href="#filelinks">' . $this->getContext()->msg( 'imagelinks' )->escaped() . '</a></li>',
                );
                if ( $metadata ) {
-                       $r[] = '<li><a href="#metadata">' . wfMessage( 'metadata' )->escaped() . '</a></li>';
+                       $r[] = '<li><a href="#metadata">' . $this->getContext()->msg( 'metadata' )->escaped() . '</a></li>';
                }
 
                Hooks::run( 'ImagePageShowTOC', array( $this, &$r ) );
@@ -260,7 +260,7 @@ class ImagePage extends Article {
         */
        protected function makeMetadataTable( $metadata ) {
                $r = "<div class=\"mw-imagepage-section-metadata\">";
-               $r .= wfMessage( 'metadata-help' )->plain();
+               $r .= $this->getContext()->msg( 'metadata-help' )->plain();
                $r .= "<table id=\"mw_metadata\" class=\"mw_metadata\">\n";
                foreach ( $metadata as $type => $stuff ) {
                        foreach ( $stuff as $v ) {
@@ -341,14 +341,14 @@ class ImagePage extends Article {
                        if ( $this->displayImg->allowInlineDisplay() ) {
                                # image
                                # "Download high res version" link below the image
-                               # $msgsize = wfMessage( 'file-info-size', $width_orig, $height_orig,
+                               # $msgsize = $this->getContext()->msg( 'file-info-size', $width_orig, $height_orig,
                                #   Linker::formatSize( $this->displayImg->getSize() ), $mime )->escaped();
                                # We'll show a thumbnail of this image
                                if ( $width > $maxWidth || $height > $maxHeight || $this->displayImg->isVectorized() ) {
                                        list( $width, $height ) = $this->getDisplayWidthHeight(
                                                $maxWidth, $maxHeight, $width, $height
                                        );
-                                       $linktext = wfMessage( 'show-big-image' )->escaped();
+                                       $linktext = $this->getContext()->msg( 'show-big-image' )->escaped();
 
                                        $thumbSizes = $this->getThumbSizes( $width, $height, $width_orig, $height_orig );
                                        # Generate thumbnails or thumbnail links as needed...
@@ -377,14 +377,14 @@ class ImagePage extends Article {
                                        $msgsmall = '';
                                        $sizeLinkBigImagePreview = $this->makeSizeLink( $params, $width, $height );
                                        if ( $sizeLinkBigImagePreview ) {
-                                               $msgsmall .= wfMessage( 'show-big-image-preview' )->
+                                               $msgsmall .= $this->getContext()->msg( 'show-big-image-preview' )->
                                                        rawParams( $sizeLinkBigImagePreview )->
                                                        parse();
                                        }
                                        if ( count( $otherSizes ) ) {
                                                $msgsmall .= ' ' .
                                                Html::rawElement( 'span', array( 'class' => 'mw-filepage-other-resolutions' ),
-                                                       wfMessage( 'show-big-image-other' )->rawParams( $lang->pipeList( $otherSizes ) )->
+                                                       $this->getContext()->msg( 'show-big-image-other' )->rawParams( $lang->pipeList( $otherSizes ) )->
                                                        params( count( $otherSizes ) )->parse()
                                                );
                                        }
@@ -394,7 +394,7 @@ class ImagePage extends Article {
                                        $msgsmall = '';
                                } else {
                                        # Image is small enough to show full size on image page
-                                       $msgsmall = wfMessage( 'file-nohires' )->parse();
+                                       $msgsmall = $this->getContext()->msg( 'file-nohires' )->parse();
                                }
 
                                $params['width'] = $width;
@@ -428,7 +428,7 @@ class ImagePage extends Article {
                                        $count = $this->displayImg->pageCount();
 
                                        if ( $page > 1 ) {
-                                               $label = $out->parse( wfMessage( 'imgmultipageprev' )->text(), false );
+                                               $label = $out->parse( $this->getContext()->msg( 'imgmultipageprev' )->text(), false );
                                                // on the client side, this link is generated in ajaxifyPageNavigation()
                                                // in the mediawiki.page.image.pagination module
                                                $link = Linker::linkKnown(
@@ -450,7 +450,7 @@ class ImagePage extends Article {
                                        }
 
                                        if ( $page < $count ) {
-                                               $label = wfMessage( 'imgmultipagenext' )->text();
+                                               $label = $this->getContext()->msg( 'imgmultipagenext' )->text();
                                                $link = Linker::linkKnown(
                                                        $this->getTitle(),
                                                        $label,
@@ -487,8 +487,8 @@ class ImagePage extends Article {
                                                '</td><td><div class="multipageimagenavbox">' .
                                                Xml::openElement( 'form', $formParams ) .
                                                Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
-                                                       wfMessage( 'imgmultigoto' )->rawParams( $select )->parse() .
-                                               Xml::submitButton( wfMessage( 'imgmultigo' )->text() ) .
+                                               $this->getContext()->msg( 'imgmultigoto' )->rawParams( $select )->parse() .
+                                               Xml::submitButton( $this->getContext()->msg( 'imgmultigo' )->text() ) .
                                                Xml::closeElement( 'form' ) .
                                                "<hr />$thumb1\n$thumb2<br style=\"clear: both\" /></div></td></tr></table>"
                                        );
@@ -502,12 +502,12 @@ class ImagePage extends Article {
                                        "</div>\n" );
                        }
 
-                       $longDesc = wfMessage( 'parentheses', $this->displayImg->getLongDesc() )->text();
+                       $longDesc = $this->getContext()->msg( 'parentheses', $this->displayImg->getLongDesc() )->text();
 
                        $medialink = "[[Media:$filename|$linktext]]";
 
                        if ( !$this->displayImg->isSafeFile() ) {
-                               $warning = wfMessage( 'mediawarning' )->plain();
+                               $warning = $this->getContext()->msg( 'mediawarning' )->plain();
                                // dirmark is needed here to separate the file name, which
                                // most likely ends in Latin characters, from the description,
                                // which may begin with the file type. In RTL environment
@@ -619,7 +619,7 @@ EOT
                        return Html::rawElement( 'a', array(
                                'href' => $thumbnail->getUrl(),
                                'class' => 'mw-thumbnail-link'
-                               ), wfMessage( 'show-big-image-size' )->numParams(
+                               ), $this->getContext()->msg( 'show-big-image-size' )->numParams(
                                        $thumbnail->getWidth(), $thumbnail->getHeight()
                                )->parse() );
                } else {
@@ -645,9 +645,9 @@ EOT
                $wrap = "<div class=\"sharedUploadNotice\">\n$1\n</div>\n";
                $repo = $this->mPage->getFile()->getRepo()->getDisplayName();
 
-               if ( $descUrl && $descText && wfMessage( 'sharedupload-desc-here' )->plain() !== '-' ) {
+               if ( $descUrl && $descText && $this->getContext()->msg( 'sharedupload-desc-here' )->plain() !== '-' ) {
                        $out->wrapWikiMsg( $wrap, array( 'sharedupload-desc-here', $repo, $descUrl ) );
-               } elseif ( $descUrl && wfMessage( 'sharedupload-desc-there' )->plain() !== '-' ) {
+               } elseif ( $descUrl && $this->getContext()->msg( 'sharedupload-desc-there' )->plain() !== '-' ) {
                        $out->wrapWikiMsg( $wrap, array( 'sharedupload-desc-there', $repo, $descUrl ) );
                } else {
                        $out->wrapWikiMsg( $wrap, array( 'sharedupload', $repo ), ''/*BACKCOMPAT*/ );
@@ -694,7 +694,7 @@ EOT
                ) {
                        $ulink = Linker::makeExternalLink(
                                $this->getUploadUrl(),
-                               wfMessage( 'uploadnewversion-linktext' )->text()
+                               $this->getContext()->msg( 'uploadnewversion-linktext' )->text()
                        );
                        $out->addHTML( "<li id=\"mw-imagepage-reupload-link\">"
                                . "<div class=\"plainlinks\">{$ulink}</div></li>\n" );
@@ -832,7 +832,7 @@ EOT
                                $liContents = $link;
                        } elseif ( count( $redirects[$element->page_title] ) === 0 ) {
                                # Redirect without usages
-                               $liContents = wfMessage( 'linkstoimage-redirect' )->rawParams( $link, '' )->parse();
+                               $liContents = $this->getContext()->msg( 'linkstoimage-redirect' )->rawParams( $link, '' )->parse();
                        } else {
                                # Redirect with usages
                                $li = '';
@@ -855,7 +855,7 @@ EOT
                                        array( 'class' => 'mw-imagepage-redirectstofile' ),
                                        $li
                                        ) . "\n";
-                               $liContents = wfMessage( 'linkstoimage-redirect' )->rawParams(
+                               $liContents = $this->getContext()->msg( 'linkstoimage-redirect' )->rawParams(
                                        $link, $ul )->parse();
                        }
                        $out->addHTML( Html::rawElement(
@@ -901,7 +901,7 @@ EOT
                        } else {
                                $link = Linker::makeExternalLink( $file->getDescriptionUrl(),
                                        $file->getTitle()->getPrefixedText() );
-                               $fromSrc = wfMessage( 'shared-repo-from', $file->getRepo()->getDisplayName() )->text();
+                               $fromSrc = $this->getContext()->msg( 'shared-repo-from', $file->getRepo()->getDisplayName() )->text();
                        }
                        $out->addHTML( "<li>{$link} {$fromSrc}</li>\n" );
                }
@@ -930,7 +930,7 @@ EOT
         */
        function showError( $description ) {
                $out = $this->getContext()->getOutput();
-               $out->setPageTitle( wfMessage( 'internalerror' ) );
+               $out->setPageTitle( $this->getContext()->msg( 'internalerror' ) );
                $out->setRobotPolicy( 'noindex,nofollow' );
                $out->setArticleRelated( false );
                $out->enableClientCache( false );
@@ -1008,7 +1008,7 @@ EOT
                        $code = wfBCP47( $lang );
                        $name = Language::fetchLanguageName( $code, $this->getContext()->getLanguage()->getCode() );
                        if ( $name !== '' ) {
-                               $display = wfMessage( 'img-lang-opt', $code, $name )->text();
+                               $display = $this->getContext()->msg( 'img-lang-opt', $code, $name )->text();
                        } else {
                                $display = $code;
                        }
@@ -1024,7 +1024,7 @@ EOT
                        // Its hard to know if the content is really in the default language, or
                        // if its just unmarked content that could be in any language.
                        $opts = Xml::option(
-                               wfMessage( 'img-lang-default' )->text(),
+                                       $this->getContext()->msg( 'img-lang-default' )->text(),
                                $defaultLang,
                                $defaultLang === $curLang
                        ) . $opts;
@@ -1032,7 +1032,7 @@ EOT
                if ( !$haveCurrentLang && $defaultLang !== $curLang ) {
                        $name = Language::fetchLanguageName( $curLang, $this->getContext()->getLanguage()->getCode() );
                        if ( $name !== '' ) {
-                               $display = wfMessage( 'img-lang-opt', $curLang, $name )->text();
+                               $display = $this->getContext()->msg( 'img-lang-opt', $curLang, $name )->text();
                        } else {
                                $display = $curLang;
                        }
@@ -1044,9 +1044,9 @@ EOT
                        array( 'id' => 'mw-imglangselector', 'name' => 'lang' ),
                        $opts
                );
-               $submit = Xml::submitButton( wfMessage( 'img-lang-go' )->text() );
+               $submit = Xml::submitButton( $this->getContext()->msg( 'img-lang-go' )->text() );
 
-               $formContents = wfMessage( 'img-lang-info' )->rawParams( $select, $submit )->parse()
+               $formContents = $this->getContext()->msg( 'img-lang-info' )->rawParams( $select, $submit )->parse()
                        . Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() );
 
                $langSelectLine = Html::rawElement( 'div', array( 'id' => 'mw-imglangselector-line' ),
index b9f7eda..41dc802 100644 (file)
@@ -395,7 +395,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @param string|int $from One of the following:
         *        - "fromdb" or WikiPage::READ_NORMAL if the data comes from a slave DB
         *        - "fromdbmaster" or WikiPage::READ_LATEST if the data comes from the master DB
-        *        - "forupdate"  or WikiPage::READ_LOCKING if the data comes from from
+        *        - "forupdate"  or WikiPage::READ_LOCKING if the data comes from
         *          the master DB using SELECT FOR UPDATE
         */
        public function loadFromRow( $data, $from ) {
@@ -975,7 +975,7 @@ class WikiPage implements Page, IDBAccessObject {
                                $source = $this->mTitle->getFullURL( 'redirect=no' );
                                return $rt->getFullURL( array( 'rdfrom' => $source ) );
                        } else {
-                               // External pages pages without "local" bit set are not valid
+                               // External pages without "local" bit set are not valid
                                // redirect targets
                                return false;
                        }
index 6f19a23..e820fe2 100644 (file)
@@ -111,7 +111,7 @@ class CoreParserFunctions {
 
                $pref = $parser->getOptions()->getDateFormat();
 
-               // Specify a different default date format other than the the normal default
+               // Specify a different default date format other than the normal default
                // if the user has 'default' for their setting
                if ( $pref == 'default' && $defaultPref ) {
                        $pref = $defaultPref;
index 5c8253a..a9daa22 100644 (file)
@@ -4862,6 +4862,7 @@ class Parser {
 
                $pairs = array(
                        "\r\n" => "\n",
+                       "\r" => "\n",
                );
                $text = str_replace( array_keys( $pairs ), array_values( $pairs ), $text );
                if ( $options->getPreSaveTransform() ) {
index 428e7b2..1a2be5f 100644 (file)
@@ -861,7 +861,7 @@ class ParserOutput extends CacheTime {
        }
 
        /**
-        * Save space for for serialization by removing useless values
+        * Save space for serialization by removing useless values
         * @return array
         */
        public function __sleep() {
index 9a7ec8c..1d77cc0 100644 (file)
@@ -34,9 +34,7 @@ class ProfilerStub extends Profiler {
        }
 
        public function scopedProfileIn( $section ) {
-               return new ScopedCallback( function () {
-                       // no-op
-               } );
+               return new ScopedCallback( null ); // no-op
        }
 
        public function getFunctionStats() {
index a40c44a..624433b 100644 (file)
@@ -139,8 +139,13 @@ class ProfilerXhprof extends Profiler {
 
                // Merge in all of the custom profile sections
                foreach ( $this->sprofiler->getFunctionStats() as $stats ) {
+                       if ( $stats['name'] === '-total' ) {
+                               // Discard section profiler running totals
+                               continue;
+                       }
+
                        // @note: getFunctionStats() values already in ms
-                       $stats['%real'] = $stats['real'] / $main['real'];
+                       $stats['%real'] = $main['real'] ? $stats['real'] / $main['real'] * 100 : 0;
                        $stats['%cpu'] = $main['cpu'] ? $stats['cpu'] / $main['cpu'] * 100 : 0;
                        $stats['%memory'] = $main['memory'] ? $stats['memory'] / $main['memory'] * 100 : 0;
                        $profile[] = $stats; // assume no section names collide with $metrics
index 2c36b68..d5da928 100644 (file)
@@ -67,8 +67,7 @@ class SectionProfiler {
        public function scopedProfileIn( $section ) {
                $this->profileInInternal( $section );
 
-               $that = $this;
-               return new ScopedCallback( $this->profileOutCallback, array( $that, $section ) );
+               return new SectionProfileCallback( $this, $section );
        }
 
        /**
@@ -502,3 +501,29 @@ class SectionProfiler {
                }
        }
 }
+
+/**
+ * Subclass ScopedCallback to avoid call_user_func_array(), which is slow
+ *
+ * This class should not be used outside of SectionProfiler
+ */
+class SectionProfileCallback extends ScopedCallback {
+       /** @var SectionProfiler */
+       protected $profiler;
+       /** @var string */
+       protected $section;
+
+       /**
+        * @param SectionProfiler $profiler
+        * @param string $section
+        */
+       public function __construct( SectionProfiler $profiler, $section ) {
+               parent::__construct( null );
+               $this->profiler = $profiler;
+               $this->section = $section;
+       }
+
+       function __destruct() {
+               $this->profiler->profileOutInternal( $this->section );
+       }
+}
index 8554670..9afae66 100644 (file)
@@ -29,6 +29,7 @@ class UDPRCFeedEngine implements RCFeedEngine {
         * @see RCFeedEngine::send
         */
        public function send( array $feed, $line ) {
-               wfErrorLog( $line, $feed['uri'] );
+               $transport = UDPTransport::newFromString( $feed['uri'] );
+               $transport->emit( $line );
        }
 }
index a0764de..c79554a 100644 (file)
@@ -293,7 +293,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
        }
 
        /**
-        * Base modules required for the the base environment of ResourceLoader
+        * Base modules required for the base environment of ResourceLoader
         *
         * @return array
         */
@@ -409,8 +409,8 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                // ATTENTION!: Because of the line below, this is not going to cause
                // infinite recursion - think carefully before making changes to this
                // code!
-               // Pre-populate modifiedTime with something because the the loop over
-               // all modules below includes the the startup module (this module).
+               // Pre-populate modifiedTime with something because the loop over
+               // all modules below includes the startup module (this module).
                $this->modifiedTime[$hash] = 1;
 
                foreach ( $loader->getModuleNames() as $name ) {
index d4f8167..b9f2024 100644 (file)
@@ -115,7 +115,7 @@ class RevisionDeleter {
         * "revdelete-restricted", "revdelete-unrestricted" indicating (un)suppression
         * or null to indicate nothing in particular.
         * You can turn the keys in $arr[0] and $arr[1] into message keys by
-        * appending -hid and and -unhid to the keys respectively.
+        * appending -hid and -unhid to the keys respectively.
         *
         * @param int $n The new bitfield.
         * @param int $o The old bitfield.
index a61a673..65ddb31 100644 (file)
@@ -135,14 +135,13 @@ class SpecialJavaScriptTest extends SpecialPage {
         */
        private function initQUnitTesting() {
                $out = $this->getOutput();
-               $testConfig = $this->getConfig()->get( 'JavaScriptTestConfig' );
 
                $out->addModules( 'test.mediawiki.qunit.testrunner' );
                $qunitTestModules = $out->getResourceLoader()->getTestModuleNames( 'qunit' );
                $out->addModules( $qunitTestModules );
 
                $summary = $this->msg( 'javascripttest-qunit-intro' )
-                       ->params( $testConfig['qunit']['documentation'] )
+                       ->params( 'https://www.mediawiki.org/wiki/Manual:JavaScript_unit_testing' )
                        ->parseAsBlock();
                $header = $this->msg( 'javascripttest-qunit-heading' )->escaped();
                $userDir = $this->getLanguage()->getDir();
@@ -160,16 +159,9 @@ HTML;
                $out->addHtml( $this->wrapSummaryHtml( $summary, 'frameworkfound' ) . $baseHtml );
 
                // This special page is disabled by default ($wgEnableJavaScriptTest), and contains
-               // no sensitive data. In order to allow TestSwarm to embed it into a test client window,
+               // no sensitive data. In order to allow test frameworks to embed it into a test client window,
                // we need to allow iframing of this page.
                $out->allowClickjacking();
-
-               // Used in ./tests/qunit/data/testrunner.js, see also documentation of
-               // $wgJavaScriptTestConfig in DefaultSettings.php
-               $out->addJsConfigVars(
-                       'QUnitTestSwarmInjectJSPath',
-                       $testConfig['qunit']['testswarm-injectjs']
-               );
        }
 
        /**
index 98d8da3..3666964 100644 (file)
@@ -25,7 +25,7 @@
  */
 
 /**
- * A special page page that list most used images
+ * A special page that lists most used images
  *
  * @ingroup SpecialPage
  */
index c77c786..a7dea88 100644 (file)
@@ -330,9 +330,9 @@ class SpecialSearch extends SpecialPage {
                        Xml::openElement( 'div', array( 'id' => 'mw-search-top-table' ) ) .
                        $this->shortDialog( $term, $num, $totalRes ) .
                        Xml::closeElement( 'div' ) .
+                       $this->searchProfileTabs( $term ) .
                        Xml::closeElement( 'form' ) .
-                       $this->didYouMeanHtml .
-                       $this->searchProfileTabs( $term )
+                       $this->didYouMeanHtml
                );
 
                $filePrefix = $wgContLang->getFormattedNsText( NS_FILE ) . ':';
index 7a907fc..73f14f9 100644 (file)
@@ -47,7 +47,8 @@
                        "Kuwaity26",
                        "Calak",
                        "Omda4wady",
-                       "Bibas"
+                       "Bibas",
+                       "Khaled"
                ]
        },
        "tog-underline": "سطر تحت الوصلات:",
        "history-feed-empty": "الصفحة المطلوبة غير موجودة.\nمن المحتمل أن تكون هذه الصفحة قد حذفت من الويكي، أو نقلت.\nحاول [[Special:Search|البحث في الويكي]] عن صفحات جديدة ذات صلة.",
        "rev-deleted-comment": "(أزيل ملخص التعديل)",
        "rev-deleted-user": "(اسم المستخدم تمت إزالته)",
-       "rev-deleted-event": "(Ù\81عÙ\84 Ø§Ù\84سجÙ\84 ØªÙ\85ت Ø¥Ø²Ø§Ù\84تÙ\87)",
+       "rev-deleted-event": "(Ù\85Ø­Ù\8aت ØªÙ\81اصÙ\8aÙ\84 Ø§Ù\84سجÙ\84)",
        "rev-deleted-user-contribs": "[اسم المستخدم أو عنوان الأيبي تمت إزالته - التعديل مخفي من المساهمات]",
        "rev-deleted-text-permission": "'''حُذِفت''' مراجعة هذه الصفحة.\nيمكنك العثور على التفاصيل في [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سجل الحذف].",
        "rev-suppressed-text-permission": "هذه النسخة قد <strong>أخفيت</strong> ([{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} تفاصيل].)",
        "revdelete-legend": "وضع ضوابط رؤية",
        "revdelete-hide-text": "نص المراجعة",
        "revdelete-hide-image": "أخف محتوى الملف",
-       "revdelete-hide-name": "أخÙ\81 Ø§Ù\84Ù\81عÙ\84 Ù\88اÙ\84Ù\87دÙ\81",
+       "revdelete-hide-name": "أخÙ\81 Ø§Ù\84Ù\87دÙ\81 Ù\88اÙ\84Ù\85عÙ\8aار",
        "revdelete-hide-comment": "ملخص التعديل",
        "revdelete-hide-user": "اسم المستخدم/عنوان الآيبي",
        "revdelete-hide-restricted": "أخف البيانات عن الإداريين إضافة إلى الآخرين",
        "right-browsearchive": "البحث في الصفحات المحذوفة",
        "right-undelete": "استرجاع صفحة",
        "right-suppressrevision": "مراجعة واسترجاع المراجعات المخفية عن مديري النظام",
+       "right-viewsuppressed": "أعرض المراجعات المخفية بواسطة أي مستخدم",
        "right-suppressionlog": "رؤية السجلات السرية",
        "right-block": "منع المستخدمين الآخرين من التعديل",
        "right-blockemail": "منع مستخدم من إرسال بريد إلكتروني",
        "right-protect": "تغيير مستويات الحماية وتعديل الصفحات المحمية",
        "right-editprotected": "تعديل الصفحات التي حمايتها \"{{int:protect-level-sysop}}\"",
        "right-editsemiprotected": "تعديل الصفحات التي حمايتها \"{{int:protect-level-autoconfirmed}}\"",
+       "right-editcontentmodel": "عدل طريقة محتوى صفحة",
        "right-editinterface": "تعديل واجهة المستخدم",
        "right-editusercssjs": "تعديل ملفات CSS و JS للمستخدمين الآخرين",
        "right-editusercss": "تعديل ملفات CSS للمستخدمين الآخرين",
        "action-viewmywatchlist": "مشاهدة قائمة مراقبتك",
        "action-viewmyprivateinfo": "مشاهدة معلوماتك الخاصة",
        "action-editmyprivateinfo": "تعديل معلوماتك الخاصة",
+       "action-editcontentmodel": "عدل عدل طريقة محتوى صفحة",
        "nchanges": "{{PLURAL:$1|لا تغييرات|تغيير واحد|تغييران|$1 تغييرات|$1 تغييرا|$1 تغيير}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|منذ الزيارة الأخيرة}}",
        "enhancedrc-history": "تاريخ",
        "thumbnail-temp-create": "تعذر إنشاء ملف الصورة المصغرة المؤقت",
        "thumbnail-dest-create": "تعذر حفظ الصورة المصغرة للوجهة",
        "thumbnail_invalid_params": "محددات التصغير غير صحيحة",
+       "thumbnail_toobigimagearea": "ملف أبعداه أكبر من $1",
        "thumbnail_dest_directory": "غير قادر على إنشاء المجلد الهدف",
        "thumbnail_image-type": "نوع الصورة غير مدعوم",
        "thumbnail_gd-library": "ضبط مكتبة GD غير مكتمل: دالة مفقودة $1",
        "tooltip-pt-mycontris": "قائمة مساهماتك",
        "tooltip-pt-login": "يفضل أن تسجل الدخول، لكنه ليس إلزاميا.",
        "tooltip-pt-logout": "تسجيل الخروج",
+       "tooltip-pt-createaccount": "نشجعك على عمل حساب وتسجيل دخولك; لكنه غير ضروري على اي حال",
        "tooltip-ca-talk": "نقاش عن صفحة المحتوى",
        "tooltip-ca-edit": "يمكنك تعديل هذه الصفحة.\nمن فضلك استخدم زر العرض المسبق قبل الحفظ.",
        "tooltip-ca-addsection": "ابدأ قسما جديدا",
        "timezone-utc": "ت ع م",
        "unknown_extension_tag": "وسم امتداد غير معروف \"$1\"",
        "duplicate-defaultsort": "'''تحذير:''' مفتاح الترتيب الافتراضي \"$2\" يتجاوز مفتاح الترتيب الافتراضي السابق \"$1\".",
+       "duplicate-displaytitle": "<strong>تحذير:</strong> أعرض عنوان \"$2\" تجاهل العنوان المعروض سابقا \"$1\".",
        "version": "نسخة",
        "version-extensions": "الامتدادات المثبتة",
        "version-skins": "الواجهات المنصبة",
        "api-error-stashfailed": "خطأ داخلي: فشل الملقم في تخزين الملفات المؤقتة.",
        "api-error-publishfailed": "خطأ داخلي: لم ينجح الخادوم في نشر ملف مؤقت",
        "api-error-stasherror": "حدث خطأ أثناء رفع الملف لتخزينه.",
+       "api-error-stashnotloggedin": "يجب عليك تسجيل الدخول لحفظ الملفات في مرفوعاتك.",
+       "api-error-stashwrongowner": "الملف الذي كنت تحاول الوصول اليه في مخبوائتك ليس لك.",
+       "api-error-stashnosuchfilekey": "الملف الذي كنت تحاول الوصول اليه في مخبوائتك غير موجود.",
        "api-error-timeout": "لم يستجب الملقم في الوقت المتوقع.",
        "api-error-unclassified": "حدث خطأ غير معروف",
        "api-error-unknown-code": "خطأ غير معروف : \" $1 \"",
        "right-pagelang": "تغيير لغة الصفحة",
        "action-pagelang": "تغيير لغة الصفحة",
        "log-name-pagelang": "تغيير سجل الصفحة",
+       "log-description-pagelang": "هذا سجل تغيرات في صفحة اللغات.",
        "logentry-pagelang-pagelang": " {{GENDER:$2|غيّر|غيّرت}} $1 لغة الصفحة «$3» من $4 إلى $5.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (مفعل)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''معطل''')",
        "mediastatistics-table-totalbytes": "الحجم المدمج",
        "mediastatistics-header-unknown": "غير معروف",
        "mediastatistics-header-bitmap": "صور Bitmap",
+       "mediastatistics-header-drawing": "رسم (صور متجهية)",
        "mediastatistics-header-audio": "صوت",
        "mediastatistics-header-video": "مقاطع الفيديو",
        "mediastatistics-header-multimedia": "ريتش ميديا",
        "mediastatistics-header-text": "نصي",
        "mediastatistics-header-executable": "تنفيذية",
        "mediastatistics-header-archive": "صيغ مضغوطة",
-       "json-error-syntax": "خطأ صياغة"
+       "json-error-syntax": "خطأ صياغة",
+       "json-error-unsupported-type": "نمط قيمة لا يمكن تشفيره قد أعطي"
 }
index 1b350c0..108a997 100644 (file)
        "anoneditwarning": "<strong>Папярэджаньне</strong>: вы не ўвайшлі ў сыстэму. Ваш IP-адрас будзе бачны ўсім, калі вы адрэдагуеце старонку. Калі вы <strong>[$1 ўвойдзеце]</strong> або <strong>[$2 створыце рахунак]</strong>, вашыя рэдагаваньні будуць зьвязаныя з вашым імем карыстальніка, а таксама вам будуць даступныя дадатковыя перавагі.",
        "anonpreviewwarning": "''Вы не ўвайшлі ў сыстэму. Падчас захаваньня Ваш IP-адрас будзе дададзены ў гісторыю рэдагаваньняў старонкі.''",
        "missingsummary": "'''Напамін:''' Вы не пазначылі кароткае апісаньне зьменаў.\nКалі Вы націсьніце кнопку «Запісаць» яшчэ раз, Вашае рэдагаваньне будзе запісанае без апісаньня.",
-       "selfredirect": "<strong>Папярэджаньне:</strong> вы ствараеце перанакіраваньне на гэты самы артыкул.\nКалі вы націсьніце «{{int:savearticle}}» яшчэ раз, перанакіраваньне будзе створанае.",
+       "selfredirect": "<strong>Папярэджаньне:</strong> вы перанакіроўваеце старонку саму на сябе.\nМагчыма, вы пазначылі няслушную старонку для перанакіраваньня або вы рэдагуеце ня тую старонку.\nКалі вы націсьніце «{{int:savearticle}}» яшчэ раз, перанакіраваньне будзе створанае.",
        "missingcommenttext": "Калі ласка, увядзіце камэнтар ніжэй.",
        "missingcommentheader": "'''Напамін:''' Вы не пазначылі загаловак камэнтара.\nКалі Вы націсьніце кнопку «{{int:savearticle}}» яшчэ раз, Ваш камэнтар захаваецца бяз тэмы.",
        "summary-preview": "Папярэдні прагляд апісаньня:",
        "history-feed-empty": "Запатрабаванай старонкі не існуе.\nМагчыма, яна была выдаленая альбо яе перанесьлі.\nПаспрабуйце [[Special:Search|пашукаць]] падобныя старонкі.",
        "rev-deleted-comment": "(апісаньне зьменаў выдаленае)",
        "rev-deleted-user": "(імя ўдзельніка выдаленае)",
-       "rev-deleted-event": "(запÑ\96Ñ\81 Ð· Ð¶Ñ\83Ñ\80нала Ð¿Ð°Ð´Ð·ÐµÑ\8fÑ\9e Ð²Ñ\8bдаленÑ\8b)",
+       "rev-deleted-event": "(падÑ\80абÑ\8fзнаÑ\81Ñ\8cÑ\86Ñ\96 Ð²Ñ\8bдаленÑ\8bÑ\8f Ð· Ð¶Ñ\83Ñ\80нала Ð¿Ð°Ð´Ð·ÐµÑ\8fÑ\9e)",
        "rev-deleted-user-contribs": "[імя ўдзельніка альбо IP-адрас выдалены — рэдагаваньне схаванае з унёску]",
        "rev-deleted-text-permission": "Гэтая вэрсія старонкі была '''выдаленая'''.\nМагчыма, падрабязнасьці могуць быць знойдзеныя ў [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журнале выдаленьняў].",
        "rev-suppressed-text-permission": "Гэтая вэрсія старонкі была <strong>схаваная</strong>.\nПадрабязнасьці могуць быць знойдзеныя ў [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} журнале хаваньняў].",
        "revdelete-legend": "Усталяваць абмежаваньні бачнасьці",
        "revdelete-hide-text": "Тэкст вэрсіі",
        "revdelete-hide-image": "Схаваць зьмест файла",
-       "revdelete-hide-name": "СÑ\85аваÑ\86Ñ\8c Ð´Ð·ÐµÑ\8fнÑ\8cне Ñ\96 Ð¼Ñ\8dÑ\82Ñ\83",
+       "revdelete-hide-name": "СÑ\85аваÑ\86Ñ\8c Ð¼Ñ\8dÑ\82Ñ\83 Ñ\96 Ð¿Ð°Ñ\80амÑ\8dÑ\82Ñ\80Ñ\8b",
        "revdelete-hide-comment": "Апісаньне зьменаў",
        "revdelete-hide-user": "Імя ўдзельніка/IP-адрас",
        "revdelete-hide-restricted": "Ужываць гэтыя абмежаваньні таксама і для адміністратараў",
        "thumbnail-temp-create": "Немагчыма стварыць часовы файл мініятуры",
        "thumbnail-dest-create": "Немагчыма захаваць мініятуру ў месцы прызначэньня",
        "thumbnail_invalid_params": "Няслушныя парамэтры мініятуры",
+       "thumbnail_toobigimagearea": "Файл з памерамі большымі, чым $1",
        "thumbnail_dest_directory": "Немагчыма стварыць мэтавую дырэкторыю",
        "thumbnail_image-type": "Тып выявы не падтрымліваецца",
        "thumbnail_gd-library": "Няпоўная канфігурацыя бібліятэкі GD: няма функцыі $1",
index 7f11733..5e137af 100644 (file)
        "filerenameerror": "Ne može se promjeniti ime datoteke \"$1\" u \"$2\".",
        "filedeleteerror": "Ne može se izbrisati datoteka \"$1\".",
        "directorycreateerror": "Nije moguće napraviti direktorijum \"$1\".",
+       "directoryreadonlyerror": "Direktorij \"$1\" je samo za čitanje.",
        "directorynotreadableerror": "Direktorij \"$1\" nije čitljiv.",
        "filenotfound": "Ne može se naći datoteka \"$1\".",
        "unexpected": "Neočekivana vrijednost: \"$1\"=\"$2\".",
        "viewsourcetext": "Možete vidjeti i kopirati izvorni tekst ove stranice:",
        "viewyourtext": "Možete da pogledate i kopirate izvor '''vaših izmjena''' na ovoj stranici:",
        "protectedinterface": "Ova stranica sadrži tekst korisničkog okruženja za softver na ovom wikiju i zaštićena je radi sprečavanja zloupotrebe.\nDa biste dodali ili izmjenili prijevode svih wikija, posjetite [//translatewiki.net/  translatewiki.net], projekat za lokalizaciju Mediawikija.",
-       "editinginterface": "'''Upozorenje:''' Mijenjate stranicu koja sadrži aktivan tekst programa.\nPromjene na ovoj stranici dovode i do promjena za druge korisnike ovog wikija.\nZa dodavanje ili promjene prijevoda svih wikija, molimo Vas koristite [//translatewiki.net/ translatewiki.net], projekt prijevoda za MediaWiki.",
+       "editinginterface": "<strong>Upozorenje:</strong> Mijenjate stranicu koja sadrži aktivan tekst programa.\nPromjene na ovoj stranici dovode i do promjena za druge korisnike ovog wikija.\nZa dodavanje ili promjene prijevoda za sve wikije, molimo Vas koristite [//translatewiki.net/ translatewiki.net], projekt prijevoda za MediaWiki.",
+       "translateinterface": "Za dodavanje ili promjenu prijevoda za sve wikije koristite [//translatewiki.net/ translatewiki.net], projekt za lokalizaciju MediaWikija.",
        "cascadeprotected": "Uređivanje ove stranice je zabranjeno jer sadrži {{PLURAL:$1|stranicu zaštićenu|stranice zaštićene}} od uređivanja iz razloga:\n$2",
        "namespaceprotected": "Vi nemate dozvulu da mijenjate stranicu '''$1'''.",
        "customcssprotected": "Nemate dozvolu za mijenjanje ove CSS stranice jer sadrži osobne postavke nekog drugog korisnika.",
        "badretype": "Šifre koje ste unijeli se ne poklapaju.",
        "userexists": "Korisničko ime koje ste unijeli je već u upotrebi.\nMolimo Vas da izaberete drugo ime.",
        "loginerror": "Greška pri prijavljivanju",
+       "createacct-error": "Došlo je do greške pri otvaranju naloga",
        "createaccounterror": "Ne može se napraviti račun: $1",
        "nocookiesnew": "Korisnički nalog je napravljen, ali niste prijavljeni. {{SITENAME}} koristi kolačiće (''cookies'') da bi se korisnici prijavili.  Vi ste onemogućili kolačiće na Vašem računaru. Molimo Vas da ih omogućite, a onda se prijavite sa svojim novim korisničkim imenom i šifrom.",
        "nocookieslogin": "{{SITENAME}} koristi kolačiće (''cookies'') da bi se korisnici prijavili.  Vi ste onemogućili kolačiće na Vašem kompjuteru.  Molimo Vas da ih omogućite i da pokušate ponovo sa prijavom.",
        "user-mail-no-addy": "Pokušaj slanja e-maila bez navedene e-mail adrese.",
        "user-mail-no-body": "Pokušano slanje e-poruke s praznim ili nerazumno kratkim sadržajem.",
        "changepassword": "Promijeni lozinku",
-       "resetpass_announce": "Prijavili ste se sa privremenim kodom koji ste dobili na e-mail.\nDa biste završili prijavu, morate unijeti novu šifru ovdje:",
+       "resetpass_announce": "Da biste završili prijavu, morate postaviti novu šifru.",
        "resetpass_text": "<!-- Unesi tekst ovdje -->",
        "resetpass_header": "Obnovi šifru za račun",
        "oldpassword": "Stara šifra:",
        "changeemail-none": "(ništa)",
        "changeemail-password": "Tvoja šifra/lozinka za {{SITENAME}}:",
        "changeemail-submit": "Promijeni e-mail",
+       "changeemail-throttled": "Previše puta ste se pokušali prijaviti.\nMolimo Vas da sačekate $1 prije nego što pokušate ponovo.",
+       "resettokens": "Resetovanje žetona",
+       "resettokens-no-tokens": "Nema žetona za resetovanje.",
+       "resettokens-legend": "Resetovanje žetona",
+       "resettokens-tokens": "Žetoni:",
        "resettokens-token-label": "$1 (trenutna vrijednost: $2)",
+       "resettokens-done": "Žetoni su resetovani",
+       "resettokens-resetbutton": "Resetuj izabrane žetone",
        "bold_sample": "Podebljan tekst",
        "bold_tip": "Podebljan tekst",
        "italic_sample": "Kurzivan tekst",
        "preview": "Pregled stranice",
        "showpreview": "Prikaži izgled",
        "showdiff": "Prikaži izmjene",
-       "anoneditwarning": "'''Upozorenje:''' Niste prijavljeni. \nVaša IP adresa će biti zabilježena u historiji ove stranice.",
+       "anoneditwarning": "<strong>Upozorenje:</strong> Niste prijavljeni. \nVaša IP adresa će biti javno vidljiva ako napravite neku izmjenu. Ako se <strong>[$1 prijavite]</strong> ili <strong>[$2 napravite račun]</strong>, vaše izmjene će biti pripisane vašem korisničkom imenu, zajedno sa drugim pogodnostima.",
        "anonpreviewwarning": "''Niste prijavljeni. Nakon spremanja izmjena vaša IP adresa će biti zapisana u historiji uređivanja ove stranice.''",
        "missingsummary": "'''Napomena:''' Niste unijeli sažetak izmjene.\nAko kliknete na Sačuvaj, Vaša izmjena će biti sačuvana bez sažetka.",
        "missingcommenttext": "Molimo unesite komentar ispod.",
        "parser-template-recursion-depth-warning": "Dubina uključivanja šablona prekoračena ($1)",
        "language-converter-depth-warning": "Prekoračena granica dubine jezičkog pretvarača ($1)",
        "node-count-exceeded-category": "Stranice sa prekoračenim brojem čvorova",
-       "node-count-exceeded-warning": "Stranice koje imaju prevelik broj čvorova",
+       "node-count-exceeded-warning": "Stranice koje su prekoračile broj čvorova",
        "expansion-depth-exceeded-category": "Stranice koje su prekoračile dubinu proširenja",
        "expansion-depth-exceeded-warning": "Stranice koje su prekoračile dubinu proširenja",
        "parser-unstrip-loop-warning": "Pronađena petlja",
        "currentrev": "Trenutna revizija",
        "currentrev-asof": "Trenutna revizija na dan $1",
        "revisionasof": "Revizija od $1",
-       "revision-info": "Izmjena od $1 korisnika $2",
+       "revision-info": "Izmjena od $1 od {{GENDER:$6|$2}}$7",
        "previousrevision": "←Starije izmjene",
        "nextrevision": "Novija izmjena →",
        "currentrevisionlink": "Trenutna verzija",
        "revdelete-show-file-submit": "Da",
        "logdelete-selected": "{{PLURAL:$1|Označena stavka zapisa|Označene stavke zapisa}}:",
        "revdelete-confirm": "Molimo potvrdite da namjeravate ovo učiniti, da razumijete posljedice i da to činite u skladu s [[{{MediaWiki:Policy-url}}|pravilima]].",
-       "revdelete-suppress-text": "Ograničenja bi trebala biti korištena '''samo''' u slijedećim slučajevima:\n* Osjetljive korisničke informacije\n*: ''kućne adrese, brojevi telefona, brojevi bankovnih kartica itd.''",
+       "revdelete-suppress-text": "Ograničenja bi trebala biti korištena <strong>samo</strong> u sijedećim slučajevima:\n* Potencijalni klevetnički podaci\n* Osjetljive korisničke informacije\n*: <em>kućne adrese, brojevi telefona, brojevi bankovnih kartica itd.</em>",
        "revdelete-legend": "Postavi ograničenja vidljivosti",
        "revdelete-hide-text": "Tekst revizije",
        "revdelete-hide-image": "Sakrij sadržaj datoteke",
-       "revdelete-hide-name": "Sakrij akciju i cilj",
+       "revdelete-hide-name": "Sakrij cilj i parametre",
        "revdelete-hide-comment": "Uredi sažetak",
        "revdelete-hide-user": "Korisničko ime urednika/IP",
        "revdelete-hide-restricted": "Ograniči podatke za administratore kao i za druge korisnike",
        "search-redirect": "(preusmjeravanje $1)",
        "search-section": "(sekcija $1)",
        "search-category": "(kategorija $1)",
+       "search-file-match": "(podudara se sadržaj datoteke)",
        "search-suggest": "Da li ste mislili: $1",
        "search-interwiki-caption": "Srodni projekti",
        "search-interwiki-default": "$1 rezultati:",
        "prefs-email": "E-mail opcije",
        "prefs-rendering": "Izgled",
        "saveprefs": "Sačuvaj",
-       "restoreprefs": "Vrati sve pretpostavljene postavke",
+       "restoreprefs": "Vrati sve pretpostavljene postavke (u svim sekcijama)",
        "prefs-editing": "Veličine tekstualnog polja",
        "rows": "Redova",
        "columns": "Kolona",
        "gender-unknown": "Ne previše detaljno",
        "gender-male": "On uređuje wiki stranice",
        "gender-female": "Ona uređuje wiki stranice",
-       "prefs-help-gender": "Optionalno: koristi se za ispravke gramatičkog roda u porukama softvera. Ova informacija će biti javna.",
+       "prefs-help-gender": "Postavljanje ovih podešavanja je optionalno: Softver koristi ove vrijednosti za vaše naslovljanje i ispravke gramatičkog roda u porukama softvera. Ova informacija bit će javna.",
        "email": "E-mail",
-       "prefs-help-realname": "Pravo ime nije obavezno.\nAko izaberete da date ime, biće korišteno za pripisivanje za vaš rad.",
+       "prefs-help-realname": "Pravo ime nije obavezno.\nAko izaberete da date ime, biće korišteno za pripisivanje vašem radu.",
        "prefs-help-email": "E-mail adresa je opcionalna, ali je potrebna jer omogućava da Vam se pošalje nova šifra u slučaju da je izgubite ili zaboravite.",
        "prefs-help-email-others": "Također možete da odaberete da vas drugi kontaktiraju putem vaše korisničke stranice ili stranice za razgovor bez otkrivanja vašeg identiteta.",
        "prefs-help-email-required": "Neophodno je navesti e-mail adresu.",
        "prefs-advancedwatchlist": "Napredne opcije",
        "prefs-displayrc": "Postavke izgleda",
        "prefs-displaywatchlist": "Postavke izgleda",
+       "prefs-tokenwatchlist": "Žeton",
        "prefs-diffs": "Razlike",
        "prefs-help-prefershttps": "Ova podešavanja će stupiti na snagu pri sljedećoj prijavi.",
        "email-address-validity-valid": "Izgleda valjano",
        "right-move": "Preusmjeravanje stranica",
        "right-move-subpages": "Preusmjeravanje stranica sa svim podstranicama",
        "right-move-rootuserpages": "Premještanje stranica osnovnih korisnika",
+       "right-move-categorypages": "Pomakni stranice kategorije",
        "right-movefile": "Premještanje datoteka",
        "right-suppressredirect": "Ne pravi preusmjeravanje sa starog imena pri preusmjeravanju stranica",
        "right-upload": "Postavljanje datoteka",
        "right-reupload-shared": "Postavljanje novih lokalnih verzija datoteka identičnih onima u zajedničkoj ostavi",
        "right-upload_by_url": "Postavljanje datoteke sa URL adrese",
        "right-purge": "Osvježavanje keša za stranice bez konfirmacije",
-       "right-autoconfirmed": "Uređivanje poluzaštićenih stranica",
+       "right-autoconfirmed": "Bez ograničavanja stavki za IP adrese",
        "right-bot": "Postavljen kao automatski proces",
        "right-nominornewtalk": "Male izmjene na stranici za razgovor ne uzrokuju prikazivanje oznake ''nova poruka'' na stranici za razgovor",
        "right-apihighlimits": "Korištenje viših ograničenja u API upitima",
        "right-deletedtext": "Pregled obrisanog teksta i izmjena između obrisanih revizija",
        "right-browsearchive": "Pretraživanje obrisanih stranica",
        "right-undelete": "Vraćanje obrisanih stranica",
-       "right-suppressrevision": "Pregled i povratak revizija sakrivenih od administratora",
+       "right-suppressrevision": "Pregled, sakrivanje i povratak određenih revizija stranice od svih korisnika",
        "right-suppressionlog": "Gledanje privatnih zapisa",
        "right-block": "Blokiranje uređivanja drugih korisnika",
        "right-blockemail": "Blokiranje korisnika da šalje e-mail",
        "action-move": "pomjerite ovu stranicu",
        "action-move-subpages": "pomjerite ovu stranicu, i njene podstranice",
        "action-move-rootuserpages": "pomjerite stranice osnovnog korisnika",
+       "action-move-categorypages": "pomakni stranice kategorije",
        "action-movefile": "pomjeri ovu datoteku",
        "action-upload": "postavljate ovu datoteku",
        "action-reupload": "stavite novu verziju postojeće datoteke",
        "action-block": "blokirate uređivanje ovog korisnika",
        "action-protect": "promijeniti nivo zaštite za ovu stranicu",
        "action-rollback": "brzo vraćanje izmjena zadnjeg korisnika koji je uređivao određenu stranicu",
-       "action-import": "uvozite ovu stranicu iz druge wiki",
-       "action-importupload": "uvezete ovu stranicu putem postavljanja datoteke",
+       "action-import": "uvozite stranice iz druge wiki",
+       "action-importupload": "uvoz stranica putem postavljanja datoteke",
        "action-patrol": "označite izmjene drugih kao patrolirane",
        "action-autopatrol": "da Vaše izmjene budu označene kao patrolirane",
        "action-unwatchedpages": "pregledate spisak nepraćenih stranica",
        "recentchanges-label-plusminus": "Veličina stranice promijenila se za ovoliko bajtova",
        "recentchanges-legend-heading": "'''Legenda:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (također pogledajte [[Special:NewPages|spisak novih stranica]])",
-       "rcnotefrom": "Ispod su izmjene od <strong>$2</strong> (do <strong>$1</strong> prikazano).",
+       "rcnotefrom": "Ispod {{PLURAL:$5|je izmjena|su izmjene}} od <strong>$3, $4</strong> (do <strong>$1</strong> prikazano).",
        "rclistfrom": "Prikaži nove izmjene počev od $3 $2",
        "rcshowhideminor": "$1 male izmjene",
        "rcshowhideminor-show": "Pokaži",
        "largefileserver": "Ova datoteka je veća nego što server dopušta.",
        "emptyfile": "Datoteka koju ste poslali je prazna. Ovo je moguće zbog greške u imenu datoteke. Molimo Vas da provjerite da li stvarno želite da pošaljete ovu datoteku.",
        "windows-nonascii-filename": "Ova wiki ne podržava imena datoteka sa posebnim znacima.",
-       "fileexists": "Datoteka sa ovim imenom već postoji.\nMolimo Vas da provjerite <strong>[[:$1]]</strong> ako niste sigurni da li želite da je promjenite.\n[[$1|thumb]]",
+       "fileexists": "Datoteka sa ovim imenom već postoji, molimo Vas provjerite <strong>[[:$1]]</strong> ako {{GENDER:|niste}} sigurni da je želite promjeniti.\n[[$1|thumb]]",
        "filepageexists": "Opis stranice za ovu datoteku je već napravljen ovdje <strong>[[:$1]]</strong>, ali datoteka sa ovim nazivom trenutno ne postoji.\nSažetak koji ste naveli neće se pojaviti na stranici opisa.\nDa bi se Vaš opis ovdje našao, potrebno je da ga ručno uredite.\n[[$1|thumb]]",
-       "fileexists-extension": "Datoteka sa sličnim nazivom postoji: [[$2|thumb]]\n* Naziv datoteke koja se postavlja: <strong>[[:$1]]</strong>\n* Naziv postojeće datoteke: <strong>[[:$2]]</strong>\nMolimo Vas da izaberete drugačiji naziv.",
+       "fileexists-extension": "Datoteka sa sličnim nazivom postoji: [[$2|thumb]]\n* Naziv datoteke koja se postavlja: <strong>[[:$1]]</strong>\n* Naziv postojeće datoteke: <strong>[[:$2]]</strong>\nDa li možda želite koristiti drugačiji naziv?",
        "fileexists-thumbnail-yes": "Izgleda da je datoteka slika smanjene veličine ''(\"thumbnail\")''. [[$1|thumb]]\nMolimo provjerite datoteku <strong>[[:$1]]</strong>.\nAko je provjerena datoteka ista slika originalne veličine, nije potrebno postavljati dodatnu sliku.",
        "file-thumbnail-no": "Naziv datoteke počinje sa <strong>$1</strong>.\nIzgleda da se radi o smanjenoj slici ''(\"thumbnail\")''.\nAko imate ovu sliku u punoj rezoluciji, postavite nju; ili promijenite naslov ove datoteke.",
        "fileexists-forbidden": "Datoteka s ovim imenom već postoji i ne može biti prepisana.\nAko i dalje želite postaviti ovu datoteku, molimo Vas da se vratite i pošaljete ovu datoteku pod novim imenom. [[File:$1|thumb|center|$1]]",
        "license-header": "Licenciranje",
        "nolicense": "Ništa nije odabrano",
        "license-nopreview": "(Pregled nije dostupan)",
-       "upload_source_url": " (validni, javno dostupni URL)",
-       "upload_source_file": " (datoteka na Vašem računaru)",
+       "upload_source_url": "(vaša izabrana datoteka od validnih, javno dostupnih URL-a)",
+       "upload_source_file": "(vaša odabrana datoteka sa Vašeg računara)",
        "listfiles-delete": "obriši",
        "listfiles-summary": "Ova posebna stranica prikazuje sve postavljene datoteke.",
        "listfiles_search_for": "Traži medije po imenu:",
        "filedelete-maintenance": "Brisanje i povratak datoteka je privremeno onemogućen tokom održavanja.",
        "filedelete-maintenance-title": "Ne mogu da obrišem datoteku",
        "mimesearch": "MIME pretraga",
-       "mimesearch-summary": "Ova stranica omogućava filtriranje datoteka prema njihovoj MIME vrsti.\nUlazni podaci: vrstasadržaja/subvrsta, npr. <code>image/jpeg</code>.",
+       "mimesearch-summary": "Ova stranica omogućava filtriranje datoteka prema njihovoj MIME vrsti.\nUlazni podaci: vrstasadržaja/subvrsta ili vrstasadržaja/*, npr. <code>image/jpeg</code>.",
        "mimetype": "MIME tip:",
        "download": "učitaj",
        "unwatchedpages": "Nepraćene stranice",
        "randompage": "Slučajna stranica",
        "randompage-nopages": "Nema stranica u {{PLURAL:$2|slijedećem imenskom prostoru|slijedećim imenskim prostorima}}: \"$1\".",
        "randomincategory": "Slučajna stranica u kategoriji",
+       "randomincategory-invalidcategory": "\"$1\" nije valjano ime kategorije.",
        "randomincategory-nopages": "Nema stranica u kategoriji [[:Category:$1|$1]].",
        "randomincategory-category": "Kategorija:",
        "randomincategory-legend": "Slučajna stranica u kategoriji",
        "pageswithprop-submit": "Idi",
        "doubleredirects": "Dvostruka preusmjerenja",
        "doubleredirectstext": "Ova stranica prikazuje stranice koje preusmjeravaju na druga preusmjerenja.\nSvaki red sadrži veze na prvo i drugo preusmjerenje, kao i na prvu liniju teksta drugog preusmjerenja, što obično daje \"pravi\" ciljni članak, na koji bi prvo preusmjerenje i trebalo da pokazuje.\n<del>Precrtane</del> stavke su riješene.",
-       "double-redirect-fixed-move": "[[$1]] je premješten, sada je preusmjerenje na [[$2]]",
-       "double-redirect-fixed-maintenance": "Ispravljanje dvostrukih preusmjerenja sa [[$1]] na [[$2]].",
+       "double-redirect-fixed-move": "[[$1]] je premješten.\nAutomatski je ažuriran i sada preusmjerava na [[$2]].",
+       "double-redirect-fixed-maintenance": "Automatsko ospravljanje dvostrukih preusmjerenja sa [[$1]] na [[$2]] je posao održavanja.",
        "double-redirect-fixer": "Popravljač preusmjerenja",
        "brokenredirects": "Pokvarena preusmjerenja",
        "brokenredirectstext": "Slijedeća preusmjerenja vode na nepostojeće stranice:",
        "wantedtemplates": "Potrebni šabloni",
        "mostlinked": "Članci sa najviše linkova",
        "mostlinkedcategories": "Kategorije sa najviše linkova",
-       "mostlinkedtemplates": "Najviše upotrebljavani šabloni",
+       "mostlinkedtemplates": "Najviše uključene stranice",
        "mostcategories": "Članci sa najviše kategorija",
        "mostimages": "Najviše linkovane slike",
        "mostrevisions": "Članci sa najviše izmjena",
        "protectedpages-cascade": "Samo prenosive zaštite",
        "protectedpages-noredirect": "Sakrij preusmjerenja",
        "protectedpagesempty": "Trenutno nijedna stranica nije zaštićena ovim parametrima.",
+       "protectedpages-timestamp": "Vremenska oznaka",
        "protectedpages-page": "Stranica",
        "protectedpages-expiry": "Istječe",
+       "protectedpages-performer": "Zaštita korisnika",
+       "protectedpages-params": "Parametri zaštite",
        "protectedpages-reason": "Razlog",
        "protectedpages-unknown-timestamp": "Nepoznato",
        "protectedpages-unknown-performer": "Nepoznati korisnik",
        "protectedtitles": "Zaštićeni naslovi",
+       "protectedtitles-summary": "Na ovoj stranici se nalazi spisak trenutno zaštićenih naslova. Za spisak trenutno zaštićenih stranica vidi [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Nijedan naslov članka trenutno nije zaštićen ovim parametrima.",
        "listusers": "Spisak korisnika",
        "listusers-editsonly": "Pokaži samo korisnike koji su uređivali",
        "listusers-creationsort": "Sortiraj po datumu pravljenja",
+       "listusers-desc": "Sortiraj u opadajućem redoslijedu",
        "usereditcount": "$1 {{PLURAL:$1|izmjena|izmjene}}",
        "usercreated": "{{GENDER:$3|Napravio|Napravila}} dana $1 u $2",
        "newpages": "Nove stranice",
        "pager-older-n": "{{PLURAL:$1|starija 1|starije $1}}",
        "suppress": "Nazdor",
        "querypage-disabled": "Ova posebna stranica je onemogućena jer smanjuje performanse.",
+       "apihelp": "API pomoć",
+       "apihelp-no-such-module": "Modul \"$1\" nije pronađen.",
        "booksources": "Štampani izvori",
        "booksources-search-legend": "Traži književne izvore",
        "booksources-search": "Traži",
        "listgrouprights-removegroup-self": "Može ukloniti {{PLURAL:$2|grupu|grupe|grupa}} sa svog računa: $1",
        "listgrouprights-addgroup-self-all": "Može dodati sve grupe na svoj račun",
        "listgrouprights-removegroup-self-all": "Može ukloniti sve grupe sa svog računa",
+       "listgrouprights-namespaceprotection-header": "Ograničenja imenskog prostora",
        "listgrouprights-namespaceprotection-namespace": "Imenski prostor",
+       "listgrouprights-namespaceprotection-restrictedto": "Prava kojima se dozvoljava korisniku da uređuje",
+       "trackingcategories": "Praćenje kategorija",
+       "trackingcategories-msg": "Praćenje kategorije",
        "trackingcategories-name": "Ime poruke",
+       "trackingcategories-desc": "Kriterij uključenja kategorije",
        "trackingcategories-nodesc": "Opis nije dostupan.",
+       "trackingcategories-disabled": "Kategorija je onemogućena",
        "mailnologin": "Nema adrese za slanje",
        "mailnologintext": "Morate biti [[Special:UserLogin|prijavljeni]]\ni imati ispravnu adresu e-pošte u vašim [[Special:Preferences|podešavanjima]]\nda biste slali e-poštu drugim korisnicima.",
        "emailuser": "Pošalji e-poštu ovom korisniku",
        "mywatchlist": "Praćeni članci",
        "watchlistfor2": "Za $1 $2",
        "nowatchlist": "Nemate ništa na svom spisku praćenih članaka.",
-       "watchlistanontext": "Molimo da $1 da možete vidjeti ili urediti stavke na Vašem spisku praćenja.",
+       "watchlistanontext": "Molimo logujte se da vidite ili uredite stavke na Vašem spisku praćenja.",
        "watchnologin": "Niste prijavljeni",
        "addwatch": "Dodaj na spisak praćenja",
        "addedwatchtext": "Stranica \"[[:$1]]\" je dodata vašem [[Special:Watchlist|spisku praćenih članaka]]. \nBuduće promjene ove stranice i njoj pridružene stranice za razgovor će biti navedene ovde.",
        "unwatchthispage": "Ukinite praćenje",
        "notanarticle": "Nije članak",
        "notvisiblerev": "Revizija je obrisana",
-       "watchlist-details": "{{PLURAL:$1|$1 stranica praćena|$1 stranice praćene|$1 stranica praćeno}} ne računajući stranice za razgovor.",
+       "watchlist-details": "{{PLURAL:$1|$1 stranica|$1 stranice|$1 stranica }} na vašem spisku praćenja, ne računajući posebno stranice za razgovor.",
        "wlheader-enotif": "Obavještavanje e-poštom je omogućeno.",
        "wlheader-showupdated": "Stranice koje su izmijenjene od kad ste ih posljednji put posjetili su prikazane '''podebljanim slovima'''",
-       "wlnote": "Ispod je {{PLURAL:$1|najskorija izmjena|'''$1''' najskorije izmjene|'''$1''' najskorijih izmjena}} načinjenih {{PLURAL:$2|posljednjeg sata|u posljednjih '''$2''' sata|u posljednjih '''$2''' sati}}, od $3, $4.",
+       "wlnote": "Ispod {{PLURAL:$1|je najskorija izmjena|su <strong>$1</strong> najskorije izmjene|<strong>$1</strong> najskorijih izmjena}} načinjenih {{PLURAL:$2|posljednjeg sata|u posljednjih <strong>$2</strong> sata|u posljednjih <strong>$2</strong> sati}}, od $3, $4.",
        "wlshowlast": "Prikaži posljednjih $1 sati $2 dana",
        "watchlist-options": "Opcije spiska praćenja",
        "watching": "Pratim...",
        "enotif_lastvisited": "Pogledajte $1 za sve izmjene od vaše posljednje posjete.",
        "enotif_lastdiff": "Vidi $1 da pregledate ovu promjenu.",
        "enotif_anon_editor": "anonimni korisnik $1",
-       "enotif_body": "Poštovani $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nSažetak urednika: $PAGESUMMARY $PAGEMINOREDIT\n\nKontaktirajte urednika:\ne-pošta: $PAGEEDITOR_EMAIL\nwiki: $PAGEEDITOR_WIKI\n\nNeće biti drugih obavještenja u slučaju daljnjih izmjena osima ako posjetite stranicu. Također možete poništiti oznake obavijesti za sve praćene stranice koje imate na vašem spisku praćenja.\n\nVaš prijateljski {{SITENAME}} sistem obavještavanja\n\n--\nZa promjenu vaših postavki e-mail obavijesti, posjetite\n{{canonicalurl:{{#special:Preferences}}}}\n\nZa promjenu postavki vašeg praćenja, posjetite\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nDa obrišete stranicu sa vašeg spiska praćenja, posjetite\n$UNWATCHURL\n\nPovratne informacije i daljnja pomoć:\n$HELPPAGE",
+       "enotif_body": "Poštovani $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nSažetak urednika: $PAGESUMMARY $PAGEMINOREDIT\n\nKontaktirajte urednika:\ne-pošta: $PAGEEDITOR_EMAIL\nwiki: $PAGEEDITOR_WIKI\n\nNeće biti drugih obavještenja u slučaju daljnjih izmjena osim ako prijavljeni ponovno posjetite stranicu. Također možete poništiti oznake obavijesti za sve praćene stranice koje imate na vašem spisku praćenja.\n\nVaš prijateljski {{SITENAME}} sistem obavještavanja\n\n--\nZa promjenu vaših postavki email obavijesti, posjetite\n{{canonicalurl:{{#special:Preferences}}}}\n\nZa promjenu postavki vašeg praćenja, posjetite\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nDa obrišete stranicu sa vašeg spiska praćenja, posjetite\n$UNWATCHURL\n\nPovratne informacije i daljnja pomoć:\n$HELPPAGE",
        "created": "napravljena",
        "changed": "promijenjena",
        "deletepage": "Obrišite stranicu",
        "exbeforeblank": "sadržaj prije brisanja je bio: '$1'",
        "delete-confirm": "Brisanje \"$1\"",
        "delete-legend": "Obriši",
-       "historywarning": "'''Upozorenje''':  Stranica koju želite da obrišete ima historiju sa otprilike $1 {{PLURAL:$1|revizijom|revizije|revizija}}:",
+       "historywarning": "<strong>Upozorenje</strong>: Stranica koju želite da obrišete ima historiju sa otprilike $1 {{PLURAL:$1|revizijom|revizije|revizija}}:",
        "confirmdeletetext": "Brisanjem ćete obrisati stranicu ili sliku zajedno sa historijom iz baze podataka, ali će se iste moći vratiti kasnije.\nMolim potvrdite svoju namjeru, da razumijete posljedice i da ovo radite u skladu sa [[{{MediaWiki:Policy-url}}|pravilima]].",
        "actioncomplete": "Akcija završena",
        "actionfailed": "Akcija nije uspjela",
        "deletecomment": "Razlog:",
        "deleteotherreason": "Ostali/dodatni razlozi:",
        "deletereasonotherlist": "Ostali razlozi",
-       "deletereason-dropdown": "*Uobičajeni razlozi brisanja\n** Zahtjev autora\n** Kršenje autorskih prava\n** Vandalizam",
+       "deletereason-dropdown": "*Uobičajeni razlozi brisanja\n** Spam\n** Vandalizam\n** Kršenje autorskih prava\n** Zahtjev autora\n** Pokvareno preusmjerenje",
        "delete-edit-reasonlist": "Uredi razloge brisanja",
        "delete-toobig": "Ova stranica ima veliku historiju promjena, preko $1 {{PLURAL:$1|revizije|revizija}}.\nBrisanje takvih stranica nije dopušteno da bi se spriječilo slučajno preopterećenje servera na kojem je {{SITENAME}}.",
        "delete-warning-toobig": "Ova stranica ima veliku historiju izmjena, preko $1 {{PLURAL:$1|izmjene|izmjena}}.\nNjeno brisanje može dovesti do opterećenja operacione baze na {{SITENAME}};\nnastavite s oprezom.",
        "protect-locked-blocked": "Ne možete promijeniti nivo zaštite dok ste blokirani.\nOvo su trenutne postavke za stranicu '''$1''':",
        "protect-locked-dblock": "Nivoi zaštite se ne mogu mijenjati jer je aktivna baza podataka zaključana.\nTrenutna postavka za stranicu '''$1''' je:",
        "protect-locked-access": "Nemate ovlasti za mijenjanje stepena zaštite.\nSlijede trenutne postavke stranice '''$1''':",
-       "protect-cascadeon": "Ova stranica je tenutno zaštićena jer je uključena u {{PLURAL:$1|stranicu, koja ima|stranice, koje imaju|stranice, koje imaju}} uključenu prenosnu zaštitu.\nMožete promijeniti stepen zaštite ove stranice, ali to neće uticati na prenosnu zaštitu.",
+       "protect-cascadeon": "Ova stranica je trenutno zaštićena jer je uključena u {{PLURAL:$1|stranicu, koja ima|stranice, koje imaju|stranice, koje imaju}} uključenu prenosnu zaštitu.\nPromijene stepena zaštite ove stranice neće uticati na prenosnu zaštitu.",
        "protect-default": "Dopusti svim korisnicima",
        "protect-fallback": "Dozvolite samo korisnicima sa \"$1\" ovlastima/privilegijama",
        "protect-level-autoconfirmed": "Dopustite samo automatski potvrđenim korisnicima",
        "ipb-unblock-addr": "Deblokiraj $1",
        "ipb-unblock": "Deblokiraj korisničko ime ili IP adresu",
        "ipb-blocklist": "Vidi postojeće blokade",
-       "ipb-blocklist-contribs": "Doprinosi za $1",
+       "ipb-blocklist-contribs": "Doprinosi za {{GENDER:$1|$1}}",
        "unblockip": "Odblokiraj korisnika",
        "unblockiptext": "Upotrebite donji upitnik da bi ste vratili\npravo pisanja ranije blokiranoj IP adresi\nili korisničkom imenu.",
        "ipusubmit": "Ukloni ovu blokadu",
        "range_block_disabled": "Administratorska mogućnost da blokira grupe je isključena.",
        "ipb_expiry_invalid": "Pogrešno vrijeme trajanja.",
        "ipb_expiry_temp": "Sakrivene blokade korisničkih imena moraju biti stalne.",
-       "ipb_hide_invalid": "Ne može se onemogućiti ovaj račun; možda ima isuviše izmjena.",
+       "ipb_hide_invalid": "Ne može se onemogućiti ovaj račun; on ima više od {{PLURAL:$1|jedne izmjene|$1 izmjena}}.",
        "ipb_already_blocked": "\"$1\" je već blokiran",
        "ipb-needreblock": "$1 je već blokiran. Da li želite promijeniti postavke?",
        "ipb-otherblocks-header": "Ostale {{PLURAL:$1|blokada|blokade}}",
        "importuploaderrortemp": "Postavljanje uvozne datoteke nije uspjelo.\nNedostaje privremeni folder.",
        "import-parse-failure": "Greška pri parsiranju XML uvoza",
        "import-noarticle": "Nema stranica za uvoz!",
-       "import-nonewrevisions": "Sve revizije su prethodno uvežene.",
+       "import-nonewrevisions": "Nijedna revizija nije uvezena (ili su sve već prisutne ili su preskočene zbog greške).",
        "xml-error-string": "$1 na liniji $2, kolona $3 (bajt $4): $5",
        "import-upload": "Postavljanje XML podataka",
        "import-token-mismatch": "Izgubljeni podaci sesije. Molimo pokušajte ponovno.",
        "import-error-create": "Stranica \"$1\" nije uvezena jer vam nije dozvoljeno da je napravite.",
        "import-error-interwiki": "Stranica \"$1\" nije uvezena jer je njen naziv rezerviran za vanjsko povezivanje (interwiki).",
        "import-error-special": "Stranica \"$1\" nije uvezena jer pripada posebnom imenskom prostoru koje ne prihvata stranice.",
-       "import-error-invalid": "Stranica \"$1\" nije uvezena jer je njen naziv neispravan.",
+       "import-error-invalid": "Stranica \"$1\" nije uvezena jer je naziv pod kojim treba biti uvezena nije valjan na ovoj wiki.",
        "import-options-wrong": "{{PLURAL:$2|Pogrešna opcija|Pogrešne opcije}}: <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "Navedena osnovna stranica ima neispravan naslov.",
        "import-rootpage-nosubpage": "Imenski prostor \"$1\" osnovne stranice ne dozvoljava podstranice.",
        "importlogpage": "Zapisnik uvoza",
        "importlogpagetext": "Administrativni uvozi stranica sa historijom izmjena sa drugih wikija.",
        "import-logentry-upload": "uvezena stranica [[$1]] putem postavljanja datoteke",
-       "import-logentry-upload-detail": "$1 {{PLURAL:$1|revizija|revizije|revizija}}",
+       "import-logentry-upload-detail": "{{PLURAL:$1|Uvezena jedna revizija|Uvezene $1 revizije|Uvezeno $1 revizija}}",
        "import-logentry-interwiki": "uveženo (''transwikied'') $1",
-       "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|revizija|revizije|revizija}} od $2",
+       "import-logentry-interwiki-detail": "{{PLURAL:$1|Uvezena $1 revizija|Uvezene $1 revizije|Uvezeno $1 revizija}} od $2",
        "javascripttest": "Testiranje JavaScript-e",
        "javascripttest-title": "Pokretanje $1 testova",
        "javascripttest-pagetext-noframework": "Ova stranica je određena za pokretanje JavaScript testova.",
        "spam_reverting": "Vraćanje na posljednju verziju koja ne sadrži linkove ka $1",
        "spam_blanking": "Sve revizije koje sadrže linkove ka $1, očisti",
        "spam_deleting": "Sve revizije koje sadrže linkove na $1, brišem",
-       "simpleantispam-label": "Provjera protiv spama.\n'''NE''' popunjavaj ovo!",
+       "simpleantispam-label": "Provjera protiv spama.\n<strong>NE</strong> popunjavajte ovo!",
        "pageinfo-title": "Informacije za \"$1\"",
        "pageinfo-not-current": "Nažalost, nemoguće je dati ove informacije za starije revizije.",
        "pageinfo-header-basic": "Osnovne informacije",
        "newimages-summary": "Ova specijalna stranica prikazuje posljednje postavljene datoteke.",
        "newimages-legend": "Filter",
        "newimages-label": "Ime datoteke (ili dio imena):",
+       "newimages-showbots": "Pokaži datoteke koje su poslali botovi",
        "noimages": "Ništa za prikazati.",
        "ilsubmit": "Traži",
        "bydate": "po datumu",
        "watchlistedit-raw-done": "Vaš spisak praćenja je ažuriran.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 naslov je dodan|$1 naslova su dodana|$1 naslova je dodano}}:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 naslov je uklonjen|$1 naslova je uklonjeno}}:",
+       "watchlistedit-clear-title": "Očišćen spisak nadgledanja",
+       "watchlistedit-clear-legend": "Očisti spisak nadgledanja",
+       "watchlistedit-clear-explain": "Svi naslovi će biti uklonjeni iz vašeg spiska nadgledanja",
+       "watchlistedit-clear-titles": "Naslovi:",
+       "watchlistedit-clear-submit": "Isprazni spisak nadgledanja (Ovo je trajno!)",
+       "watchlistedit-clear-done": "Vaš spisak praćenja je očišćen.",
        "watchlisttools-clear": "Očisti spisak nadgledanja",
        "watchlisttools-view": "Pregled promjena praćenih stranica",
        "watchlisttools-edit": "Pogledaj i uredi listu praćenih članaka.",
        "duplicate-defaultsort": "Upozorenje: Postavljeni ključ sortiranja \"$2\" zamjenjuje raniji ključ \"$1\".",
        "version": "Verzija",
        "version-extensions": "Instalirana proširenja (ekstenzije)",
-       "version-skins": "Kože",
+       "version-skins": "Instalirane kože",
        "version-specialpages": "Posebne stranice",
        "version-parserhooks": "Kuke parsera",
        "version-variables": "Promjenjive",
        "version-hook-name": "Naziv kuke",
        "version-hook-subscribedby": "Pretplaćeno od",
        "version-version": "(Verzija $1)",
+       "version-no-ext-name": "[nema imena]",
        "version-license": "Licenca",
        "version-ext-license": "Licenca",
        "version-ext-colheader-name": "Proširenje",
        "htmlform-yes": "Da",
        "htmlform-chosen-placeholder": "Izaberite opciju",
        "htmlform-cloner-create": "Dodaj još",
+       "htmlform-cloner-delete": "Ukloni",
        "sqlite-has-fts": "$1 sa podrškom pretrage cijelog teksta",
        "sqlite-no-fts": "$1 bez podrške pretrage cijelog teksta",
        "logentry-delete-delete": "$1 je {{GENDER:$2|obrisao|obrisala}} stranicu $3",
        "duration-centuries": "$1 {{PLURAL:$1|vijek|vijeka|vijekova}}",
        "duration-millennia": "$1 {{PLURAL:$1|milenij|milenija}}",
        "rotate-comment": "Slika rotirana za $1 {{PLURAL:$1|stepen|stepeni}} u smjeru kazaljke na satu",
+       "limitreport-cputime": "Vrijeme korištenja CPU",
        "limitreport-walltime": "Korištenje u realnom vremenu",
        "limitreport-walltime-value": "$1 {{PLURAL:$1|sekunda|sekunde|sekundi}}",
        "expandtemplates": "Proširi šablone",
-       "expand_templates_intro": "Ova posebna stranica uzima neki tekst i proširuje sve šablone u njemu rekurzivno.\nOna također proširuje parserske funkcije poput\n<nowiki>{{</nowiki>#language:…}} i varijable poput\n<nowiki>{{</nowiki>CURRENTDAY}}&mdash;u principu gotovo sve između dvostrukih zagrada.\nOvo se uradi putem poziva relevantnog parserskog nivoa iz same MediaWiki.",
+       "expand_templates_intro": "Ova posebna stranica uzima neki tekst i proširuje sve šablone u njemu rekurzivno.\nOna također proširuje parserske funkcije poput\n<code><nowiki>{{</nowiki>#language:…}}</code> i varijable poput\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>. U principu proširuje gotovo sve između dvostrukih zagrada.",
        "expand_templates_title": "Naslov konteksta, za {{FULLPAGENAME}} itd.:",
        "expand_templates_input": "Tekst unosa:",
        "expand_templates_output": "Rezultat",
        "expand_templates_remove_comments": "Ukloni komentare",
        "expand_templates_remove_nowiki": "Onemogući oznake <nowiki> u rezultatima",
        "expand_templates_generate_xml": "Prikaži XML stablo parsera",
+       "expand_templates_generate_rawhtml": "Pokaži izvorni HTML",
        "expand_templates_preview": "Pregled",
        "pagelang-name": "Stranica",
        "pagelang-language": "Jezik",
        "pagelang-select-lang": "Izaberi jezik",
+       "mediastatistics-table-count": "Broj datoteka",
        "mediastatistics-header-unknown": "Nepoznato",
+       "mediastatistics-header-drawing": "Crteži (vektorske slike)",
+       "mediastatistics-header-audio": "Zvuk",
+       "mediastatistics-header-video": "Videa",
+       "mediastatistics-header-multimedia": "Rich media",
+       "mediastatistics-header-office": "Kancelarija",
+       "mediastatistics-header-text": "Tekstualno",
+       "mediastatistics-header-executable": "Izvršni",
+       "mediastatistics-header-archive": "Kompresovani formati",
        "json-error-syntax": "Sintaksna greška"
 }
index 342c3f5..6944825 100644 (file)
        "duration-decades": "$1 {{PLURAL:$1|دەیە|دەیە}}",
        "duration-centuries": "$1 {{PLURAL:$1|سەدە|سەدە}}",
        "duration-millennia": "$1 {{PLURAL:$1|ھەزارە|ھەزارە}}",
-       "expand_templates_ok": "باشە"
+       "expand_templates_output": "ئاکام",
+       "expand_templates_ok": "باشە",
+       "expand_templates_preview": "پێشبینین",
+       "pagelang-name": "پەڕە",
+       "pagelang-language": "زمان",
+       "pagelang-select-lang": "زمان ھەڵبژێرە",
+       "right-pagelang": "زمانی پەڕە بگۆڕە",
+       "action-pagelang": "زمانی پەڕەکە بگۆڕە",
+       "mediastatistics": "ئامارەکانی میدیا",
+       "mediastatistics-nbytes": "{{PLURAL:$1|$1 بایت|$1 بایت}} ($2؛ $3%)",
+       "mediastatistics-table-mimetype": "جۆری MIME",
+       "mediastatistics-table-count": "ژمارەی پەڕگەکان",
+       "mediastatistics-header-unknown": "نەزانراو",
+       "mediastatistics-header-bitmap": "وێنەی Bitmap",
+       "mediastatistics-header-audio": "دەنگ",
+       "mediastatistics-header-video": "ڤیدیۆکان",
+       "json-error-syntax": "ھەڵەی ئیملایی"
 }
index 7df6b42..ff5dce7 100644 (file)
        "anoneditwarning": "'''Varování:''' Nejste přihlášen(a). Pokud uložíte jakoukoli editaci, bude vaše IP adresa zveřejněna v historii této stránky. Pokud se <strong>[$1 přihlásíte]</strong> nebo si <strong>[$2 vytvoříte účet]</strong>, budou vaše editace připsány vašemu uživatelskému jménu a získáte i další výhody.",
        "anonpreviewwarning": "''Nejste přihlášen(a). Uložením zveřejníte svou IP adresu v historii této stránky.''",
        "missingsummary": "'''Připomenutí:''' Nezadali jste shrnutí editace. Pokud ještě jednou kliknete na Uložit změny, bude vaše editace zapsána bez shrnutí.",
-       "selfredirect": "<strong>Upozornění:</strong> Vytváříte přesměrování na týž článek. Pokud ještě jednou kliknete na „{{int:savearticle}}“, bude přesměrování vytvořeno.",
+       "selfredirect": "<strong>Upozornění:</strong> Pokušíte se tuto stránku přesměrovat samu na sebe.\nMožná jste uvedli chybný cíl přesměrování nebo editujete špatnou stránku.\nPokud ještě jednou kliknete na „{{int:savearticle}}“, bude přesměrování přesto vytvořeno.",
        "missingcommenttext": "Zadejte komentář",
        "missingcommentheader": "'''Připomenutí:''' Nezadali jste předmět/nadpis pro tento komentář.\nPokud ještě jednou kliknete na „{{int:savearticle}}“, bude vaše editace zapsána i bez toho.",
        "summary-preview": "Náhled shrnutí:",
        "history-feed-empty": "Požadovaná stránka neexistuje.\nMohla být smazána či přejmenována.\nZkuste [[Special:Search|hledání]].",
        "rev-deleted-comment": "(shrnutí editace odstraněno)",
        "rev-deleted-user": "(uživatelské jméno odstraněno)",
-       "rev-deleted-event": "(záznam odstraněn)",
+       "rev-deleted-event": "(podrobnosti odstraněny)",
        "rev-deleted-user-contribs": "[uživatelské jméno nebo IP adresa odstraněny – editace v příspěvcích skryta]",
        "rev-deleted-text-permission": "Tato revize byla '''smazána'''.\nPodrobnosti mohou být uvedeny v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} knize smazaných stránek].",
        "rev-suppressed-text-permission": "Tato revize byla <strong>utajena</strong>. Podrobnosti jsou uvedeny v [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} knize utajení].",
        "revdelete-legend": "Nastavit omezení viditelnosti",
        "revdelete-hide-text": "Text revize",
        "revdelete-hide-image": "Skrýt obsah souboru",
-       "revdelete-hide-name": "Skrýt událost a cíl",
+       "revdelete-hide-name": "Skrýt cíl a parametry",
        "revdelete-hide-comment": "Shrnutí editace",
        "revdelete-hide-user": "Uživatelské jméno / IP adresa",
        "revdelete-hide-restricted": "Utajit data i před správci",
        "thumbnail-temp-create": "Dočasný soubor náhledu nelze vytvořit.",
        "thumbnail-dest-create": "Náhled nelze uložit na dané místo.",
        "thumbnail_invalid_params": "Neplatný parametr náhledu",
+       "thumbnail_toobigimagearea": "Soubor s rozměry většími než $1",
        "thumbnail_dest_directory": "Nelze vytvořit cílový adresář",
        "thumbnail_image-type": "Nepodporovaný typ obrázku",
        "thumbnail_gd-library": "Neúplná konfigurace knihovny GD: chybí funkce $1",
index b0200ec..b8a6559 100644 (file)
        "anoneditwarning": "<strong>Warnung:</strong> Du bist nicht angemeldet. Deine IP-Adresse wird öffentlich sichtbar, falls du Bearbeitungen durchführst. Wenn du dich <strong>[$1 anmeldest]</strong> oder <strong>[$2 ein Benutzerkonto erstellst]</strong>, werden deine Bearbeitungen zusammen mit anderen Beiträgen deinem Benutzernamen zugeordnet.",
        "anonpreviewwarning": "''Du bist nicht angemeldet. Beim Speichern wird deine IP-Adresse in der Versionsgeschichte aufgezeichnet.''",
        "missingsummary": "'''Hinweis:''' Du hast keine Zusammenfassung angegeben. Wenn du erneut auf „{{int:savearticle}}“ klickst, wird deine Änderung ohne Zusammenfassung übernommen.",
-       "selfredirect": "<strong>Warnung:</strong> Du erstellst eine Weiterleitung auf den gleichen Artikel.\nWenn du erneut auf „{{int:savearticle}}“ klickst, wird die Weiterleitung erstellt.",
+       "selfredirect": "<strong>Warnung:</strong> Du leitest auf diese Seite selbst weiter.\nDu hast vermutlich das falsche Weiterleitungsziel angegeben oder du bearbeitest die falsche Seite.\nWenn du erneut auf „{{int:savearticle}}“ klickst, wird die Weiterleitung dennoch erstellt.",
        "missingcommenttext": "Dein Abschnitt enthält keinen Text.",
        "missingcommentheader": "'''Achtung:''' Du hast kein Betreff/Überschrift eingegeben. Wenn du erneut auf „{{int:savearticle}}“ klickst, wird deine Bearbeitung ohne Überschrift gespeichert.",
        "summary-preview": "Vorschau der Zusammenfassungszeile:",
        "history-feed-empty": "Die angeforderte Seite existiert nicht. Vielleicht wurde sie gelöscht oder verschoben. [[Special:Search|Durchsuche]] {{SITENAME}} nach passenden neuen Seiten.",
        "rev-deleted-comment": "(Zusammenfassung entfernt)",
        "rev-deleted-user": "(Benutzername entfernt)",
-       "rev-deleted-event": "(Logbuchaktion entfernt)",
+       "rev-deleted-event": "(Logbucheinzelheiten entfernt)",
        "rev-deleted-user-contribs": "[Benutzername oder IP-Adresse entfernt – Bearbeitung aus Beiträgen versteckt]",
        "rev-deleted-text-permission": "Diese Version wurde '''gelöscht'''.\nNähere Angaben zum Löschvorgang sowie eine Begründung stehen im [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} Lösch-Logbuch].",
        "rev-suppressed-text-permission": "Diese Seitenversion wurde <strong>unterdrückt</strong>.\nEinzelheiten können im [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} Oversight-Logbuch] gefunden werden.",
        "revdelete-legend": "Setzen der Sichtbarkeitseinschränkungen",
        "revdelete-hide-text": "Text der Version",
        "revdelete-hide-image": "Dateiinhalt verstecken",
-       "revdelete-hide-name": "Logbuchaktion und Ziel verstecken",
+       "revdelete-hide-name": "Ziel und Parameter verstecken",
        "revdelete-hide-comment": "Bearbeitungszusammenfassung",
        "revdelete-hide-user": "Benutzername/IP-Adresse des Bearbeiters",
        "revdelete-hide-restricted": "Daten sowohl vor Administratoren als auch anderen Benutzern unterdrücken",
        "thumbnail-temp-create": "Die Datei für die temporäre Miniaturansicht konnte nicht erstellt werden",
        "thumbnail-dest-create": "Die Miniaturansicht konnte nicht am vorgesehenen Ort gespeichert werden",
        "thumbnail_invalid_params": "Ungültige Thumbnail-Parameter",
+       "thumbnail_toobigimagearea": "Datei mit Abmessungen größer als $1",
        "thumbnail_dest_directory": "Zielverzeichnis kann nicht erstellt werden.",
        "thumbnail_image-type": "Bildtyp nicht unterstützt",
        "thumbnail_gd-library": "Unvollständige Konfiguration der GD-Bibliothek: Fehlende Funktion $1",
index c455a76..1f2056c 100644 (file)
        "welcomecreation-msg": "Hesabê şıma abiyo.\n[[Special:Preferences|{{SITENAME}} vurnayişê tercihanê xo]], xo vir ra mekere.",
        "yourname": "Nameyê karberi:",
        "userlogin-yourname": "Nameyê karberi",
-       "userlogin-yourname-ph": "Namey ğoyé karberi cı kewe",
-       "createacct-another-username-ph": "Namey karberi de fi",
+       "userlogin-yourname-ph": "Nameyê xoyê karberi cı kewe",
+       "createacct-another-username-ph": "Nameyê karberi cı kewe",
        "yourpassword": "Parola",
        "userlogin-yourpassword": "Parola",
        "userlogin-yourpassword-ph": "Parolaya xo cıkewe",
        "passwordreset-emailtitle": "Hesab timarê {{SITENAME}}",
        "passwordreset-emailtext-ip": "Jeweri, {{SITENAME}} ra (ma heta şımayê, $1 IP adresi ra) ($4) teferuatê hesabdê şıma  va wa biyaro xo viri. Karbero ke cêrdeyo {{PLURAL:$3|hesaba|eno hesaba}} ena e-posta adresiya aleqey cı esto:\n\n$2\n\n{{PLURAL:$3|ena parola idaretena|ena parola idareten}} {{PLURAL:$5|jew roc|$5  roca}}rêya.\nEna parolaya deqewe de u xorê ju parolaya newi bıweçine. Parolaya şıma emaya şıma viri se  yana  ena e-posta şıma nê weştase u şıma qayıl niye parolaya xo bıvurnese, ena mesacer peygoş bıkerê.",
        "passwordreset-emailtext-user": "$1 enê karberi, {{SITENAME}}  ra ($4) teferuatê hesab dê şıma  va wa biyaro xo viri. Karbero ke cêrdeyo {{PLURAL:$3|hesaba|eno hesaba}} ena e-posta adresiya aleqey cı esto:\n\n$2\n\n{{PLURAL:$3|ena parola idaretena|ena parola idareten}} {{PLURAL:$5|jew roc|$5  roca}}rêya.\nEna parolaya deqewe de u xorê ju parolaya newi bıweçine. Parolaya şıma emaya şıma viri se  yana  ena e-posta şıma nê weştase u şıma qayıl niye parolaya xo bıvurnese, ena mesacer peygoş bıkerê.",
-       "passwordreset-emailelement": "Namey karberi: $1\nParola vêrdiye: $2",
+       "passwordreset-emailelement": "Nameyê karberi: $1\nParolaya vêrdiye: $2",
        "passwordreset-emailsent": "Yew e-posteyê esterıtışê parola rışiya.",
        "passwordreset-emailsent-capture": "Yew e-posteyê esterıtışê parolayo ke rışiya, no cêr mocniyayo.",
        "passwordreset-emailerror-capture": "Yew e-posteyê esterıtışê parolayo ke rışiya, no cêr mocniyayo, ema {{GENDER:$2|karber}}i rê rıştış de mıwefeq nêbi: $1",
        "history-feed-item-nocomment": "$1 miyanê $2i de",
        "history-feed-empty": "Pela cıgeyrayiye çıniya.\nBeno ke ena esteriya, ya zi namê cı vuriyo.\nSeba pelanê muhimanê newan [[Special:Search|cıgeyrayışê wiki de]] bıcerebne.",
        "rev-deleted-comment": "(Timarkerdışe enay hewadeyayo)",
-       "rev-deleted-user": "(namey karberi esteriyo)",
+       "rev-deleted-user": "(nameyê karberi esteriyo)",
        "rev-deleted-event": "(fealiyetê cıkewtışi esteriyo)",
        "rev-deleted-user-contribs": "[namey karberi ya zi adresa IPy esteriya - vurnayış iştırakan ra nımniyo]",
        "rev-deleted-text-permission": "Çımraviyarnayışê ena pele '''esteriyo'''.\nBeno ke [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log] de teferruat esto.",
        "prefs-help-email": "Dayışê adresa e-postey keyfiyo, labelê seba eyarê parola lazıma, wexto ke şıma naye xo vira kerê.",
        "prefs-help-email-others": "Pera ğoya kerderi de zew link vırazése karberé bini şımaré şenê mesac bırşé. Lakin e-posta adresa şıma héç cayé de niasena.",
        "prefs-help-email-required": "E-mail adrese mecburiya.",
-       "prefs-info": "Melumata şıma",
+       "prefs-info": "Melumato bıngehên",
        "prefs-i18n": "Şar şélıg kerdış",
        "prefs-signature": "İmza",
        "prefs-dateformat": "Formatê tarixi",
        "right-writeapi": "İstıfadey APIyê nuştey",
        "right-delete": "Pele bestere",
        "right-bigdelete": "Pelanê be tarixanê dergan bestere",
-       "right-deletelogentry": "besternayış u mebesternayışa re qeyde definayışê xısusi",
-       "right-deleterevision": "Vurnayışê xısusiyê ke ê pelanê, inan bestere ya zi peyser bia",
+       "right-deletelogentry": "Qeydanê cıkewtışanê xısusiyan bestere û peyser biya",
+       "right-deleterevision": "Vurnayışanê xısusiyanê pele bestere ya zi peyser biya",
        "right-deletedhistory": "Qeydanê tarixanê esterıteyan de qayt ke, bê nuştey inan",
        "right-deletedtext": "Mabênê newede vurnayışanê esterıtiyan de qaytê nuştey esterıtey u vurnayışan ke",
        "right-browsearchive": "Pelanê esterıteyan bıgeyre",
        "right-undelete": "Jû pela esterıtiye peyser bia",
-       "right-suppressrevision": "İdarekeran ra dızdeni/miyanki, newede vurnayışan de qayt ke u newede vıraze",
+       "right-suppressrevision": "İdarekeran ra miyanki, newede vurnayışan de qayt ke u newede vıraze",
+       "right-viewsuppressed": "İdarekeran ra miyanki newede vurnayışan de qayt ke",
        "right-suppressionlog": "Rocekanê xasan bıvêne",
        "right-block": "Karberanê binan karê vurnayışi ra bloke bıke",
        "right-blockemail": "Yew karberê erşawıtışê/rıştena e-maili ra bloke bıke",
-       "right-hideuser": "Yew namey karberi  şari ra dızdeni/miyanki bloke bıke",
+       "right-hideuser": "Yew nameyê karberi şari ra miyanki bloke bıke",
        "right-ipblock-exempt": "Blokanê IPi, oto-blokan u blokanê menzıli ra ravêre",
        "right-proxyunbannable": "Blokanê otomatikiê proksiyan ra ravêre",
        "right-unblockself": "Blokeyınan ake",
        "noemailtext": "no/na karber yew e-postayo meqbul nêdawa/o",
        "nowikiemailtext": "no/na karber/e, karberanê binani ra gırewtışê e-postayi tercih nêkerd.",
        "emailnotarget": "Qandê Gêreninamey karberiyo wuna çınyo yana xırabo.",
-       "emailtarget": "Namey Karberi defiyê de.",
+       "emailtarget": "Nameyê karberiyê gırewtoği cıkewên.",
        "emailusername": "Nameyê karberi:",
        "emailusernamesubmit": "İtaet",
        "email-legend": "karberê {{SITENAME}} binan re e-posta bıerşaw",
        "emailblock": "e-mail blok biyo",
        "blocklist-nousertalk": "ti nieşken pele minaqaşe xo bivurne",
        "ipblocklist-empty": "Lista kılitkerdışi venga.",
-       "ipblocklist-no-results": "Adresa IPya waştiye ya zi namey karberi kılit nêbiyo.",
+       "ipblocklist-no-results": "Adresa IPya waştiye ya zi nameyê karberi kılit nêbiyo.",
        "blocklink": "kılit ke",
        "unblocklink": "bloki wedare",
        "change-blocklink": "kılitkerdışi bıvurne",
index 82e3fdf..b064b40 100644 (file)
        "history-feed-empty": "La pàgina serchêda l'an gh'é mìa; la pré èser stēda scanşlêda dal sît o gh'é stê cambiê nòm. Verifichêr cun [[Special:Search|pàgina 'd sèirca]] se gh'é dal pàgini nōvi.",
        "rev-deleted-comment": "(argumèint ed la mudéfica armôs)",
        "rev-deleted-user": "(nòm utèint armôs)",
-       "rev-deleted-event": "(asiòun dal log armôsa)",
+       "rev-deleted-event": "(particulêr dal log armôs)",
        "rev-deleted-user-contribs": "(nòm utèint o indirés IP armôs - mudéfica lughêda da la stòria)",
        "rev-deleted-text-permission": "Cla versiòun ché 'd la pàgina l'é stêda <strong> scanşlêda </strong>. \nConsultêr al [{{fullurl:{{#Special:Log}}/delete|page={{PAGENAMEE}}}} log dal canşladûri] per nōv particulêr.",
        "rev-suppressed-text-permission": "Cla versiòun ché 'd la pàgina l'é stêda <strong> scanşlêda </strong>. Consultêr al [{{fullurl:{{#Special:Log}}/ suppress |page={{ FULLPAGENAMEE }}}} log dal canşladûri] per nōv particulêr.",
        "right-markbotedits": "Sègna al mudéfichi da turnêr a mèter cme préma cme fâti da 'na mâchina in avtomâtich",
        "right-noratelimit": "An n'é mìa ublighê al lémit 'd asiòun",
        "right-import": "Côpia dal pàgini da 'd j êter wiki",
+       "right-importupload": "Zuntêr da pàgini da un file carghê.",
+       "right-patrol": "Sègna al mudéfichi 'd j êter utèint cme verifichêdi",
+       "right-autopatrol": "Sègna in avtomâtich al mudéfichi che t'é fât cme verifichêdi",
+       "right-patrolmarks": "Drōva la funsiòun ed veréfica dal j ûltmi mudéfichi",
+       "right-unwatchedpages": "Fa vèder un elèinch ed pàgini mìa guardêdi",
+       "right-mergehistory": "Al mèt insèm la stôria dal pàgini",
+       "right-userrights": "Mudéfica i dirét ed l'utèint",
+       "right-userrights-interwiki": "Mudéfica i dirét ed j êter utèint 'd êtri wiki",
+       "right-siteadmin": "Blōca e şblōca al databêş",
+       "right-override-export-depth": "Pôrta fōra al pàgini cun insèm al pàgini coleghêdi per 'na larghèsa ed 5",
+       "right-sendemail": "Spidés pôsta eletrônica a êter utèint",
        "newuserlogpage": "Utèint nōv",
        "action-read": "lēzer cla pàgina ché",
        "action-edit": "Mudifichêr cla pàgina ché",
index 583fc5d..da49fe9 100644 (file)
        "anoneditwarning": "<strong>Warning:</strong> You are not logged in. Your IP address will be publicly visible if you make any edits. If you <strong>[$1 log in]</strong> or <strong>[$2 create an account]</strong>, your edits will be attributed to your username, along with other benefits.",
        "anonpreviewwarning": "<em>You are not logged in. Saving will record your IP address in this page's edit history.</em>",
        "missingsummary": "<strong>Reminder:</strong> You have not provided an edit summary.\nIf you click \"{{int:savearticle}}\" again, your edit will be saved without one.",
-       "selfredirect": "<strong>Warning:</strong> You are creating redirect to the same article.\nIf you click \"{{int:savearticle}}\" again, the redirect will be created.",
+       "selfredirect": "<strong>Warning:</strong> You are redirecting this page to itself.\nYou may have specified the wrong target for the redirect, or you may be editing the wrong page.\nIf you click \"{{int:savearticle}}\" again, the redirect will be created anyway.",
        "missingcommenttext": "Please enter a comment below.",
        "missingcommentheader": "<strong>Reminder:</strong> You have not provided a subject/headline for this comment.\nIf you click \"{{int:savearticle}}\" again, your edit will be saved without one.",
        "summary-preview": "Summary preview:",
        "content-model-text": "plain text",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "content-model-json": "JSON",
+       "content-json-empty-object": "Empty object",
+       "content-json-empty-array": "Empty array",
        "duplicate-args-category": "Pages using duplicate arguments in template calls",
        "duplicate-args-category-desc": "The page contains template calls that use duplicates of arguments, such as <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "<strong>Warning:</strong> This page contains too many expensive parser function calls.\n\nIt should have less than $2 {{PLURAL:$2|call|calls}}, there {{PLURAL:$1|is now $1 call|are now $1 calls}}.",
        "history-feed-empty": "The requested page does not exist.\nIt may have been deleted from the wiki, or renamed.\nTry [[Special:Search|searching on the wiki]] for relevant new pages.",
        "rev-deleted-comment": "(edit summary removed)",
        "rev-deleted-user": "(username removed)",
-       "rev-deleted-event": "(log action removed)",
+       "rev-deleted-event": "(log details removed)",
        "rev-deleted-user-contribs": "[username or IP address removed - edit hidden from contributions]",
        "rev-deleted-text-permission": "This page revision has been <strong>deleted</strong>.\nDetails can be found in the [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log].",
        "rev-suppressed-text-permission": "This page revision has been <strong>suppressed</strong>.\nDetails can be found in the [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} suppression log].",
        "revdelete-legend": "Set visibility restrictions",
        "revdelete-hide-text": "Revision text",
        "revdelete-hide-image": "Hide file content",
-       "revdelete-hide-name": "Hide action and target",
+       "revdelete-hide-name": "Hide target and parameters",
        "revdelete-hide-comment": "Edit summary",
        "revdelete-hide-user": "Editor's username/IP address",
        "revdelete-hide-restricted": "Suppress data from administrators as well as others",
index 4564aac..047993d 100644 (file)
        "anoneditwarning": "<strong>Advertencia:</strong> No has iniciado sesión. Tu dirección IP será visible públicamente si haces cualquier edición. Si <strong>[$1 inicias sesión]</strong> o <strong>[$2 creas una cuenta]</strong>, tus ediciones se atribuirán a tu nombre de usuario, junto con otros beneficios.",
        "anonpreviewwarning": "<em>No has iniciado sesión. Al guardar los cambios se almacenará tu dirección IP en el historial de edición de esta página.</em>",
        "missingsummary": "<strong>Recordatorio:</strong> No has escrito un resumen de edición.\nSi haces clic nuevamente en «{{int:savearticle}}» tu edición se grabará sin él.",
+       "selfredirect": "<strong>Advertencia:</strong> estás redirigiendo esta página a sí misma.\nPuedes haber especificado erróneamente el destino de la redirección o puedes estar editando la página equivocada.\nSi haces clic de nuevo en \"{{int:savearticle}}\", la redirección se creará de todas maneras.",
        "missingcommenttext": "Escribe un comentario a continuación.",
        "missingcommentheader": "<strong>Recordatorio:</strong> No has escrito un asunto/encabezado para este comentario.\nSi haces clic nuevamente en \"{{int:savearticle}}\" tu edición se grabará sin él.",
        "summary-preview": "Previsualización del resumen:",
        "history-feed-empty": "La página solicitada no existe.\nPuede haber sido borrada del wiki o renombrada.\nPrueba a [[Special:Search|buscar en el wiki]] nuevas páginas relevantes.",
        "rev-deleted-comment": "(resumen de edición eliminado)",
        "rev-deleted-user": "(nombre de usuario eliminado)",
-       "rev-deleted-event": "(entrada borrada)",
+       "rev-deleted-event": "(detalles del registro eliminados)",
        "rev-deleted-user-contribs": "[nombre de usuario o dirección IP eliminada - edición ocultada de la lista de contribuciones]",
        "rev-deleted-text-permission": "Esta revisión de la página ha sido '''borrada'''.\nPuede haber detalles en el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de borrados].",
        "rev-suppressed-text-permission": "Esta revisión de la página se <strong>suprimió</strong>.\nLos detalles se pueden ver en el [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registro de supresión].",
        "revdelete-legend": "Establecer restricciones de revisión:",
        "revdelete-hide-text": "Texto de la revisión",
        "revdelete-hide-image": "Ocultar el contenido del archivo",
-       "revdelete-hide-name": "Ocultar acción y objetivo",
+       "revdelete-hide-name": "Ocultar objetivo y parámetros.",
        "revdelete-hide-comment": "Resumen de edición",
        "revdelete-hide-user": "Nombre/IP del editor",
        "revdelete-hide-restricted": "Suprimir datos a los administradores así como al resto",
index eaea3f6..24d209e 100644 (file)
        "revdel-restore": "تغییر پیدایی",
        "pagehist": "تاریخچهٔ صفحه",
        "deletedhist": "تاریخچهٔ حذف‌شده",
-       "revdelete-hide-current": "خطا در پنهان‌کردن مورد مورخ $2 ساعت $1: این نسخه، نسخهٔ اخیر است و قابل پنهان‌کردن نیست.",
-       "revdelete-show-no-access": "خطا در پنهانکردن مورد مورخ $2 ساعت $1: این نسخه علامت «محدودیت» دارد و شما به آن دسترسی ندارید.",
-       "revdelete-modify-no-access": "خطا در پنهانکردن مورد مورخ $2 ساعت $1: این نسخه علامت «محدودیت» دارد و شما به آن دسترسی ندارید.",
-       "revdelete-modify-missing": "خطا در پنهانکردن مورد شمارهٔ $1: این نسخه در پایگاه داده وجود ندارد!",
+       "revdelete-hide-current": "خطا در پنهان کردن مورد مورخ $2 ساعت $1: این نسخه، نسخهٔ اخیر است و قابل پنهان کردن نیست.",
+       "revdelete-show-no-access": "خطا در پنهان کردن مورد مورخ $2 ساعت $1: این نسخه علامت «محدودیت» دارد و شما به آن دسترسی ندارید.",
+       "revdelete-modify-no-access": "خطا در پنهان کردن مورد مورخ $2 ساعت $1: این نسخه علامت «محدودیت» دارد و شما به آن دسترسی ندارید.",
+       "revdelete-modify-missing": "خطا در پنهان کردن مورد شمارهٔ $1: این نسخه در پایگاه داده وجود ندارد!",
        "revdelete-no-change": "'''هشدار:''' مورد مورخ $2 ساعت $1 از قبل تنظیمات پیدایی درخواست شده را دارا بود.",
-       "revdelete-concurrent-change": "خطا در پنهانکردن مورد مورخ $2 ساعت $1: به نظر می‌رسد که در مدتی که شما برای تغییر وضعیت آن تلاش می‌کردید وضعیت آن توسط فرد دیگری تغییر یافته است.\nلطفاً سیاهه‌ها را بررسی کنید.",
+       "revdelete-concurrent-change": "خطا در پنهان کردن مورد مورخ $2 ساعت $1: به نظر می‌رسد که در مدتی که شما برای تغییر وضعیت آن تلاش می‌کردید وضعیت آن توسط فرد دیگری تغییر یافته است.\nلطفاً سیاهه‌ها را بررسی کنید.",
        "revdelete-only-restricted": "خطا در پنهان کردن مورد مورخ $2 ساعت $1: شما نمی‌توانید موارد را از دید مدیران پنهان کنید مگر آن که یکی دیگر از گزینه‌های پنهان‌سازی را نیز انتخاب کنید.",
        "revdelete-reason-dropdown": "*دلایل متداول حذف\n** نقض حق تکثیر\n** اظهار نظر یا اطلاعات فردی نامناسب\n** نام کاربری نامناسب\n** اطلاعات به طور بالقوه افتراآمیز",
        "revdelete-otherreason": "دلیل دیگر/اضافی:",
        "right-suppressionlog": "مشاهدهٔ سیاهه‌های خصوصی",
        "right-block": "قطع دسترسی ویرایشی دیگر کاربران",
        "right-blockemail": "قطع دسترسی دیگر کاربران برای ارسال رایانامه",
-       "right-hideuser": "قطع دسترسی کاربر و پنهانکردن آن از دید عموم",
+       "right-hideuser": "قطع دسترسی کاربر و پنهان کردن آن از دید عموم",
        "right-ipblock-exempt": "تاثیر نپذیرفتن از قطع دسترسی‌های آی‌پی، خودکار یا فاصله‌ای",
        "right-proxyunbannable": "تاثیر نپذیرفتن از قطع دسترسی خودکار پروکسی‌ها",
        "right-unblockself": "بازکردن دسترسی خود",
        "rclistfrom": "نمایش تغییرات تازه با شروع از $3 $2",
        "rcshowhideminor": "$1 ویرایش‌های جزئی",
        "rcshowhideminor-show": "نمایش",
-       "rcshowhideminor-hide": "پنهانکردن",
+       "rcshowhideminor-hide": "پنهان کردن",
        "rcshowhidebots": "$1 ربات‌ها",
        "rcshowhidebots-show": "نمایش",
-       "rcshowhidebots-hide": "پنهانکردن",
+       "rcshowhidebots-hide": "پنهان کردن",
        "rcshowhideliu": "$1 کاربران ثبت‌نام‌کردە",
        "rcshowhideliu-show": "نمایش",
-       "rcshowhideliu-hide": "پنهانکردن",
+       "rcshowhideliu-hide": "پنهان کردن",
        "rcshowhideanons": "$1 کاربران ناشناس",
        "rcshowhideanons-show": "نمایش",
-       "rcshowhideanons-hide": "پنهانکردن",
+       "rcshowhideanons-hide": "پنهان کردن",
        "rcshowhidepatr": "$1 ویرایش‌های گشت‌خورده",
        "rcshowhidepatr-show": "نمایش",
-       "rcshowhidepatr-hide": "پنهانکردن",
+       "rcshowhidepatr-hide": "پنهان کردن",
        "rcshowhidemine": "$1 ویرایش‌های من",
        "rcshowhidemine-show": "نمایش",
-       "rcshowhidemine-hide": "پنهانکردن",
+       "rcshowhidemine-hide": "پنهان کردن",
        "rclinks": "نمایش آخرین $1 تغییر در $2 روز اخیر<br />$3",
        "diff": "تفاوت",
        "hist": "تاریخچه",
        "protectedpages-indef": "فقط محافظت‌های بی‌پایان",
        "protectedpages-summary": "در این صفحه فهرست صفحات موجود است که در حال حاضر محافظت شده اند. برای فهرست عنوان‌هایی که از ایجاد محافظت شده‌اند، به [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]] مراجعه کنید.",
        "protectedpages-cascade": "فقط محافظت‌های آبشاری",
-       "protectedpages-noredirect": "پنهانکردن تغییر مسیرها",
+       "protectedpages-noredirect": "پنهان کردن تغییر مسیرها",
        "protectedpagesempty": "در حال حاضر هیچ‌صفحه‌ای محافظت نشده‌است.",
        "protectedpages-timestamp": "برچسب زمان",
        "protectedpages-page": "صفحه",
        "blocklist": "کاربران بسته‌شده",
        "ipblocklist": "کاربران بسته‌شده",
        "ipblocklist-legend": "جستجوی کاربر بسته شده",
-       "blocklist-userblocks": "پنهانکردن بسته‌شدن‌های حساب",
-       "blocklist-tempblocks": "پنهانکردن بستن‌های موقت",
-       "blocklist-addressblocks": "پنهانکردن تک آی‌پی‌های بسته شده",
+       "blocklist-userblocks": "پنهان کردن بسته‌شدن‌های حساب",
+       "blocklist-tempblocks": "پنهان کردن بستن‌های موقت",
+       "blocklist-addressblocks": "پنهان کردن تک آی‌پی‌های بسته شده",
        "blocklist-rangeblocks": "پنهان کردنی قطع دسترسی بازه‌ها",
        "blocklist-timestamp": "برچسب زمان",
        "blocklist-target": "هدف",
        "sorbsreason": "نشانی آی‌پی شما توسط DNSBL مورد استفاده {{SITENAME}} به عنوان یک پروکسی باز گزارش شده‌است.",
        "sorbs_create_account_reason": "نشانی آی‌پی شما توسط DNSBL مورد استفاده {{SITENAME}} به عنوان یک پروکسی باز گزارش شده‌است.\nشما اجازهٔ ساختن حساب کاربری ندارید.",
        "xffblockreason": "نشانی آی‌پی در X-Forwarded-For header موجود است و پروکسی شما یا سروری که از آن استفاده می‌کنید بسته‌شده‌است. دلیل بسته‌شدن: $1",
-       "cant-see-hidden-user": "کاربری که می‌خواهید ببندید قبلاً بسته شده و پنهان گردیده‌است. چون شما دسترسی پنهان‌کردن کاربران را ندارید، نمی‌توانید قطع دسترسی کاربر را ببینید یا ویرایش کنید.",
+       "cant-see-hidden-user": "کاربری که می‌خواهید ببندید قبلاً بسته شده و پنهان گردیده است. چون شما دسترسی پنهان کردن کاربران را ندارید، نمی‌توانید قطع دسترسی کاربر را ببینید یا ویرایش کنید.",
        "ipbblocked": "شما نمی‌توانید دسترسی دیگر کاربران را ببندید یا باز کنید زیرا دسترسی خودتان بسته است.",
        "ipbnounblockself": "شما مجاز به باز کردن دسترسی خود نیستید.",
        "lockdb": "قفل کردن پایگاه داده",
index 0fc292d..b315742 100644 (file)
        "anoneditwarning": "<strong>אזהרה:</strong> אינכם מחוברים לחשבון. כתובת ה־IP שלכם תוצג בפומבי אם תבצעו עריכות כלשהן. אם <strong>[$1 תיכנסו לחשבון]</strong> או <strong>[$2 תיצרו חשבון]</strong>, העריכות שלכם תיוחסנה לשם המשתמש שלכם ותקבלו גם יתרונות אחרים.",
        "anonpreviewwarning": "''אינכם מחוברים לחשבון. שמירה תגרום לכתובת ה־IP שלכם להירשם בהיסטוריית העריכות של הדף.''",
        "missingsummary": "<strong>תזכורת:</strong> לא הזנת תקציר עריכה.\nלחיצה חוזרת על הכפתור \"{{int:savearticle}}\" תגרום לעריכה שלך להישמר בלעדיו.",
-       "selfredirect": "<strong>×\90×\96×\94ר×\94:</strong> × ×\99ס×\99ת ×\9c×\99צ×\95ר ×\94פנ×\99×\94 ×\9e×\93×£ ×\96×\94 ×\9cעצ×\9e×\95.\n×\9c×\97×\99צ×\94 ×\97×\95×\96רת ×¢×\9c ×\94×\9bפת×\95ר \"{{int:savearticle}}\" ×ª×\92ר×\95×\9d ×\9c×\94פנ×\99×\94 ×\9c×\94×\99×\95×\95צר.",
+       "selfredirect": "<strong>×\90×\96×\94ר×\94:</strong> × ×\99ס×\99ת ×\9c×\99צ×\95ר ×\94פנ×\99×\94 ×\9e×\93×£ ×\96×\94 ×\9cעצ×\9e×\95.\n×\90×\95×\9c×\99 ×\9bת×\91ת ×\99×¢×\93 ×©×\92×\95×\99 ×\9c×\94פנ×\99×\94, ×\95×\90×\95×\9c×\99 ×¢×¨×\9bת ×\90ת ×\94×\93×£ ×\94×\9c×\90Ö¾× ×\9b×\95×\9f.\n×\9c×\97×\99צ×\94 ×\97×\95×\96רת ×¢×\9c ×\94×\9bפת×\95ר \"{{int:savearticle}}\" ×ª×\92ר×\95×\9d ×\9c×\94פנ×\99×\94 ×\9c×\94×\99×\95×\95צר ×\91×\9b×\9c ×\96×\90ת.",
        "missingcommenttext": "יש להקליד את ההודעה למטה.",
        "missingcommentheader": "<strong>תזכורת:</strong> לא הזנת נושא/כותרת להודעה זו.\nלחיצה חוזרת על הכפתור \"{{int:savearticle}}\" תגרום לעריכה שלך להישמר ללא נושא/כותרת.",
        "summary-preview": "תצוגה מקדימה של התקציר:",
        "history-feed-empty": "הדף המבוקש לא נמצא.\nייתכן שהוא נמחק, או ששמו שונה.\nבאפשרותך לנסות [[Special:Search|לחפש]] דפים רלוונטיים חדשים.",
        "rev-deleted-comment": "(תקציר העריכה הוסר)",
        "rev-deleted-user": "(שם המשתמש הוסר)",
-       "rev-deleted-event": "(פע×\95×\9cת ×\94×\99×\95×\9e×\9f ×\94×\95סר×\94)",
+       "rev-deleted-event": "(פר×\98×\99×\9d ×\9e×\94×\99×\95×\9e×\9f ×\94×\95סר×\95)",
        "rev-deleted-user-contribs": "[שם המשתמש או כתובת ה־IP הוסרו – העריכה הוסתרה מדף התרומות]",
        "rev-deleted-text-permission": "גרסת הדף הזו <strong>נמחקה</strong>.\nניתן למצוא פרטים על כך ב[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} יומן המחיקות].",
        "rev-suppressed-text-permission": "גרסת הדף הזו <strong>הועלמה</strong>.\nניתן למצוא פרטים על כך ב[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} יומן ההעלמות].",
        "revdelete-legend": "הגדרת הגבלות התצוגה",
        "revdelete-hide-text": "תוכן הגרסה",
        "revdelete-hide-image": "הסתרת תוכן הקובץ",
-       "revdelete-hide-name": "×\94סתרת ×\94פע×\95×\9c×\94 ×\95×\93×£ ×\94×\99×¢×\93",
+       "revdelete-hide-name": "×\94סתרת ×\93×£ ×\94×\99×¢×\93 ×\95×\94פר×\9e×\98ר×\99×\9d",
        "revdelete-hide-comment": "תקציר העריכה",
        "revdelete-hide-user": "שם המשתמש או כתובת ה־IP של העורך",
        "revdelete-hide-restricted": "העלמת המידע גם ממפעילי המערכת",
        "thumbnail-temp-create": "לא הצליחה יצירת קובץ תמונה ממוזערת זמני",
        "thumbnail-dest-create": "לא הייתה אפשרות לשמור את התמונה הממוזערת אל יעדה",
        "thumbnail_invalid_params": "פרמטרים שגויים לתמונה הממוזערת",
+       "thumbnail_toobigimagearea": "קובץ בגודל של יותר מ־$1",
        "thumbnail_dest_directory": "לא ניתן היה ליצור את תיקיית היעד",
        "thumbnail_image-type": "סוג התמונה אינו נתמך",
        "thumbnail_gd-library": "הגדרת הספריה GD אינה שלמה: חסרה הפונקציה $1",
index 00faf9d..6e3f367 100644 (file)
@@ -77,7 +77,8 @@
                        "C.R.",
                        "Elitre",
                        "Laurentius",
-                       "Macofe"
+                       "Macofe",
+                       "Ricordisamoa"
                ]
        },
        "tog-underline": "Sottolinea i collegamenti:",
        "history-feed-empty": "La pagina richiesta non esiste; potrebbe essere stata cancellata dal sito o rinominata. Verificare con la [[Special:Search|pagina di ricerca]] se vi sono nuove pagine.",
        "rev-deleted-comment": "(Oggetto della modifica rimosso)",
        "rev-deleted-user": "(nome utente rimosso)",
-       "rev-deleted-event": "(azione del log rimossa)",
+       "rev-deleted-event": "(dettagli del log rimossi)",
        "rev-deleted-user-contribs": "[nome utente o indirizzo IP rimosso - edit nascosto dalla cronologia]",
        "rev-deleted-text-permission": "Questa versione della pagina è stata '''cancellata'''.\nConsultare il [{{fullurl:{{#Special:Log}}/delete|page={{PAGENAMEE}}}} log delle cancellazioni] per ulteriori dettagli.",
        "rev-suppressed-text-permission": "Questa versione della pagina è stata '''soppressa'''.\nConsultare il [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} log delle soppressioni] per ulteriori dettagli.",
index f6184c0..46796ac 100644 (file)
@@ -61,7 +61,8 @@
                        "Rxy",
                        "Mfuji",
                        "Takot",
-                       "SkyDaisy9"
+                       "SkyDaisy9",
+                       "Los688"
                ]
        },
        "tog-underline": "リンクの下線:",
@@ -89,7 +90,7 @@
        "tog-shownumberswatching": "ページをウォッチしている利用者数を表示",
        "tog-oldsig": "既存の署名:",
        "tog-fancysig": "署名をウィキ文として扱う (自動リンクなし)",
-       "tog-uselivepreview": "ライブプレビューを使用 (開発中)",
+       "tog-uselivepreview": "ライブプレビューを使用",
        "tog-forceeditsummary": "要約欄が空欄の場合に確認を促す",
        "tog-watchlisthideown": "自分の編集をウォッチリストに表示しない",
        "tog-watchlisthidebots": "ボットによる編集をウォッチリストに表示しない",
        "history-feed-empty": "要求されたページは存在しません。\nこのウィキから既に削除されたか、名前が変更された可能性があります。\n[[Special:Search|このウィキの検索]]で関連する新しいページを探してみてください。",
        "rev-deleted-comment": "(要約は除去されています)",
        "rev-deleted-user": "(利用者名は除去されています)",
-       "rev-deleted-event": "(記録は除去されています)",
+       "rev-deleted-event": "(è¨\98é\8c²ã\81®è©³ç´°ã\81¯é\99¤å\8e»ã\81\95ã\82\8cã\81¦ã\81\84ã\81¾ã\81\99)",
        "rev-deleted-user-contribs": "[利用者名またはIPアドレスは除去されました - その編集は投稿記録で非表示にされています]",
        "rev-deleted-text-permission": "この版は<strong>削除されています</strong>。\n[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 削除記録]に詳細情報があるかもしれません。",
        "rev-suppressed-text-permission": "この版は<strong>秘匿されています</strong>。[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 秘匿記録]に詳細情報があるかもしれません。",
index d3c9690..08e42c5 100644 (file)
        "blankarticle": "<strong>Ескерту:</strong> Сіз бастамақшы болған бет бос.\nЕгер сіз «{{int:savearticle}}» дегенші қайта шертсеңіз бет қандайда бір мағлұматсыз басталады.",
        "anoneditwarning": "<strong> Ескерту:</strong>  Сіз жүйеге кірмегенсіз.\nIP мекенжайыңыз бұл беттің өңделу тарихында жазылып алынады.",
        "anonpreviewwarning": "<em>Сіз жүйеге кірмегенсіз. IP мекенжайыңыз бұл беттің өңделу тарихында жазылып алынады.</em>",
-       "missingsummary": "<strong>Ð\95Ñ\81кеÑ\80Ñ\82Ñ\83:</strong> Ó¨Ò£Ð´ÐµÐ¼ÐµÐ½Ñ\96Ò£ Ò\9bÑ\8bÑ\81Ò\9bаÑ\88а Ñ\82үйÑ\96ндемеÑ\81Ñ\96н ÐµÐ½Ð³Ñ\96збепÑ\81Ñ\96з.\n«{{int:savearticle}}» Ð±Ð°Ñ\82Ñ\8bÑ\80маÑ\81Ñ\8bн Ò\9bайÑ\82а Ð±Ð°Ñ\81Ñ\81аңÑ\8bз Ó©Ò£Ð´ÐµÐ½Ð¼ÐµÒ£Ñ\96з Ñ\82үйÑ\96ндемеÑ\81Ñ\96з Ñ\81аÒ\9bÑ\82аладÑ\8b.",
+       "missingsummary": "<strong>Ескерту:</strong> Өңдеменің қысқаша түйіндемесін енгізбепсіз.\n«{{int:savearticle}}» батырмасын қайта бассаңыз өңдемеңіз түйіндемесіз сақталады.",
        "missingcommenttext": "Пікіріңізді төменге енгізіңіз.",
        "missingcommentheader": "<strong>Ескерту:</strong> Бұл пікірге тақырыпы/бас жолы жазбапсыз.\n«{{int:savearticle}}» түймесін тағы бассаңыз өңдемеңіз түйіндемесіз сақталады.",
        "summary-preview": "Қысқаша түйіндемесін қарап шығу:",
index 139f3c1..6022e8f 100644 (file)
        "anoneditwarning": "<strong>Предупредување:</strong> Не сте најавени. Вашата IP-адреса ќе биде јавно видлива ако уредувате. Ако <strong>[$1 се најавите]</strong> или <strong>[$2 направите сметка]</strong>, тогаш уредувањата ќе се припишуваат на вашето корисничко име, покрај другите погодности.",
        "anonpreviewwarning": "''Не сте најавени. Ако ја зачувате, Вашата IP-адреса ќе биде заведена во историјата на уредување на страницата.''",
        "missingsummary": "'''Потсетник:''' Не внесовте опис на измените. Ако притиснете Зачувај повторно, вашите измени ќе се зачуваат без опис.",
-       "selfredirect": "<strong>Ð\9fÑ\80едÑ\83пÑ\80едÑ\83ваÑ\9aе:</strong> Ð¡Ð¾Ð·Ð´Ð°Ð²Ð°Ñ\82е Ð¿Ñ\80енаÑ\81оÑ\87Ñ\83ваÑ\9aе ÐºÐ¾Ð½ Ð¸Ñ\81Ñ\82аÑ\82а Ñ\81Ñ\82аÑ\82иÑ\98а.\nÐ\90ко Ñ\81Ñ\82иÑ\81неÑ\82е Ð½Ð° â\80\9e{{int:savearticle}}â\80\9c Ð¿Ð¾Ð²Ñ\82оÑ\80но, Ñ\82огаÑ\88 Ð¿Ñ\80енаÑ\81оÑ\87Ñ\83ваÑ\9aеÑ\82о ќе се создаде.",
+       "selfredirect": "<strong>Ð\9fÑ\80едÑ\83пÑ\80едÑ\83ваÑ\9aе:</strong> Ð¡Ð¾Ð·Ð´Ð°Ð²Ð°Ñ\82е Ð¿Ñ\80енаÑ\81оÑ\87Ñ\83ваÑ\9aе ÐºÐ¾Ð½ Ð¸Ñ\81Ñ\82аÑ\82а Ñ\81Ñ\82аÑ\82иÑ\98а.\nÐ\9cоже Ð´Ð° Ñ\81Ñ\82е Ñ\83кажале Ð³Ñ\80еÑ\88на Ñ\86елна Ñ\81Ñ\82Ñ\80аниÑ\86а, Ð¸Ð»Ð¸ Ð¿Ð°Ðº Ñ\83Ñ\80едÑ\83ваÑ\82е Ð¿Ð¾Ð³Ñ\80еÑ\88на Ñ\81Ñ\82Ñ\80аниÑ\86а.\nÐ\90ко Ñ\81Ñ\82иÑ\81неÑ\82е Ð½Ð° â\80\9e{{int:savearticle}}â\80\9c Ð¿Ð¾Ð²Ñ\82оÑ\80но, Ñ\82огаÑ\88 Ð¿Ñ\80енаÑ\81оÑ\87Ñ\83ваÑ\9aеÑ\82о Ð±ÐµÐ·Ð´Ñ\80Ñ\83Ð³о ќе се создаде.",
        "missingcommenttext": "Ве молиме внесете коментар подолу.",
        "missingcommentheader": "'''Потсетување:''' Не внесовте наслов за овој коментар.\nАко повторно стиснете на „{{int:savearticle}}“, уредувањето ќе биде зачувано без наслов.",
        "summary-preview": "Изглед на описот:",
        "thumbnail-temp-create": "Не можам да создадам привремена податотека на минијатурата",
        "thumbnail-dest-create": "Не можам да ја зачувам минијатурата во одредницата",
        "thumbnail_invalid_params": "Параметрите на минијатурата се погрешни",
+       "thumbnail_toobigimagearea": "Податотека со димензии поголеми од $1",
        "thumbnail_dest_directory": "Целниот именик не може да се создаде",
        "thumbnail_image-type": "Неподдржан тип на слика",
        "thumbnail_gd-library": "Нецелосни поставки на графичката библиотека: недостасува функцијата $1",
index bbe6a1c..526cd4b 100644 (file)
        "anoneditwarning": "'''Attenzione:''' Nun avite fatto l'acciesso. 'A cronologgia d' 'a vosta sarrà visibbele pubbrecamente si facite cocche cagnamiento. Si <strong>[$1 tràse]</strong> o <strong>[$2 crìe nu cunto]</strong>, 'e cagnamiente vuoste ve sarranno attribbuite a vvuje, nzieme a n'ati migliuramente.",
        "anonpreviewwarning": "''Nun avite fatto 'o login. Sarvann' 'a paggena, l'indirizzo IP d' 'o vuosto sarrà riggistrato dint'a cronologgia.''",
        "missingsummary": "'''Attenziò:''' nun s'è specificato l'oggetto 'e stu cagnamiento. Clicann' 'a \"{{int:savearticle}}\" n'ata vota 'o cagnamiento sarrà sarvato cu l'oggetto abbacante.",
-       "selfredirect": "<strong>Attenziò:</strong> State crianno nu redirect a 'o stesso articolo.\nSi cliccate \"{{int:savearticle}}\" n'ata vota, si criarrà 'o redirect.",
+       "selfredirect": "<strong>Attenziò:</strong> State crianno nu redirect a 'o stesso articolo.\nPuò darse c'avites specificato 'o pizzo sbagliato p' 'o redirect, o ca stavate cagnanno 'o pizzo sbagliato.\nSi cliccate \"{{int:savearticle}}\" n'ata vota, si criarrà 'o redirect.",
        "missingcommenttext": "Pe' piacere scrivete nu commento ccà abbascio.",
        "missingcommentheader": "'''Attenziò:''' nun s'è specificato l'oggetto/titolo 'e stu commento. Clicann' 'a \"{{int:savearticle}}\" n'ata vota 'o cagnamiento sarrà sarvato c' 'o titolo abbacante.",
        "summary-preview": "Anteprimma'e l'oggetto:",
        "thumbnail-temp-create": "Nun se può crià na miniatura temporanea d' 'o file",
        "thumbnail-dest-create": "Nun se può astipà 'a miniatura dint' 'a destinazione",
        "thumbnail_invalid_params": "Parametre 'e miniatura invalide",
+       "thumbnail_toobigimagearea": "Diminziona d' 'o File cchiù grossa d'$1",
        "thumbnail_dest_directory": "Nun se può crià 'a cartella 'e destinazione",
        "thumbnail_image-type": "'O tipo d'immaggene nun è suppurtato",
        "thumbnail_gd-library": "Configurazione d' 'a libbreria GD incompleta: funziona perza $1",
index 1a5e64e..ac04924 100644 (file)
        "revdelete-legend": "Ustaw ograniczenia widoczności",
        "revdelete-hide-text": "Tekst wersji",
        "revdelete-hide-image": "Ukryj zawartość pliku",
-       "revdelete-hide-name": "Ukryj akcję i cel",
+       "revdelete-hide-name": "Ukryj cel i parametry",
        "revdelete-hide-comment": "Opis zmian",
        "revdelete-hide-user": "Nazwa użytkownika/adres IP",
        "revdelete-hide-restricted": "Ukryj informacje przed administratorami tak samo jak przed innymi",
        "nimagelinks": "Używane na $1 {{PLURAL:$1|stronie|stronach}}",
        "ntransclusions": "używany na $1 {{PLURAL:$1|stronie|stronach}}",
        "specialpage-empty": "Ta strona raportu jest pusta.",
-       "lonelypages": "Porzucone strony",
+       "lonelypages": "Osierocone strony",
        "lonelypagestext": "Do poniższych stron nie linkuje żadna inna strona lub nie są one dołączone do innych stron w {{GRAMMAR:MS.lp|{{SITENAME}}}}.",
        "uncategorizedpages": "Nieskategoryzowane strony",
        "uncategorizedcategories": "Nieskategoryzowane kategorie",
index 21a976a..ef2564f 100644 (file)
        "anoneditwarning": "<strong>Aviso</strong>: Não iniciou sessão. O seu endereço IP será registado no histórico de edições desta página. Se <strong>[$1 iniciar sessão]</strong> ou <strong>[$2 criar uma conta]</strong>, as suas edições serão registadas com o seu nome de utilizador(a), bem como usufruir de outros benefícios.",
        "anonpreviewwarning": "''Não iniciou sessão. Ao gravar, registará o seu endereço IP no histórico de edições da página.''",
        "missingsummary": "'''Atenção:''' Não introduziu um resumo da edição.\nSe clicar novamente \"Gravar página\" a sua edição será gravada sem resumo.",
+       "selfredirect": "<strong>Aviso:</strong> Está a redirecionar esta página para si mesma.\nPode ter especificado o destino errado para a página ou até a editar a página errada.\nSe clicar em \"{{int:savearticle}}\" novamente, o redirecionamento será criado na mesma.",
        "missingcommenttext": "Introduza um comentário abaixo, por favor.",
        "missingcommentheader": "'''Atenção:''' Não introduziu um assunto ou cabeçalho para este comentário.\nSe clicar novamente \"{{int:savearticle}}\", a sua edição será gravada sem assunto ou cabeçalho.",
        "summary-preview": "Antevisão do resumo:",
        "history-feed-empty": "A página solicitada não existe.\nPode ter sido eliminada da wiki ou o nome sido alterado.\nTente [[Special:Search|pesquisar na wiki]] novas páginas relevantes.",
        "rev-deleted-comment": "(resumo da edição suprimido)",
        "rev-deleted-user": "(nome de utilizador removido)",
-       "rev-deleted-event": "(entrada removida)",
+       "rev-deleted-event": "(registos de detalhes eliminados)",
        "rev-deleted-user-contribs": "[nome de utilizador ou IP removido - edição ocultada das contribuições]",
        "rev-deleted-text-permission": "Esta revisão de página foi <strong>eliminada</strong>.\nEncontrará detalhes no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registo de eliminações].",
        "rev-suppressed-text-permission": "Esta revisão de página foi <strong>suprimida</strong>.\nPode consultar os detalhes no [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registo de supressões].",
        "revdelete-legend": "Definir restrições de visibilidade",
        "revdelete-hide-text": "Revisão do texto",
        "revdelete-hide-image": "Ocultar conteúdo do ficheiro",
-       "revdelete-hide-name": "Ocultar operação e destino",
+       "revdelete-hide-name": "Ocultar destino e parâmetros",
        "revdelete-hide-comment": "Resumo da edição",
        "revdelete-hide-user": "Nome de utilizador/endereço de IP",
        "revdelete-hide-restricted": "Ocultar dados dos administradores e de todos os outros",
index 813064d..42ae44c 100644 (file)
        "content-model-text": "Name for the plain text content model, used when decribing what type of content a page contains.\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}\n{{Identical|Plain text}}",
        "content-model-javascript": "Name for the JavaScript content model, used when decribing what type of content a page contains.\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}",
        "content-model-css": "Name for the CSS content model, used when decribing what type of content a page contains.\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}",
+       "content-model-json": "Name for the JSON content model, used when decribing what type of content a page contains.\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}",
+       "content-json-empty-object": "Used to represent an object with no properties on a JSON content model page.",
+       "content-json-empty-array": "Used to represent an array with no values on a JSON content model page.",
        "duplicate-args-category": "This message is used as a category name for a [[mw:Special:MyLanguage/Help:Tracking categories|tracking category]] where pages are placed automatically if they contain template calls that use duplicates of arguments, such as <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "duplicate-args-category-desc": "Duplicate arguments category description. Shown on [[Special:TrackingCategories]].\n\nSee also:\n* {{msg-mw|Duplicate-args-category}}",
        "expensive-parserfunction-warning": "On some (expensive) [[MetaWikipedia:Help:ParserFunctions|parser functions]] (e.g. <code><nowiki>{{#ifexist:}}</nowiki></code>) there is a limit of how many times it may be used. This is an error message shown when the limit is exceeded.\n\nParameters:\n* $1 - the current number of parser function calls\n* $2 - the allowed number of parser function calls\nSee also [[:mw:Manual:$wgExpensiveParserFunctionLimit|$wgExpensiveParserFunctionLimit in the MediaWiki manual]].\n\nSee also:\n* {{msg-mw|Expensive-parserfunction-category}}",
index ff387d9..b465a25 100644 (file)
        "anoneditwarning": "<strong>Atenție:</strong> Nu v-ați autentificat. Adresa dumneavoastră IP va fi vizibilă în mod public dacă efectuați modificări. Dacă vă <strong>[$1 autentificați]</strong> sau vă <strong>[$2 creați un cont]</strong>, modificările dumneavoastră vor fi asociate numelui de utilizator, pe lângă alte beneficii.",
        "anonpreviewwarning": "''Nu v-ați autentificat. Dacă salvați pagina adresa dumneavoastră IP va fi înregistrată în istoric.''",
        "missingsummary": "'''Atenție:''' Nu ați completat caseta „descriere modificări”. Dacă apăsați din nou butonul „salvează pagina” modificările vor fi salvate fără descriere.",
-       "selfredirect": "<strong>Atenție:</strong> Sunteți pe cale să creați o redirecționare către același articol.\nDacă apăsați din nou pe „{{int:savearticle}}”, redirecționarea va fi creată.",
+       "selfredirect": "<strong>Atenție:</strong> Sunteți pe cale să redirecționați această pagină către ea însăși.\nProbabil ați greșit ținta redirecționării sau ați modificat pagina greșită.\nDacă apăsați din nou pe „{{int:savearticle}}”, redirecționarea va fi creată oricum.",
        "missingcommenttext": "Vă rugăm să introduceți un comentariu.",
        "missingcommentheader": "'''Atenție,''' nu ați pus titlu sau subiect la acest comentariu.\nDacă dați din nou clic pe „{{int:savearticle}}” modificarea va fi salvată fără titlu.",
        "summary-preview": "Previzualizare descriere:",
        "thumbnail-temp-create": "Imposibil de creat miniatura temporară",
        "thumbnail-dest-create": "Imposibil de salvat miniatura la destinație",
        "thumbnail_invalid_params": "Parametrii invalizi ai imaginii miniatură",
+       "thumbnail_toobigimagearea": "Fișier cu dimensiuni mai mari de $1",
        "thumbnail_dest_directory": "Nu poate fi creat directorul destinație",
        "thumbnail_image-type": "Acest tip de imagine nu este suportat",
        "thumbnail_gd-library": "Configurație incompletă a bibliotecii GD: lipsește funcția $1",
index 6f83e8e..832b19d 100644 (file)
        "anoneditwarning": "<strong>Внимание!</strong> Вы не авторизовались на сайте. Ваш IP-адрес будет публично видимым, если вы будете вносить любые правки. Если вы <strong>[$1 войдёте]</strong> или <strong>[$2 создадите учётную запись]</strong>, правки вместо этого будут связаны с вашим именем пользователя, а также у вас появятся другие преимущества.",
        "anonpreviewwarning": "''Вы не представились системе. Сохранение приведёт к записи вашего IP-адреса в историю изменений страницы.''",
        "missingsummary": "'''Напоминание.''' Вы не дали краткого описания изменений. При повторном нажатии на кнопку «{{int:savearticle}}», ваши изменения будут сохранены без комментария.",
+       "selfredirect": "<strong>Внимание:</strong> Вы создаете перенаправление на ту же самую статью.\nЕсли Вы нажмёте кнопку «{{int:savearticle}}» ещё раз, перенаправление всё же будет создано.",
        "missingcommenttext": "Пожалуйста, введите ниже ваше сообщение.",
        "missingcommentheader": "'''Напоминание.''' Вы не указали тему/заголовок для этого комментария.\nПри повторном нажатии на кнопку «{{int:savearticle}}», ваша правка будет записана без заголовка.",
        "summary-preview": "Описание будет:",
        "right-protect": "изменение уровня защиты страниц и правка каскадно защищённых страниц",
        "right-editprotected": "правка страниц, защищённых как «{{int:protect-level-sysop}}»",
        "right-editsemiprotected": "правка страниц, защищённых как «{{int:protect-level-autoconfirmed}}»",
+       "right-editcontentmodel": "Редактирование контентной модели страницы",
        "right-editinterface": "изменение пользовательского интерфейса",
        "right-editusercssjs": "правка CSS- и JS-файлов других участников",
        "right-editusercss": "правка CSS-файлов других участников",
        "action-viewmywatchlist": "просмотр вашего списка наблюдения",
        "action-viewmyprivateinfo": "просмотр вашей частной информации",
        "action-editmyprivateinfo": "редактирование вашей частной информации",
+       "action-editcontentmodel": "редактирование контентной модели страницы",
        "nchanges": "$1 {{PLURAL:$1|изменение|изменения|изменений}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|с последнего посещения}}",
        "enhancedrc-history": "история",
index a965ded..c2dbff8 100644 (file)
        "anoneditwarning": "<strong>Opozorilo:</strong> Niste prijavljeni. Vaš IP-naslov bo javno viden, če naredite kakršno koli urejanje. Če se <strong>[$1 prijavite]</strong> ali <strong>[$2 ustvarite račun]</strong>, bodo vaša urejanja pripisana vašemu uporabniškemu imenu skupaj z drugimi prednostmi.",
        "anonpreviewwarning": "Niste prijavljeni. Ob spremembi strani se bo vaš IP-naslov zapisal v zgodovini urejanja te strani.",
        "missingsummary": "'''Opozorilo:''' Niste napisali povzetka urejanja. Ob ponovnem kliku gumba ''Shrani'' se bo vaše urejanje shranilo brez njega.",
-       "selfredirect": "<strong>Opozorilo:</strong> Ustvarjate preusmeritev na isti članek.\nČe ponovno kliknete »{{int:savearticle}}«, bomo preusmeritev ustvarili.",
+       "selfredirect": "<strong>Opozorilo:</strong> Stran preusmerjate na samo nase.\nMorda ste za cilj preusmeritve navedli napačno stran ali pa morda urejate napačno stran.\nČe ponovno kliknete »{{int:savearticle}}«, bomo preusmeritev vseeno ustvarili.",
        "missingcommenttext": "Prosimo, vpišite v spodnje polje komentar.",
        "missingcommentheader": "'''Opozorilo:''' Niste vnesli zadeve/naslova za ta komentar.\nČe boste ponovno kliknili »{{int:savearticle}}«, bo vaše urejanje shranjeno brez le-tega.",
        "summary-preview": "Predogled povzetka",
        "thumbnail-temp-create": "Ne morem ustvariti začasne datoteke sličice",
        "thumbnail-dest-create": "Ne morem shraniti sličice na ciljno mesto",
        "thumbnail_invalid_params": "Neveljavni parametri za sličico",
+       "thumbnail_toobigimagearea": "Datoteke z dimenzijami, večjimi od $1",
        "thumbnail_dest_directory": "Ne morem ustvariti ciljnega direktorija",
        "thumbnail_image-type": "Vrsta slike ni podprta",
        "thumbnail_gd-library": "Nepopolna konfiguracija knjižice GD: manjka funkcija $1",
index 6f52692..ae7899e 100644 (file)
        "thumbnail-temp-create": "Kunde inte skapa temporär miniatyrfil",
        "thumbnail-dest-create": "Kunde inte spara miniatyr till destinationen",
        "thumbnail_invalid_params": "Ogiltiga parametrar för miniatyrbilden",
+       "thumbnail_toobigimagearea": "Fil med dimensioner som är större än $1",
        "thumbnail_dest_directory": "Kan inte skapa målkatalogen",
        "thumbnail_image-type": "Bildtypen stöds inte",
        "thumbnail_gd-library": "Inkomplett GD library konfigurering: saknar funktionen $1",
index 742f39d..1864352 100644 (file)
        "anoneditwarning": "<strong>警告:</strong>您没有登录。您做出任何编辑后您的IP地址会公开可见。如果您<strong>[$1 登陆]</strong>或<strong>[$2 注册]</strong>一个账户,您的编辑将归属于您的用户名,以及有其他好处。",
        "anonpreviewwarning": "<em>你没有登录。保存会记录你的IP地址于该页面的编辑历史中。</em>",
        "missingsummary": "'''提示:'''你没有提供编辑摘要。如果你再次点击“{{int:savearticle}}”,你的编辑将不带编辑摘要保存。",
-       "selfredirect": "<strong>è­¦å\91\8aï¼\9a</strong>æ\82¨æ­£å\9c¨å\88\9b建é\87\8då®\9aå\90\91å\88°å\90\8cä¸\80æ\9d¡ç\9b®ç\9a\84é\87\8då®\9aå\90\91ã\80\82\nå¦\82æ\9e\9cæ\82¨å\86\8d次ç\82¹å\87»â\80\9c{{int:savearticle}}â\80\9dï¼\8cé\87\8då®\9aå\90\91å°\86被创建。",
+       "selfredirect": "<strong>è­¦å\91\8aï¼\9a</strong>æ\82¨æ­£å\9c¨å°\86此页é\9d¢é\87\8då®\9aå\90\91è\87³å®\83è\87ªå·±ã\80\82\næ\82¨å\8f¯è\83½æ\8c\87å®\9aäº\86é\94\99误ç\9a\84é\87\8då®\9aå\90\91ç\9b®æ \87ï¼\8cæ\88\96è\80\85æ\82¨æ­£å\9c¨ç¼\96è¾\91é\94\99误ç\9a\84页é\9d¢ã\80\82\nå¦\82æ\9e\9cæ\82¨å\86\8d次ç\82¹å\87»â\80\9c{{int:savearticle}}â\80\9dï¼\8cé\87\8då®\9aå\90\91å°\86æ\97 è®ºå¦\82ä½\95被创建。",
        "missingcommenttext": "请在下面输入评论。",
        "missingcommentheader": "'''提示:''' 您还没有为此评论提供一个标题。如果您再次点击“{{int:savearticle}}”,您的编辑将不带标题保存。",
        "summary-preview": "摘要预览:",
        "history-feed-empty": "所请求的页面不存在。它可能已被删除或重命名。\n尝试[[Special:Search|搜索本站]]获得相关的新建页面。",
        "rev-deleted-comment": "(编辑摘要被删除)",
        "rev-deleted-user": "(用户名被删除)",
-       "rev-deleted-event": "(日志操作被删除)",
+       "rev-deleted-event": "(日志详情已移除)",
        "rev-deleted-user-contribs": "[用户名或IP地址被删除 - 编辑在贡献中隐藏]",
        "rev-deleted-text-permission": "本页面版本已被'''删除'''。详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。",
        "rev-suppressed-text-permission": "此页面修订已经被<strong>监督隐藏</strong>。详细信息可在[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 监督日志]中找到。",
        "revdelete-legend": "设置可见性之限制",
        "revdelete-hide-text": "版本文字",
        "revdelete-hide-image": "隐藏文件内容",
-       "revdelete-hide-name": "隐藏动作和目标",
+       "revdelete-hide-name": "隐藏目标和参数",
        "revdelete-hide-comment": "编辑摘要",
        "revdelete-hide-user": "编者用户名/IP地址",
        "revdelete-hide-restricted": "同时阻止管理员与其他用户查看数据",
        "thumbnail-temp-create": "无法创建临时缩略图文件",
        "thumbnail-dest-create": "无法将缩略图保存到目标地点",
        "thumbnail_invalid_params": "不正确的缩略图参数",
+       "thumbnail_toobigimagearea": "尺寸超过$1的文件",
        "thumbnail_dest_directory": "无法建立目标目录",
        "thumbnail_image-type": "图像类型不支持",
        "thumbnail_gd-library": "未完成的GD设置:功能遗失 $1",
index 108fe9f..2f8b7d2 100644 (file)
@@ -1321,7 +1321,7 @@ abstract class LoggedUpdateMaintenance extends Maintenance {
        }
 
        /**
-        * Message to show the the update log was unable to log the completion of this update
+        * Message to show that the update log was unable to log the completion of this update
         * @return string
         */
        protected function updatelogFailedMessage() {
index 3e2c6c9..dd4f760 100644 (file)
@@ -44,7 +44,7 @@ class FetchText extends Maintenance {
         *   \n
         *   text  (may be empty)
         *
-        * note that that the text string itself is *not* followed by newline
+        * note that the text string itself is *not* followed by newline
         */
        public function execute() {
                $db = wfGetDB( DB_SLAVE );
index f77978f..686d9f2 100644 (file)
@@ -84,7 +84,7 @@ class PopulateParentId extends LoggedUpdateMaintenance {
                                                "rev_id < " . intval( $row->rev_id ) ),
                                        __METHOD__,
                                        array( 'ORDER BY' => 'rev_id DESC' ) );
-                               # If there are none, check the the highest ID with a lower timestamp
+                               # If there are none, check the highest ID with a lower timestamp
                                if ( !$previousID ) {
                                        # Get the highest older timestamp
                                        $lastTimestamp = $db->selectField(
index 45cc339..eb7eca1 100644 (file)
@@ -141,7 +141,7 @@ class RecompressTracked {
                        $header .= "({$this->slaveId})";
                }
                $header .= ' ' . wfWikiID();
-               wfErrorLog( sprintf( "%-50s %s\n", $header, $msg ), $file );
+               MWLoggerLegacyLogger::emit( sprintf( "%-50s %s\n", $header, $msg ), $file );
        }
 
        /**
index 136530a..19a775a 100644 (file)
@@ -1,4 +1,4 @@
-The icons used here are derived from the crystalsvg icons in the the
+The icons used here are derived from the crystalsvg icons in the
 pics/crystalsvg/ directory of kdelibs-3.4.0 they were modified on 2005-05-15
 by Ævar Arnfjörð Bjarmason for use in MediaWiki.
 
index 26171f1..d3db318 100644 (file)
@@ -5,5 +5,15 @@
                ]
        },
        "ooui-outline-control-move-down": "Onderwarp ummeneer zetten",
-       "ooui-outline-control-move-up": "Onderwarp umhoge zetten"
+       "ooui-outline-control-move-up": "Onderwarp umhoge zetten",
+       "ooui-outline-control-remove": "Element vortdoon",
+       "ooui-toolbar-more": "Meer",
+       "ooui-toolgroup-expand": "Meer",
+       "ooui-toolgroup-collapse": "Minder",
+       "ooui-dialog-message-accept": "Okee",
+       "ooui-dialog-message-reject": "Aofbreken",
+       "ooui-dialog-process-error": "Der gung iets fout",
+       "ooui-dialog-process-dismiss": "Sluten",
+       "ooui-dialog-process-retry": "Opniej proberen",
+       "ooui-dialog-process-continue": "Deurgaon"
 }
index 5737548..a40728a 100644 (file)
        "ooui-outline-control-move-up": "Prestavi predmet višje",
        "ooui-outline-control-remove": "Odstrani vnos",
        "ooui-toolbar-more": "Več",
+       "ooui-toolgroup-expand": "Več",
+       "ooui-toolgroup-collapse": "Manj",
        "ooui-dialog-message-accept": "V redu",
        "ooui-dialog-message-reject": "Prekliči",
        "ooui-dialog-process-error": "Nekaj je šlo narobe",
        "ooui-dialog-process-dismiss": "Skrij",
-       "ooui-dialog-process-retry": "Poskusi znova"
+       "ooui-dialog-process-retry": "Poskusi znova",
+       "ooui-dialog-process-continue": "Nadaljuj"
 }
index f4d4a5c..0197a4c 100644 (file)
@@ -28,5 +28,6 @@
        "ooui-dialog-message-reject": "Скасувати",
        "ooui-dialog-process-error": "Щось пішло не так",
        "ooui-dialog-process-dismiss": "Приховати",
-       "ooui-dialog-process-retry": "Спробуйте ще раз"
+       "ooui-dialog-process-retry": "Спробуйте ще раз",
+       "ooui-dialog-process-continue": "Продовжити"
 }
index 4c4512b..cc6550b 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.5.0
+ * OOjs UI v0.6.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-12T20:13:21Z
+ * Date: 2014-12-16T21:01:07Z
  */
 .oo-ui-progressBarWidget-slide-frames from {
        margin-left: -40%;
        display: block;
        background: rgba(0, 0, 0, 0.4);
 }
-.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
+.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
        overflow-y: hidden;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
        width: 100%;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-scrollable {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-scrollable {
        overflow-y: auto;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
        padding: 2em;
 }
-.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
+.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
        position: absolute;
        top: 0;
        left: 0;
        bottom: 3em;
        overflow-y: auto;
 }
-.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
+.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
        padding: 1.5em;
 }
 .oo-ui-bookletLayout-outlinePanel {
        border-right: solid 1px #dddddd;
 }
-.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
+.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
        box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
 }
 .oo-ui-fieldLayout {
 .oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        display: block;
 }
+.oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-tool-title {
        display: none;
 }
 .oo-ui-popupToolGroup-right > .oo-ui-toolGroup-tools {
        right: 0;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
-       display: inline-block;
+.oo-ui-popupToolGroup .oo-ui-tool-link {
+       display: table;
+       width: 100%;
        vertical-align: middle;
+       white-space: nowrap;
+       text-decoration: none;
 }
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon,
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       display: inline-table;
-       vertical-align: middle;
-       width: 100%;
-       margin-right: -2.5em;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-title-text,
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
        display: table-cell;
+       vertical-align: middle;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-title-text {
-       width: 100%;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
        text-align: right;
-       padding-right: 2.5em;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel:not(:empty) {
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel:not(:empty) {
        padding-left: 3em;
 }
 .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        height: 2em;
        width: 2em;
-       margin-right: 0.25em;
+       min-width: 2em;
 }
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
+       padding-left: 0.25em;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
        line-height: 2em;
        font-size: 0.8em;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
        color: #888888;
 }
 .oo-ui-listToolGroup .oo-ui-tool {
-       display: inline-block;
-       width: 100%;
+       display: block;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
 .oo-ui-listToolGroup .oo-ui-tool-link {
-       display: block;
        cursor: pointer;
-       white-space: nowrap;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
        cursor: default;
 .oo-ui-listToolGroup .oo-ui-tool {
        border: solid 1px transparent;
        margin: -1px 0;
-}
-.oo-ui-listToolGroup .oo-ui-tool-link {
-       padding-right: 0.5em;
+       padding: 0 0.25em 0 0;
 }
 .oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled {
        border-color: rgba(0, 0, 0, 0.1);
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
        color: #cccccc;
 }
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-accel {
        color: #dddddd;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
        display: block;
 }
 .oo-ui-menuToolGroup .oo-ui-tool-link {
-       display: block;
        cursor: pointer;
-       white-space: nowrap;
 }
 .oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
        cursor: default;
 .oo-ui-menuToolGroup.oo-ui-popupToolGroup-active {
        border-color: rgba(0, 0, 0, 0.25);
 }
-.oo-ui-menuToolGroup .oo-ui-tool-link {
-       padding: 0 1em 0 0.25em;
-       display: block;
-       cursor: pointer;
-       white-space: nowrap;
+.oo-ui-menuToolGroup .oo-ui-tool {
+       padding: 0 0.75em 0 0.25em;
 }
 .oo-ui-menuToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        background-image: none;
 }
 .oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
        border-radius: 0;
-       margin-bottom: -1px;
        margin-left: -1px;
 }
 .oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:first-child .oo-ui-buttonElement-button {
index eefc652..6fcf2fc 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.5.0
+ * OOjs UI v0.6.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-12T20:13:09Z
+ * Date: 2014-12-16T21:00:55Z
  */
 /* Instantiation */
 
index 1236d0b..fee6163 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.5.0
+ * OOjs UI v0.6.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-12T20:13:21Z
+ * Date: 2014-12-16T21:01:07Z
  */
 .oo-ui-progressBarWidget-slide-frames from {
        margin-left: -40%;
        display: block;
        background: rgba(0, 0, 0, 0.4);
 }
-.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
+.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
        overflow-y: hidden;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
        width: 100%;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-scrollable {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-scrollable {
        overflow-y: auto;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
        padding: 2em;
 }
-.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
+.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
        position: absolute;
        top: 0;
        left: 0;
        bottom: 3em;
        overflow-y: auto;
 }
-.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
+.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
        padding: 1.5em;
 }
 .oo-ui-bookletLayout-outlinePanel {
        border-right: solid 1px #dddddd;
 }
-.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
+.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
        box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
 }
 .oo-ui-fieldLayout {
 .oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        display: block;
 }
+.oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-tool-title {
        display: none;
 }
 .oo-ui-popupToolGroup-right > .oo-ui-toolGroup-tools {
        right: 0;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
-       display: inline-block;
+.oo-ui-popupToolGroup .oo-ui-tool-link {
+       display: table;
+       width: 100%;
        vertical-align: middle;
+       white-space: nowrap;
+       text-decoration: none;
 }
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon,
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       display: inline-table;
-       vertical-align: middle;
-       width: 100%;
-       margin-right: -2.5em;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-title-text,
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
        display: table-cell;
+       vertical-align: middle;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-title-text {
-       width: 100%;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
        text-align: right;
-       padding-right: 2.5em;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel:not(:empty) {
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel:not(:empty) {
        padding-left: 3em;
 }
 .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        height: 2em;
        width: 2em;
-       margin-right: 0.25em;
+       min-width: 2em;
 }
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
+       padding-left: 0.25em;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
        line-height: 2em;
        font-size: 0.8em;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
        color: #888888;
 }
 .oo-ui-listToolGroup .oo-ui-tool {
-       display: inline-block;
-       width: 100%;
+       display: block;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
 .oo-ui-listToolGroup .oo-ui-tool-link {
-       display: block;
        cursor: pointer;
-       white-space: nowrap;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
        cursor: default;
 .oo-ui-listToolGroup .oo-ui-tool {
        border: solid 1px transparent;
        margin: -1px 0;
-}
-.oo-ui-listToolGroup .oo-ui-tool-link {
-       padding-right: 0.5em;
+       padding: 0 0.25em 0 0;
 }
 .oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled {
        border-color: rgba(0, 0, 0, 0.1);
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
        color: #cccccc;
 }
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-accel {
        color: #dddddd;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
        display: block;
 }
 .oo-ui-menuToolGroup .oo-ui-tool-link {
-       display: block;
        cursor: pointer;
-       white-space: nowrap;
 }
 .oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
        cursor: default;
 .oo-ui-menuToolGroup.oo-ui-popupToolGroup-active {
        border-color: rgba(0, 0, 0, 0.25);
 }
-.oo-ui-menuToolGroup .oo-ui-tool-link {
-       padding: 0 1em 0 0.25em;
-       display: block;
-       cursor: pointer;
-       white-space: nowrap;
+.oo-ui-menuToolGroup .oo-ui-tool {
+       padding: 0 0.75em 0 0.25em;
 }
 .oo-ui-menuToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        background-image: none;
 }
 .oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
        border-radius: 0;
-       margin-bottom: -1px;
        margin-left: -1px;
 }
 .oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:first-child .oo-ui-buttonElement-button {
index 2d1060c..24e0df6 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.5.0
+ * OOjs UI v0.6.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-12T20:13:21Z
+ * Date: 2014-12-16T21:01:07Z
  */
 .oo-ui-progressBarWidget-slide-frames from {
        margin-left: -40%;
        display: block;
        background: rgba(0, 0, 0, 0.4);
 }
-.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
+.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
        overflow-y: hidden;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
        width: 100%;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-scrollable {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-scrollable {
        overflow-y: auto;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
        padding: 2em;
 }
-.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
+.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
        position: absolute;
        top: 0;
        left: 0;
        bottom: 3em;
        overflow-y: auto;
 }
-.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
+.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
        padding: 1.5em;
 }
 .oo-ui-bookletLayout-outlinePanel {
        border-right: solid 1px #dddddd;
 }
-.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
+.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
        box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
 }
 .oo-ui-fieldLayout {
 .oo-ui-toolGroup.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-title {
        color: #000000;
 }
-.oo-ui-toolGroup.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-toolGroup.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-accel {
        color: #888888;
 }
 .oo-ui-barToolGroup > .oo-ui-iconElement-icon,
 .oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        display: block;
 }
+.oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-tool-title {
        display: none;
 }
 .oo-ui-popupToolGroup-right > .oo-ui-toolGroup-tools {
        right: 0;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
-       display: inline-block;
+.oo-ui-popupToolGroup .oo-ui-tool-link {
+       display: table;
+       width: 100%;
        vertical-align: middle;
+       white-space: nowrap;
+       text-decoration: none;
 }
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon,
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       display: inline-table;
-       vertical-align: middle;
-       width: 100%;
-       margin-right: -2.5em;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-title-text,
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
        display: table-cell;
+       vertical-align: middle;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-title-text {
-       width: 100%;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
        text-align: right;
-       padding-right: 2.5em;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel:not(:empty) {
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel:not(:empty) {
        padding-left: 3em;
 }
 .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        height: 2em;
        width: 2em;
-       margin-right: 0.25em;
+       min-width: 2em;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
+       padding-left: 0.25em;
 }
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
        line-height: 2em;
        font-size: 0.8em;
 }
 .oo-ui-listToolGroup .oo-ui-tool {
-       display: inline-block;
-       width: 100%;
+       display: block;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
 .oo-ui-listToolGroup .oo-ui-tool-link {
-       display: block;
        cursor: pointer;
-       white-space: nowrap;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
        cursor: default;
 }
 .oo-ui-listToolGroup .oo-ui-tool {
-       padding: 0 0.25em;
-}
-.oo-ui-listToolGroup .oo-ui-tool-link {
-       padding-right: 0.5em;
+       padding: 0 0.5em 0 0.25em;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
        background-color: #eeeeee;
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
        color: #cccccc;
 }
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-accel {
        color: #dddddd;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
        display: block;
 }
 .oo-ui-menuToolGroup .oo-ui-tool-link {
-       display: block;
        cursor: pointer;
-       white-space: nowrap;
 }
 .oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
        cursor: default;
 .oo-ui-menuToolGroup.oo-ui-popupToolGroup-active {
        border-color: #aaaaaa;
 }
-.oo-ui-menuToolGroup .oo-ui-tool-link {
-       padding: 0 1em 0 0.25em;
-       display: block;
-       cursor: pointer;
-       white-space: nowrap;
+.oo-ui-menuToolGroup .oo-ui-tool {
+       padding: 0 0.75em 0 0.25em;
 }
 .oo-ui-menuToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        background-image: none;
 }
 .oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
        border-radius: 0;
-       margin-bottom: -1px;
        margin-left: -1px;
 }
 .oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:first-child .oo-ui-buttonElement-button {
 .oo-ui-radioInputWidget {
        position: relative;
        line-height: 2em;
+       white-space: nowrap;
 }
 .oo-ui-radioInputWidget * {
        font: inherit;
        margin: 0 0.4em;
 }
 .oo-ui-radioInputWidget input[type="radio"] + span::before {
-               transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+       -webkit-transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
           -moz-transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            -ms-transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
             -o-transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
-       -webkit-transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+               transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        content: "";
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
index b590c97..f39ec57 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.5.0
+ * OOjs UI v0.6.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-12T20:13:09Z
+ * Date: 2014-12-16T21:00:55Z
  */
 /**
  * @class
index 0713084..4409643 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.5.0
+ * OOjs UI v0.6.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-12T20:13:21Z
+ * Date: 2014-12-16T21:01:07Z
  */
 .oo-ui-progressBarWidget-slide-frames from {
        margin-left: -40%;
        display: block;
        background: rgba(0, 0, 0, 0.4);
 }
-.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
+.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
        overflow-y: hidden;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
        width: 100%;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-scrollable {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-scrollable {
        overflow-y: auto;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
        padding: 2em;
 }
-.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
+.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
        position: absolute;
        top: 0;
        left: 0;
        bottom: 3em;
        overflow-y: auto;
 }
-.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
+.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
 }
-.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
+.oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout {
        padding: 1.5em;
 }
 .oo-ui-bookletLayout-outlinePanel {
        border-right: solid 1px #dddddd;
 }
-.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
+.oo-ui-bookletLayout-outlinePanel .oo-ui-outlineControlsWidget {
        box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
 }
 .oo-ui-fieldLayout {
 .oo-ui-toolGroup.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-title {
        color: #000000;
 }
-.oo-ui-toolGroup.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-toolGroup.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-accel {
        color: #888888;
 }
 .oo-ui-barToolGroup > .oo-ui-iconElement-icon,
 .oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        display: block;
 }
+.oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-barToolGroup .oo-ui-tool-link .oo-ui-tool-title {
        display: none;
 }
 .oo-ui-popupToolGroup-right > .oo-ui-toolGroup-tools {
        right: 0;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
-       display: inline-block;
+.oo-ui-popupToolGroup .oo-ui-tool-link {
+       display: table;
+       width: 100%;
        vertical-align: middle;
+       white-space: nowrap;
+       text-decoration: none;
 }
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon,
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       display: inline-table;
-       vertical-align: middle;
-       width: 100%;
-       margin-right: -2.5em;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-title-text,
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
        display: table-cell;
+       vertical-align: middle;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-title-text {
-       width: 100%;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
        text-align: right;
-       padding-right: 2.5em;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel:not(:empty) {
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel:not(:empty) {
        padding-left: 3em;
 }
 .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        height: 2em;
        width: 2em;
-       margin-right: 0.25em;
+       min-width: 2em;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
+       padding-left: 0.25em;
 }
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
        line-height: 2em;
        font-size: 0.8em;
 }
 .oo-ui-listToolGroup .oo-ui-tool {
-       display: inline-block;
-       width: 100%;
+       display: block;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
 .oo-ui-listToolGroup .oo-ui-tool-link {
-       display: block;
        cursor: pointer;
-       white-space: nowrap;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
        cursor: default;
 }
 .oo-ui-listToolGroup .oo-ui-tool {
-       padding: 0 0.25em;
-}
-.oo-ui-listToolGroup .oo-ui-tool-link {
-       padding-right: 0.5em;
+       padding: 0 0.5em 0 0.25em;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
        background-color: #eeeeee;
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
        color: #cccccc;
 }
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title .oo-ui-tool-accel {
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-accel {
        color: #dddddd;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
        display: block;
 }
 .oo-ui-menuToolGroup .oo-ui-tool-link {
-       display: block;
        cursor: pointer;
-       white-space: nowrap;
 }
 .oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
        cursor: default;
 .oo-ui-menuToolGroup.oo-ui-popupToolGroup-active {
        border-color: #aaaaaa;
 }
-.oo-ui-menuToolGroup .oo-ui-tool-link {
-       padding: 0 1em 0 0.25em;
-       display: block;
-       cursor: pointer;
-       white-space: nowrap;
+.oo-ui-menuToolGroup .oo-ui-tool {
+       padding: 0 0.75em 0 0.25em;
 }
 .oo-ui-menuToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        background-image: none;
 }
 .oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
        border-radius: 0;
-       margin-bottom: -1px;
        margin-left: -1px;
 }
 .oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:first-child .oo-ui-buttonElement-button {
 .oo-ui-radioInputWidget {
        position: relative;
        line-height: 2em;
+       white-space: nowrap;
 }
 .oo-ui-radioInputWidget * {
        font: inherit;
        margin: 0 0.4em;
 }
 .oo-ui-radioInputWidget input[type="radio"] + span::before {
-               transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+       -webkit-transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
           -moz-transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            -ms-transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
             -o-transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
-       -webkit-transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+               transition: background-size 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        content: "";
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
index 7f519a6..5f75895 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.5.0
+ * OOjs UI v0.6.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-12T20:13:09Z
+ * Date: 2014-12-16T21:00:55Z
  */
 ( function ( OO ) {
 
@@ -2730,10 +2730,9 @@ OO.ui.WindowManager.prototype.afterWindowResize = function () {
  *
  * @param {jQuery.Event} e Mouse wheel event
  */
-OO.ui.WindowManager.prototype.onWindowMouseWheel = function ( e ) {
-       // Kill all events in the parent window if the child window is isolated,
-       // or if the event didn't come from the child window
-       return !( this.shouldIsolate() || !$.contains( this.getCurrentWindow().$frame[0], e.target ) );
+OO.ui.WindowManager.prototype.onWindowMouseWheel = function () {
+       // Kill all events in the parent window if the child window is isolated
+       return !this.shouldIsolate();
 };
 
 /**
@@ -2751,9 +2750,8 @@ OO.ui.WindowManager.prototype.onDocumentKeyDown = function ( e ) {
                case OO.ui.Keys.UP:
                case OO.ui.Keys.RIGHT:
                case OO.ui.Keys.DOWN:
-                       // Kill all events in the parent window if the child window is isolated,
-                       // or if the event didn't come from the child window
-                       return !( this.shouldIsolate() || !$.contains( this.getCurrentWindow().$frame[0], e.target ) );
+                       // Kill all events in the parent window if the child window is isolated
+                       return !this.shouldIsolate();
        }
 };
 
@@ -3183,6 +3181,10 @@ OO.ui.WindowManager.prototype.toggleGlobalEvents = function ( on ) {
                                // Start listening for top-level window dimension changes
                                'orientationchange resize': this.onWindowResizeHandler
                        } );
+                       // Disable window scrolling in isolated windows
+                       if ( !this.shouldIsolate() ) {
+                               $( this.getElementDocument().body ).css( 'overflow', 'hidden' );
+                       }
                        this.globalEvents = true;
                }
        } else if ( this.globalEvents ) {
@@ -3197,6 +3199,9 @@ OO.ui.WindowManager.prototype.toggleGlobalEvents = function ( on ) {
                        // Stop listening for top-level window dimension changes
                        'orientationchange resize': this.onWindowResizeHandler
                } );
+               if ( !this.shouldIsolate() ) {
+                       $( this.getElementDocument().body ).css( 'overflow', '' );
+               }
                this.globalEvents = false;
        }
 
@@ -5433,10 +5438,16 @@ OO.ui.ClippableElement.prototype.clip = function () {
                ccOffset = $container.offset() || { top: 0, left: 0 },
                ccHeight = $container.innerHeight() - buffer,
                ccWidth = $container.innerWidth() - buffer,
+               cHeight = this.$clippable.outerHeight() + buffer,
+               cWidth = this.$clippable.outerWidth() + buffer,
                scrollTop = this.$clippableScroller.scrollTop(),
                scrollLeft = this.$clippableScroller.scrollLeft(),
-               desiredWidth = ( ccOffset.left + scrollLeft + ccWidth ) - cOffset.left,
-               desiredHeight = ( ccOffset.top + scrollTop + ccHeight ) - cOffset.top,
+               desiredWidth = cOffset.left < 0 ?
+                       cWidth + cOffset.left :
+                       ( ccOffset.left + scrollLeft + ccWidth ) - cOffset.left,
+               desiredHeight = cOffset.top < 0 ?
+                       cHeight + cOffset.top :
+                       ( ccOffset.top + scrollTop + ccHeight ) - cOffset.top,
                naturalWidth = this.$clippable.prop( 'scrollWidth' ),
                naturalHeight = this.$clippable.prop( 'scrollHeight' ),
                clipWidth = desiredWidth < naturalWidth,
@@ -5493,7 +5504,6 @@ OO.ui.Tool = function OoUiTool( toolGroup, config ) {
        this.toolbar = this.toolGroup.getToolbar();
        this.active = false;
        this.$title = this.$( '<span>' );
-       this.$titleText = this.$( '<span>' );
        this.$accel = this.$( '<span>' );
        this.$link = this.$( '<a>' );
        this.title = null;
@@ -5502,7 +5512,7 @@ OO.ui.Tool = function OoUiTool( toolGroup, config ) {
        this.toolbar.connect( this, { updateState: 'onUpdateState' } );
 
        // Initialization
-       this.$titleText.addClass( 'oo-ui-tool-title-text' );
+       this.$title.addClass( 'oo-ui-tool-title' );
        this.$accel
                .addClass( 'oo-ui-tool-accel' )
                .prop( {
@@ -5511,12 +5521,9 @@ OO.ui.Tool = function OoUiTool( toolGroup, config ) {
                        dir: 'ltr',
                        lang: 'en'
                } );
-       this.$title
-               .addClass( 'oo-ui-tool-title' )
-               .append( this.$titleText, this.$accel );
        this.$link
                .addClass( 'oo-ui-tool-link' )
-               .append( this.$icon, this.$title )
+               .append( this.$icon, this.$title, this.$accel )
                .prop( 'tabIndex', 0 )
                .attr( 'role', 'button' );
        this.$element
@@ -5704,7 +5711,7 @@ OO.ui.Tool.prototype.updateTitle = function () {
                accel = this.toolbar.getToolAccelerator( this.constructor.static.name ),
                tooltipParts = [];
 
-       this.$titleText.text( this.title );
+       this.$title.text( this.title );
        this.$accel.text( accel );
 
        if ( titleTooltips && typeof this.title === 'string' && this.title.length ) {
@@ -8224,7 +8231,7 @@ OO.ui.ListToolGroup.prototype.populate = function () {
        // 'display' attribute and restores it, and the tool uses a <span> and can be hidden and re-shown.
        // Is this a jQuery bug? http://jsfiddle.net/gtj4hu3h/
        if ( this.getExpandCollapseTool().$element.css( 'display' ) === 'inline' ) {
-               this.getExpandCollapseTool().$element.css( 'display', 'inline-block' );
+               this.getExpandCollapseTool().$element.css( 'display', 'block' );
        }
 
        this.updateCollapsibleState();
index 6c7b4d4..f7c4217 100644 (file)
                                                                .prop( 'tabIndex', 0 );
                                                }
                                        } else {
-                                               // The toggle-link will be in one the the cells (td or th) of the first row
+                                               // The toggle-link will be in one of the cells (td or th) of the first row
                                                $firstItem = $collapsible.find( 'tr:first th, tr:first td' );
                                                $toggle = $firstItem.find( '> .mw-collapsible-toggle' );
 
index 0d3341b..3918be7 100644 (file)
                                                buildCollationTable();
 
                                                // Legacy fix of .sortbottoms
-                                               // Wrap them inside inside a tfoot (because that's what they actually want to be) &
+                                               // Wrap them inside a tfoot (because that's what they actually want to be)
                                                // and put the <tfoot> at the end of the <table>
                                                var $tfoot,
                                                        $sortbottoms = $table.find( '> tbody > tr.sortbottom' );
index 02bae5a..cf54cf9 100644 (file)
@@ -506,7 +506,7 @@ a.feedlink {
 table.wikitable {
        margin: 1em 0;
        background-color: #f9f9f9;
-       border: 1px #aaa solid;
+       border: 1px solid #aaa;
        border-collapse: collapse;
        color: black;
 }
@@ -515,7 +515,7 @@ table.wikitable > tr > th,
 table.wikitable > tr > td,
 table.wikitable > * > tr > th,
 table.wikitable > * > tr > td {
-       border: 1px #aaa solid;
+       border: 1px solid #aaa;
        padding: 0.2em;
 }
 
index f9944b4..70d54ce 100644 (file)
                // This causes further calls to addButton to go to insertion directly
                // instead of to the queue.
                // It is important that this is after the one and only loop through
-               // the the queue
+               // the queue
                isReady = true;
 
                // Apply to dynamically created textboxes as well as normal ones
index d93e291..4afccda 100644 (file)
        padding: 0.5em 1em;
 }
 
-.mw-json td {
-       background-color: #eee;
-       font-style: italic;
-}
-
 .mw-json .value {
        background-color: #dcfae3;
        font-family: monospace, monospace;
        white-space: pre-wrap;
 }
 
+.mw-json-empty {
+       background-color: #fff;
+       font-style: italic;
+}
+
 .mw-json tr {
        margin-bottom: 0.5em;
+       background-color: #eee;
 }
 
 .mw-json th {
index 9235d69..40f8ef9 100644 (file)
                                if ( 'documentMode' in document && document.documentMode <= 9 ) {
 
                                        $style = getMarker().prev();
-                                       // Verify that the the element before Marker actually is a
+                                       // Verify that the element before Marker actually is a
                                        // <style> tag and one that came from ResourceLoader
                                        // (not some other style tag or even a `<meta>` or `<script>`).
                                        if ( $style.data( 'ResourceLoaderDynamicStyleTag' ) === true ) {
diff --git a/tests/phpunit/includes/ArrayUtilsTest.php b/tests/phpunit/includes/ArrayUtilsTest.php
deleted file mode 100644 (file)
index 7bdb1ca..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-<?php
-/**
- * Test class for ArrayUtils class
- *
- * @group Database
- */
-
-class ArrayUtilsTest extends MediaWikiTestCase {
-       private $search;
-
-       /**
-        * @covers ArrayUtils::findLowerBound
-        * @dataProvider provideFindLowerBound
-        */
-       function testFindLowerBound(
-               $valueCallback, $valueCount, $comparisonCallback, $target, $expected
-       ) {
-               $this->assertSame(
-                       ArrayUtils::findLowerBound(
-                               $valueCallback, $valueCount, $comparisonCallback, $target
-                       ), $expected
-               );
-       }
-
-       function provideFindLowerBound() {
-               $self = $this;
-               $indexValueCallback = function ( $size ) use ( $self ) {
-                       return function ( $val ) use ( $self, $size ) {
-                               $self->assertTrue( $val >= 0 );
-                               $self->assertTrue( $val < $size );
-                               return $val;
-                       };
-               };
-               $comparisonCallback = function ( $a, $b ) {
-                       return $a - $b;
-               };
-
-               return array(
-                       array(
-                               $indexValueCallback( 0 ),
-                               0,
-                               $comparisonCallback,
-                               1,
-                               false,
-                       ),
-                       array(
-                               $indexValueCallback( 1 ),
-                               1,
-                               $comparisonCallback,
-                               -1,
-                               false,
-                       ),
-                       array(
-                               $indexValueCallback( 1 ),
-                               1,
-                               $comparisonCallback,
-                               0,
-                               0,
-                       ),
-                       array(
-                               $indexValueCallback( 1 ),
-                               1,
-                               $comparisonCallback,
-                               1,
-                               0,
-                       ),
-                       array(
-                               $indexValueCallback( 2 ),
-                               2,
-                               $comparisonCallback,
-                               -1,
-                               false,
-                       ),
-                       array(
-                               $indexValueCallback( 2 ),
-                               2,
-                               $comparisonCallback,
-                               0,
-                               0,
-                       ),
-                       array(
-                               $indexValueCallback( 2 ),
-                               2,
-                               $comparisonCallback,
-                               0.5,
-                               0,
-                       ),
-                       array(
-                               $indexValueCallback( 2 ),
-                               2,
-                               $comparisonCallback,
-                               1,
-                               1,
-                       ),
-                       array(
-                               $indexValueCallback( 2 ),
-                               2,
-                               $comparisonCallback,
-                               1.5,
-                               1,
-                       ),
-                       array(
-                               $indexValueCallback( 3 ),
-                               3,
-                               $comparisonCallback,
-                               1,
-                               1,
-                       ),
-                       array(
-                               $indexValueCallback( 3 ),
-                               3,
-                               $comparisonCallback,
-                               1.5,
-                               1,
-                       ),
-                       array(
-                               $indexValueCallback( 3 ),
-                               3,
-                               $comparisonCallback,
-                               2,
-                               2,
-                       ),
-                       array(
-                               $indexValueCallback( 3 ),
-                               3,
-                               $comparisonCallback,
-                               3,
-                               2,
-                       ),
-               );
-       }
-
-       /**
-        * @covers ArrayUtils::arrayDiffAssocRecursive
-        * @dataProvider provideArrayDiffAssocRecursive
-        */
-       function testArrayDiffAssocRecursive( $expected ) {
-               $args = func_get_args();
-               array_shift( $args );
-               $this->assertEquals( call_user_func_array(
-                       'ArrayUtils::arrayDiffAssocRecursive', $args
-               ), $expected );
-       }
-
-       function provideArrayDiffAssocRecursive() {
-               return array(
-                       array(
-                               array(),
-                               array(),
-                               array(),
-                       ),
-                       array(
-                               array(),
-                               array(),
-                               array(),
-                               array(),
-                       ),
-                       array(
-                               array( 1 ),
-                               array( 1 ),
-                               array(),
-                       ),
-                       array(
-                               array( 1 ),
-                               array( 1 ),
-                               array(),
-                               array(),
-                       ),
-                       array(
-                               array(),
-                               array(),
-                               array( 1 ),
-                       ),
-                       array(
-                               array(),
-                               array(),
-                               array( 1 ),
-                               array( 2 ),
-                       ),
-                       array(
-                               array( '' => 1 ),
-                               array( '' => 1 ),
-                               array(),
-                       ),
-                       array(
-                               array(),
-                               array(),
-                               array( '' => 1 ),
-                       ),
-                       array(
-                               array( 1 ),
-                               array( 1 ),
-                               array( 2 ),
-                       ),
-                       array(
-                               array(),
-                               array( 1 ),
-                               array( 2 ),
-                               array( 1 ),
-                       ),
-                       array(
-                               array(),
-                               array( 1 ),
-                               array( 1, 2 ),
-                       ),
-                       array(
-                               array( 1 => 1 ),
-                               array( 1 => 1 ),
-                               array( 1 ),
-                       ),
-                       array(
-                               array(),
-                               array( 1 => 1 ),
-                               array( 1 ),
-                               array( 1 => 1),
-                       ),
-                       array(
-                               array(),
-                               array( 1 => 1 ),
-                               array( 1, 1, 1 ),
-                       ),
-                       array(
-                               array(),
-                               array( array() ),
-                               array(),
-                       ),
-                       array(
-                               array(),
-                               array( array( array() ) ),
-                               array(),
-                       ),
-                       array(
-                               array( 1, array( 1 ) ),
-                               array( 1, array( 1 ) ),
-                               array(),
-                       ),
-                       array(
-                               array( 1 ),
-                               array( 1, array( 1 ) ),
-                               array( 2, array( 1 ) ),
-                       ),
-                       array(
-                               array(),
-                               array( 1, array( 1 ) ),
-                               array( 2, array( 1 ) ),
-                               array( 1, array( 2 ) ),
-                       ),
-                       array(
-                               array( 1 ),
-                               array( 1, array() ),
-                               array( 2 ),
-                       ),
-                       array(
-                               array(),
-                               array( 1, array() ),
-                               array( 2 ),
-                               array( 1 ),
-                       ),
-                       array(
-                               array( 1, array( 1 => 2 ) ),
-                               array( 1, array( 1, 2 ) ),
-                               array( 2, array( 1 ) ),
-                       ),
-                       array(
-                               array( 1 ),
-                               array( 1, array( 1, 2 ) ),
-                               array( 2, array( 1 ) ),
-                               array( 2, array( 1 => 2 ) ),
-                       ),
-                       array(
-                               array( 1 => array( 1, 2 ) ),
-                               array( 1, array( 1, 2 ) ),
-                               array( 1, array( 2 ) ),
-                       ),
-                       array(
-                               array( 1 => array( array( 2, 3 ), 2 ) ),
-                               array( 1, array( array( 2, 3 ), 2 ) ),
-                               array( 1, array( 2 ) ),
-                       ),
-                       array(
-                               array( 1 => array( array( 2 ), 2 ) ),
-                               array( 1, array( array( 2, 3 ), 2 ) ),
-                               array( 1, array( array( 1 => 3 ) ) ),
-                       ),
-                       array(
-                               array( 1 => array( 1 => 2 ) ),
-                               array( 1, array( array( 2, 3 ), 2 ) ),
-                               array( 1, array( array( 1 => 3, 0 => 2 ) ) ),
-                       ),
-                       array(
-                               array( 1 => array( 1 => 2 ) ),
-                               array( 1, array( array( 2, 3 ), 2 ) ),
-                               array( 1, array( array( 1 => 3 ) ) ),
-                               array( 1 => array( array( 2 ) ) ),
-                       ),
-                       array(
-                               array(),
-                               array( 1, array( array( 2, 3 ), 2 ) ),
-                               array( 1 => array( 1 => 2, 0 => array( 1 => 3, 0 => 2 ) ), 0 => 1 ),
-                       ),
-                       array(
-                               array(),
-                               array( 1, array( array( 2, 3 ), 2 ) ),
-                               array( 1 => array( 1 => 2 ) ),
-                               array( 1 => array( array( 1 => 3 ) ) ),
-                               array( 1 => array( array( 2 ) ) ),
-                               array( 1 ),
-                       ),
-               );
-       }
-}
diff --git a/tests/phpunit/includes/ArticleTablesTest.php b/tests/phpunit/includes/ArticleTablesTest.php
deleted file mode 100644 (file)
index 9f2b7a0..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-/**
- * @group Database
- */
-class ArticleTablesTest extends MediaWikiLangTestCase {
-       /**
-        * Make sure that bug 14404 doesn't strike again. We don't want
-        * templatelinks based on the user language when {{int:}} is used, only the
-        * content language.
-        *
-        * @covers Title::getTemplateLinksFrom
-        * @covers Title::getLinksFrom
-        */
-       public function testTemplatelinksUsesContentLanguage() {
-               $title = Title::newFromText( 'Bug 14404' );
-               $page = WikiPage::factory( $title );
-               $user = new User();
-               $user->mRights = array( 'createpage', 'edit', 'purge' );
-               $this->setMwGlobals( 'wgLanguageCode', 'es' );
-               $this->setMwGlobals( 'wgContLang', Language::factory( 'es' ) );
-               $this->setMwGlobals( 'wgLang', Language::factory( 'fr' ) );
-
-               $page->doEditContent(
-                       new WikitextContent( '{{:{{int:history}}}}' ),
-                       'Test code for bug 14404',
-                       0,
-                       false,
-                       $user
-               );
-               $templates1 = $title->getTemplateLinksFrom();
-
-               $this->setMwGlobals( 'wgLang', Language::factory( 'de' ) );
-               $page = WikiPage::factory( $title ); // In order to force the re-rendering of the same wikitext
-
-               // We need an edit, a purge is not enough to regenerate the tables
-               $page->doEditContent(
-                       new WikitextContent( '{{:{{int:history}}}}' ),
-                       'Test code for bug 14404',
-                       EDIT_UPDATE,
-                       false,
-                       $user
-               );
-               $templates2 = $title->getTemplateLinksFrom();
-
-               /**
-                * @var Title[] $templates1
-                * @var Title[] $templates2
-                */
-               $this->assertEquals( $templates1, $templates2 );
-               $this->assertEquals( $templates1[0]->getFullText(), 'Historial' );
-       }
-}
diff --git a/tests/phpunit/includes/ArticleTest.php b/tests/phpunit/includes/ArticleTest.php
deleted file mode 100644 (file)
index ae069ea..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-class ArticleTest extends MediaWikiTestCase {
-
-       /**
-        * @var Title
-        */
-       private $title;
-       /**
-        * @var Article
-        */
-       private $article;
-
-       /** creates a title object and its article object */
-       protected function setUp() {
-               parent::setUp();
-               $this->title = Title::makeTitle( NS_MAIN, 'SomePage' );
-               $this->article = new Article( $this->title );
-       }
-
-       /** cleanup title object and its article object */
-       protected function tearDown() {
-               parent::tearDown();
-               $this->title = null;
-               $this->article = null;
-       }
-
-       /**
-        * @covers Article::__get
-        */
-       public function testImplementsGetMagic() {
-               $this->assertEquals( false, $this->article->mLatest, "Article __get magic" );
-       }
-
-       /**
-        * @depends testImplementsGetMagic
-        * @covers Article::__set
-        */
-       public function testImplementsSetMagic() {
-               $this->article->mLatest = 2;
-               $this->assertEquals( 2, $this->article->mLatest, "Article __set magic" );
-       }
-
-       /**
-        * @depends testImplementsSetMagic
-        * @covers Article::__call
-        */
-       public function testImplementsCallMagic() {
-               $this->article->mLatest = 33;
-               $this->article->mDataLoaded = true;
-               $this->assertEquals( 33, $this->article->getLatest(), "Article __call magic" );
-       }
-
-       /**
-        * @covers Article::__get
-        * @covers Article::__set
-        */
-       public function testGetOrSetOnNewProperty() {
-               $this->article->ext_someNewProperty = 12;
-               $this->assertEquals( 12, $this->article->ext_someNewProperty,
-                       "Article get/set magic on new field" );
-
-               $this->article->ext_someNewProperty = -8;
-               $this->assertEquals( -8, $this->article->ext_someNewProperty,
-                       "Article get/set magic on update to new field" );
-       }
-
-       /**
-        * Checks for the existence of the backwards compatibility static functions
-        * (forwarders to WikiPage class)
-        *
-        * @covers Article::selectFields
-        * @covers Article::onArticleCreate
-        * @covers Article::onArticleDelete
-        * @covers Article::onArticleEdit
-        * @covers Article::getAutosummary
-        */
-       public function testStaticFunctions() {
-               $this->hideDeprecated( 'Article::selectFields' );
-               $this->hideDeprecated( 'Article::getAutosummary' );
-               $this->hideDeprecated( 'WikiPage::getAutosummary' );
-               $this->hideDeprecated( 'CategoryPage::getAutosummary' ); // Inherited from Article
-
-               $this->assertEquals( WikiPage::selectFields(), Article::selectFields(),
-                       "Article static functions" );
-               $this->assertEquals( true, is_callable( "Article::onArticleCreate" ),
-                       "Article static functions" );
-               $this->assertEquals( true, is_callable( "Article::onArticleDelete" ),
-                       "Article static functions" );
-               $this->assertEquals( true, is_callable( "ImagePage::onArticleEdit" ),
-                       "Article static functions" );
-               $this->assertTrue( is_string( CategoryPage::getAutosummary( '', '', 0 ) ),
-                       "Article static functions" );
-       }
-}
diff --git a/tests/phpunit/includes/ExternalStoreTest.php b/tests/phpunit/includes/ExternalStoreTest.php
deleted file mode 100644 (file)
index 07c2957..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-/**
- * External Store tests
- */
-
-class ExternalStoreTest extends MediaWikiTestCase {
-
-       /**
-        * @covers ExternalStore::fetchFromURL
-        */
-       public function testExternalFetchFromURL() {
-               $this->setMwGlobals( 'wgExternalStores', false );
-
-               $this->assertFalse(
-                       ExternalStore::fetchFromURL( 'FOO://cluster1/200' ),
-                       'Deny if wgExternalStores is not set to a non-empty array'
-               );
-
-               $this->setMwGlobals( 'wgExternalStores', array( 'FOO' ) );
-
-               $this->assertEquals(
-                       ExternalStore::fetchFromURL( 'FOO://cluster1/200' ),
-                       'Hello',
-                       'Allow FOO://cluster1/200'
-               );
-               $this->assertEquals(
-                       ExternalStore::fetchFromURL( 'FOO://cluster1/300/0' ),
-                       'Hello',
-                       'Allow FOO://cluster1/300/0'
-               );
-               # Assertions for r68900
-               $this->assertFalse(
-                       ExternalStore::fetchFromURL( 'ftp.example.org' ),
-                       'Deny domain ftp.example.org'
-               );
-               $this->assertFalse(
-                       ExternalStore::fetchFromURL( '/example.txt' ),
-                       'Deny path /example.txt'
-               );
-               $this->assertFalse(
-                       ExternalStore::fetchFromURL( 'http://' ),
-                       'Deny protocol http://'
-               );
-       }
-}
-
-class ExternalStoreFOO {
-
-       protected $data = array(
-               'cluster1' => array(
-                       '200' => 'Hello',
-                       '300' => array(
-                               'Hello', 'World',
-                       ),
-               ),
-       );
-
-       /**
-        * Fetch data from given URL
-        * @param string $url An url of the form FOO://cluster/id or FOO://cluster/id/itemid.
-        * @return mixed
-        */
-       function fetchFromURL( $url ) {
-               // Based on ExternalStoreDB
-               $path = explode( '/', $url );
-               $cluster = $path[2];
-               $id = $path[3];
-               if ( isset( $path[4] ) ) {
-                       $itemID = $path[4];
-               } else {
-                       $itemID = false;
-               }
-
-               if ( !isset( $this->data[$cluster][$id] ) ) {
-                       return null;
-               }
-
-               if ( $itemID !== false
-                       && is_array( $this->data[$cluster][$id] )
-                       && isset( $this->data[$cluster][$id][$itemID] )
-               ) {
-                       return $this->data[$cluster][$id][$itemID];
-               }
-
-               return $this->data[$cluster][$id];
-       }
-}
diff --git a/tests/phpunit/includes/ImagePage404Test.php b/tests/phpunit/includes/ImagePage404Test.php
deleted file mode 100644 (file)
index 197a2b3..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-/**
- * For doing Image Page tests that rely on 404 thumb handling
- */
-class ImagePage404Test extends MediaWikiMediaTestCase {
-
-       protected function getRepoOptions() {
-               return parent::getRepoOptions() + array( 'transformVia404' => true );
-       }
-
-       function setUp() {
-               $this->setMwGlobals( 'wgImageLimits', array(
-                       array( 320, 240 ),
-                       array( 640, 480 ),
-                       array( 800, 600 ),
-                       array( 1024, 768 ),
-                       array( 1280, 1024 )
-               ) );
-               parent::setUp();
-       }
-
-       function getImagePage( $filename ) {
-               $title = Title::makeTitleSafe( NS_FILE, $filename );
-               $file = $this->dataFile( $filename );
-               $iPage = new ImagePage( $title );
-               $iPage->setFile( $file );
-               return $iPage;
-       }
-
-       /**
-        * @dataProvider providerGetThumbSizes
-        * @param string $filename
-        * @param int $expectedNumberThumbs How many thumbnails to show
-        */
-       function testGetThumbSizes( $filename, $expectedNumberThumbs ) {
-               $iPage = $this->getImagePage( $filename );
-               $reflection = new ReflectionClass( $iPage );
-               $reflMethod = $reflection->getMethod( 'getThumbSizes' );
-               $reflMethod->setAccessible( true );
-
-               $actual = $reflMethod->invoke( $iPage, 545, 700 );
-               $this->assertEquals( count( $actual ), $expectedNumberThumbs );
-       }
-
-       function providerGetThumbSizes() {
-               return array(
-                       array( 'animated.gif', 6 ),
-                       array( 'Toll_Texas_1.svg', 6 ),
-                       array( '80x60-Greyscale.xcf', 6 ),
-                       array( 'jpeg-comment-binary.jpg', 6 ),
-               );
-       }
-}
diff --git a/tests/phpunit/includes/ImagePageTest.php b/tests/phpunit/includes/ImagePageTest.php
deleted file mode 100644 (file)
index 3c255b5..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-class ImagePageTest extends MediaWikiMediaTestCase {
-
-       function setUp() {
-               $this->setMwGlobals( 'wgImageLimits', array(
-                       array( 320, 240 ),
-                       array( 640, 480 ),
-                       array( 800, 600 ),
-                       array( 1024, 768 ),
-                       array( 1280, 1024 )
-               ) );
-               parent::setUp();
-       }
-
-       function getImagePage( $filename ) {
-               $title = Title::makeTitleSafe( NS_FILE, $filename );
-               $file = $this->dataFile( $filename );
-               $iPage = new ImagePage( $title );
-               $iPage->setFile( $file );
-               return $iPage;
-       }
-
-       /**
-        * @dataProvider providerGetDisplayWidthHeight
-        * @param array $dim Array [maxWidth, maxHeight, width, height]
-        * @param array $expected Array [width, height] The width and height we expect to display at
-        */
-       function testGetDisplayWidthHeight( $dim, $expected ) {
-               $iPage = $this->getImagePage( 'animated.gif' );
-               $reflection = new ReflectionClass( $iPage );
-               $reflMethod = $reflection->getMethod( 'getDisplayWidthHeight' );
-               $reflMethod->setAccessible( true );
-
-               $actual = $reflMethod->invoke( $iPage, $dim[0], $dim[1], $dim[2], $dim[3] );
-               $this->assertEquals( $actual, $expected );
-       }
-
-       function providerGetDisplayWidthHeight() {
-               return array(
-                       array(
-                               array( 1024.0, 768.0, 600.0, 600.0 ),
-                               array( 600.0, 600.0 )
-                       ),
-                       array(
-                               array( 1024.0, 768.0, 1600.0, 600.0 ),
-                               array( 1024.0, 384.0 )
-                       ),
-                       array(
-                               array( 1024.0, 768.0, 1024.0, 768.0 ),
-                               array( 1024.0, 768.0 )
-                       ),
-                       array(
-                               array( 1024.0, 768.0, 800.0, 1000.0 ),
-                               array( 614.0, 768.0 )
-                       ),
-                       array(
-                               array( 1024.0, 768.0, 0, 1000 ),
-                               array( 0, 0 )
-                       ),
-                       array(
-                               array( 1024.0, 768.0, 2000, 0 ),
-                               array( 0, 0 )
-                       ),
-               );
-       }
-
-       /**
-        * @dataProvider providerGetThumbSizes
-        * @param string $filename
-        * @param int $expectedNumberThumbs How many thumbnails to show
-        */
-       function testGetThumbSizes( $filename, $expectedNumberThumbs ) {
-               $iPage = $this->getImagePage( $filename );
-               $reflection = new ReflectionClass( $iPage );
-               $reflMethod = $reflection->getMethod( 'getThumbSizes' );
-               $reflMethod->setAccessible( true );
-
-               $actual = $reflMethod->invoke( $iPage, 545, 700 );
-               $this->assertEquals( count( $actual ), $expectedNumberThumbs );
-       }
-
-       function providerGetThumbSizes() {
-               return array(
-                       array( 'animated.gif', 2 ),
-                       array( 'Toll_Texas_1.svg', 1 ),
-                       array( '80x60-Greyscale.xcf', 1 ),
-                       array( 'jpeg-comment-binary.jpg', 2 ),
-               );
-       }
-}
diff --git a/tests/phpunit/includes/LinksUpdateTest.php b/tests/phpunit/includes/LinksUpdateTest.php
deleted file mode 100644 (file)
index 02f6b2a..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-<?php
-
-/**
- * @group Database
- * ^--- make sure temporary tables are used.
- */
-class LinksUpdateTest extends MediaWikiTestCase {
-
-       function __construct( $name = null, array $data = array(), $dataName = '' ) {
-               parent::__construct( $name, $data, $dataName );
-
-               $this->tablesUsed = array_merge( $this->tablesUsed,
-                       array(
-                               'interwiki',
-                               'page_props',
-                               'pagelinks',
-                               'categorylinks',
-                               'langlinks',
-                               'externallinks',
-                               'imagelinks',
-                               'templatelinks',
-                               'iwlinks'
-                       )
-               );
-       }
-
-       protected function setUp() {
-               parent::setUp();
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->replace(
-                       'interwiki',
-                       array( 'iw_prefix' ),
-                       array(
-                               'iw_prefix' => 'linksupdatetest',
-                               'iw_url' => 'http://testing.com/wiki/$1',
-                               'iw_api' => 'http://testing.com/w/api.php',
-                               'iw_local' => 0,
-                               'iw_trans' => 0,
-                               'iw_wikiid' => 'linksupdatetest',
-                       )
-               );
-       }
-
-       protected function makeTitleAndParserOutput( $name, $id ) {
-               $t = Title::newFromText( $name );
-               $t->mArticleID = $id; # XXX: this is fugly
-
-               $po = new ParserOutput();
-               $po->setTitleText( $t->getPrefixedText() );
-
-               return array( $t, $po );
-       }
-
-       /**
-        * @covers ParserOutput::addLink
-        */
-       public function testUpdate_pagelinks() {
-               /** @var ParserOutput $po */
-               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
-
-               $po->addLink( Title::newFromText( "Foo" ) );
-               $po->addLink( Title::newFromText( "Special:Foo" ) ); // special namespace should be ignored
-               $po->addLink( Title::newFromText( "linksupdatetest:Foo" ) ); // interwiki link should be ignored
-               $po->addLink( Title::newFromText( "#Foo" ) ); // hash link should be ignored
-
-               $update = $this->assertLinksUpdate(
-                       $t,
-                       $po,
-                       'pagelinks',
-                       'pl_namespace,
-                       pl_title',
-                       'pl_from = 111',
-                       array( array( NS_MAIN, 'Foo' ) )
-               );
-               $this->assertArrayEquals( array(
-                       Title::makeTitle( NS_MAIN, 'Foo' ),  // newFromText doesn't yield the same internal state....
-               ), $update->getAddedLinks() );
-
-               $po = new ParserOutput();
-               $po->setTitleText( $t->getPrefixedText() );
-
-               $po->addLink( Title::newFromText( "Bar" ) );
-               $po->addLink( Title::newFromText( "Talk:Bar" ) );
-
-               $update = $this->assertLinksUpdate(
-                       $t,
-                       $po,
-                       'pagelinks',
-                       'pl_namespace,
-                       pl_title',
-                       'pl_from = 111',
-                       array(
-                               array( NS_MAIN, 'Bar' ),
-                               array( NS_TALK, 'Bar' ),
-                       )
-               );
-               $this->assertArrayEquals( array(
-                       Title::makeTitle( NS_MAIN, 'Bar' ),
-                       Title::makeTitle( NS_TALK, 'Bar' ),
-               ), $update->getAddedLinks() );
-               $this->assertArrayEquals( array(
-                       Title::makeTitle( NS_MAIN, 'Foo' ),
-               ), $update->getRemovedLinks() );
-       }
-
-       /**
-        * @covers ParserOutput::addExternalLink
-        */
-       public function testUpdate_externallinks() {
-               /** @var ParserOutput $po */
-               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
-
-               $po->addExternalLink( "http://testing.com/wiki/Foo" );
-
-               $this->assertLinksUpdate( $t, $po, 'externallinks', 'el_to, el_index', 'el_from = 111', array(
-                       array( 'http://testing.com/wiki/Foo', 'http://com.testing./wiki/Foo' ),
-               ) );
-       }
-
-       /**
-        * @covers ParserOutput::addCategory
-        */
-       public function testUpdate_categorylinks() {
-               /** @var ParserOutput $po */
-               $this->setMwGlobals( 'wgCategoryCollation', 'uppercase' );
-
-               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
-
-               $po->addCategory( "Foo", "FOO" );
-
-               $this->assertLinksUpdate( $t, $po, 'categorylinks', 'cl_to, cl_sortkey', 'cl_from = 111', array(
-                       array( 'Foo', "FOO\nTESTING" ),
-               ) );
-       }
-
-       /**
-        * @covers ParserOutput::addInterwikiLink
-        */
-       public function testUpdate_iwlinks() {
-               /** @var ParserOutput $po */
-               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
-
-               $target = Title::makeTitleSafe( NS_MAIN, "Foo", '', 'linksupdatetest' );
-               $po->addInterwikiLink( $target );
-
-               $this->assertLinksUpdate( $t, $po, 'iwlinks', 'iwl_prefix, iwl_title', 'iwl_from = 111', array(
-                       array( 'linksupdatetest', 'Foo' ),
-               ) );
-       }
-
-       /**
-        * @covers ParserOutput::addTemplate
-        */
-       public function testUpdate_templatelinks() {
-               /** @var ParserOutput $po */
-               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
-
-               $po->addTemplate( Title::newFromText( "Template:Foo" ), 23, 42 );
-
-               $this->assertLinksUpdate(
-                       $t,
-                       $po,
-                       'templatelinks',
-                       'tl_namespace,
-                       tl_title',
-                       'tl_from = 111',
-                       array( array( NS_TEMPLATE, 'Foo' ) )
-               );
-       }
-
-       /**
-        * @covers ParserOutput::addImage
-        */
-       public function testUpdate_imagelinks() {
-               /** @var ParserOutput $po */
-               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
-
-               $po->addImage( "Foo.png" );
-
-               $this->assertLinksUpdate( $t, $po, 'imagelinks', 'il_to', 'il_from = 111', array(
-                       array( 'Foo.png' ),
-               ) );
-       }
-
-       /**
-        * @covers ParserOutput::addLanguageLink
-        */
-       public function testUpdate_langlinks() {
-               $this->setMwGlobals( array(
-                       'wgCapitalLinks' => true,
-               ) );
-
-               /** @var ParserOutput $po */
-               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
-
-               $po->addLanguageLink( Title::newFromText( "en:Foo" )->getFullText() );
-
-               $this->assertLinksUpdate( $t, $po, 'langlinks', 'll_lang, ll_title', 'll_from = 111', array(
-                       array( 'En', 'Foo' ),
-               ) );
-       }
-
-       /**
-        * @covers ParserOutput::setProperty
-        */
-       public function testUpdate_page_props() {
-               global $wgPagePropsHaveSortkey;
-
-               /** @var ParserOutput $po */
-               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
-
-               $fields = array( 'pp_propname', 'pp_value' );
-               $expected = array();
-
-               $po->setProperty( "bool", true );
-               $expected[] = array( "bool", true );
-
-               $po->setProperty( "float", 4.0 + 1.0 / 4.0 );
-               $expected[] = array( "float", 4.0 + 1.0 / 4.0 );
-
-               $po->setProperty( "int", -7 );
-               $expected[] = array( "int", -7 );
-
-               $po->setProperty( "string", "33 bar" );
-               $expected[] = array( "string", "33 bar" );
-
-               // compute expected sortkey values
-               if ( $wgPagePropsHaveSortkey ) {
-                       $fields[] = 'pp_sortkey';
-
-                       foreach ( $expected as &$row ) {
-                               $value = $row[1];
-
-                               if ( is_int( $value ) || is_float( $value ) || is_bool( $value ) ) {
-                                       $row[] = floatval( $value );
-                               } else {
-                                       $row[] = null;
-                               }
-                       }
-               }
-
-               $this->assertLinksUpdate( $t, $po, 'page_props', $fields, 'pp_page = 111', $expected );
-       }
-
-       public function testUpdate_page_props_without_sortkey() {
-               $this->setMwGlobals( 'wgPagePropsHaveSortkey', false );
-
-               $this->testUpdate_page_props();
-       }
-
-       // @todo test recursive, too!
-
-       protected function assertLinksUpdate( Title $title, ParserOutput $parserOutput,
-               $table, $fields, $condition, array $expectedRows
-       ) {
-               $update = new LinksUpdate( $title, $parserOutput );
-
-               //NOTE: make sure LinksUpdate does not generate warnings when called inside a transaction.
-               $update->beginTransaction();
-               $update->doUpdate();
-               $update->commitTransaction();
-
-               $this->assertSelect( $table, $fields, $condition, $expectedRows );
-               return $update;
-       }
-}
diff --git a/tests/phpunit/includes/LocalFileTest.php b/tests/phpunit/includes/LocalFileTest.php
deleted file mode 100644 (file)
index 5c5052e..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-<?php
-
-/**
- * These tests should work regardless of $wgCapitalLinks
- * @group Database
- * @todo Split tests into providers and test methods
- */
-
-class LocalFileTest extends MediaWikiTestCase {
-
-       protected function setUp() {
-               parent::setUp();
-
-               $this->setMwGlobals( 'wgCapitalLinks', true );
-
-               $info = array(
-                       'name' => 'test',
-                       'directory' => '/testdir',
-                       'url' => '/testurl',
-                       'hashLevels' => 2,
-                       'transformVia404' => false,
-                       'backend' => new FSFileBackend( array(
-                               'name' => 'local-backend',
-                               'wikiId' => wfWikiId(),
-                               'containerPaths' => array(
-                                       'cont1' => "/testdir/local-backend/tempimages/cont1",
-                                       'cont2' => "/testdir/local-backend/tempimages/cont2"
-                               )
-                       ) )
-               );
-               $this->repo_hl0 = new LocalRepo( array( 'hashLevels' => 0 ) + $info );
-               $this->repo_hl2 = new LocalRepo( array( 'hashLevels' => 2 ) + $info );
-               $this->repo_lc = new LocalRepo( array( 'initialCapital' => false ) + $info );
-               $this->file_hl0 = $this->repo_hl0->newFile( 'test!' );
-               $this->file_hl2 = $this->repo_hl2->newFile( 'test!' );
-               $this->file_lc = $this->repo_lc->newFile( 'test!' );
-       }
-
-       /**
-        * @covers File::getHashPath
-        */
-       public function testGetHashPath() {
-               $this->assertEquals( '', $this->file_hl0->getHashPath() );
-               $this->assertEquals( 'a/a2/', $this->file_hl2->getHashPath() );
-               $this->assertEquals( 'c/c4/', $this->file_lc->getHashPath() );
-       }
-
-       /**
-        * @covers File::getRel
-        */
-       public function testGetRel() {
-               $this->assertEquals( 'Test!', $this->file_hl0->getRel() );
-               $this->assertEquals( 'a/a2/Test!', $this->file_hl2->getRel() );
-               $this->assertEquals( 'c/c4/test!', $this->file_lc->getRel() );
-       }
-
-       /**
-        * @covers File::getUrlRel
-        */
-       public function testGetUrlRel() {
-               $this->assertEquals( 'Test%21', $this->file_hl0->getUrlRel() );
-               $this->assertEquals( 'a/a2/Test%21', $this->file_hl2->getUrlRel() );
-               $this->assertEquals( 'c/c4/test%21', $this->file_lc->getUrlRel() );
-       }
-
-       /**
-        * @covers File::getArchivePath
-        */
-       public function testGetArchivePath() {
-               $this->assertEquals(
-                       'mwstore://local-backend/test-public/archive',
-                       $this->file_hl0->getArchivePath()
-               );
-               $this->assertEquals(
-                       'mwstore://local-backend/test-public/archive/a/a2',
-                       $this->file_hl2->getArchivePath()
-               );
-               $this->assertEquals(
-                       'mwstore://local-backend/test-public/archive/!',
-                       $this->file_hl0->getArchivePath( '!' )
-               );
-               $this->assertEquals(
-                       'mwstore://local-backend/test-public/archive/a/a2/!',
-                       $this->file_hl2->getArchivePath( '!' )
-               );
-       }
-
-       /**
-        * @covers File::getThumbPath
-        */
-       public function testGetThumbPath() {
-               $this->assertEquals(
-                       'mwstore://local-backend/test-thumb/Test!',
-                       $this->file_hl0->getThumbPath()
-               );
-               $this->assertEquals(
-                       'mwstore://local-backend/test-thumb/a/a2/Test!',
-                       $this->file_hl2->getThumbPath()
-               );
-               $this->assertEquals(
-                       'mwstore://local-backend/test-thumb/Test!/x',
-                       $this->file_hl0->getThumbPath( 'x' )
-               );
-               $this->assertEquals(
-                       'mwstore://local-backend/test-thumb/a/a2/Test!/x',
-                       $this->file_hl2->getThumbPath( 'x' )
-               );
-       }
-
-       /**
-        * @covers File::getArchiveUrl
-        */
-       public function testGetArchiveUrl() {
-               $this->assertEquals( '/testurl/archive', $this->file_hl0->getArchiveUrl() );
-               $this->assertEquals( '/testurl/archive/a/a2', $this->file_hl2->getArchiveUrl() );
-               $this->assertEquals( '/testurl/archive/%21', $this->file_hl0->getArchiveUrl( '!' ) );
-               $this->assertEquals( '/testurl/archive/a/a2/%21', $this->file_hl2->getArchiveUrl( '!' ) );
-       }
-
-       /**
-        * @covers File::getThumbUrl
-        */
-       public function testGetThumbUrl() {
-               $this->assertEquals( '/testurl/thumb/Test%21', $this->file_hl0->getThumbUrl() );
-               $this->assertEquals( '/testurl/thumb/a/a2/Test%21', $this->file_hl2->getThumbUrl() );
-               $this->assertEquals( '/testurl/thumb/Test%21/x', $this->file_hl0->getThumbUrl( 'x' ) );
-               $this->assertEquals( '/testurl/thumb/a/a2/Test%21/x', $this->file_hl2->getThumbUrl( 'x' ) );
-       }
-
-       /**
-        * @covers File::getArchiveVirtualUrl
-        */
-       public function testGetArchiveVirtualUrl() {
-               $this->assertEquals( 'mwrepo://test/public/archive', $this->file_hl0->getArchiveVirtualUrl() );
-               $this->assertEquals(
-                       'mwrepo://test/public/archive/a/a2',
-                       $this->file_hl2->getArchiveVirtualUrl()
-               );
-               $this->assertEquals(
-                       'mwrepo://test/public/archive/%21',
-                       $this->file_hl0->getArchiveVirtualUrl( '!' )
-               );
-               $this->assertEquals(
-                       'mwrepo://test/public/archive/a/a2/%21',
-                       $this->file_hl2->getArchiveVirtualUrl( '!' )
-               );
-       }
-
-       /**
-        * @covers File::getThumbVirtualUrl
-        */
-       public function testGetThumbVirtualUrl() {
-               $this->assertEquals( 'mwrepo://test/thumb/Test%21', $this->file_hl0->getThumbVirtualUrl() );
-               $this->assertEquals( 'mwrepo://test/thumb/a/a2/Test%21', $this->file_hl2->getThumbVirtualUrl() );
-               $this->assertEquals(
-                       'mwrepo://test/thumb/Test%21/%21',
-                       $this->file_hl0->getThumbVirtualUrl( '!' )
-               );
-               $this->assertEquals(
-                       'mwrepo://test/thumb/a/a2/Test%21/%21',
-                       $this->file_hl2->getThumbVirtualUrl( '!' )
-               );
-       }
-
-       /**
-        * @covers File::getUrl
-        */
-       public function testGetUrl() {
-               $this->assertEquals( '/testurl/Test%21', $this->file_hl0->getUrl() );
-               $this->assertEquals( '/testurl/a/a2/Test%21', $this->file_hl2->getUrl() );
-       }
-
-       /**
-        * @covers ::wfLocalFile
-        */
-       public function testWfLocalFile() {
-               $file = wfLocalFile( "File:Some_file_that_probably_doesn't exist.png" );
-               $this->assertThat(
-                       $file,
-                       $this->isInstanceOf( 'LocalFile' ),
-                       'wfLocalFile() returns LocalFile for valid Titles'
-               );
-       }
-}
diff --git a/tests/phpunit/includes/MWFunctionTest.php b/tests/phpunit/includes/MWFunctionTest.php
deleted file mode 100644 (file)
index f4d1799..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-/**
- * @covers MWFunction
- */
-class MWFunctionTest extends MediaWikiTestCase {
-       public function testNewObjFunction() {
-               $arg1 = 'Foo';
-               $arg2 = 'Bar';
-               $arg3 = array( 'Baz' );
-               $arg4 = new ExampleObject;
-
-               $args = array( $arg1, $arg2, $arg3, $arg4 );
-
-               $newObject = new MWBlankClass( $arg1, $arg2, $arg3, $arg4 );
-               $this->hideDeprecated( 'MWFunction::newObj' );
-               $this->assertEquals(
-                       MWFunction::newObj( 'MWBlankClass', $args )->args,
-                       $newObject->args
-               );
-       }
-}
-
-class MWBlankClass {
-
-       public $args = array();
-
-       function __construct( $arg1, $arg2, $arg3, $arg4 ) {
-               $this->args = array( $arg1, $arg2, $arg3, $arg4 );
-       }
-}
-
-class ExampleObject {
-}
index 89d1de7..4d63ea6 100644 (file)
@@ -177,13 +177,13 @@ mw.loader.implement("test.quux",function($,jQuery){mw.test.baz({token:123});},{"
 }</script>
 '
                        ),
-                       // Load module script with with ESI
+                       // Load module script with ESI
                        array(
                                array( 'test.foo', ResourceLoaderModule::TYPE_SCRIPTS, true ),
                                '<script><esi:include src="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.foo&amp;only=scripts&amp;skin=fallback&amp;*" /></script>
 '
                        ),
-                       // Load module styles with with ESI
+                       // Load module styles with ESI
                        array(
                                array( 'test.foo', ResourceLoaderModule::TYPE_STYLES, true ),
                                '<style><esi:include src="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.foo&amp;only=styles&amp;skin=fallback&amp;*" /></style>
diff --git a/tests/phpunit/includes/PasswordTest.php b/tests/phpunit/includes/PasswordTest.php
deleted file mode 100644 (file)
index 5ad8aca..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-/**
- * Testing framework for the Password infrastructure
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-class PasswordTest extends MediaWikiTestCase {
-       /**
-        * @covers InvalidPassword::equals
-        */
-       public function testInvalidUnequalInvalid() {
-               $invalid1 = User::getPasswordFactory()->newFromCiphertext( null );
-               $invalid2 = User::getPasswordFactory()->newFromCiphertext( null );
-
-               $this->assertFalse( $invalid1->equals( $invalid2 ) );
-       }
-
-       public function testInvalidPlaintext() {
-               $invalid = User::getPasswordFactory()->newFromPlaintext( null );
-
-               $this->assertInstanceOf( 'InvalidPassword', $invalid );
-       }
-}
diff --git a/tests/phpunit/includes/RequestContextTest.php b/tests/phpunit/includes/RequestContextTest.php
deleted file mode 100644 (file)
index a9e5be2..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-/**
- * @group Database
- * @group RequestContext
- */
-class RequestContextTest extends MediaWikiTestCase {
-
-       /**
-        * Test the relationship between title and wikipage in RequestContext
-        * @covers RequestContext::getWikiPage
-        * @covers RequestContext::getTitle
-        */
-       public function testWikiPageTitle() {
-               $context = new RequestContext();
-
-               $curTitle = Title::newFromText( "A" );
-               $context->setTitle( $curTitle );
-               $this->assertTrue( $curTitle->equals( $context->getWikiPage()->getTitle() ),
-                       "When a title is first set WikiPage should be created on-demand for that title." );
-
-               $curTitle = Title::newFromText( "B" );
-               $context->setWikiPage( WikiPage::factory( $curTitle ) );
-               $this->assertTrue( $curTitle->equals( $context->getTitle() ),
-                       "Title must be updated when a new WikiPage is provided." );
-
-               $curTitle = Title::newFromText( "C" );
-               $context->setTitle( $curTitle );
-               $this->assertTrue(
-                       $curTitle->equals( $context->getWikiPage()->getTitle() ),
-                       "When a title is updated the WikiPage should be purged "
-                               . "and recreated on-demand with the new title."
-               );
-       }
-
-       /**
-        * @covers RequestContext::importScopedSession
-        */
-       public function testImportScopedSession() {
-               $context = RequestContext::getMain();
-
-               $oInfo = $context->exportSession();
-               $this->assertEquals( '127.0.0.1', $oInfo['ip'], "Correct initial IP address." );
-               $this->assertEquals( 0, $oInfo['userId'], "Correct initial user ID." );
-
-               $user = User::newFromName( 'UnitTestContextUser' );
-               $user->addToDatabase();
-
-               $sinfo = array(
-                       'sessionId' => 'd612ee607c87e749ef14da4983a702cd',
-                       'userId' => $user->getId(),
-                       'ip' => '192.0.2.0',
-                       'headers' => array(
-                               'USER-AGENT' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:18.0) Gecko/20100101 Firefox/18.0'
-                       )
-               );
-               // importScopedSession() sets these variables
-               $this->setMwGlobals( array(
-                       'wgUser' => new User,
-                       'wgRequest' => new FauxRequest,
-               ) );
-               $sc = RequestContext::importScopedSession( $sinfo ); // load new context
-
-               $info = $context->exportSession();
-               $this->assertEquals( $sinfo['ip'], $info['ip'], "Correct IP address." );
-               $this->assertEquals( $sinfo['headers'], $info['headers'], "Correct headers." );
-               $this->assertEquals( $sinfo['sessionId'], $info['sessionId'], "Correct session ID." );
-               $this->assertEquals( $sinfo['userId'], $info['userId'], "Correct user ID." );
-               $this->assertEquals(
-                       $sinfo['ip'],
-                       $context->getRequest()->getIP(),
-                       "Correct context IP address."
-               );
-               $this->assertEquals(
-                       $sinfo['headers'],
-                       $context->getRequest()->getAllHeaders(),
-                       "Correct context headers."
-               );
-               $this->assertEquals( $sinfo['sessionId'], session_id(), "Correct context session ID." );
-               $this->assertEquals( true, $context->getUser()->isLoggedIn(), "Correct context user." );
-               $this->assertEquals( $sinfo['userId'], $context->getUser()->getId(), "Correct context user ID." );
-               $this->assertEquals(
-                       'UnitTestContextUser',
-                       $context->getUser()->getName(),
-                       "Correct context user name."
-               );
-
-               unset( $sc ); // restore previous context
-
-               $info = $context->exportSession();
-               $this->assertEquals( $oInfo['ip'], $info['ip'], "Correct restored IP address." );
-               $this->assertEquals( $oInfo['headers'], $info['headers'], "Correct restored headers." );
-               $this->assertEquals( $oInfo['sessionId'], $info['sessionId'], "Correct restored session ID." );
-               $this->assertEquals( $oInfo['userId'], $info['userId'], "Correct restored user ID." );
-       }
-}
diff --git a/tests/phpunit/includes/SpecialPageTest.php b/tests/phpunit/includes/SpecialPageTest.php
deleted file mode 100644 (file)
index 245cdff..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-
-/**
- * @covers SpecialPage
- *
- * @group Database
- *
- * @licence GNU GPL v2+
- * @author Katie Filbert < aude.wiki@gmail.com >
- */
-class SpecialPageTest extends MediaWikiTestCase {
-
-       protected function setUp() {
-               parent::setUp();
-
-               $this->setMwGlobals( array(
-                       'wgScript' => '/index.php',
-                       'wgContLang' => Language::factory( 'en' )
-               ) );
-       }
-
-       /**
-        * @dataProvider getTitleForProvider
-        */
-       public function testGetTitleFor( $expectedName, $name ) {
-               $title = SpecialPage::getTitleFor( $name );
-               $expected = Title::makeTitle( NS_SPECIAL, $expectedName );
-               $this->assertEquals( $expected, $title );
-       }
-
-       public function getTitleForProvider() {
-               return array(
-                       array( 'UserLogin', 'Userlogin' )
-               );
-       }
-
-       /**
-        * @expectedException PHPUnit_Framework_Error_Notice
-        */
-       public function testInvalidGetTitleFor() {
-               $title = SpecialPage::getTitleFor( 'cat' );
-               $expected = Title::makeTitle( NS_SPECIAL, 'Cat' );
-               $this->assertEquals( $expected, $title );
-       }
-
-       /**
-        * @expectedException PHPUnit_Framework_Error_Notice
-        * @dataProvider getTitleForWithWarningProvider
-        */
-       public function testGetTitleForWithWarning( $expected, $name ) {
-               $title = SpecialPage::getTitleFor( $name );
-               $this->assertEquals( $expected, $title );
-       }
-
-       public function getTitleForWithWarningProvider() {
-               return array(
-                       array( Title::makeTitle( NS_SPECIAL, 'UserLogin' ), 'UserLogin' )
-               );
-       }
-
-       /**
-        * @dataProvider requireLoginAnonProvider
-        */
-       public function testRequireLoginAnon( $expected, $reason, $title ) {
-               $specialPage = new SpecialPage( 'Watchlist', 'viewmywatchlist' );
-
-               $user = User::newFromId( 0 );
-               $specialPage->getContext()->setUser( $user );
-               $specialPage->getContext()->setLanguage( Language::factory( 'en' ) );
-
-               $this->setExpectedException( 'UserNotLoggedIn', $expected );
-
-               // $specialPage->requireLogin( [ $reason [, $title ] ] )
-               call_user_func_array(
-                       array( $specialPage, 'requireLogin' ),
-                       array_filter( array( $reason, $title ) )
-               );
-       }
-
-       public function requireLoginAnonProvider() {
-               $lang = 'en';
-
-               $expected1 = wfMessage( 'exception-nologin-text' )->inLanguage( $lang )->text();
-               $expected2 = wfMessage( 'about' )->inLanguage( $lang )->text();
-
-               return array(
-                       array( $expected1, null, null ),
-                       array( $expected2, 'about', null ),
-                       array( $expected2, 'about', 'about' ),
-               );
-       }
-
-       public function testRequireLoginNotAnon() {
-               $specialPage = new SpecialPage( 'Watchlist', 'viewmywatchlist' );
-
-               $user = User::newFromName( "UTSysop" );
-               $specialPage->getContext()->setUser( $user );
-
-               $specialPage->requireLogin();
-
-               // no exception thrown, logged in use can access special page
-               $this->assertTrue( true );
-       }
-
-}
diff --git a/tests/phpunit/includes/WikiPageTest.php b/tests/phpunit/includes/WikiPageTest.php
deleted file mode 100644 (file)
index c011e9a..0000000
+++ /dev/null
@@ -1,1301 +0,0 @@
-<?php
-
-/**
- * @group ContentHandler
- * @group Database
- * ^--- important, causes temporary tables to be used instead of the real database
- * @group medium
- **/
-class WikiPageTest extends MediaWikiLangTestCase {
-
-       protected $pages_to_delete;
-
-       function __construct( $name = null, array $data = array(), $dataName = '' ) {
-               parent::__construct( $name, $data, $dataName );
-
-               $this->tablesUsed = array_merge(
-                       $this->tablesUsed,
-                       array( 'page',
-                               'revision',
-                               'text',
-
-                               'recentchanges',
-                               'logging',
-
-                               'page_props',
-                               'pagelinks',
-                               'categorylinks',
-                               'langlinks',
-                               'externallinks',
-                               'imagelinks',
-                               'templatelinks',
-                               'iwlinks' ) );
-       }
-
-       protected function setUp() {
-               parent::setUp();
-               $this->pages_to_delete = array();
-
-               LinkCache::singleton()->clear(); # avoid cached redirect status, etc
-       }
-
-       protected function tearDown() {
-               foreach ( $this->pages_to_delete as $p ) {
-                       /* @var $p WikiPage */
-
-                       try {
-                               if ( $p->exists() ) {
-                                       $p->doDeleteArticle( "testing done." );
-                               }
-                       } catch ( MWException $ex ) {
-                               // fail silently
-                       }
-               }
-               parent::tearDown();
-       }
-
-       /**
-        * @param Title|string $title
-        * @param string|null $model
-        * @return WikiPage
-        */
-       protected function newPage( $title, $model = null ) {
-               if ( is_string( $title ) ) {
-                       $ns = $this->getDefaultWikitextNS();
-                       $title = Title::newFromText( $title, $ns );
-               }
-
-               $p = new WikiPage( $title );
-
-               $this->pages_to_delete[] = $p;
-
-               return $p;
-       }
-
-       /**
-        * @param string|Title|WikiPage $page
-        * @param string $text
-        * @param int $model
-        *
-        * @return WikiPage
-        */
-       protected function createPage( $page, $text, $model = null ) {
-               if ( is_string( $page ) || $page instanceof Title ) {
-                       $page = $this->newPage( $page, $model );
-               }
-
-               $content = ContentHandler::makeContent( $text, $page->getTitle(), $model );
-               $page->doEditContent( $content, "testing", EDIT_NEW );
-
-               return $page;
-       }
-
-       /**
-        * @covers WikiPage::doEditContent
-        */
-       public function testDoEditContent() {
-               $page = $this->newPage( "WikiPageTest_testDoEditContent" );
-               $title = $page->getTitle();
-
-               $content = ContentHandler::makeContent(
-                       "[[Lorem ipsum]] dolor sit amet, consetetur sadipscing elitr, sed diam "
-                               . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.",
-                       $title,
-                       CONTENT_MODEL_WIKITEXT
-               );
-
-               $page->doEditContent( $content, "[[testing]] 1" );
-
-               $this->assertTrue( $title->getArticleID() > 0, "Title object should have new page id" );
-               $this->assertTrue( $page->getId() > 0, "WikiPage should have new page id" );
-               $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" );
-               $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" );
-
-               $id = $page->getId();
-
-               # ------------------------
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
-               $n = $res->numRows();
-               $res->free();
-
-               $this->assertEquals( 1, $n, 'pagelinks should contain one link from the page' );
-
-               # ------------------------
-               $page = new WikiPage( $title );
-
-               $retrieved = $page->getContent();
-               $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
-
-               # ------------------------
-               $content = ContentHandler::makeContent(
-                       "At vero eos et accusam et justo duo [[dolores]] et ea rebum. "
-                               . "Stet clita kasd [[gubergren]], no sea takimata sanctus est.",
-                       $title,
-                       CONTENT_MODEL_WIKITEXT
-               );
-
-               $page->doEditContent( $content, "testing 2" );
-
-               # ------------------------
-               $page = new WikiPage( $title );
-
-               $retrieved = $page->getContent();
-               $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
-
-               # ------------------------
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
-               $n = $res->numRows();
-               $res->free();
-
-               $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
-       }
-
-       /**
-        * @covers WikiPage::doEdit
-        */
-       public function testDoEdit() {
-               $this->hideDeprecated( "WikiPage::doEdit" );
-               $this->hideDeprecated( "WikiPage::getText" );
-               $this->hideDeprecated( "Revision::getText" );
-
-               //NOTE: assume help namespace will default to wikitext
-               $title = Title::newFromText( "Help:WikiPageTest_testDoEdit" );
-
-               $page = $this->newPage( $title );
-
-               $text = "[[Lorem ipsum]] dolor sit amet, consetetur sadipscing elitr, sed diam "
-                       . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.";
-
-               $page->doEdit( $text, "[[testing]] 1" );
-
-               $this->assertTrue( $title->getArticleID() > 0, "Title object should have new page id" );
-               $this->assertTrue( $page->getId() > 0, "WikiPage should have new page id" );
-               $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" );
-               $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" );
-
-               $id = $page->getId();
-
-               # ------------------------
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
-               $n = $res->numRows();
-               $res->free();
-
-               $this->assertEquals( 1, $n, 'pagelinks should contain one link from the page' );
-
-               # ------------------------
-               $page = new WikiPage( $title );
-
-               $retrieved = $page->getText();
-               $this->assertEquals( $text, $retrieved, 'retrieved text doesn\'t equal original' );
-
-               # ------------------------
-               $text = "At vero eos et accusam et justo duo [[dolores]] et ea rebum. "
-                       . "Stet clita kasd [[gubergren]], no sea takimata sanctus est.";
-
-               $page->doEdit( $text, "testing 2" );
-
-               # ------------------------
-               $page = new WikiPage( $title );
-
-               $retrieved = $page->getText();
-               $this->assertEquals( $text, $retrieved, 'retrieved text doesn\'t equal original' );
-
-               # ------------------------
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
-               $n = $res->numRows();
-               $res->free();
-
-               $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
-       }
-
-       /**
-        * @covers WikiPage::doQuickEdit
-        */
-       public function testDoQuickEdit() {
-               global $wgUser;
-
-               $this->hideDeprecated( "WikiPage::doQuickEdit" );
-
-               //NOTE: assume help namespace will default to wikitext
-               $page = $this->createPage( "Help:WikiPageTest_testDoQuickEdit", "original text" );
-
-               $text = "quick text";
-               $page->doQuickEdit( $text, $wgUser, "testing q" );
-
-               # ---------------------
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertEquals( $text, $page->getText() );
-       }
-
-       /**
-        * @covers WikiPage::doQuickEditContent
-        */
-       public function testDoQuickEditContent() {
-               global $wgUser;
-
-               $page = $this->createPage(
-                       "WikiPageTest_testDoQuickEditContent",
-                       "original text",
-                       CONTENT_MODEL_WIKITEXT
-               );
-
-               $content = ContentHandler::makeContent(
-                       "quick text",
-                       $page->getTitle(),
-                       CONTENT_MODEL_WIKITEXT
-               );
-               $page->doQuickEditContent( $content, $wgUser, "testing q" );
-
-               # ---------------------
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertTrue( $content->equals( $page->getContent() ) );
-       }
-
-       /**
-        * @covers WikiPage::doDeleteArticle
-        */
-       public function testDoDeleteArticle() {
-               $page = $this->createPage(
-                       "WikiPageTest_testDoDeleteArticle",
-                       "[[original text]] foo",
-                       CONTENT_MODEL_WIKITEXT
-               );
-               $id = $page->getId();
-
-               $page->doDeleteArticle( "testing deletion" );
-
-               $this->assertFalse(
-                       $page->getTitle()->getArticleID() > 0,
-                       "Title object should now have page id 0"
-               );
-               $this->assertFalse( $page->getId() > 0, "WikiPage should now have page id 0" );
-               $this->assertFalse(
-                       $page->exists(),
-                       "WikiPage::exists should return false after page was deleted"
-               );
-               $this->assertNull(
-                       $page->getContent(),
-                       "WikiPage::getContent should return null after page was deleted"
-               );
-               $this->assertFalse(
-                       $page->getText(),
-                       "WikiPage::getText should return false after page was deleted"
-               );
-
-               $t = Title::newFromText( $page->getTitle()->getPrefixedText() );
-               $this->assertFalse(
-                       $t->exists(),
-                       "Title::exists should return false after page was deleted"
-               );
-
-               # ------------------------
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
-               $n = $res->numRows();
-               $res->free();
-
-               $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
-       }
-
-       /**
-        * @covers WikiPage::doDeleteUpdates
-        */
-       public function testDoDeleteUpdates() {
-               $page = $this->createPage(
-                       "WikiPageTest_testDoDeleteArticle",
-                       "[[original text]] foo",
-                       CONTENT_MODEL_WIKITEXT
-               );
-               $id = $page->getId();
-
-               $page->doDeleteUpdates( $id );
-
-               # ------------------------
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
-               $n = $res->numRows();
-               $res->free();
-
-               $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
-       }
-
-       /**
-        * @covers WikiPage::getRevision
-        */
-       public function testGetRevision() {
-               $page = $this->newPage( "WikiPageTest_testGetRevision" );
-
-               $rev = $page->getRevision();
-               $this->assertNull( $rev );
-
-               # -----------------
-               $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
-
-               $rev = $page->getRevision();
-
-               $this->assertEquals( $page->getLatest(), $rev->getId() );
-               $this->assertEquals( "some text", $rev->getContent()->getNativeData() );
-       }
-
-       /**
-        * @covers WikiPage::getContent
-        */
-       public function testGetContent() {
-               $page = $this->newPage( "WikiPageTest_testGetContent" );
-
-               $content = $page->getContent();
-               $this->assertNull( $content );
-
-               # -----------------
-               $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
-
-               $content = $page->getContent();
-               $this->assertEquals( "some text", $content->getNativeData() );
-       }
-
-       /**
-        * @covers WikiPage::getText
-        */
-       public function testGetText() {
-               $this->hideDeprecated( "WikiPage::getText" );
-
-               $page = $this->newPage( "WikiPageTest_testGetText" );
-
-               $text = $page->getText();
-               $this->assertFalse( $text );
-
-               # -----------------
-               $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
-
-               $text = $page->getText();
-               $this->assertEquals( "some text", $text );
-       }
-
-       /**
-        * @covers WikiPage::getRawText
-        */
-       public function testGetRawText() {
-               $this->hideDeprecated( "WikiPage::getRawText" );
-
-               $page = $this->newPage( "WikiPageTest_testGetRawText" );
-
-               $text = $page->getRawText();
-               $this->assertFalse( $text );
-
-               # -----------------
-               $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
-
-               $text = $page->getRawText();
-               $this->assertEquals( "some text", $text );
-       }
-
-       /**
-        * @covers WikiPage::getContentModel
-        */
-       public function testGetContentModel() {
-               global $wgContentHandlerUseDB;
-
-               if ( !$wgContentHandlerUseDB ) {
-                       $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' );
-               }
-
-               $page = $this->createPage(
-                       "WikiPageTest_testGetContentModel",
-                       "some text",
-                       CONTENT_MODEL_JAVASCRIPT
-               );
-
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $page->getContentModel() );
-       }
-
-       /**
-        * @covers WikiPage::getContentHandler
-        */
-       public function testGetContentHandler() {
-               global $wgContentHandlerUseDB;
-
-               if ( !$wgContentHandlerUseDB ) {
-                       $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' );
-               }
-
-               $page = $this->createPage(
-                       "WikiPageTest_testGetContentHandler",
-                       "some text",
-                       CONTENT_MODEL_JAVASCRIPT
-               );
-
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertEquals( 'JavaScriptContentHandler', get_class( $page->getContentHandler() ) );
-       }
-
-       /**
-        * @covers WikiPage::exists
-        */
-       public function testExists() {
-               $page = $this->newPage( "WikiPageTest_testExists" );
-               $this->assertFalse( $page->exists() );
-
-               # -----------------
-               $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
-               $this->assertTrue( $page->exists() );
-
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertTrue( $page->exists() );
-
-               # -----------------
-               $page->doDeleteArticle( "done testing" );
-               $this->assertFalse( $page->exists() );
-
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertFalse( $page->exists() );
-       }
-
-       public static function provideHasViewableContent() {
-               return array(
-                       array( 'WikiPageTest_testHasViewableContent', false, true ),
-                       array( 'Special:WikiPageTest_testHasViewableContent', false ),
-                       array( 'MediaWiki:WikiPageTest_testHasViewableContent', false ),
-                       array( 'Special:Userlogin', true ),
-                       array( 'MediaWiki:help', true ),
-               );
-       }
-
-       /**
-        * @dataProvider provideHasViewableContent
-        * @covers WikiPage::hasViewableContent
-        */
-       public function testHasViewableContent( $title, $viewable, $create = false ) {
-               $page = $this->newPage( $title );
-               $this->assertEquals( $viewable, $page->hasViewableContent() );
-
-               if ( $create ) {
-                       $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
-                       $this->assertTrue( $page->hasViewableContent() );
-
-                       $page = new WikiPage( $page->getTitle() );
-                       $this->assertTrue( $page->hasViewableContent() );
-               }
-       }
-
-       public static function provideGetRedirectTarget() {
-               return array(
-                       array( 'WikiPageTest_testGetRedirectTarget_1', CONTENT_MODEL_WIKITEXT, "hello world", null ),
-                       array(
-                               'WikiPageTest_testGetRedirectTarget_2',
-                               CONTENT_MODEL_WIKITEXT,
-                               "#REDIRECT [[hello world]]",
-                               "Hello world"
-                       ),
-               );
-       }
-
-       /**
-        * @dataProvider provideGetRedirectTarget
-        * @covers WikiPage::getRedirectTarget
-        */
-       public function testGetRedirectTarget( $title, $model, $text, $target ) {
-               $this->setMwGlobals( array(
-                       'wgCapitalLinks' => true,
-               ) );
-
-               $page = $this->createPage( $title, $text, $model );
-
-               # sanity check, because this test seems to fail for no reason for some people.
-               $c = $page->getContent();
-               $this->assertEquals( 'WikitextContent', get_class( $c ) );
-
-               # now, test the actual redirect
-               $t = $page->getRedirectTarget();
-               $this->assertEquals( $target, is_null( $t ) ? null : $t->getPrefixedText() );
-       }
-
-       /**
-        * @dataProvider provideGetRedirectTarget
-        * @covers WikiPage::isRedirect
-        */
-       public function testIsRedirect( $title, $model, $text, $target ) {
-               $page = $this->createPage( $title, $text, $model );
-               $this->assertEquals( !is_null( $target ), $page->isRedirect() );
-       }
-
-       public static function provideIsCountable() {
-               return array(
-
-                       // any
-                       array( 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               '',
-                               'any',
-                               true
-                       ),
-                       array( 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               'Foo',
-                               'any',
-                               true
-                       ),
-
-                       // comma
-                       array( 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               'Foo',
-                               'comma',
-                               false
-                       ),
-                       array( 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               'Foo, bar',
-                               'comma',
-                               true
-                       ),
-
-                       // link
-                       array( 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               'Foo',
-                               'link',
-                               false
-                       ),
-                       array( 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               'Foo [[bar]]',
-                               'link',
-                               true
-                       ),
-
-                       // redirects
-                       array( 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               '#REDIRECT [[bar]]',
-                               'any',
-                               false
-                       ),
-                       array( 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               '#REDIRECT [[bar]]',
-                               'comma',
-                               false
-                       ),
-                       array( 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               '#REDIRECT [[bar]]',
-                               'link',
-                               false
-                       ),
-
-                       // not a content namespace
-                       array( 'Talk:WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               'Foo',
-                               'any',
-                               false
-                       ),
-                       array( 'Talk:WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               'Foo, bar',
-                               'comma',
-                               false
-                       ),
-                       array( 'Talk:WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               'Foo [[bar]]',
-                               'link',
-                               false
-                       ),
-
-                       // not a content namespace, different model
-                       array( 'MediaWiki:WikiPageTest_testIsCountable.js',
-                               null,
-                               'Foo',
-                               'any',
-                               false
-                       ),
-                       array( 'MediaWiki:WikiPageTest_testIsCountable.js',
-                               null,
-                               'Foo, bar',
-                               'comma',
-                               false
-                       ),
-                       array( 'MediaWiki:WikiPageTest_testIsCountable.js',
-                               null,
-                               'Foo [[bar]]',
-                               'link',
-                               false
-                       ),
-               );
-       }
-
-       /**
-        * @dataProvider provideIsCountable
-        * @covers WikiPage::isCountable
-        */
-       public function testIsCountable( $title, $model, $text, $mode, $expected ) {
-               global $wgContentHandlerUseDB;
-
-               $this->setMwGlobals( 'wgArticleCountMethod', $mode );
-
-               $title = Title::newFromText( $title );
-
-               if ( !$wgContentHandlerUseDB
-                       && $model
-                       && ContentHandler::getDefaultModelFor( $title ) != $model
-               ) {
-                       $this->markTestSkipped( "Can not use non-default content model $model for "
-                               . $title->getPrefixedDBkey() . " with \$wgContentHandlerUseDB disabled." );
-               }
-
-               $page = $this->createPage( $title, $text, $model );
-
-               $editInfo = $page->prepareContentForEdit( $page->getContent() );
-
-               $v = $page->isCountable();
-               $w = $page->isCountable( $editInfo );
-
-               $this->assertEquals(
-                       $expected,
-                       $v,
-                       "isCountable( null ) returned unexpected value " . var_export( $v, true )
-                               . " instead of " . var_export( $expected, true )
-                       . " in mode `$mode` for text \"$text\""
-               );
-
-               $this->assertEquals(
-                       $expected,
-                       $w,
-                       "isCountable( \$editInfo ) returned unexpected value " . var_export( $v, true )
-                               . " instead of " . var_export( $expected, true )
-                       . " in mode `$mode` for text \"$text\""
-               );
-       }
-
-       public static function provideGetParserOutput() {
-               return array(
-                       array( CONTENT_MODEL_WIKITEXT, "hello ''world''\n", "<p>hello <i>world</i></p>" ),
-                       // @todo more...?
-               );
-       }
-
-       /**
-        * @dataProvider provideGetParserOutput
-        * @covers WikiPage::getParserOutput
-        */
-       public function testGetParserOutput( $model, $text, $expectedHtml ) {
-               $page = $this->createPage( 'WikiPageTest_testGetParserOutput', $text, $model );
-
-               $opt = $page->makeParserOptions( 'canonical' );
-               $po = $page->getParserOutput( $opt );
-               $text = $po->getText();
-
-               $text = trim( preg_replace( '/<!--.*?-->/sm', '', $text ) ); # strip injected comments
-               $text = preg_replace( '!\s*(</p>)!sm', '\1', $text ); # don't let tidy confuse us
-
-               $this->assertEquals( $expectedHtml, $text );
-
-               return $po;
-       }
-
-       /**
-        * @covers WikiPage::getParserOutput
-        */
-       public function testGetParserOutput_nonexisting() {
-               static $count = 0;
-               $count++;
-
-               $page = new WikiPage( new Title( "WikiPageTest_testGetParserOutput_nonexisting_$count" ) );
-
-               $opt = new ParserOptions();
-               $po = $page->getParserOutput( $opt );
-
-               $this->assertFalse( $po, "getParserOutput() shall return false for non-existing pages." );
-       }
-
-       /**
-        * @covers WikiPage::getParserOutput
-        */
-       public function testGetParserOutput_badrev() {
-               $page = $this->createPage( 'WikiPageTest_testGetParserOutput', "dummy", CONTENT_MODEL_WIKITEXT );
-
-               $opt = new ParserOptions();
-               $po = $page->getParserOutput( $opt, $page->getLatest() + 1234 );
-
-               // @todo would be neat to also test deleted revision
-
-               $this->assertFalse( $po, "getParserOutput() shall return false for non-existing revisions." );
-       }
-
-       public static $sections =
-
-               "Intro
-
-== stuff ==
-hello world
-
-== test ==
-just a test
-
-== foo ==
-more stuff
-";
-
-       public function dataReplaceSection() {
-               //NOTE: assume the Help namespace to contain wikitext
-               return array(
-                       array( 'Help:WikiPageTest_testReplaceSection',
-                               CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
-                               "0",
-                               "No more",
-                               null,
-                               trim( preg_replace( '/^Intro/sm', 'No more', WikiPageTest::$sections ) )
-                       ),
-                       array( 'Help:WikiPageTest_testReplaceSection',
-                               CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
-                               "",
-                               "No more",
-                               null,
-                               "No more"
-                       ),
-                       array( 'Help:WikiPageTest_testReplaceSection',
-                               CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
-                               "2",
-                               "== TEST ==\nmore fun",
-                               null,
-                               trim( preg_replace( '/^== test ==.*== foo ==/sm',
-                                       "== TEST ==\nmore fun\n\n== foo ==",
-                                       WikiPageTest::$sections ) )
-                       ),
-                       array( 'Help:WikiPageTest_testReplaceSection',
-                               CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
-                               "8",
-                               "No more",
-                               null,
-                               trim( WikiPageTest::$sections )
-                       ),
-                       array( 'Help:WikiPageTest_testReplaceSection',
-                               CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
-                               "new",
-                               "No more",
-                               "New",
-                               trim( WikiPageTest::$sections ) . "\n\n== New ==\n\nNo more"
-                       ),
-               );
-       }
-
-       /**
-        * @dataProvider dataReplaceSection
-        * @covers WikiPage::replaceSection
-        */
-       public function testReplaceSection( $title, $model, $text, $section, $with,
-               $sectionTitle, $expected
-       ) {
-               $this->hideDeprecated( "WikiPage::replaceSection" );
-
-               $page = $this->createPage( $title, $text, $model );
-               $text = $page->replaceSection( $section, $with, $sectionTitle );
-               $text = trim( $text );
-
-               $this->assertEquals( $expected, $text );
-       }
-
-       /**
-        * @dataProvider dataReplaceSection
-        * @covers WikiPage::replaceSectionContent
-        */
-       public function testReplaceSectionContent( $title, $model, $text, $section,
-               $with, $sectionTitle, $expected
-       ) {
-               $page = $this->createPage( $title, $text, $model );
-
-               $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
-               $c = $page->replaceSectionContent( $section, $content, $sectionTitle );
-
-               $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
-       }
-
-       /**
-        * @dataProvider dataReplaceSection
-        * @covers WikiPage::replaceSectionAtRev
-        */
-       public function testReplaceSectionAtRev( $title, $model, $text, $section,
-               $with, $sectionTitle, $expected
-       ) {
-               $page = $this->createPage( $title, $text, $model );
-               $baseRevId = $page->getLatest();
-
-               $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
-               $c = $page->replaceSectionAtRev( $section, $content, $sectionTitle, $baseRevId );
-
-               $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
-       }
-
-       /* @todo FIXME: fix this!
-       public function testGetUndoText() {
-       $this->checkHasDiff3();
-
-       $text = "one";
-       $page = $this->createPage( "WikiPageTest_testGetUndoText", $text );
-       $rev1 = $page->getRevision();
-
-       $text .= "\n\ntwo";
-       $page->doEditContent(
-               ContentHandler::makeContent( $text, $page->getTitle() ),
-               "adding section two"
-       );
-       $rev2 = $page->getRevision();
-
-       $text .= "\n\nthree";
-       $page->doEditContent(
-               ContentHandler::makeContent( $text, $page->getTitle() ),
-               "adding section three"
-       );
-       $rev3 = $page->getRevision();
-
-       $text .= "\n\nfour";
-       $page->doEditContent(
-               ContentHandler::makeContent( $text, $page->getTitle() ),
-               "adding section four"
-       );
-       $rev4 = $page->getRevision();
-
-       $text .= "\n\nfive";
-       $page->doEditContent(
-               ContentHandler::makeContent( $text, $page->getTitle() ),
-               "adding section five"
-       );
-       $rev5 = $page->getRevision();
-
-       $text .= "\n\nsix";
-       $page->doEditContent(
-               ContentHandler::makeContent( $text, $page->getTitle() ),
-               "adding section six"
-       );
-       $rev6 = $page->getRevision();
-
-       $undo6 = $page->getUndoText( $rev6 );
-       if ( $undo6 === false ) $this->fail( "getUndoText failed for rev6" );
-       $this->assertEquals( "one\n\ntwo\n\nthree\n\nfour\n\nfive", $undo6 );
-
-       $undo3 = $page->getUndoText( $rev4, $rev2 );
-       if ( $undo3 === false ) $this->fail( "getUndoText failed for rev4..rev2" );
-       $this->assertEquals( "one\n\ntwo\n\nfive", $undo3 );
-
-       $undo2 = $page->getUndoText( $rev2 );
-       if ( $undo2 === false ) $this->fail( "getUndoText failed for rev2" );
-       $this->assertEquals( "one\n\nfive", $undo2 );
-       }
-        */
-
-       /**
-        * @todo FIXME: this is a better rollback test than the one below, but it
-        * keeps failing in jenkins for some reason.
-        */
-       public function broken_testDoRollback() {
-               $admin = new User();
-               $admin->setName( "Admin" );
-
-               $text = "one";
-               $page = $this->newPage( "WikiPageTest_testDoRollback" );
-               $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
-                       "section one", EDIT_NEW, false, $admin );
-
-               $user1 = new User();
-               $user1->setName( "127.0.1.11" );
-               $text .= "\n\ntwo";
-               $page = new WikiPage( $page->getTitle() );
-               $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
-                       "adding section two", 0, false, $user1 );
-
-               $user2 = new User();
-               $user2->setName( "127.0.2.13" );
-               $text .= "\n\nthree";
-               $page = new WikiPage( $page->getTitle() );
-               $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
-                       "adding section three", 0, false, $user2 );
-
-               # we are having issues with doRollback spuriously failing. Apparently
-               # the last revision somehow goes missing or not committed under some
-               # circumstances. So, make sure the last revision has the right user name.
-               $dbr = wfGetDB( DB_SLAVE );
-               $this->assertEquals( 3, Revision::countByPageId( $dbr, $page->getId() ) );
-
-               $page = new WikiPage( $page->getTitle() );
-               $rev3 = $page->getRevision();
-               $this->assertEquals( '127.0.2.13', $rev3->getUserText() );
-
-               $rev2 = $rev3->getPrevious();
-               $this->assertEquals( '127.0.1.11', $rev2->getUserText() );
-
-               $rev1 = $rev2->getPrevious();
-               $this->assertEquals( 'Admin', $rev1->getUserText() );
-
-               # now, try the actual rollback
-               $admin->addGroup( "sysop" ); #XXX: make the test user a sysop...
-               $token = $admin->getEditToken(
-                       array( $page->getTitle()->getPrefixedText(), $user2->getName() ),
-                       null
-               );
-               $errors = $page->doRollback(
-                       $user2->getName(),
-                       "testing revert",
-                       $token,
-                       false,
-                       $details,
-                       $admin
-               );
-
-               if ( $errors ) {
-                       $this->fail( "Rollback failed:\n" . print_r( $errors, true )
-                               . ";\n" . print_r( $details, true ) );
-               }
-
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertEquals( $rev2->getSha1(), $page->getRevision()->getSha1(),
-                       "rollback did not revert to the correct revision" );
-               $this->assertEquals( "one\n\ntwo", $page->getContent()->getNativeData() );
-       }
-
-       /**
-        * @todo FIXME: the above rollback test is better, but it keeps failing in jenkins for some reason.
-        * @covers WikiPage::doRollback
-        */
-       public function testDoRollback() {
-               $admin = new User();
-               $admin->setName( "Admin" );
-
-               $text = "one";
-               $page = $this->newPage( "WikiPageTest_testDoRollback" );
-               $page->doEditContent(
-                       ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
-                       "section one",
-                       EDIT_NEW,
-                       false,
-                       $admin
-               );
-               $rev1 = $page->getRevision();
-
-               $user1 = new User();
-               $user1->setName( "127.0.1.11" );
-               $text .= "\n\ntwo";
-               $page = new WikiPage( $page->getTitle() );
-               $page->doEditContent(
-                       ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
-                       "adding section two",
-                       0,
-                       false,
-                       $user1
-               );
-
-               # now, try the rollback
-               $admin->addGroup( "sysop" ); #XXX: make the test user a sysop...
-               $token = $admin->getEditToken(
-                       array( $page->getTitle()->getPrefixedText(), $user1->getName() ),
-                       null
-               );
-               $errors = $page->doRollback(
-                       $user1->getName(),
-                       "testing revert",
-                       $token,
-                       false,
-                       $details,
-                       $admin
-               );
-
-               if ( $errors ) {
-                       $this->fail( "Rollback failed:\n" . print_r( $errors, true )
-                               . ";\n" . print_r( $details, true ) );
-               }
-
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
-                       "rollback did not revert to the correct revision" );
-               $this->assertEquals( "one", $page->getContent()->getNativeData() );
-       }
-
-       /**
-        * @covers WikiPage::doRollback
-        */
-       public function testDoRollbackFailureSameContent() {
-               $admin = new User();
-               $admin->setName( "Admin" );
-               $admin->addGroup( "sysop" ); #XXX: make the test user a sysop...
-
-               $text = "one";
-               $page = $this->newPage( "WikiPageTest_testDoRollback" );
-               $page->doEditContent(
-                       ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
-                       "section one",
-                       EDIT_NEW,
-                       false,
-                       $admin
-               );
-               $rev1 = $page->getRevision();
-
-               $user1 = new User();
-               $user1->setName( "127.0.1.11" );
-               $user1->addGroup( "sysop" ); #XXX: make the test user a sysop...
-               $text .= "\n\ntwo";
-               $page = new WikiPage( $page->getTitle() );
-               $page->doEditContent(
-                       ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
-                       "adding section two",
-                       0,
-                       false,
-                       $user1
-               );
-
-               # now, do a the rollback from the same user was doing the edit before
-               $resultDetails = array();
-               $token = $user1->getEditToken(
-                       array( $page->getTitle()->getPrefixedText(), $user1->getName() ),
-                       null
-               );
-               $errors = $page->doRollback(
-                       $user1->getName(),
-                       "testing revert same user",
-                       $token,
-                       false,
-                       $resultDetails,
-                       $admin
-               );
-
-               $this->assertEquals( array(), $errors, "Rollback failed same user" );
-
-               # now, try the rollback
-               $resultDetails = array();
-               $token = $admin->getEditToken(
-                       array( $page->getTitle()->getPrefixedText(), $user1->getName() ),
-                       null
-               );
-               $errors = $page->doRollback(
-                       $user1->getName(),
-                       "testing revert",
-                       $token,
-                       false,
-                       $resultDetails,
-                       $admin
-               );
-
-               $this->assertEquals( array( array( 'alreadyrolled', 'WikiPageTest testDoRollback',
-                       '127.0.1.11', 'Admin' ) ), $errors, "Rollback not failed" );
-
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
-                       "rollback did not revert to the correct revision" );
-               $this->assertEquals( "one", $page->getContent()->getNativeData() );
-       }
-
-       public static function provideGetAutosummary() {
-               return array(
-                       array(
-                               'Hello there, world!',
-                               '#REDIRECT [[Foo]]',
-                               0,
-                               '/^Redirected page .*Foo/'
-                       ),
-
-                       array(
-                               null,
-                               'Hello world!',
-                               EDIT_NEW,
-                               '/^Created page .*Hello/'
-                       ),
-
-                       array(
-                               'Hello there, world!',
-                               '',
-                               0,
-                               '/^Blanked/'
-                       ),
-
-                       array(
-                               'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
-                               eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
-                               voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
-                               clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.',
-                               'Hello world!',
-                               0,
-                               '/^Replaced .*Hello/'
-                       ),
-
-                       array(
-                               'foo',
-                               'bar',
-                               0,
-                               '/^$/'
-                       ),
-               );
-       }
-
-       /**
-        * @dataProvider provideGetAutoSummary
-        * @covers WikiPage::getAutosummary
-        */
-       public function testGetAutosummary( $old, $new, $flags, $expected ) {
-               $this->hideDeprecated( "WikiPage::getAutosummary" );
-
-               $page = $this->newPage( "WikiPageTest_testGetAutosummary" );
-
-               $summary = $page->getAutosummary( $old, $new, $flags );
-
-               $this->assertTrue( (bool)preg_match( $expected, $summary ),
-                       "Autosummary didn't match expected pattern $expected: $summary" );
-       }
-
-       public static function provideGetAutoDeleteReason() {
-               return array(
-                       array(
-                               array(),
-                               false,
-                               false
-                       ),
-
-                       array(
-                               array(
-                                       array( "first edit", null ),
-                               ),
-                               "/first edit.*only contributor/",
-                               false
-                       ),
-
-                       array(
-                               array(
-                                       array( "first edit", null ),
-                                       array( "second edit", null ),
-                               ),
-                               "/second edit.*only contributor/",
-                               true
-                       ),
-
-                       array(
-                               array(
-                                       array( "first edit", "127.0.2.22" ),
-                                       array( "second edit", "127.0.3.33" ),
-                               ),
-                               "/second edit/",
-                               true
-                       ),
-
-                       array(
-                               array(
-                                       array(
-                                               "first edit: "
-                                                       . "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam "
-                                                       . " nonumy eirmod tempor invidunt ut labore et dolore magna "
-                                                       . "aliquyam erat, sed diam voluptua. At vero eos et accusam "
-                                                       . "et justo duo dolores et ea rebum. Stet clita kasd gubergren, "
-                                                       . "no sea  takimata sanctus est Lorem ipsum dolor sit amet.'",
-                                               null
-                                       ),
-                               ),
-                               '/first edit:.*\.\.\."/',
-                               false
-                       ),
-
-                       array(
-                               array(
-                                       array( "first edit", "127.0.2.22" ),
-                                       array( "", "127.0.3.33" ),
-                               ),
-                               "/before blanking.*first edit/",
-                               true
-                       ),
-
-               );
-       }
-
-       /**
-        * @dataProvider provideGetAutoDeleteReason
-        * @covers WikiPage::getAutoDeleteReason
-        */
-       public function testGetAutoDeleteReason( $edits, $expectedResult, $expectedHistory ) {
-               global $wgUser;
-
-               //NOTE: assume Help namespace to contain wikitext
-               $page = $this->newPage( "Help:WikiPageTest_testGetAutoDeleteReason" );
-
-               $c = 1;
-
-               foreach ( $edits as $edit ) {
-                       $user = new User();
-
-                       if ( !empty( $edit[1] ) ) {
-                               $user->setName( $edit[1] );
-                       } else {
-                               $user = $wgUser;
-                       }
-
-                       $content = ContentHandler::makeContent( $edit[0], $page->getTitle(), $page->getContentModel() );
-
-                       $page->doEditContent( $content, "test edit $c", $c < 2 ? EDIT_NEW : 0, false, $user );
-
-                       $c += 1;
-               }
-
-               $reason = $page->getAutoDeleteReason( $hasHistory );
-
-               if ( is_bool( $expectedResult ) || is_null( $expectedResult ) ) {
-                       $this->assertEquals( $expectedResult, $reason );
-               } else {
-                       $this->assertTrue( (bool)preg_match( $expectedResult, $reason ),
-                               "Autosummary didn't match expected pattern $expectedResult: $reason" );
-               }
-
-               $this->assertEquals( $expectedHistory, $hasHistory,
-                       "expected \$hasHistory to be " . var_export( $expectedHistory, true ) );
-
-               $page->doDeleteArticle( "done" );
-       }
-
-       public static function providePreSaveTransform() {
-               return array(
-                       array( 'hello this is ~~~',
-                               "hello this is [[Special:Contributions/127.0.0.1|127.0.0.1]]",
-                       ),
-                       array( 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
-                               'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
-                       ),
-               );
-       }
-
-       /**
-        * @dataProvider providePreSaveTransform
-        * @covers WikiPage::preSaveTransform
-        */
-       public function testPreSaveTransform( $text, $expected ) {
-               $this->hideDeprecated( 'WikiPage::preSaveTransform' );
-               $user = new User();
-               $user->setName( "127.0.0.1" );
-
-               //NOTE: assume Help namespace to contain wikitext
-               $page = $this->newPage( "Help:WikiPageTest_testPreloadTransform" );
-               $text = $page->preSaveTransform( $text, $user );
-
-               $this->assertEquals( $expected, $text );
-       }
-
-       /**
-        * @covers WikiPage::factory
-        */
-       public function testWikiPageFactory() {
-               $title = Title::makeTitle( NS_FILE, 'Someimage.png' );
-               $page = WikiPage::factory( $title );
-               $this->assertEquals( 'WikiFilePage', get_class( $page ) );
-
-               $title = Title::makeTitle( NS_CATEGORY, 'SomeCategory' );
-               $page = WikiPage::factory( $title );
-               $this->assertEquals( 'WikiCategoryPage', get_class( $page ) );
-
-               $title = Title::makeTitle( NS_MAIN, 'SomePage' );
-               $page = WikiPage::factory( $title );
-               $this->assertEquals( 'WikiPage', get_class( $page ) );
-       }
-}
diff --git a/tests/phpunit/includes/WikiPageTestContentHandlerUseDB.php b/tests/phpunit/includes/WikiPageTestContentHandlerUseDB.php
deleted file mode 100644 (file)
index 3db7628..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-/**
- * @group ContentHandler
- * @group Database
- * ^--- important, causes temporary tables to be used instead of the real database
- */
-class WikiPageTestContentHandlerUseDB extends WikiPageTest {
-
-       protected function setUp() {
-               parent::setUp();
-               $this->setMwGlobals( 'wgContentHandlerUseDB', false );
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               $page_table = $dbw->tableName( 'page' );
-               $revision_table = $dbw->tableName( 'revision' );
-               $archive_table = $dbw->tableName( 'archive' );
-
-               if ( $dbw->fieldExists( $page_table, 'page_content_model' ) ) {
-                       $dbw->query( "alter table $page_table drop column page_content_model" );
-                       $dbw->query( "alter table $revision_table drop column rev_content_model" );
-                       $dbw->query( "alter table $revision_table drop column rev_content_format" );
-                       $dbw->query( "alter table $archive_table drop column ar_content_model" );
-                       $dbw->query( "alter table $archive_table drop column ar_content_format" );
-               }
-       }
-
-       /**
-        * @covers WikiPage::getContentModel
-        */
-       public function testGetContentModel() {
-               $page = $this->createPage(
-                       "WikiPageTest_testGetContentModel",
-                       "some text",
-                       CONTENT_MODEL_JAVASCRIPT
-               );
-
-               $page = new WikiPage( $page->getTitle() );
-
-               // NOTE: since the content model is not recorded in the database,
-               //       we expect to get the default, namely CONTENT_MODEL_WIKITEXT
-               $this->assertEquals( CONTENT_MODEL_WIKITEXT, $page->getContentModel() );
-       }
-
-       /**
-        * @covers WikiPage::getContentHandler
-        */
-       public function testGetContentHandler() {
-               $page = $this->createPage(
-                       "WikiPageTest_testGetContentHandler",
-                       "some text",
-                       CONTENT_MODEL_JAVASCRIPT
-               );
-
-               // NOTE: since the content model is not recorded in the database,
-               //       we expect to get the default, namely CONTENT_MODEL_WIKITEXT
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertEquals( 'WikitextContentHandler', get_class( $page->getContentHandler() ) );
-       }
-}
diff --git a/tests/phpunit/includes/XmlTypeCheckTest.php b/tests/phpunit/includes/XmlTypeCheckTest.php
deleted file mode 100644 (file)
index 8d6f1ed..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-/**
- * PHPUnit tests for XMLTypeCheck.
- * @author physikerwelt
- * @group Xml
- * @covers XMLTypeCheck
- */
-class XmlTypeCheckTest extends MediaWikiTestCase {
-       const WELL_FORMED_XML = "<root><child /></root>";
-       const MAL_FORMED_XML = "<root><child /></error>";
-
-       /**
-        * @covers XMLTypeCheck::newFromString
-        * @covers XMLTypeCheck::getRootElement
-        */
-       public function testWellFormedXML() {
-               $testXML = XmlTypeCheck::newFromString( self::WELL_FORMED_XML );
-               $this->assertTrue( $testXML->wellFormed );
-               $this->assertEquals( 'root', $testXML->getRootElement() );
-       }
-
-       /**
-        * @covers XMLTypeCheck::newFromString
-        */
-       public function testMalFormedXML() {
-               $testXML = XmlTypeCheck::newFromString( self::MAL_FORMED_XML );
-               $this->assertFalse( $testXML->wellFormed );
-       }
-
-}
index 9a552fa..8c27b10 100644 (file)
@@ -46,6 +46,17 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
                $this->apiContext = new ApiTestContext();
        }
 
+       protected function tearDown() {
+               // Avoid leaking session over tests
+               if ( session_id() != '' ) {
+                       global $wgUser;
+                       $wgUser->logout();
+                       session_destroy();
+               }
+
+               parent::tearDown();
+       }
+
        /**
         * Edits or creates a page/revision
         * @param string $pageName Page title
index 5170856..c00545f 100644 (file)
@@ -13,6 +13,13 @@ class ApiFormatWddxTest extends ApiFormatTestBase {
                        $this->markTestSkipped( "Function 'wddx_deserialize' not exist, skipping." );
                }
 
+               if ( wfIsHHVM() && false === strpos( wddx_serialize_value( "Test for &" ), '&amp;' ) ) {
+                       # Some version of HHVM fails to escape the ampersand
+                       #
+                       # https://phabricator.wikimedia.org/T75531
+                       $this->markTestSkipped( "wddx_deserialize is bugged under this version of HHVM" );
+               }
+
                $data = $this->apiRequest( 'wddx', array( 'action' => 'query', 'meta' => 'siteinfo' ) );
 
                $this->assertInternalType( 'array', wddx_deserialize( $data ) );
index d4151a5..0ad8ecc 100644 (file)
@@ -6,48 +6,76 @@
  */
 class JsonContentTest extends MediaWikiLangTestCase {
 
+       protected function setUp() {
+               parent::setUp();
+
+               $this->setMwGlobals( 'wgWellFormedXml', true );
+       }
+
        public static function provideValidConstruction() {
                return array(
-                       array( 'foo', CONTENT_MODEL_JSON, false, null ),
-                       array( FormatJson::encode( array() ), CONTENT_MODEL_JSON, true, array() ),
-                       array( FormatJson::encode( array( 'foo' ) ), CONTENT_MODEL_JSON, true, array( 'foo' ) ),
+                       array( 'foo', false, null ),
+                       array( '{}', true, (object)array() ),
+                       array( '{ "0": "bar" }', true, (object)array( 'bar' ) ),
                );
        }
 
        /**
         * @dataProvider provideValidConstruction
         */
-       public function testValidConstruct( $text, $modelId, $isValid, $expected ) {
-               $obj = new JsonContent( $text, $modelId );
+       public function testIsValid( $text, $isValid, $expected ) {
+               $obj = new JsonContent( $text, CONTENT_MODEL_JSON );
                $this->assertEquals( $isValid, $obj->isValid() );
-               $this->assertEquals( $expected, $obj->getJsonData() );
+               $this->assertEquals( $expected, $obj->getData()->getValue() );
        }
 
        public static function provideDataToEncode() {
                return array(
-                       array( array() ),
-                       array( array( 'foo' ) ),
-                       array( array( 'foo', 'bar' ) ),
-                       array( array( 'baz' => 'foo', 'bar' ) ),
-                       array( array( 'baz' => 1000, 'bar' ) ),
+                       array(
+                               // Round-trip empty array
+                               '[]',
+                               '[]',
+                       ),
+                       array(
+                               // Round-trip empty object
+                               '{}',
+                               '{}',
+                       ),
+                       array(
+                               // Round-trip empty array/object (nested)
+                               '{ "foo": {}, "bar": [] }',
+                               "{\n    \"foo\": {},\n    \"bar\": []\n}",
+                       ),
+                       array(
+                               '{ "foo": "bar" }',
+                               "{\n    \"foo\": \"bar\"\n}",
+                       ),
+                       array(
+                               '{ "foo": 1000 }',
+                               "{\n    \"foo\": 1000\n}",
+                       ),
+                       array(
+                               '{ "foo": 1000, "0": "bar" }',
+                               "{\n    \"foo\": 1000,\n    \"0\": \"bar\"\n}",
+                       ),
                );
        }
 
        /**
         * @dataProvider provideDataToEncode
         */
-       public function testBeautifyUsesFormatJson( $data ) {
-               $obj = new JsonContent( FormatJson::encode( $data ) );
-               $this->assertEquals( FormatJson::encode( $data, true ), $obj->beautifyJSON() );
+       public function testBeautifyJson( $input, $beautified ) {
+               $obj = new JsonContent( $input );
+               $this->assertEquals( $beautified, $obj->beautifyJSON() );
        }
 
        /**
         * @dataProvider provideDataToEncode
         */
-       public function testPreSaveTransform( $data ) {
-               $obj = new JsonContent( FormatJson::encode( $data ) );
+       public function testPreSaveTransform( $input, $transformed ) {
+               $obj = new JsonContent( $input );
                $newObj = $obj->preSaveTransform( $this->getMockTitle(), $this->getMockUser(), $this->getMockParserOptions() );
-               $this->assertTrue( $newObj->equals( new JsonContent( FormatJson::encode( $data, true ) ) ) );
+               $this->assertTrue( $newObj->equals( new JsonContent( $transformed ) ) );
        }
 
        private function getMockTitle() {
@@ -70,33 +98,33 @@ class JsonContentTest extends MediaWikiLangTestCase {
        public static function provideDataAndParserText() {
                return array(
                        array(
-                               array(),
-                               '<table class="mw-json"><tbody></tbody></table>'
+                               (object)array(),
+                               '<table class="mw-json"><tbody><tr><td class="mw-json-empty">Empty object</td></tr></tbody></table>'
                        ),
                        array(
-                               array( 'foo' ),
+                               (object)array( 'foo' ),
                                '<table class="mw-json"><tbody><tr><th>0</th><td class="value">&quot;foo&quot;</td></tr></tbody></table>'
                        ),
                        array(
-                               array( 'foo', 'bar' ),
+                               (object)array( 'foo', 'bar' ),
                                '<table class="mw-json"><tbody><tr><th>0</th><td class="value">&quot;foo&quot;</td></tr>' .
                                "\n" .
                                '<tr><th>1</th><td class="value">&quot;bar&quot;</td></tr></tbody></table>'
                        ),
                        array(
-                               array( 'baz' => 'foo', 'bar' ),
+                               (object)array( 'baz' => 'foo', 'bar' ),
                                '<table class="mw-json"><tbody><tr><th>baz</th><td class="value">&quot;foo&quot;</td></tr>' .
                                "\n" .
                                '<tr><th>0</th><td class="value">&quot;bar&quot;</td></tr></tbody></table>'
                        ),
                        array(
-                               array( 'baz' => 1000, 'bar' ),
+                               (object)array( 'baz' => 1000, 'bar' ),
                                '<table class="mw-json"><tbody><tr><th>baz</th><td class="value">1000</td></tr>' .
                                "\n" .
                                '<tr><th>0</th><td class="value">&quot;bar&quot;</td></tr></tbody></table>'
                        ),
                        array(
-                               array( '<script>alert("evil!")</script>'),
+                               (object)array( '<script>alert("evil!")</script>'),
                                '<table class="mw-json"><tbody><tr><th>0</th><td class="value">&quot;&lt;script&gt;alert(&quot;evil!&quot;)&lt;/script&gt;&quot;</td></tr></tbody></table>',
                        ),
                );
diff --git a/tests/phpunit/includes/context/RequestContextTest.php b/tests/phpunit/includes/context/RequestContextTest.php
new file mode 100644 (file)
index 0000000..a9e5be2
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+
+/**
+ * @group Database
+ * @group RequestContext
+ */
+class RequestContextTest extends MediaWikiTestCase {
+
+       /**
+        * Test the relationship between title and wikipage in RequestContext
+        * @covers RequestContext::getWikiPage
+        * @covers RequestContext::getTitle
+        */
+       public function testWikiPageTitle() {
+               $context = new RequestContext();
+
+               $curTitle = Title::newFromText( "A" );
+               $context->setTitle( $curTitle );
+               $this->assertTrue( $curTitle->equals( $context->getWikiPage()->getTitle() ),
+                       "When a title is first set WikiPage should be created on-demand for that title." );
+
+               $curTitle = Title::newFromText( "B" );
+               $context->setWikiPage( WikiPage::factory( $curTitle ) );
+               $this->assertTrue( $curTitle->equals( $context->getTitle() ),
+                       "Title must be updated when a new WikiPage is provided." );
+
+               $curTitle = Title::newFromText( "C" );
+               $context->setTitle( $curTitle );
+               $this->assertTrue(
+                       $curTitle->equals( $context->getWikiPage()->getTitle() ),
+                       "When a title is updated the WikiPage should be purged "
+                               . "and recreated on-demand with the new title."
+               );
+       }
+
+       /**
+        * @covers RequestContext::importScopedSession
+        */
+       public function testImportScopedSession() {
+               $context = RequestContext::getMain();
+
+               $oInfo = $context->exportSession();
+               $this->assertEquals( '127.0.0.1', $oInfo['ip'], "Correct initial IP address." );
+               $this->assertEquals( 0, $oInfo['userId'], "Correct initial user ID." );
+
+               $user = User::newFromName( 'UnitTestContextUser' );
+               $user->addToDatabase();
+
+               $sinfo = array(
+                       'sessionId' => 'd612ee607c87e749ef14da4983a702cd',
+                       'userId' => $user->getId(),
+                       'ip' => '192.0.2.0',
+                       'headers' => array(
+                               'USER-AGENT' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:18.0) Gecko/20100101 Firefox/18.0'
+                       )
+               );
+               // importScopedSession() sets these variables
+               $this->setMwGlobals( array(
+                       'wgUser' => new User,
+                       'wgRequest' => new FauxRequest,
+               ) );
+               $sc = RequestContext::importScopedSession( $sinfo ); // load new context
+
+               $info = $context->exportSession();
+               $this->assertEquals( $sinfo['ip'], $info['ip'], "Correct IP address." );
+               $this->assertEquals( $sinfo['headers'], $info['headers'], "Correct headers." );
+               $this->assertEquals( $sinfo['sessionId'], $info['sessionId'], "Correct session ID." );
+               $this->assertEquals( $sinfo['userId'], $info['userId'], "Correct user ID." );
+               $this->assertEquals(
+                       $sinfo['ip'],
+                       $context->getRequest()->getIP(),
+                       "Correct context IP address."
+               );
+               $this->assertEquals(
+                       $sinfo['headers'],
+                       $context->getRequest()->getAllHeaders(),
+                       "Correct context headers."
+               );
+               $this->assertEquals( $sinfo['sessionId'], session_id(), "Correct context session ID." );
+               $this->assertEquals( true, $context->getUser()->isLoggedIn(), "Correct context user." );
+               $this->assertEquals( $sinfo['userId'], $context->getUser()->getId(), "Correct context user ID." );
+               $this->assertEquals(
+                       'UnitTestContextUser',
+                       $context->getUser()->getName(),
+                       "Correct context user name."
+               );
+
+               unset( $sc ); // restore previous context
+
+               $info = $context->exportSession();
+               $this->assertEquals( $oInfo['ip'], $info['ip'], "Correct restored IP address." );
+               $this->assertEquals( $oInfo['headers'], $info['headers'], "Correct restored headers." );
+               $this->assertEquals( $oInfo['sessionId'], $info['sessionId'], "Correct restored session ID." );
+               $this->assertEquals( $oInfo['userId'], $info['userId'], "Correct restored user ID." );
+       }
+}
diff --git a/tests/phpunit/includes/deferred/LinksUpdateTest.php b/tests/phpunit/includes/deferred/LinksUpdateTest.php
new file mode 100644 (file)
index 0000000..02f6b2a
--- /dev/null
@@ -0,0 +1,266 @@
+<?php
+
+/**
+ * @group Database
+ * ^--- make sure temporary tables are used.
+ */
+class LinksUpdateTest extends MediaWikiTestCase {
+
+       function __construct( $name = null, array $data = array(), $dataName = '' ) {
+               parent::__construct( $name, $data, $dataName );
+
+               $this->tablesUsed = array_merge( $this->tablesUsed,
+                       array(
+                               'interwiki',
+                               'page_props',
+                               'pagelinks',
+                               'categorylinks',
+                               'langlinks',
+                               'externallinks',
+                               'imagelinks',
+                               'templatelinks',
+                               'iwlinks'
+                       )
+               );
+       }
+
+       protected function setUp() {
+               parent::setUp();
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->replace(
+                       'interwiki',
+                       array( 'iw_prefix' ),
+                       array(
+                               'iw_prefix' => 'linksupdatetest',
+                               'iw_url' => 'http://testing.com/wiki/$1',
+                               'iw_api' => 'http://testing.com/w/api.php',
+                               'iw_local' => 0,
+                               'iw_trans' => 0,
+                               'iw_wikiid' => 'linksupdatetest',
+                       )
+               );
+       }
+
+       protected function makeTitleAndParserOutput( $name, $id ) {
+               $t = Title::newFromText( $name );
+               $t->mArticleID = $id; # XXX: this is fugly
+
+               $po = new ParserOutput();
+               $po->setTitleText( $t->getPrefixedText() );
+
+               return array( $t, $po );
+       }
+
+       /**
+        * @covers ParserOutput::addLink
+        */
+       public function testUpdate_pagelinks() {
+               /** @var ParserOutput $po */
+               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+               $po->addLink( Title::newFromText( "Foo" ) );
+               $po->addLink( Title::newFromText( "Special:Foo" ) ); // special namespace should be ignored
+               $po->addLink( Title::newFromText( "linksupdatetest:Foo" ) ); // interwiki link should be ignored
+               $po->addLink( Title::newFromText( "#Foo" ) ); // hash link should be ignored
+
+               $update = $this->assertLinksUpdate(
+                       $t,
+                       $po,
+                       'pagelinks',
+                       'pl_namespace,
+                       pl_title',
+                       'pl_from = 111',
+                       array( array( NS_MAIN, 'Foo' ) )
+               );
+               $this->assertArrayEquals( array(
+                       Title::makeTitle( NS_MAIN, 'Foo' ),  // newFromText doesn't yield the same internal state....
+               ), $update->getAddedLinks() );
+
+               $po = new ParserOutput();
+               $po->setTitleText( $t->getPrefixedText() );
+
+               $po->addLink( Title::newFromText( "Bar" ) );
+               $po->addLink( Title::newFromText( "Talk:Bar" ) );
+
+               $update = $this->assertLinksUpdate(
+                       $t,
+                       $po,
+                       'pagelinks',
+                       'pl_namespace,
+                       pl_title',
+                       'pl_from = 111',
+                       array(
+                               array( NS_MAIN, 'Bar' ),
+                               array( NS_TALK, 'Bar' ),
+                       )
+               );
+               $this->assertArrayEquals( array(
+                       Title::makeTitle( NS_MAIN, 'Bar' ),
+                       Title::makeTitle( NS_TALK, 'Bar' ),
+               ), $update->getAddedLinks() );
+               $this->assertArrayEquals( array(
+                       Title::makeTitle( NS_MAIN, 'Foo' ),
+               ), $update->getRemovedLinks() );
+       }
+
+       /**
+        * @covers ParserOutput::addExternalLink
+        */
+       public function testUpdate_externallinks() {
+               /** @var ParserOutput $po */
+               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+               $po->addExternalLink( "http://testing.com/wiki/Foo" );
+
+               $this->assertLinksUpdate( $t, $po, 'externallinks', 'el_to, el_index', 'el_from = 111', array(
+                       array( 'http://testing.com/wiki/Foo', 'http://com.testing./wiki/Foo' ),
+               ) );
+       }
+
+       /**
+        * @covers ParserOutput::addCategory
+        */
+       public function testUpdate_categorylinks() {
+               /** @var ParserOutput $po */
+               $this->setMwGlobals( 'wgCategoryCollation', 'uppercase' );
+
+               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+               $po->addCategory( "Foo", "FOO" );
+
+               $this->assertLinksUpdate( $t, $po, 'categorylinks', 'cl_to, cl_sortkey', 'cl_from = 111', array(
+                       array( 'Foo', "FOO\nTESTING" ),
+               ) );
+       }
+
+       /**
+        * @covers ParserOutput::addInterwikiLink
+        */
+       public function testUpdate_iwlinks() {
+               /** @var ParserOutput $po */
+               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+               $target = Title::makeTitleSafe( NS_MAIN, "Foo", '', 'linksupdatetest' );
+               $po->addInterwikiLink( $target );
+
+               $this->assertLinksUpdate( $t, $po, 'iwlinks', 'iwl_prefix, iwl_title', 'iwl_from = 111', array(
+                       array( 'linksupdatetest', 'Foo' ),
+               ) );
+       }
+
+       /**
+        * @covers ParserOutput::addTemplate
+        */
+       public function testUpdate_templatelinks() {
+               /** @var ParserOutput $po */
+               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+               $po->addTemplate( Title::newFromText( "Template:Foo" ), 23, 42 );
+
+               $this->assertLinksUpdate(
+                       $t,
+                       $po,
+                       'templatelinks',
+                       'tl_namespace,
+                       tl_title',
+                       'tl_from = 111',
+                       array( array( NS_TEMPLATE, 'Foo' ) )
+               );
+       }
+
+       /**
+        * @covers ParserOutput::addImage
+        */
+       public function testUpdate_imagelinks() {
+               /** @var ParserOutput $po */
+               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+               $po->addImage( "Foo.png" );
+
+               $this->assertLinksUpdate( $t, $po, 'imagelinks', 'il_to', 'il_from = 111', array(
+                       array( 'Foo.png' ),
+               ) );
+       }
+
+       /**
+        * @covers ParserOutput::addLanguageLink
+        */
+       public function testUpdate_langlinks() {
+               $this->setMwGlobals( array(
+                       'wgCapitalLinks' => true,
+               ) );
+
+               /** @var ParserOutput $po */
+               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+               $po->addLanguageLink( Title::newFromText( "en:Foo" )->getFullText() );
+
+               $this->assertLinksUpdate( $t, $po, 'langlinks', 'll_lang, ll_title', 'll_from = 111', array(
+                       array( 'En', 'Foo' ),
+               ) );
+       }
+
+       /**
+        * @covers ParserOutput::setProperty
+        */
+       public function testUpdate_page_props() {
+               global $wgPagePropsHaveSortkey;
+
+               /** @var ParserOutput $po */
+               list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+               $fields = array( 'pp_propname', 'pp_value' );
+               $expected = array();
+
+               $po->setProperty( "bool", true );
+               $expected[] = array( "bool", true );
+
+               $po->setProperty( "float", 4.0 + 1.0 / 4.0 );
+               $expected[] = array( "float", 4.0 + 1.0 / 4.0 );
+
+               $po->setProperty( "int", -7 );
+               $expected[] = array( "int", -7 );
+
+               $po->setProperty( "string", "33 bar" );
+               $expected[] = array( "string", "33 bar" );
+
+               // compute expected sortkey values
+               if ( $wgPagePropsHaveSortkey ) {
+                       $fields[] = 'pp_sortkey';
+
+                       foreach ( $expected as &$row ) {
+                               $value = $row[1];
+
+                               if ( is_int( $value ) || is_float( $value ) || is_bool( $value ) ) {
+                                       $row[] = floatval( $value );
+                               } else {
+                                       $row[] = null;
+                               }
+                       }
+               }
+
+               $this->assertLinksUpdate( $t, $po, 'page_props', $fields, 'pp_page = 111', $expected );
+       }
+
+       public function testUpdate_page_props_without_sortkey() {
+               $this->setMwGlobals( 'wgPagePropsHaveSortkey', false );
+
+               $this->testUpdate_page_props();
+       }
+
+       // @todo test recursive, too!
+
+       protected function assertLinksUpdate( Title $title, ParserOutput $parserOutput,
+               $table, $fields, $condition, array $expectedRows
+       ) {
+               $update = new LinksUpdate( $title, $parserOutput );
+
+               //NOTE: make sure LinksUpdate does not generate warnings when called inside a transaction.
+               $update->beginTransaction();
+               $update->doUpdate();
+               $update->commitTransaction();
+
+               $this->assertSelect( $table, $fields, $condition, $expectedRows );
+               return $update;
+       }
+}
diff --git a/tests/phpunit/includes/deferred/SearchUpdateTest.php b/tests/phpunit/includes/deferred/SearchUpdateTest.php
new file mode 100644 (file)
index 0000000..c627537
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+class MockSearch extends SearchEngine {
+       public static $id;
+       public static $title;
+       public static $text;
+
+       public function __construct( $db ) {
+       }
+
+       public function update( $id, $title, $text ) {
+               self::$id = $id;
+               self::$title = $title;
+               self::$text = $text;
+       }
+}
+
+/**
+ * @group Search
+ * @group Database
+ */
+class SearchUpdateTest extends MediaWikiTestCase {
+
+       protected function setUp() {
+               parent::setUp();
+               $this->setMwGlobals( 'wgSearchType', 'MockSearch' );
+       }
+
+       public function updateText( $text ) {
+               return trim( SearchUpdate::updateText( $text ) );
+       }
+
+       /**
+        * @covers SearchUpdate::updateText
+        */
+       public function testUpdateText() {
+               $this->assertEquals(
+                       'test',
+                       $this->updateText( '<div>TeSt</div>' ),
+                       'HTML stripped, text lowercased'
+               );
+
+               $this->assertEquals(
+                       'foo bar boz quux',
+                       $this->updateText( <<<EOT
+<table style="color:red; font-size:100px">
+       <tr class="scary"><td><div>foo</div></td><tr>bar</td></tr>
+       <tr><td>boz</td><tr>quux</td></tr>
+</table>
+EOT
+                       ), 'Stripping HTML tables' );
+
+               $this->assertEquals(
+                       'a b',
+                       $this->updateText( 'a > b' ),
+                       'Handle unclosed tags'
+               );
+
+               $text = str_pad( "foo <barbarbar \n", 10000, 'x' );
+
+               $this->assertNotEquals(
+                       '',
+                       $this->updateText( $text ),
+                       'Bug 18609'
+               );
+       }
+
+       /**
+        * @covers SearchUpdate::updateText
+        * @todo give this test a real name explaining what is being tested here
+        */
+       public function testBug32712() {
+               $text = "text „http://example.com“ text";
+               $result = $this->updateText( $text );
+               $processed = preg_replace( '/Q/u', 'Q', $result );
+               $this->assertTrue(
+                       $processed != '',
+                       'Link surrounded by unicode quotes should not fail UTF-8 validation'
+               );
+       }
+}
diff --git a/tests/phpunit/includes/externalstore/ExternalStoreTest.php b/tests/phpunit/includes/externalstore/ExternalStoreTest.php
new file mode 100644 (file)
index 0000000..07c2957
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+/**
+ * External Store tests
+ */
+
+class ExternalStoreTest extends MediaWikiTestCase {
+
+       /**
+        * @covers ExternalStore::fetchFromURL
+        */
+       public function testExternalFetchFromURL() {
+               $this->setMwGlobals( 'wgExternalStores', false );
+
+               $this->assertFalse(
+                       ExternalStore::fetchFromURL( 'FOO://cluster1/200' ),
+                       'Deny if wgExternalStores is not set to a non-empty array'
+               );
+
+               $this->setMwGlobals( 'wgExternalStores', array( 'FOO' ) );
+
+               $this->assertEquals(
+                       ExternalStore::fetchFromURL( 'FOO://cluster1/200' ),
+                       'Hello',
+                       'Allow FOO://cluster1/200'
+               );
+               $this->assertEquals(
+                       ExternalStore::fetchFromURL( 'FOO://cluster1/300/0' ),
+                       'Hello',
+                       'Allow FOO://cluster1/300/0'
+               );
+               # Assertions for r68900
+               $this->assertFalse(
+                       ExternalStore::fetchFromURL( 'ftp.example.org' ),
+                       'Deny domain ftp.example.org'
+               );
+               $this->assertFalse(
+                       ExternalStore::fetchFromURL( '/example.txt' ),
+                       'Deny path /example.txt'
+               );
+               $this->assertFalse(
+                       ExternalStore::fetchFromURL( 'http://' ),
+                       'Deny protocol http://'
+               );
+       }
+}
+
+class ExternalStoreFOO {
+
+       protected $data = array(
+               'cluster1' => array(
+                       '200' => 'Hello',
+                       '300' => array(
+                               'Hello', 'World',
+                       ),
+               ),
+       );
+
+       /**
+        * Fetch data from given URL
+        * @param string $url An url of the form FOO://cluster/id or FOO://cluster/id/itemid.
+        * @return mixed
+        */
+       function fetchFromURL( $url ) {
+               // Based on ExternalStoreDB
+               $path = explode( '/', $url );
+               $cluster = $path[2];
+               $id = $path[3];
+               if ( isset( $path[4] ) ) {
+                       $itemID = $path[4];
+               } else {
+                       $itemID = false;
+               }
+
+               if ( !isset( $this->data[$cluster][$id] ) ) {
+                       return null;
+               }
+
+               if ( $itemID !== false
+                       && is_array( $this->data[$cluster][$id] )
+                       && isset( $this->data[$cluster][$id][$itemID] )
+               ) {
+                       return $this->data[$cluster][$id][$itemID];
+               }
+
+               return $this->data[$cluster][$id];
+       }
+}
diff --git a/tests/phpunit/includes/filerepo/file/LocalFileTest.php b/tests/phpunit/includes/filerepo/file/LocalFileTest.php
new file mode 100644 (file)
index 0000000..5c5052e
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+
+/**
+ * These tests should work regardless of $wgCapitalLinks
+ * @group Database
+ * @todo Split tests into providers and test methods
+ */
+
+class LocalFileTest extends MediaWikiTestCase {
+
+       protected function setUp() {
+               parent::setUp();
+
+               $this->setMwGlobals( 'wgCapitalLinks', true );
+
+               $info = array(
+                       'name' => 'test',
+                       'directory' => '/testdir',
+                       'url' => '/testurl',
+                       'hashLevels' => 2,
+                       'transformVia404' => false,
+                       'backend' => new FSFileBackend( array(
+                               'name' => 'local-backend',
+                               'wikiId' => wfWikiId(),
+                               'containerPaths' => array(
+                                       'cont1' => "/testdir/local-backend/tempimages/cont1",
+                                       'cont2' => "/testdir/local-backend/tempimages/cont2"
+                               )
+                       ) )
+               );
+               $this->repo_hl0 = new LocalRepo( array( 'hashLevels' => 0 ) + $info );
+               $this->repo_hl2 = new LocalRepo( array( 'hashLevels' => 2 ) + $info );
+               $this->repo_lc = new LocalRepo( array( 'initialCapital' => false ) + $info );
+               $this->file_hl0 = $this->repo_hl0->newFile( 'test!' );
+               $this->file_hl2 = $this->repo_hl2->newFile( 'test!' );
+               $this->file_lc = $this->repo_lc->newFile( 'test!' );
+       }
+
+       /**
+        * @covers File::getHashPath
+        */
+       public function testGetHashPath() {
+               $this->assertEquals( '', $this->file_hl0->getHashPath() );
+               $this->assertEquals( 'a/a2/', $this->file_hl2->getHashPath() );
+               $this->assertEquals( 'c/c4/', $this->file_lc->getHashPath() );
+       }
+
+       /**
+        * @covers File::getRel
+        */
+       public function testGetRel() {
+               $this->assertEquals( 'Test!', $this->file_hl0->getRel() );
+               $this->assertEquals( 'a/a2/Test!', $this->file_hl2->getRel() );
+               $this->assertEquals( 'c/c4/test!', $this->file_lc->getRel() );
+       }
+
+       /**
+        * @covers File::getUrlRel
+        */
+       public function testGetUrlRel() {
+               $this->assertEquals( 'Test%21', $this->file_hl0->getUrlRel() );
+               $this->assertEquals( 'a/a2/Test%21', $this->file_hl2->getUrlRel() );
+               $this->assertEquals( 'c/c4/test%21', $this->file_lc->getUrlRel() );
+       }
+
+       /**
+        * @covers File::getArchivePath
+        */
+       public function testGetArchivePath() {
+               $this->assertEquals(
+                       'mwstore://local-backend/test-public/archive',
+                       $this->file_hl0->getArchivePath()
+               );
+               $this->assertEquals(
+                       'mwstore://local-backend/test-public/archive/a/a2',
+                       $this->file_hl2->getArchivePath()
+               );
+               $this->assertEquals(
+                       'mwstore://local-backend/test-public/archive/!',
+                       $this->file_hl0->getArchivePath( '!' )
+               );
+               $this->assertEquals(
+                       'mwstore://local-backend/test-public/archive/a/a2/!',
+                       $this->file_hl2->getArchivePath( '!' )
+               );
+       }
+
+       /**
+        * @covers File::getThumbPath
+        */
+       public function testGetThumbPath() {
+               $this->assertEquals(
+                       'mwstore://local-backend/test-thumb/Test!',
+                       $this->file_hl0->getThumbPath()
+               );
+               $this->assertEquals(
+                       'mwstore://local-backend/test-thumb/a/a2/Test!',
+                       $this->file_hl2->getThumbPath()
+               );
+               $this->assertEquals(
+                       'mwstore://local-backend/test-thumb/Test!/x',
+                       $this->file_hl0->getThumbPath( 'x' )
+               );
+               $this->assertEquals(
+                       'mwstore://local-backend/test-thumb/a/a2/Test!/x',
+                       $this->file_hl2->getThumbPath( 'x' )
+               );
+       }
+
+       /**
+        * @covers File::getArchiveUrl
+        */
+       public function testGetArchiveUrl() {
+               $this->assertEquals( '/testurl/archive', $this->file_hl0->getArchiveUrl() );
+               $this->assertEquals( '/testurl/archive/a/a2', $this->file_hl2->getArchiveUrl() );
+               $this->assertEquals( '/testurl/archive/%21', $this->file_hl0->getArchiveUrl( '!' ) );
+               $this->assertEquals( '/testurl/archive/a/a2/%21', $this->file_hl2->getArchiveUrl( '!' ) );
+       }
+
+       /**
+        * @covers File::getThumbUrl
+        */
+       public function testGetThumbUrl() {
+               $this->assertEquals( '/testurl/thumb/Test%21', $this->file_hl0->getThumbUrl() );
+               $this->assertEquals( '/testurl/thumb/a/a2/Test%21', $this->file_hl2->getThumbUrl() );
+               $this->assertEquals( '/testurl/thumb/Test%21/x', $this->file_hl0->getThumbUrl( 'x' ) );
+               $this->assertEquals( '/testurl/thumb/a/a2/Test%21/x', $this->file_hl2->getThumbUrl( 'x' ) );
+       }
+
+       /**
+        * @covers File::getArchiveVirtualUrl
+        */
+       public function testGetArchiveVirtualUrl() {
+               $this->assertEquals( 'mwrepo://test/public/archive', $this->file_hl0->getArchiveVirtualUrl() );
+               $this->assertEquals(
+                       'mwrepo://test/public/archive/a/a2',
+                       $this->file_hl2->getArchiveVirtualUrl()
+               );
+               $this->assertEquals(
+                       'mwrepo://test/public/archive/%21',
+                       $this->file_hl0->getArchiveVirtualUrl( '!' )
+               );
+               $this->assertEquals(
+                       'mwrepo://test/public/archive/a/a2/%21',
+                       $this->file_hl2->getArchiveVirtualUrl( '!' )
+               );
+       }
+
+       /**
+        * @covers File::getThumbVirtualUrl
+        */
+       public function testGetThumbVirtualUrl() {
+               $this->assertEquals( 'mwrepo://test/thumb/Test%21', $this->file_hl0->getThumbVirtualUrl() );
+               $this->assertEquals( 'mwrepo://test/thumb/a/a2/Test%21', $this->file_hl2->getThumbVirtualUrl() );
+               $this->assertEquals(
+                       'mwrepo://test/thumb/Test%21/%21',
+                       $this->file_hl0->getThumbVirtualUrl( '!' )
+               );
+               $this->assertEquals(
+                       'mwrepo://test/thumb/a/a2/Test%21/%21',
+                       $this->file_hl2->getThumbVirtualUrl( '!' )
+               );
+       }
+
+       /**
+        * @covers File::getUrl
+        */
+       public function testGetUrl() {
+               $this->assertEquals( '/testurl/Test%21', $this->file_hl0->getUrl() );
+               $this->assertEquals( '/testurl/a/a2/Test%21', $this->file_hl2->getUrl() );
+       }
+
+       /**
+        * @covers ::wfLocalFile
+        */
+       public function testWfLocalFile() {
+               $file = wfLocalFile( "File:Some_file_that_probably_doesn't exist.png" );
+               $this->assertThat(
+                       $file,
+                       $this->isInstanceOf( 'LocalFile' ),
+                       'wfLocalFile() returns LocalFile for valid Titles'
+               );
+       }
+}
diff --git a/tests/phpunit/includes/libs/ArrayUtilsTest.php b/tests/phpunit/includes/libs/ArrayUtilsTest.php
new file mode 100644 (file)
index 0000000..7bdb1ca
--- /dev/null
@@ -0,0 +1,311 @@
+<?php
+/**
+ * Test class for ArrayUtils class
+ *
+ * @group Database
+ */
+
+class ArrayUtilsTest extends MediaWikiTestCase {
+       private $search;
+
+       /**
+        * @covers ArrayUtils::findLowerBound
+        * @dataProvider provideFindLowerBound
+        */
+       function testFindLowerBound(
+               $valueCallback, $valueCount, $comparisonCallback, $target, $expected
+       ) {
+               $this->assertSame(
+                       ArrayUtils::findLowerBound(
+                               $valueCallback, $valueCount, $comparisonCallback, $target
+                       ), $expected
+               );
+       }
+
+       function provideFindLowerBound() {
+               $self = $this;
+               $indexValueCallback = function ( $size ) use ( $self ) {
+                       return function ( $val ) use ( $self, $size ) {
+                               $self->assertTrue( $val >= 0 );
+                               $self->assertTrue( $val < $size );
+                               return $val;
+                       };
+               };
+               $comparisonCallback = function ( $a, $b ) {
+                       return $a - $b;
+               };
+
+               return array(
+                       array(
+                               $indexValueCallback( 0 ),
+                               0,
+                               $comparisonCallback,
+                               1,
+                               false,
+                       ),
+                       array(
+                               $indexValueCallback( 1 ),
+                               1,
+                               $comparisonCallback,
+                               -1,
+                               false,
+                       ),
+                       array(
+                               $indexValueCallback( 1 ),
+                               1,
+                               $comparisonCallback,
+                               0,
+                               0,
+                       ),
+                       array(
+                               $indexValueCallback( 1 ),
+                               1,
+                               $comparisonCallback,
+                               1,
+                               0,
+                       ),
+                       array(
+                               $indexValueCallback( 2 ),
+                               2,
+                               $comparisonCallback,
+                               -1,
+                               false,
+                       ),
+                       array(
+                               $indexValueCallback( 2 ),
+                               2,
+                               $comparisonCallback,
+                               0,
+                               0,
+                       ),
+                       array(
+                               $indexValueCallback( 2 ),
+                               2,
+                               $comparisonCallback,
+                               0.5,
+                               0,
+                       ),
+                       array(
+                               $indexValueCallback( 2 ),
+                               2,
+                               $comparisonCallback,
+                               1,
+                               1,
+                       ),
+                       array(
+                               $indexValueCallback( 2 ),
+                               2,
+                               $comparisonCallback,
+                               1.5,
+                               1,
+                       ),
+                       array(
+                               $indexValueCallback( 3 ),
+                               3,
+                               $comparisonCallback,
+                               1,
+                               1,
+                       ),
+                       array(
+                               $indexValueCallback( 3 ),
+                               3,
+                               $comparisonCallback,
+                               1.5,
+                               1,
+                       ),
+                       array(
+                               $indexValueCallback( 3 ),
+                               3,
+                               $comparisonCallback,
+                               2,
+                               2,
+                       ),
+                       array(
+                               $indexValueCallback( 3 ),
+                               3,
+                               $comparisonCallback,
+                               3,
+                               2,
+                       ),
+               );
+       }
+
+       /**
+        * @covers ArrayUtils::arrayDiffAssocRecursive
+        * @dataProvider provideArrayDiffAssocRecursive
+        */
+       function testArrayDiffAssocRecursive( $expected ) {
+               $args = func_get_args();
+               array_shift( $args );
+               $this->assertEquals( call_user_func_array(
+                       'ArrayUtils::arrayDiffAssocRecursive', $args
+               ), $expected );
+       }
+
+       function provideArrayDiffAssocRecursive() {
+               return array(
+                       array(
+                               array(),
+                               array(),
+                               array(),
+                       ),
+                       array(
+                               array(),
+                               array(),
+                               array(),
+                               array(),
+                       ),
+                       array(
+                               array( 1 ),
+                               array( 1 ),
+                               array(),
+                       ),
+                       array(
+                               array( 1 ),
+                               array( 1 ),
+                               array(),
+                               array(),
+                       ),
+                       array(
+                               array(),
+                               array(),
+                               array( 1 ),
+                       ),
+                       array(
+                               array(),
+                               array(),
+                               array( 1 ),
+                               array( 2 ),
+                       ),
+                       array(
+                               array( '' => 1 ),
+                               array( '' => 1 ),
+                               array(),
+                       ),
+                       array(
+                               array(),
+                               array(),
+                               array( '' => 1 ),
+                       ),
+                       array(
+                               array( 1 ),
+                               array( 1 ),
+                               array( 2 ),
+                       ),
+                       array(
+                               array(),
+                               array( 1 ),
+                               array( 2 ),
+                               array( 1 ),
+                       ),
+                       array(
+                               array(),
+                               array( 1 ),
+                               array( 1, 2 ),
+                       ),
+                       array(
+                               array( 1 => 1 ),
+                               array( 1 => 1 ),
+                               array( 1 ),
+                       ),
+                       array(
+                               array(),
+                               array( 1 => 1 ),
+                               array( 1 ),
+                               array( 1 => 1),
+                       ),
+                       array(
+                               array(),
+                               array( 1 => 1 ),
+                               array( 1, 1, 1 ),
+                       ),
+                       array(
+                               array(),
+                               array( array() ),
+                               array(),
+                       ),
+                       array(
+                               array(),
+                               array( array( array() ) ),
+                               array(),
+                       ),
+                       array(
+                               array( 1, array( 1 ) ),
+                               array( 1, array( 1 ) ),
+                               array(),
+                       ),
+                       array(
+                               array( 1 ),
+                               array( 1, array( 1 ) ),
+                               array( 2, array( 1 ) ),
+                       ),
+                       array(
+                               array(),
+                               array( 1, array( 1 ) ),
+                               array( 2, array( 1 ) ),
+                               array( 1, array( 2 ) ),
+                       ),
+                       array(
+                               array( 1 ),
+                               array( 1, array() ),
+                               array( 2 ),
+                       ),
+                       array(
+                               array(),
+                               array( 1, array() ),
+                               array( 2 ),
+                               array( 1 ),
+                       ),
+                       array(
+                               array( 1, array( 1 => 2 ) ),
+                               array( 1, array( 1, 2 ) ),
+                               array( 2, array( 1 ) ),
+                       ),
+                       array(
+                               array( 1 ),
+                               array( 1, array( 1, 2 ) ),
+                               array( 2, array( 1 ) ),
+                               array( 2, array( 1 => 2 ) ),
+                       ),
+                       array(
+                               array( 1 => array( 1, 2 ) ),
+                               array( 1, array( 1, 2 ) ),
+                               array( 1, array( 2 ) ),
+                       ),
+                       array(
+                               array( 1 => array( array( 2, 3 ), 2 ) ),
+                               array( 1, array( array( 2, 3 ), 2 ) ),
+                               array( 1, array( 2 ) ),
+                       ),
+                       array(
+                               array( 1 => array( array( 2 ), 2 ) ),
+                               array( 1, array( array( 2, 3 ), 2 ) ),
+                               array( 1, array( array( 1 => 3 ) ) ),
+                       ),
+                       array(
+                               array( 1 => array( 1 => 2 ) ),
+                               array( 1, array( array( 2, 3 ), 2 ) ),
+                               array( 1, array( array( 1 => 3, 0 => 2 ) ) ),
+                       ),
+                       array(
+                               array( 1 => array( 1 => 2 ) ),
+                               array( 1, array( array( 2, 3 ), 2 ) ),
+                               array( 1, array( array( 1 => 3 ) ) ),
+                               array( 1 => array( array( 2 ) ) ),
+                       ),
+                       array(
+                               array(),
+                               array( 1, array( array( 2, 3 ), 2 ) ),
+                               array( 1 => array( 1 => 2, 0 => array( 1 => 3, 0 => 2 ) ), 0 => 1 ),
+                       ),
+                       array(
+                               array(),
+                               array( 1, array( array( 2, 3 ), 2 ) ),
+                               array( 1 => array( 1 => 2 ) ),
+                               array( 1 => array( array( 1 => 3 ) ) ),
+                               array( 1 => array( array( 2 ) ) ),
+                               array( 1 ),
+                       ),
+               );
+       }
+}
diff --git a/tests/phpunit/includes/libs/XmlTypeCheckTest.php b/tests/phpunit/includes/libs/XmlTypeCheckTest.php
new file mode 100644 (file)
index 0000000..8d6f1ed
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/**
+ * PHPUnit tests for XMLTypeCheck.
+ * @author physikerwelt
+ * @group Xml
+ * @covers XMLTypeCheck
+ */
+class XmlTypeCheckTest extends MediaWikiTestCase {
+       const WELL_FORMED_XML = "<root><child /></root>";
+       const MAL_FORMED_XML = "<root><child /></error>";
+
+       /**
+        * @covers XMLTypeCheck::newFromString
+        * @covers XMLTypeCheck::getRootElement
+        */
+       public function testWellFormedXML() {
+               $testXML = XmlTypeCheck::newFromString( self::WELL_FORMED_XML );
+               $this->assertTrue( $testXML->wellFormed );
+               $this->assertEquals( 'root', $testXML->getRootElement() );
+       }
+
+       /**
+        * @covers XMLTypeCheck::newFromString
+        */
+       public function testMalFormedXML() {
+               $testXML = XmlTypeCheck::newFromString( self::MAL_FORMED_XML );
+               $this->assertFalse( $testXML->wellFormed );
+       }
+
+}
diff --git a/tests/phpunit/includes/page/ArticleTablesTest.php b/tests/phpunit/includes/page/ArticleTablesTest.php
new file mode 100644 (file)
index 0000000..9f2b7a0
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * @group Database
+ */
+class ArticleTablesTest extends MediaWikiLangTestCase {
+       /**
+        * Make sure that bug 14404 doesn't strike again. We don't want
+        * templatelinks based on the user language when {{int:}} is used, only the
+        * content language.
+        *
+        * @covers Title::getTemplateLinksFrom
+        * @covers Title::getLinksFrom
+        */
+       public function testTemplatelinksUsesContentLanguage() {
+               $title = Title::newFromText( 'Bug 14404' );
+               $page = WikiPage::factory( $title );
+               $user = new User();
+               $user->mRights = array( 'createpage', 'edit', 'purge' );
+               $this->setMwGlobals( 'wgLanguageCode', 'es' );
+               $this->setMwGlobals( 'wgContLang', Language::factory( 'es' ) );
+               $this->setMwGlobals( 'wgLang', Language::factory( 'fr' ) );
+
+               $page->doEditContent(
+                       new WikitextContent( '{{:{{int:history}}}}' ),
+                       'Test code for bug 14404',
+                       0,
+                       false,
+                       $user
+               );
+               $templates1 = $title->getTemplateLinksFrom();
+
+               $this->setMwGlobals( 'wgLang', Language::factory( 'de' ) );
+               $page = WikiPage::factory( $title ); // In order to force the re-rendering of the same wikitext
+
+               // We need an edit, a purge is not enough to regenerate the tables
+               $page->doEditContent(
+                       new WikitextContent( '{{:{{int:history}}}}' ),
+                       'Test code for bug 14404',
+                       EDIT_UPDATE,
+                       false,
+                       $user
+               );
+               $templates2 = $title->getTemplateLinksFrom();
+
+               /**
+                * @var Title[] $templates1
+                * @var Title[] $templates2
+                */
+               $this->assertEquals( $templates1, $templates2 );
+               $this->assertEquals( $templates1[0]->getFullText(), 'Historial' );
+       }
+}
diff --git a/tests/phpunit/includes/page/ArticleTest.php b/tests/phpunit/includes/page/ArticleTest.php
new file mode 100644 (file)
index 0000000..ae069ea
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+
+class ArticleTest extends MediaWikiTestCase {
+
+       /**
+        * @var Title
+        */
+       private $title;
+       /**
+        * @var Article
+        */
+       private $article;
+
+       /** creates a title object and its article object */
+       protected function setUp() {
+               parent::setUp();
+               $this->title = Title::makeTitle( NS_MAIN, 'SomePage' );
+               $this->article = new Article( $this->title );
+       }
+
+       /** cleanup title object and its article object */
+       protected function tearDown() {
+               parent::tearDown();
+               $this->title = null;
+               $this->article = null;
+       }
+
+       /**
+        * @covers Article::__get
+        */
+       public function testImplementsGetMagic() {
+               $this->assertEquals( false, $this->article->mLatest, "Article __get magic" );
+       }
+
+       /**
+        * @depends testImplementsGetMagic
+        * @covers Article::__set
+        */
+       public function testImplementsSetMagic() {
+               $this->article->mLatest = 2;
+               $this->assertEquals( 2, $this->article->mLatest, "Article __set magic" );
+       }
+
+       /**
+        * @depends testImplementsSetMagic
+        * @covers Article::__call
+        */
+       public function testImplementsCallMagic() {
+               $this->article->mLatest = 33;
+               $this->article->mDataLoaded = true;
+               $this->assertEquals( 33, $this->article->getLatest(), "Article __call magic" );
+       }
+
+       /**
+        * @covers Article::__get
+        * @covers Article::__set
+        */
+       public function testGetOrSetOnNewProperty() {
+               $this->article->ext_someNewProperty = 12;
+               $this->assertEquals( 12, $this->article->ext_someNewProperty,
+                       "Article get/set magic on new field" );
+
+               $this->article->ext_someNewProperty = -8;
+               $this->assertEquals( -8, $this->article->ext_someNewProperty,
+                       "Article get/set magic on update to new field" );
+       }
+
+       /**
+        * Checks for the existence of the backwards compatibility static functions
+        * (forwarders to WikiPage class)
+        *
+        * @covers Article::selectFields
+        * @covers Article::onArticleCreate
+        * @covers Article::onArticleDelete
+        * @covers Article::onArticleEdit
+        * @covers Article::getAutosummary
+        */
+       public function testStaticFunctions() {
+               $this->hideDeprecated( 'Article::selectFields' );
+               $this->hideDeprecated( 'Article::getAutosummary' );
+               $this->hideDeprecated( 'WikiPage::getAutosummary' );
+               $this->hideDeprecated( 'CategoryPage::getAutosummary' ); // Inherited from Article
+
+               $this->assertEquals( WikiPage::selectFields(), Article::selectFields(),
+                       "Article static functions" );
+               $this->assertEquals( true, is_callable( "Article::onArticleCreate" ),
+                       "Article static functions" );
+               $this->assertEquals( true, is_callable( "Article::onArticleDelete" ),
+                       "Article static functions" );
+               $this->assertEquals( true, is_callable( "ImagePage::onArticleEdit" ),
+                       "Article static functions" );
+               $this->assertTrue( is_string( CategoryPage::getAutosummary( '', '', 0 ) ),
+                       "Article static functions" );
+       }
+}
diff --git a/tests/phpunit/includes/page/ImagePage404Test.php b/tests/phpunit/includes/page/ImagePage404Test.php
new file mode 100644 (file)
index 0000000..197a2b3
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+/**
+ * For doing Image Page tests that rely on 404 thumb handling
+ */
+class ImagePage404Test extends MediaWikiMediaTestCase {
+
+       protected function getRepoOptions() {
+               return parent::getRepoOptions() + array( 'transformVia404' => true );
+       }
+
+       function setUp() {
+               $this->setMwGlobals( 'wgImageLimits', array(
+                       array( 320, 240 ),
+                       array( 640, 480 ),
+                       array( 800, 600 ),
+                       array( 1024, 768 ),
+                       array( 1280, 1024 )
+               ) );
+               parent::setUp();
+       }
+
+       function getImagePage( $filename ) {
+               $title = Title::makeTitleSafe( NS_FILE, $filename );
+               $file = $this->dataFile( $filename );
+               $iPage = new ImagePage( $title );
+               $iPage->setFile( $file );
+               return $iPage;
+       }
+
+       /**
+        * @dataProvider providerGetThumbSizes
+        * @param string $filename
+        * @param int $expectedNumberThumbs How many thumbnails to show
+        */
+       function testGetThumbSizes( $filename, $expectedNumberThumbs ) {
+               $iPage = $this->getImagePage( $filename );
+               $reflection = new ReflectionClass( $iPage );
+               $reflMethod = $reflection->getMethod( 'getThumbSizes' );
+               $reflMethod->setAccessible( true );
+
+               $actual = $reflMethod->invoke( $iPage, 545, 700 );
+               $this->assertEquals( count( $actual ), $expectedNumberThumbs );
+       }
+
+       function providerGetThumbSizes() {
+               return array(
+                       array( 'animated.gif', 6 ),
+                       array( 'Toll_Texas_1.svg', 6 ),
+                       array( '80x60-Greyscale.xcf', 6 ),
+                       array( 'jpeg-comment-binary.jpg', 6 ),
+               );
+       }
+}
diff --git a/tests/phpunit/includes/page/ImagePageTest.php b/tests/phpunit/includes/page/ImagePageTest.php
new file mode 100644 (file)
index 0000000..3c255b5
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+class ImagePageTest extends MediaWikiMediaTestCase {
+
+       function setUp() {
+               $this->setMwGlobals( 'wgImageLimits', array(
+                       array( 320, 240 ),
+                       array( 640, 480 ),
+                       array( 800, 600 ),
+                       array( 1024, 768 ),
+                       array( 1280, 1024 )
+               ) );
+               parent::setUp();
+       }
+
+       function getImagePage( $filename ) {
+               $title = Title::makeTitleSafe( NS_FILE, $filename );
+               $file = $this->dataFile( $filename );
+               $iPage = new ImagePage( $title );
+               $iPage->setFile( $file );
+               return $iPage;
+       }
+
+       /**
+        * @dataProvider providerGetDisplayWidthHeight
+        * @param array $dim Array [maxWidth, maxHeight, width, height]
+        * @param array $expected Array [width, height] The width and height we expect to display at
+        */
+       function testGetDisplayWidthHeight( $dim, $expected ) {
+               $iPage = $this->getImagePage( 'animated.gif' );
+               $reflection = new ReflectionClass( $iPage );
+               $reflMethod = $reflection->getMethod( 'getDisplayWidthHeight' );
+               $reflMethod->setAccessible( true );
+
+               $actual = $reflMethod->invoke( $iPage, $dim[0], $dim[1], $dim[2], $dim[3] );
+               $this->assertEquals( $actual, $expected );
+       }
+
+       function providerGetDisplayWidthHeight() {
+               return array(
+                       array(
+                               array( 1024.0, 768.0, 600.0, 600.0 ),
+                               array( 600.0, 600.0 )
+                       ),
+                       array(
+                               array( 1024.0, 768.0, 1600.0, 600.0 ),
+                               array( 1024.0, 384.0 )
+                       ),
+                       array(
+                               array( 1024.0, 768.0, 1024.0, 768.0 ),
+                               array( 1024.0, 768.0 )
+                       ),
+                       array(
+                               array( 1024.0, 768.0, 800.0, 1000.0 ),
+                               array( 614.0, 768.0 )
+                       ),
+                       array(
+                               array( 1024.0, 768.0, 0, 1000 ),
+                               array( 0, 0 )
+                       ),
+                       array(
+                               array( 1024.0, 768.0, 2000, 0 ),
+                               array( 0, 0 )
+                       ),
+               );
+       }
+
+       /**
+        * @dataProvider providerGetThumbSizes
+        * @param string $filename
+        * @param int $expectedNumberThumbs How many thumbnails to show
+        */
+       function testGetThumbSizes( $filename, $expectedNumberThumbs ) {
+               $iPage = $this->getImagePage( $filename );
+               $reflection = new ReflectionClass( $iPage );
+               $reflMethod = $reflection->getMethod( 'getThumbSizes' );
+               $reflMethod->setAccessible( true );
+
+               $actual = $reflMethod->invoke( $iPage, 545, 700 );
+               $this->assertEquals( count( $actual ), $expectedNumberThumbs );
+       }
+
+       function providerGetThumbSizes() {
+               return array(
+                       array( 'animated.gif', 2 ),
+                       array( 'Toll_Texas_1.svg', 1 ),
+                       array( '80x60-Greyscale.xcf', 1 ),
+                       array( 'jpeg-comment-binary.jpg', 2 ),
+               );
+       }
+}
diff --git a/tests/phpunit/includes/page/WikiPageTest.php b/tests/phpunit/includes/page/WikiPageTest.php
new file mode 100644 (file)
index 0000000..c011e9a
--- /dev/null
@@ -0,0 +1,1301 @@
+<?php
+
+/**
+ * @group ContentHandler
+ * @group Database
+ * ^--- important, causes temporary tables to be used instead of the real database
+ * @group medium
+ **/
+class WikiPageTest extends MediaWikiLangTestCase {
+
+       protected $pages_to_delete;
+
+       function __construct( $name = null, array $data = array(), $dataName = '' ) {
+               parent::__construct( $name, $data, $dataName );
+
+               $this->tablesUsed = array_merge(
+                       $this->tablesUsed,
+                       array( 'page',
+                               'revision',
+                               'text',
+
+                               'recentchanges',
+                               'logging',
+
+                               'page_props',
+                               'pagelinks',
+                               'categorylinks',
+                               'langlinks',
+                               'externallinks',
+                               'imagelinks',
+                               'templatelinks',
+                               'iwlinks' ) );
+       }
+
+       protected function setUp() {
+               parent::setUp();
+               $this->pages_to_delete = array();
+
+               LinkCache::singleton()->clear(); # avoid cached redirect status, etc
+       }
+
+       protected function tearDown() {
+               foreach ( $this->pages_to_delete as $p ) {
+                       /* @var $p WikiPage */
+
+                       try {
+                               if ( $p->exists() ) {
+                                       $p->doDeleteArticle( "testing done." );
+                               }
+                       } catch ( MWException $ex ) {
+                               // fail silently
+                       }
+               }
+               parent::tearDown();
+       }
+
+       /**
+        * @param Title|string $title
+        * @param string|null $model
+        * @return WikiPage
+        */
+       protected function newPage( $title, $model = null ) {
+               if ( is_string( $title ) ) {
+                       $ns = $this->getDefaultWikitextNS();
+                       $title = Title::newFromText( $title, $ns );
+               }
+
+               $p = new WikiPage( $title );
+
+               $this->pages_to_delete[] = $p;
+
+               return $p;
+       }
+
+       /**
+        * @param string|Title|WikiPage $page
+        * @param string $text
+        * @param int $model
+        *
+        * @return WikiPage
+        */
+       protected function createPage( $page, $text, $model = null ) {
+               if ( is_string( $page ) || $page instanceof Title ) {
+                       $page = $this->newPage( $page, $model );
+               }
+
+               $content = ContentHandler::makeContent( $text, $page->getTitle(), $model );
+               $page->doEditContent( $content, "testing", EDIT_NEW );
+
+               return $page;
+       }
+
+       /**
+        * @covers WikiPage::doEditContent
+        */
+       public function testDoEditContent() {
+               $page = $this->newPage( "WikiPageTest_testDoEditContent" );
+               $title = $page->getTitle();
+
+               $content = ContentHandler::makeContent(
+                       "[[Lorem ipsum]] dolor sit amet, consetetur sadipscing elitr, sed diam "
+                               . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.",
+                       $title,
+                       CONTENT_MODEL_WIKITEXT
+               );
+
+               $page->doEditContent( $content, "[[testing]] 1" );
+
+               $this->assertTrue( $title->getArticleID() > 0, "Title object should have new page id" );
+               $this->assertTrue( $page->getId() > 0, "WikiPage should have new page id" );
+               $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" );
+               $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" );
+
+               $id = $page->getId();
+
+               # ------------------------
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
+               $n = $res->numRows();
+               $res->free();
+
+               $this->assertEquals( 1, $n, 'pagelinks should contain one link from the page' );
+
+               # ------------------------
+               $page = new WikiPage( $title );
+
+               $retrieved = $page->getContent();
+               $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
+
+               # ------------------------
+               $content = ContentHandler::makeContent(
+                       "At vero eos et accusam et justo duo [[dolores]] et ea rebum. "
+                               . "Stet clita kasd [[gubergren]], no sea takimata sanctus est.",
+                       $title,
+                       CONTENT_MODEL_WIKITEXT
+               );
+
+               $page->doEditContent( $content, "testing 2" );
+
+               # ------------------------
+               $page = new WikiPage( $title );
+
+               $retrieved = $page->getContent();
+               $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
+
+               # ------------------------
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
+               $n = $res->numRows();
+               $res->free();
+
+               $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
+       }
+
+       /**
+        * @covers WikiPage::doEdit
+        */
+       public function testDoEdit() {
+               $this->hideDeprecated( "WikiPage::doEdit" );
+               $this->hideDeprecated( "WikiPage::getText" );
+               $this->hideDeprecated( "Revision::getText" );
+
+               //NOTE: assume help namespace will default to wikitext
+               $title = Title::newFromText( "Help:WikiPageTest_testDoEdit" );
+
+               $page = $this->newPage( $title );
+
+               $text = "[[Lorem ipsum]] dolor sit amet, consetetur sadipscing elitr, sed diam "
+                       . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.";
+
+               $page->doEdit( $text, "[[testing]] 1" );
+
+               $this->assertTrue( $title->getArticleID() > 0, "Title object should have new page id" );
+               $this->assertTrue( $page->getId() > 0, "WikiPage should have new page id" );
+               $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" );
+               $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" );
+
+               $id = $page->getId();
+
+               # ------------------------
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
+               $n = $res->numRows();
+               $res->free();
+
+               $this->assertEquals( 1, $n, 'pagelinks should contain one link from the page' );
+
+               # ------------------------
+               $page = new WikiPage( $title );
+
+               $retrieved = $page->getText();
+               $this->assertEquals( $text, $retrieved, 'retrieved text doesn\'t equal original' );
+
+               # ------------------------
+               $text = "At vero eos et accusam et justo duo [[dolores]] et ea rebum. "
+                       . "Stet clita kasd [[gubergren]], no sea takimata sanctus est.";
+
+               $page->doEdit( $text, "testing 2" );
+
+               # ------------------------
+               $page = new WikiPage( $title );
+
+               $retrieved = $page->getText();
+               $this->assertEquals( $text, $retrieved, 'retrieved text doesn\'t equal original' );
+
+               # ------------------------
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
+               $n = $res->numRows();
+               $res->free();
+
+               $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
+       }
+
+       /**
+        * @covers WikiPage::doQuickEdit
+        */
+       public function testDoQuickEdit() {
+               global $wgUser;
+
+               $this->hideDeprecated( "WikiPage::doQuickEdit" );
+
+               //NOTE: assume help namespace will default to wikitext
+               $page = $this->createPage( "Help:WikiPageTest_testDoQuickEdit", "original text" );
+
+               $text = "quick text";
+               $page->doQuickEdit( $text, $wgUser, "testing q" );
+
+               # ---------------------
+               $page = new WikiPage( $page->getTitle() );
+               $this->assertEquals( $text, $page->getText() );
+       }
+
+       /**
+        * @covers WikiPage::doQuickEditContent
+        */
+       public function testDoQuickEditContent() {
+               global $wgUser;
+
+               $page = $this->createPage(
+                       "WikiPageTest_testDoQuickEditContent",
+                       "original text",
+                       CONTENT_MODEL_WIKITEXT
+               );
+
+               $content = ContentHandler::makeContent(
+                       "quick text",
+                       $page->getTitle(),
+                       CONTENT_MODEL_WIKITEXT
+               );
+               $page->doQuickEditContent( $content, $wgUser, "testing q" );
+
+               # ---------------------
+               $page = new WikiPage( $page->getTitle() );
+               $this->assertTrue( $content->equals( $page->getContent() ) );
+       }
+
+       /**
+        * @covers WikiPage::doDeleteArticle
+        */
+       public function testDoDeleteArticle() {
+               $page = $this->createPage(
+                       "WikiPageTest_testDoDeleteArticle",
+                       "[[original text]] foo",
+                       CONTENT_MODEL_WIKITEXT
+               );
+               $id = $page->getId();
+
+               $page->doDeleteArticle( "testing deletion" );
+
+               $this->assertFalse(
+                       $page->getTitle()->getArticleID() > 0,
+                       "Title object should now have page id 0"
+               );
+               $this->assertFalse( $page->getId() > 0, "WikiPage should now have page id 0" );
+               $this->assertFalse(
+                       $page->exists(),
+                       "WikiPage::exists should return false after page was deleted"
+               );
+               $this->assertNull(
+                       $page->getContent(),
+                       "WikiPage::getContent should return null after page was deleted"
+               );
+               $this->assertFalse(
+                       $page->getText(),
+                       "WikiPage::getText should return false after page was deleted"
+               );
+
+               $t = Title::newFromText( $page->getTitle()->getPrefixedText() );
+               $this->assertFalse(
+                       $t->exists(),
+                       "Title::exists should return false after page was deleted"
+               );
+
+               # ------------------------
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
+               $n = $res->numRows();
+               $res->free();
+
+               $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
+       }
+
+       /**
+        * @covers WikiPage::doDeleteUpdates
+        */
+       public function testDoDeleteUpdates() {
+               $page = $this->createPage(
+                       "WikiPageTest_testDoDeleteArticle",
+                       "[[original text]] foo",
+                       CONTENT_MODEL_WIKITEXT
+               );
+               $id = $page->getId();
+
+               $page->doDeleteUpdates( $id );
+
+               # ------------------------
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
+               $n = $res->numRows();
+               $res->free();
+
+               $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
+       }
+
+       /**
+        * @covers WikiPage::getRevision
+        */
+       public function testGetRevision() {
+               $page = $this->newPage( "WikiPageTest_testGetRevision" );
+
+               $rev = $page->getRevision();
+               $this->assertNull( $rev );
+
+               # -----------------
+               $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
+
+               $rev = $page->getRevision();
+
+               $this->assertEquals( $page->getLatest(), $rev->getId() );
+               $this->assertEquals( "some text", $rev->getContent()->getNativeData() );
+       }
+
+       /**
+        * @covers WikiPage::getContent
+        */
+       public function testGetContent() {
+               $page = $this->newPage( "WikiPageTest_testGetContent" );
+
+               $content = $page->getContent();
+               $this->assertNull( $content );
+
+               # -----------------
+               $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
+
+               $content = $page->getContent();
+               $this->assertEquals( "some text", $content->getNativeData() );
+       }
+
+       /**
+        * @covers WikiPage::getText
+        */
+       public function testGetText() {
+               $this->hideDeprecated( "WikiPage::getText" );
+
+               $page = $this->newPage( "WikiPageTest_testGetText" );
+
+               $text = $page->getText();
+               $this->assertFalse( $text );
+
+               # -----------------
+               $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
+
+               $text = $page->getText();
+               $this->assertEquals( "some text", $text );
+       }
+
+       /**
+        * @covers WikiPage::getRawText
+        */
+       public function testGetRawText() {
+               $this->hideDeprecated( "WikiPage::getRawText" );
+
+               $page = $this->newPage( "WikiPageTest_testGetRawText" );
+
+               $text = $page->getRawText();
+               $this->assertFalse( $text );
+
+               # -----------------
+               $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
+
+               $text = $page->getRawText();
+               $this->assertEquals( "some text", $text );
+       }
+
+       /**
+        * @covers WikiPage::getContentModel
+        */
+       public function testGetContentModel() {
+               global $wgContentHandlerUseDB;
+
+               if ( !$wgContentHandlerUseDB ) {
+                       $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' );
+               }
+
+               $page = $this->createPage(
+                       "WikiPageTest_testGetContentModel",
+                       "some text",
+                       CONTENT_MODEL_JAVASCRIPT
+               );
+
+               $page = new WikiPage( $page->getTitle() );
+               $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $page->getContentModel() );
+       }
+
+       /**
+        * @covers WikiPage::getContentHandler
+        */
+       public function testGetContentHandler() {
+               global $wgContentHandlerUseDB;
+
+               if ( !$wgContentHandlerUseDB ) {
+                       $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' );
+               }
+
+               $page = $this->createPage(
+                       "WikiPageTest_testGetContentHandler",
+                       "some text",
+                       CONTENT_MODEL_JAVASCRIPT
+               );
+
+               $page = new WikiPage( $page->getTitle() );
+               $this->assertEquals( 'JavaScriptContentHandler', get_class( $page->getContentHandler() ) );
+       }
+
+       /**
+        * @covers WikiPage::exists
+        */
+       public function testExists() {
+               $page = $this->newPage( "WikiPageTest_testExists" );
+               $this->assertFalse( $page->exists() );
+
+               # -----------------
+               $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
+               $this->assertTrue( $page->exists() );
+
+               $page = new WikiPage( $page->getTitle() );
+               $this->assertTrue( $page->exists() );
+
+               # -----------------
+               $page->doDeleteArticle( "done testing" );
+               $this->assertFalse( $page->exists() );
+
+               $page = new WikiPage( $page->getTitle() );
+               $this->assertFalse( $page->exists() );
+       }
+
+       public static function provideHasViewableContent() {
+               return array(
+                       array( 'WikiPageTest_testHasViewableContent', false, true ),
+                       array( 'Special:WikiPageTest_testHasViewableContent', false ),
+                       array( 'MediaWiki:WikiPageTest_testHasViewableContent', false ),
+                       array( 'Special:Userlogin', true ),
+                       array( 'MediaWiki:help', true ),
+               );
+       }
+
+       /**
+        * @dataProvider provideHasViewableContent
+        * @covers WikiPage::hasViewableContent
+        */
+       public function testHasViewableContent( $title, $viewable, $create = false ) {
+               $page = $this->newPage( $title );
+               $this->assertEquals( $viewable, $page->hasViewableContent() );
+
+               if ( $create ) {
+                       $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
+                       $this->assertTrue( $page->hasViewableContent() );
+
+                       $page = new WikiPage( $page->getTitle() );
+                       $this->assertTrue( $page->hasViewableContent() );
+               }
+       }
+
+       public static function provideGetRedirectTarget() {
+               return array(
+                       array( 'WikiPageTest_testGetRedirectTarget_1', CONTENT_MODEL_WIKITEXT, "hello world", null ),
+                       array(
+                               'WikiPageTest_testGetRedirectTarget_2',
+                               CONTENT_MODEL_WIKITEXT,
+                               "#REDIRECT [[hello world]]",
+                               "Hello world"
+                       ),
+               );
+       }
+
+       /**
+        * @dataProvider provideGetRedirectTarget
+        * @covers WikiPage::getRedirectTarget
+        */
+       public function testGetRedirectTarget( $title, $model, $text, $target ) {
+               $this->setMwGlobals( array(
+                       'wgCapitalLinks' => true,
+               ) );
+
+               $page = $this->createPage( $title, $text, $model );
+
+               # sanity check, because this test seems to fail for no reason for some people.
+               $c = $page->getContent();
+               $this->assertEquals( 'WikitextContent', get_class( $c ) );
+
+               # now, test the actual redirect
+               $t = $page->getRedirectTarget();
+               $this->assertEquals( $target, is_null( $t ) ? null : $t->getPrefixedText() );
+       }
+
+       /**
+        * @dataProvider provideGetRedirectTarget
+        * @covers WikiPage::isRedirect
+        */
+       public function testIsRedirect( $title, $model, $text, $target ) {
+               $page = $this->createPage( $title, $text, $model );
+               $this->assertEquals( !is_null( $target ), $page->isRedirect() );
+       }
+
+       public static function provideIsCountable() {
+               return array(
+
+                       // any
+                       array( 'WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               '',
+                               'any',
+                               true
+                       ),
+                       array( 'WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               'Foo',
+                               'any',
+                               true
+                       ),
+
+                       // comma
+                       array( 'WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               'Foo',
+                               'comma',
+                               false
+                       ),
+                       array( 'WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               'Foo, bar',
+                               'comma',
+                               true
+                       ),
+
+                       // link
+                       array( 'WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               'Foo',
+                               'link',
+                               false
+                       ),
+                       array( 'WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               'Foo [[bar]]',
+                               'link',
+                               true
+                       ),
+
+                       // redirects
+                       array( 'WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               '#REDIRECT [[bar]]',
+                               'any',
+                               false
+                       ),
+                       array( 'WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               '#REDIRECT [[bar]]',
+                               'comma',
+                               false
+                       ),
+                       array( 'WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               '#REDIRECT [[bar]]',
+                               'link',
+                               false
+                       ),
+
+                       // not a content namespace
+                       array( 'Talk:WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               'Foo',
+                               'any',
+                               false
+                       ),
+                       array( 'Talk:WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               'Foo, bar',
+                               'comma',
+                               false
+                       ),
+                       array( 'Talk:WikiPageTest_testIsCountable',
+                               CONTENT_MODEL_WIKITEXT,
+                               'Foo [[bar]]',
+                               'link',
+                               false
+                       ),
+
+                       // not a content namespace, different model
+                       array( 'MediaWiki:WikiPageTest_testIsCountable.js',
+                               null,
+                               'Foo',
+                               'any',
+                               false
+                       ),
+                       array( 'MediaWiki:WikiPageTest_testIsCountable.js',
+                               null,
+                               'Foo, bar',
+                               'comma',
+                               false
+                       ),
+                       array( 'MediaWiki:WikiPageTest_testIsCountable.js',
+                               null,
+                               'Foo [[bar]]',
+                               'link',
+                               false
+                       ),
+               );
+       }
+
+       /**
+        * @dataProvider provideIsCountable
+        * @covers WikiPage::isCountable
+        */
+       public function testIsCountable( $title, $model, $text, $mode, $expected ) {
+               global $wgContentHandlerUseDB;
+
+               $this->setMwGlobals( 'wgArticleCountMethod', $mode );
+
+               $title = Title::newFromText( $title );
+
+               if ( !$wgContentHandlerUseDB
+                       && $model
+                       && ContentHandler::getDefaultModelFor( $title ) != $model
+               ) {
+                       $this->markTestSkipped( "Can not use non-default content model $model for "
+                               . $title->getPrefixedDBkey() . " with \$wgContentHandlerUseDB disabled." );
+               }
+
+               $page = $this->createPage( $title, $text, $model );
+
+               $editInfo = $page->prepareContentForEdit( $page->getContent() );
+
+               $v = $page->isCountable();
+               $w = $page->isCountable( $editInfo );
+
+               $this->assertEquals(
+                       $expected,
+                       $v,
+                       "isCountable( null ) returned unexpected value " . var_export( $v, true )
+                               . " instead of " . var_export( $expected, true )
+                       . " in mode `$mode` for text \"$text\""
+               );
+
+               $this->assertEquals(
+                       $expected,
+                       $w,
+                       "isCountable( \$editInfo ) returned unexpected value " . var_export( $v, true )
+                               . " instead of " . var_export( $expected, true )
+                       . " in mode `$mode` for text \"$text\""
+               );
+       }
+
+       public static function provideGetParserOutput() {
+               return array(
+                       array( CONTENT_MODEL_WIKITEXT, "hello ''world''\n", "<p>hello <i>world</i></p>" ),
+                       // @todo more...?
+               );
+       }
+
+       /**
+        * @dataProvider provideGetParserOutput
+        * @covers WikiPage::getParserOutput
+        */
+       public function testGetParserOutput( $model, $text, $expectedHtml ) {
+               $page = $this->createPage( 'WikiPageTest_testGetParserOutput', $text, $model );
+
+               $opt = $page->makeParserOptions( 'canonical' );
+               $po = $page->getParserOutput( $opt );
+               $text = $po->getText();
+
+               $text = trim( preg_replace( '/<!--.*?-->/sm', '', $text ) ); # strip injected comments
+               $text = preg_replace( '!\s*(</p>)!sm', '\1', $text ); # don't let tidy confuse us
+
+               $this->assertEquals( $expectedHtml, $text );
+
+               return $po;
+       }
+
+       /**
+        * @covers WikiPage::getParserOutput
+        */
+       public function testGetParserOutput_nonexisting() {
+               static $count = 0;
+               $count++;
+
+               $page = new WikiPage( new Title( "WikiPageTest_testGetParserOutput_nonexisting_$count" ) );
+
+               $opt = new ParserOptions();
+               $po = $page->getParserOutput( $opt );
+
+               $this->assertFalse( $po, "getParserOutput() shall return false for non-existing pages." );
+       }
+
+       /**
+        * @covers WikiPage::getParserOutput
+        */
+       public function testGetParserOutput_badrev() {
+               $page = $this->createPage( 'WikiPageTest_testGetParserOutput', "dummy", CONTENT_MODEL_WIKITEXT );
+
+               $opt = new ParserOptions();
+               $po = $page->getParserOutput( $opt, $page->getLatest() + 1234 );
+
+               // @todo would be neat to also test deleted revision
+
+               $this->assertFalse( $po, "getParserOutput() shall return false for non-existing revisions." );
+       }
+
+       public static $sections =
+
+               "Intro
+
+== stuff ==
+hello world
+
+== test ==
+just a test
+
+== foo ==
+more stuff
+";
+
+       public function dataReplaceSection() {
+               //NOTE: assume the Help namespace to contain wikitext
+               return array(
+                       array( 'Help:WikiPageTest_testReplaceSection',
+                               CONTENT_MODEL_WIKITEXT,
+                               WikiPageTest::$sections,
+                               "0",
+                               "No more",
+                               null,
+                               trim( preg_replace( '/^Intro/sm', 'No more', WikiPageTest::$sections ) )
+                       ),
+                       array( 'Help:WikiPageTest_testReplaceSection',
+                               CONTENT_MODEL_WIKITEXT,
+                               WikiPageTest::$sections,
+                               "",
+                               "No more",
+                               null,
+                               "No more"
+                       ),
+                       array( 'Help:WikiPageTest_testReplaceSection',
+                               CONTENT_MODEL_WIKITEXT,
+                               WikiPageTest::$sections,
+                               "2",
+                               "== TEST ==\nmore fun",
+                               null,
+                               trim( preg_replace( '/^== test ==.*== foo ==/sm',
+                                       "== TEST ==\nmore fun\n\n== foo ==",
+                                       WikiPageTest::$sections ) )
+                       ),
+                       array( 'Help:WikiPageTest_testReplaceSection',
+                               CONTENT_MODEL_WIKITEXT,
+                               WikiPageTest::$sections,
+                               "8",
+                               "No more",
+                               null,
+                               trim( WikiPageTest::$sections )
+                       ),
+                       array( 'Help:WikiPageTest_testReplaceSection',
+                               CONTENT_MODEL_WIKITEXT,
+                               WikiPageTest::$sections,
+                               "new",
+                               "No more",
+                               "New",
+                               trim( WikiPageTest::$sections ) . "\n\n== New ==\n\nNo more"
+                       ),
+               );
+       }
+
+       /**
+        * @dataProvider dataReplaceSection
+        * @covers WikiPage::replaceSection
+        */
+       public function testReplaceSection( $title, $model, $text, $section, $with,
+               $sectionTitle, $expected
+       ) {
+               $this->hideDeprecated( "WikiPage::replaceSection" );
+
+               $page = $this->createPage( $title, $text, $model );
+               $text = $page->replaceSection( $section, $with, $sectionTitle );
+               $text = trim( $text );
+
+               $this->assertEquals( $expected, $text );
+       }
+
+       /**
+        * @dataProvider dataReplaceSection
+        * @covers WikiPage::replaceSectionContent
+        */
+       public function testReplaceSectionContent( $title, $model, $text, $section,
+               $with, $sectionTitle, $expected
+       ) {
+               $page = $this->createPage( $title, $text, $model );
+
+               $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
+               $c = $page->replaceSectionContent( $section, $content, $sectionTitle );
+
+               $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
+       }
+
+       /**
+        * @dataProvider dataReplaceSection
+        * @covers WikiPage::replaceSectionAtRev
+        */
+       public function testReplaceSectionAtRev( $title, $model, $text, $section,
+               $with, $sectionTitle, $expected
+       ) {
+               $page = $this->createPage( $title, $text, $model );
+               $baseRevId = $page->getLatest();
+
+               $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
+               $c = $page->replaceSectionAtRev( $section, $content, $sectionTitle, $baseRevId );
+
+               $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
+       }
+
+       /* @todo FIXME: fix this!
+       public function testGetUndoText() {
+       $this->checkHasDiff3();
+
+       $text = "one";
+       $page = $this->createPage( "WikiPageTest_testGetUndoText", $text );
+       $rev1 = $page->getRevision();
+
+       $text .= "\n\ntwo";
+       $page->doEditContent(
+               ContentHandler::makeContent( $text, $page->getTitle() ),
+               "adding section two"
+       );
+       $rev2 = $page->getRevision();
+
+       $text .= "\n\nthree";
+       $page->doEditContent(
+               ContentHandler::makeContent( $text, $page->getTitle() ),
+               "adding section three"
+       );
+       $rev3 = $page->getRevision();
+
+       $text .= "\n\nfour";
+       $page->doEditContent(
+               ContentHandler::makeContent( $text, $page->getTitle() ),
+               "adding section four"
+       );
+       $rev4 = $page->getRevision();
+
+       $text .= "\n\nfive";
+       $page->doEditContent(
+               ContentHandler::makeContent( $text, $page->getTitle() ),
+               "adding section five"
+       );
+       $rev5 = $page->getRevision();
+
+       $text .= "\n\nsix";
+       $page->doEditContent(
+               ContentHandler::makeContent( $text, $page->getTitle() ),
+               "adding section six"
+       );
+       $rev6 = $page->getRevision();
+
+       $undo6 = $page->getUndoText( $rev6 );
+       if ( $undo6 === false ) $this->fail( "getUndoText failed for rev6" );
+       $this->assertEquals( "one\n\ntwo\n\nthree\n\nfour\n\nfive", $undo6 );
+
+       $undo3 = $page->getUndoText( $rev4, $rev2 );
+       if ( $undo3 === false ) $this->fail( "getUndoText failed for rev4..rev2" );
+       $this->assertEquals( "one\n\ntwo\n\nfive", $undo3 );
+
+       $undo2 = $page->getUndoText( $rev2 );
+       if ( $undo2 === false ) $this->fail( "getUndoText failed for rev2" );
+       $this->assertEquals( "one\n\nfive", $undo2 );
+       }
+        */
+
+       /**
+        * @todo FIXME: this is a better rollback test than the one below, but it
+        * keeps failing in jenkins for some reason.
+        */
+       public function broken_testDoRollback() {
+               $admin = new User();
+               $admin->setName( "Admin" );
+
+               $text = "one";
+               $page = $this->newPage( "WikiPageTest_testDoRollback" );
+               $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
+                       "section one", EDIT_NEW, false, $admin );
+
+               $user1 = new User();
+               $user1->setName( "127.0.1.11" );
+               $text .= "\n\ntwo";
+               $page = new WikiPage( $page->getTitle() );
+               $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
+                       "adding section two", 0, false, $user1 );
+
+               $user2 = new User();
+               $user2->setName( "127.0.2.13" );
+               $text .= "\n\nthree";
+               $page = new WikiPage( $page->getTitle() );
+               $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
+                       "adding section three", 0, false, $user2 );
+
+               # we are having issues with doRollback spuriously failing. Apparently
+               # the last revision somehow goes missing or not committed under some
+               # circumstances. So, make sure the last revision has the right user name.
+               $dbr = wfGetDB( DB_SLAVE );
+               $this->assertEquals( 3, Revision::countByPageId( $dbr, $page->getId() ) );
+
+               $page = new WikiPage( $page->getTitle() );
+               $rev3 = $page->getRevision();
+               $this->assertEquals( '127.0.2.13', $rev3->getUserText() );
+
+               $rev2 = $rev3->getPrevious();
+               $this->assertEquals( '127.0.1.11', $rev2->getUserText() );
+
+               $rev1 = $rev2->getPrevious();
+               $this->assertEquals( 'Admin', $rev1->getUserText() );
+
+               # now, try the actual rollback
+               $admin->addGroup( "sysop" ); #XXX: make the test user a sysop...
+               $token = $admin->getEditToken(
+                       array( $page->getTitle()->getPrefixedText(), $user2->getName() ),
+                       null
+               );
+               $errors = $page->doRollback(
+                       $user2->getName(),
+                       "testing revert",
+                       $token,
+                       false,
+                       $details,
+                       $admin
+               );
+
+               if ( $errors ) {
+                       $this->fail( "Rollback failed:\n" . print_r( $errors, true )
+                               . ";\n" . print_r( $details, true ) );
+               }
+
+               $page = new WikiPage( $page->getTitle() );
+               $this->assertEquals( $rev2->getSha1(), $page->getRevision()->getSha1(),
+                       "rollback did not revert to the correct revision" );
+               $this->assertEquals( "one\n\ntwo", $page->getContent()->getNativeData() );
+       }
+
+       /**
+        * @todo FIXME: the above rollback test is better, but it keeps failing in jenkins for some reason.
+        * @covers WikiPage::doRollback
+        */
+       public function testDoRollback() {
+               $admin = new User();
+               $admin->setName( "Admin" );
+
+               $text = "one";
+               $page = $this->newPage( "WikiPageTest_testDoRollback" );
+               $page->doEditContent(
+                       ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
+                       "section one",
+                       EDIT_NEW,
+                       false,
+                       $admin
+               );
+               $rev1 = $page->getRevision();
+
+               $user1 = new User();
+               $user1->setName( "127.0.1.11" );
+               $text .= "\n\ntwo";
+               $page = new WikiPage( $page->getTitle() );
+               $page->doEditContent(
+                       ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
+                       "adding section two",
+                       0,
+                       false,
+                       $user1
+               );
+
+               # now, try the rollback
+               $admin->addGroup( "sysop" ); #XXX: make the test user a sysop...
+               $token = $admin->getEditToken(
+                       array( $page->getTitle()->getPrefixedText(), $user1->getName() ),
+                       null
+               );
+               $errors = $page->doRollback(
+                       $user1->getName(),
+                       "testing revert",
+                       $token,
+                       false,
+                       $details,
+                       $admin
+               );
+
+               if ( $errors ) {
+                       $this->fail( "Rollback failed:\n" . print_r( $errors, true )
+                               . ";\n" . print_r( $details, true ) );
+               }
+
+               $page = new WikiPage( $page->getTitle() );
+               $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
+                       "rollback did not revert to the correct revision" );
+               $this->assertEquals( "one", $page->getContent()->getNativeData() );
+       }
+
+       /**
+        * @covers WikiPage::doRollback
+        */
+       public function testDoRollbackFailureSameContent() {
+               $admin = new User();
+               $admin->setName( "Admin" );
+               $admin->addGroup( "sysop" ); #XXX: make the test user a sysop...
+
+               $text = "one";
+               $page = $this->newPage( "WikiPageTest_testDoRollback" );
+               $page->doEditContent(
+                       ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
+                       "section one",
+                       EDIT_NEW,
+                       false,
+                       $admin
+               );
+               $rev1 = $page->getRevision();
+
+               $user1 = new User();
+               $user1->setName( "127.0.1.11" );
+               $user1->addGroup( "sysop" ); #XXX: make the test user a sysop...
+               $text .= "\n\ntwo";
+               $page = new WikiPage( $page->getTitle() );
+               $page->doEditContent(
+                       ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
+                       "adding section two",
+                       0,
+                       false,
+                       $user1
+               );
+
+               # now, do a the rollback from the same user was doing the edit before
+               $resultDetails = array();
+               $token = $user1->getEditToken(
+                       array( $page->getTitle()->getPrefixedText(), $user1->getName() ),
+                       null
+               );
+               $errors = $page->doRollback(
+                       $user1->getName(),
+                       "testing revert same user",
+                       $token,
+                       false,
+                       $resultDetails,
+                       $admin
+               );
+
+               $this->assertEquals( array(), $errors, "Rollback failed same user" );
+
+               # now, try the rollback
+               $resultDetails = array();
+               $token = $admin->getEditToken(
+                       array( $page->getTitle()->getPrefixedText(), $user1->getName() ),
+                       null
+               );
+               $errors = $page->doRollback(
+                       $user1->getName(),
+                       "testing revert",
+                       $token,
+                       false,
+                       $resultDetails,
+                       $admin
+               );
+
+               $this->assertEquals( array( array( 'alreadyrolled', 'WikiPageTest testDoRollback',
+                       '127.0.1.11', 'Admin' ) ), $errors, "Rollback not failed" );
+
+               $page = new WikiPage( $page->getTitle() );
+               $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
+                       "rollback did not revert to the correct revision" );
+               $this->assertEquals( "one", $page->getContent()->getNativeData() );
+       }
+
+       public static function provideGetAutosummary() {
+               return array(
+                       array(
+                               'Hello there, world!',
+                               '#REDIRECT [[Foo]]',
+                               0,
+                               '/^Redirected page .*Foo/'
+                       ),
+
+                       array(
+                               null,
+                               'Hello world!',
+                               EDIT_NEW,
+                               '/^Created page .*Hello/'
+                       ),
+
+                       array(
+                               'Hello there, world!',
+                               '',
+                               0,
+                               '/^Blanked/'
+                       ),
+
+                       array(
+                               'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
+                               eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
+                               voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
+                               clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.',
+                               'Hello world!',
+                               0,
+                               '/^Replaced .*Hello/'
+                       ),
+
+                       array(
+                               'foo',
+                               'bar',
+                               0,
+                               '/^$/'
+                       ),
+               );
+       }
+
+       /**
+        * @dataProvider provideGetAutoSummary
+        * @covers WikiPage::getAutosummary
+        */
+       public function testGetAutosummary( $old, $new, $flags, $expected ) {
+               $this->hideDeprecated( "WikiPage::getAutosummary" );
+
+               $page = $this->newPage( "WikiPageTest_testGetAutosummary" );
+
+               $summary = $page->getAutosummary( $old, $new, $flags );
+
+               $this->assertTrue( (bool)preg_match( $expected, $summary ),
+                       "Autosummary didn't match expected pattern $expected: $summary" );
+       }
+
+       public static function provideGetAutoDeleteReason() {
+               return array(
+                       array(
+                               array(),
+                               false,
+                               false
+                       ),
+
+                       array(
+                               array(
+                                       array( "first edit", null ),
+                               ),
+                               "/first edit.*only contributor/",
+                               false
+                       ),
+
+                       array(
+                               array(
+                                       array( "first edit", null ),
+                                       array( "second edit", null ),
+                               ),
+                               "/second edit.*only contributor/",
+                               true
+                       ),
+
+                       array(
+                               array(
+                                       array( "first edit", "127.0.2.22" ),
+                                       array( "second edit", "127.0.3.33" ),
+                               ),
+                               "/second edit/",
+                               true
+                       ),
+
+                       array(
+                               array(
+                                       array(
+                                               "first edit: "
+                                                       . "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam "
+                                                       . " nonumy eirmod tempor invidunt ut labore et dolore magna "
+                                                       . "aliquyam erat, sed diam voluptua. At vero eos et accusam "
+                                                       . "et justo duo dolores et ea rebum. Stet clita kasd gubergren, "
+                                                       . "no sea  takimata sanctus est Lorem ipsum dolor sit amet.'",
+                                               null
+                                       ),
+                               ),
+                               '/first edit:.*\.\.\."/',
+                               false
+                       ),
+
+                       array(
+                               array(
+                                       array( "first edit", "127.0.2.22" ),
+                                       array( "", "127.0.3.33" ),
+                               ),
+                               "/before blanking.*first edit/",
+                               true
+                       ),
+
+               );
+       }
+
+       /**
+        * @dataProvider provideGetAutoDeleteReason
+        * @covers WikiPage::getAutoDeleteReason
+        */
+       public function testGetAutoDeleteReason( $edits, $expectedResult, $expectedHistory ) {
+               global $wgUser;
+
+               //NOTE: assume Help namespace to contain wikitext
+               $page = $this->newPage( "Help:WikiPageTest_testGetAutoDeleteReason" );
+
+               $c = 1;
+
+               foreach ( $edits as $edit ) {
+                       $user = new User();
+
+                       if ( !empty( $edit[1] ) ) {
+                               $user->setName( $edit[1] );
+                       } else {
+                               $user = $wgUser;
+                       }
+
+                       $content = ContentHandler::makeContent( $edit[0], $page->getTitle(), $page->getContentModel() );
+
+                       $page->doEditContent( $content, "test edit $c", $c < 2 ? EDIT_NEW : 0, false, $user );
+
+                       $c += 1;
+               }
+
+               $reason = $page->getAutoDeleteReason( $hasHistory );
+
+               if ( is_bool( $expectedResult ) || is_null( $expectedResult ) ) {
+                       $this->assertEquals( $expectedResult, $reason );
+               } else {
+                       $this->assertTrue( (bool)preg_match( $expectedResult, $reason ),
+                               "Autosummary didn't match expected pattern $expectedResult: $reason" );
+               }
+
+               $this->assertEquals( $expectedHistory, $hasHistory,
+                       "expected \$hasHistory to be " . var_export( $expectedHistory, true ) );
+
+               $page->doDeleteArticle( "done" );
+       }
+
+       public static function providePreSaveTransform() {
+               return array(
+                       array( 'hello this is ~~~',
+                               "hello this is [[Special:Contributions/127.0.0.1|127.0.0.1]]",
+                       ),
+                       array( 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
+                               'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
+                       ),
+               );
+       }
+
+       /**
+        * @dataProvider providePreSaveTransform
+        * @covers WikiPage::preSaveTransform
+        */
+       public function testPreSaveTransform( $text, $expected ) {
+               $this->hideDeprecated( 'WikiPage::preSaveTransform' );
+               $user = new User();
+               $user->setName( "127.0.0.1" );
+
+               //NOTE: assume Help namespace to contain wikitext
+               $page = $this->newPage( "Help:WikiPageTest_testPreloadTransform" );
+               $text = $page->preSaveTransform( $text, $user );
+
+               $this->assertEquals( $expected, $text );
+       }
+
+       /**
+        * @covers WikiPage::factory
+        */
+       public function testWikiPageFactory() {
+               $title = Title::makeTitle( NS_FILE, 'Someimage.png' );
+               $page = WikiPage::factory( $title );
+               $this->assertEquals( 'WikiFilePage', get_class( $page ) );
+
+               $title = Title::makeTitle( NS_CATEGORY, 'SomeCategory' );
+               $page = WikiPage::factory( $title );
+               $this->assertEquals( 'WikiCategoryPage', get_class( $page ) );
+
+               $title = Title::makeTitle( NS_MAIN, 'SomePage' );
+               $page = WikiPage::factory( $title );
+               $this->assertEquals( 'WikiPage', get_class( $page ) );
+       }
+}
diff --git a/tests/phpunit/includes/page/WikiPageTestContentHandlerUseDB.php b/tests/phpunit/includes/page/WikiPageTestContentHandlerUseDB.php
new file mode 100644 (file)
index 0000000..3db7628
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @group ContentHandler
+ * @group Database
+ * ^--- important, causes temporary tables to be used instead of the real database
+ */
+class WikiPageTestContentHandlerUseDB extends WikiPageTest {
+
+       protected function setUp() {
+               parent::setUp();
+               $this->setMwGlobals( 'wgContentHandlerUseDB', false );
+
+               $dbw = wfGetDB( DB_MASTER );
+
+               $page_table = $dbw->tableName( 'page' );
+               $revision_table = $dbw->tableName( 'revision' );
+               $archive_table = $dbw->tableName( 'archive' );
+
+               if ( $dbw->fieldExists( $page_table, 'page_content_model' ) ) {
+                       $dbw->query( "alter table $page_table drop column page_content_model" );
+                       $dbw->query( "alter table $revision_table drop column rev_content_model" );
+                       $dbw->query( "alter table $revision_table drop column rev_content_format" );
+                       $dbw->query( "alter table $archive_table drop column ar_content_model" );
+                       $dbw->query( "alter table $archive_table drop column ar_content_format" );
+               }
+       }
+
+       /**
+        * @covers WikiPage::getContentModel
+        */
+       public function testGetContentModel() {
+               $page = $this->createPage(
+                       "WikiPageTest_testGetContentModel",
+                       "some text",
+                       CONTENT_MODEL_JAVASCRIPT
+               );
+
+               $page = new WikiPage( $page->getTitle() );
+
+               // NOTE: since the content model is not recorded in the database,
+               //       we expect to get the default, namely CONTENT_MODEL_WIKITEXT
+               $this->assertEquals( CONTENT_MODEL_WIKITEXT, $page->getContentModel() );
+       }
+
+       /**
+        * @covers WikiPage::getContentHandler
+        */
+       public function testGetContentHandler() {
+               $page = $this->createPage(
+                       "WikiPageTest_testGetContentHandler",
+                       "some text",
+                       CONTENT_MODEL_JAVASCRIPT
+               );
+
+               // NOTE: since the content model is not recorded in the database,
+               //       we expect to get the default, namely CONTENT_MODEL_WIKITEXT
+               $page = new WikiPage( $page->getTitle() );
+               $this->assertEquals( 'WikitextContentHandler', get_class( $page->getContentHandler() ) );
+       }
+}
diff --git a/tests/phpunit/includes/password/PasswordTest.php b/tests/phpunit/includes/password/PasswordTest.php
new file mode 100644 (file)
index 0000000..5ad8aca
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Testing framework for the Password infrastructure
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+class PasswordTest extends MediaWikiTestCase {
+       /**
+        * @covers InvalidPassword::equals
+        */
+       public function testInvalidUnequalInvalid() {
+               $invalid1 = User::getPasswordFactory()->newFromCiphertext( null );
+               $invalid2 = User::getPasswordFactory()->newFromCiphertext( null );
+
+               $this->assertFalse( $invalid1->equals( $invalid2 ) );
+       }
+
+       public function testInvalidPlaintext() {
+               $invalid = User::getPasswordFactory()->newFromPlaintext( null );
+
+               $this->assertInstanceOf( 'InvalidPassword', $invalid );
+       }
+}
diff --git a/tests/phpunit/includes/search/SearchUpdateTest.php b/tests/phpunit/includes/search/SearchUpdateTest.php
deleted file mode 100644 (file)
index c627537..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-class MockSearch extends SearchEngine {
-       public static $id;
-       public static $title;
-       public static $text;
-
-       public function __construct( $db ) {
-       }
-
-       public function update( $id, $title, $text ) {
-               self::$id = $id;
-               self::$title = $title;
-               self::$text = $text;
-       }
-}
-
-/**
- * @group Search
- * @group Database
- */
-class SearchUpdateTest extends MediaWikiTestCase {
-
-       protected function setUp() {
-               parent::setUp();
-               $this->setMwGlobals( 'wgSearchType', 'MockSearch' );
-       }
-
-       public function updateText( $text ) {
-               return trim( SearchUpdate::updateText( $text ) );
-       }
-
-       /**
-        * @covers SearchUpdate::updateText
-        */
-       public function testUpdateText() {
-               $this->assertEquals(
-                       'test',
-                       $this->updateText( '<div>TeSt</div>' ),
-                       'HTML stripped, text lowercased'
-               );
-
-               $this->assertEquals(
-                       'foo bar boz quux',
-                       $this->updateText( <<<EOT
-<table style="color:red; font-size:100px">
-       <tr class="scary"><td><div>foo</div></td><tr>bar</td></tr>
-       <tr><td>boz</td><tr>quux</td></tr>
-</table>
-EOT
-                       ), 'Stripping HTML tables' );
-
-               $this->assertEquals(
-                       'a b',
-                       $this->updateText( 'a > b' ),
-                       'Handle unclosed tags'
-               );
-
-               $text = str_pad( "foo <barbarbar \n", 10000, 'x' );
-
-               $this->assertNotEquals(
-                       '',
-                       $this->updateText( $text ),
-                       'Bug 18609'
-               );
-       }
-
-       /**
-        * @covers SearchUpdate::updateText
-        * @todo give this test a real name explaining what is being tested here
-        */
-       public function testBug32712() {
-               $text = "text „http://example.com“ text";
-               $result = $this->updateText( $text );
-               $processed = preg_replace( '/Q/u', 'Q', $result );
-               $this->assertTrue(
-                       $processed != '',
-                       'Link surrounded by unicode quotes should not fail UTF-8 validation'
-               );
-       }
-}
diff --git a/tests/phpunit/includes/specialpage/SpecialPageTest.php b/tests/phpunit/includes/specialpage/SpecialPageTest.php
new file mode 100644 (file)
index 0000000..245cdff
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * @covers SpecialPage
+ *
+ * @group Database
+ *
+ * @licence GNU GPL v2+
+ * @author Katie Filbert < aude.wiki@gmail.com >
+ */
+class SpecialPageTest extends MediaWikiTestCase {
+
+       protected function setUp() {
+               parent::setUp();
+
+               $this->setMwGlobals( array(
+                       'wgScript' => '/index.php',
+                       'wgContLang' => Language::factory( 'en' )
+               ) );
+       }
+
+       /**
+        * @dataProvider getTitleForProvider
+        */
+       public function testGetTitleFor( $expectedName, $name ) {
+               $title = SpecialPage::getTitleFor( $name );
+               $expected = Title::makeTitle( NS_SPECIAL, $expectedName );
+               $this->assertEquals( $expected, $title );
+       }
+
+       public function getTitleForProvider() {
+               return array(
+                       array( 'UserLogin', 'Userlogin' )
+               );
+       }
+
+       /**
+        * @expectedException PHPUnit_Framework_Error_Notice
+        */
+       public function testInvalidGetTitleFor() {
+               $title = SpecialPage::getTitleFor( 'cat' );
+               $expected = Title::makeTitle( NS_SPECIAL, 'Cat' );
+               $this->assertEquals( $expected, $title );
+       }
+
+       /**
+        * @expectedException PHPUnit_Framework_Error_Notice
+        * @dataProvider getTitleForWithWarningProvider
+        */
+       public function testGetTitleForWithWarning( $expected, $name ) {
+               $title = SpecialPage::getTitleFor( $name );
+               $this->assertEquals( $expected, $title );
+       }
+
+       public function getTitleForWithWarningProvider() {
+               return array(
+                       array( Title::makeTitle( NS_SPECIAL, 'UserLogin' ), 'UserLogin' )
+               );
+       }
+
+       /**
+        * @dataProvider requireLoginAnonProvider
+        */
+       public function testRequireLoginAnon( $expected, $reason, $title ) {
+               $specialPage = new SpecialPage( 'Watchlist', 'viewmywatchlist' );
+
+               $user = User::newFromId( 0 );
+               $specialPage->getContext()->setUser( $user );
+               $specialPage->getContext()->setLanguage( Language::factory( 'en' ) );
+
+               $this->setExpectedException( 'UserNotLoggedIn', $expected );
+
+               // $specialPage->requireLogin( [ $reason [, $title ] ] )
+               call_user_func_array(
+                       array( $specialPage, 'requireLogin' ),
+                       array_filter( array( $reason, $title ) )
+               );
+       }
+
+       public function requireLoginAnonProvider() {
+               $lang = 'en';
+
+               $expected1 = wfMessage( 'exception-nologin-text' )->inLanguage( $lang )->text();
+               $expected2 = wfMessage( 'about' )->inLanguage( $lang )->text();
+
+               return array(
+                       array( $expected1, null, null ),
+                       array( $expected2, 'about', null ),
+                       array( $expected2, 'about', 'about' ),
+               );
+       }
+
+       public function testRequireLoginNotAnon() {
+               $specialPage = new SpecialPage( 'Watchlist', 'viewmywatchlist' );
+
+               $user = User::newFromName( "UTSysop" );
+               $specialPage->getContext()->setUser( $user );
+
+               $specialPage->requireLogin();
+
+               // no exception thrown, logged in use can access special page
+               $this->assertTrue( true );
+       }
+
+}
diff --git a/tests/phpunit/includes/utils/MWFunctionTest.php b/tests/phpunit/includes/utils/MWFunctionTest.php
new file mode 100644 (file)
index 0000000..f4d1799
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @covers MWFunction
+ */
+class MWFunctionTest extends MediaWikiTestCase {
+       public function testNewObjFunction() {
+               $arg1 = 'Foo';
+               $arg2 = 'Bar';
+               $arg3 = array( 'Baz' );
+               $arg4 = new ExampleObject;
+
+               $args = array( $arg1, $arg2, $arg3, $arg4 );
+
+               $newObject = new MWBlankClass( $arg1, $arg2, $arg3, $arg4 );
+               $this->hideDeprecated( 'MWFunction::newObj' );
+               $this->assertEquals(
+                       MWFunction::newObj( 'MWBlankClass', $args )->args,
+                       $newObject->args
+               );
+       }
+}
+
+class MWBlankClass {
+
+       public $args = array();
+
+       function __construct( $arg1, $arg2, $arg3, $arg4 ) {
+               $this->args = array( $arg1, $arg2, $arg3, $arg4 );
+       }
+}
+
+class ExampleObject {
+}
index 574c11e..860408f 100644 (file)
@@ -39,6 +39,7 @@ phpunit.php enables colors for other OSs at runtime
                        <file>suites/UploadFromUrlTestSuite.php</file>
                </testsuite>
                <testsuite name="extensions">
+                       <directory>structure</directory>
                        <file>suites/ExtensionsTestSuite.php</file>
                        <file>suites/ExtensionsParserTestSuite.php</file>
                        <file>suites/LessTestSuite.php</file>
index b800bc2..7294d62 100644 (file)
@@ -30,6 +30,8 @@
        // and assuming failure.
        QUnit.config.testTimeout = 30 * 1000;
 
+       QUnit.config.requireExpects = true;
+
        // Add a checkbox to QUnit header to toggle MediaWiki ResourceLoader debug mode.
        QUnit.config.urlConfig.push( {
                id: 'debug',
                tooltip: 'Enable debug mode in ResourceLoader'
        } );
 
-       QUnit.config.requireExpects = true;
-
-       /**
-        * Load TestSwarm agent
-        */
-       // Only if the current url indicates that there is a TestSwarm instance watching us
-       // (TestSwarm appends swarmURL to the test suites url it loads in iframes).
-       // Otherwise this is just a simple view of Special:JavaScriptTest/qunit directly,
-       // no point in loading inject.js in that case. Also, make sure that this instance
-       // of MediaWiki has actually been configured with the required url to that inject.js
-       // script. By default it is false.
-       if ( QUnit.urlParams.swarmURL && mw.config.get( 'QUnitTestSwarmInjectJSPath' ) ) {
-               jQuery.getScript( QUnit.fixurl( mw.config.get( 'QUnitTestSwarmInjectJSPath' ) ) );
-       }
-
        /**
         * CompletenessTest
         *
index 3d8612d..a972b21 100644 (file)
--- a/thumb.php
+++ b/thumb.php
@@ -135,12 +135,12 @@ function wfStreamThumb( array $params ) {
                // Format is <timestamp>!<name>
                $bits = explode( '!', $fileName, 2 );
                if ( count( $bits ) != 2 ) {
-                       wfThumbError( 404, wfMessage( 'badtitletext' )->text() );
+                       wfThumbError( 404, wfMessage( 'badtitletext' )->parse() );
                        return;
                }
                $title = Title::makeTitleSafe( NS_FILE, $bits[1] );
                if ( !$title ) {
-                       wfThumbError( 404, wfMessage( 'badtitletext' )->text() );
+                       wfThumbError( 404, wfMessage( 'badtitletext' )->parse() );
                        return;
                }
                $img = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $title, $fileName );
@@ -150,7 +150,7 @@ function wfStreamThumb( array $params ) {
 
        // Check the source file title
        if ( !$img ) {
-               wfThumbError( 404, wfMessage( 'badtitletext' )->text() );
+               wfThumbError( 404, wfMessage( 'badtitletext' )->parse() );
                return;
        }
 
@@ -262,7 +262,7 @@ function wfStreamThumb( array $params ) {
                return;
        }
 
-       // For 404 handled thumbnails, we only use the the base name of the URI
+       // For 404 handled thumbnails, we only use the base name of the URI
        // for the thumb params and the parent directory for the source file name.
        // Check that the zone relative path matches up so squid caches won't pick
        // up thumbs that would not be purged on source file deletion (bug 34231).
@@ -310,10 +310,10 @@ function wfStreamThumb( array $params ) {
 
        $user = RequestContext::getMain()->getUser();
        if ( !wfThumbIsStandard( $img, $params ) && $user->pingLimiter( 'renderfile-nonstandard' ) ) {
-               wfThumbError( 500, wfMessage( 'actionthrottledtext' ) );
+               wfThumbError( 500, wfMessage( 'actionthrottledtext' )->parse() );
                return;
        } elseif ( $user->pingLimiter( 'renderfile' ) ) {
-               wfThumbError( 500, wfMessage( 'actionthrottledtext' ) );
+               wfThumbError( 500, wfMessage( 'actionthrottledtext' )->parse() );
                return;
        }
 
@@ -598,7 +598,7 @@ function wfExtractThumbParams( $file, $params ) {
  * Output a thumbnail generation error message
  *
  * @param int $status
- * @param string $msg
+ * @param string $msg HTML
  * @return void
  */
 function wfThumbError( $status, $msg ) {