Merge "Add missing __METHOD__ to select in LocalRepo"
authorAaron Schulz <aschulz@wikimedia.org>
Sat, 23 Jun 2012 19:25:32 +0000 (19:25 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 23 Jun 2012 19:25:32 +0000 (19:25 +0000)
RELEASE-NOTES-1.20
includes/AutoLoader.php
includes/job/Job.php [new file with mode: 0644]
includes/job/JobQueue.php [deleted file]
resources/Resources.php
resources/mediawiki/mediawiki.Uri.js
skins/common/commonElements.css
tests/phpunit/includes/WikiPageTest.php
tests/phpunit/includes/parser/ParserMethodsTest.php [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js

index 1577b14..3c545ee 100644 (file)
@@ -125,7 +125,7 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
   who don't have access to /tmp can specify an alternative.
 * (bug 27283) SqlBagOStuff breaks PostgreSQL transactions.
 * (bug 35727) mw.Api ajax() should put token parameter last.
-* (bug 260) Handle <pre> overflow automatically with a scroll bar.
+* (bug 37708) mw.Uri.clone() should make a deep copy.
 
 === API changes in 1.20 ===
 * (bug 34316) Add ability to retrieve maximum upload size from MediaWiki API.
index d7927de..d92aa99 100644 (file)
@@ -630,7 +630,7 @@ $wgAutoloadLocalClasses = array(
        'DoubleRedirectJob' => 'includes/job/DoubleRedirectJob.php',
        'EmaillingJob' => 'includes/job/EmaillingJob.php',
        'EnotifNotifyJob' => 'includes/job/EnotifNotifyJob.php',
-       'Job' => 'includes/job/JobQueue.php',
+       'Job' => 'includes/job/Job.php',
        'RefreshLinksJob' => 'includes/job/RefreshLinksJob.php',
        'RefreshLinksJob2' => 'includes/job/RefreshLinksJob.php',
        'UploadFromUrlJob' => 'includes/job/UploadFromUrlJob.php',
diff --git a/includes/job/Job.php b/includes/job/Job.php
new file mode 100644 (file)
index 0000000..7b7ec0c
--- /dev/null
@@ -0,0 +1,447 @@
+<?php
+/**
+ * Job queue base code.
+ *
+ * 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
+ * @defgroup JobQueue JobQueue
+ */
+
+/**
+ * Class to both describe a background job and handle jobs.
+ *
+ * @ingroup JobQueue
+ */
+abstract class Job {
+
+       /**
+        * @var Title
+        */
+       var $title;
+
+       var $command,
+               $params,
+               $id,
+               $removeDuplicates,
+               $error;
+
+       /*-------------------------------------------------------------------------
+        * Abstract functions
+        *------------------------------------------------------------------------*/
+
+       /**
+        * Run the job
+        * @return boolean success
+        */
+       abstract function run();
+
+       /*-------------------------------------------------------------------------
+        * Static functions
+        *------------------------------------------------------------------------*/
+
+       /**
+        * Pop a job of a certain type.  This tries less hard than pop() to
+        * actually find a job; it may be adversely affected by concurrent job
+        * runners.
+        *
+        * @param $type string
+        *
+        * @return Job
+        */
+       static function pop_type( $type ) {
+               wfProfilein( __METHOD__ );
+
+               $dbw = wfGetDB( DB_MASTER );
+
+               $dbw->begin( __METHOD__ );
+
+               $row = $dbw->selectRow(
+                       'job',
+                       '*',
+                       array( 'job_cmd' => $type ),
+                       __METHOD__,
+                       array( 'LIMIT' => 1, 'FOR UPDATE' )
+               );
+
+               if ( $row === false ) {
+                       $dbw->commit( __METHOD__ );
+                       wfProfileOut( __METHOD__ );
+                       return false;
+               }
+
+               /* Ensure we "own" this row */
+               $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
+               $affected = $dbw->affectedRows();
+               $dbw->commit( __METHOD__ );
+
+               if ( $affected == 0 ) {
+                       wfProfileOut( __METHOD__ );
+                       return false;
+               }
+
+               wfIncrStats( 'job-pop' );
+               $namespace = $row->job_namespace;
+               $dbkey = $row->job_title;
+               $title = Title::makeTitleSafe( $namespace, $dbkey );
+               $job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ),
+                       $row->job_id );
+
+               $job->removeDuplicates();
+
+               wfProfileOut( __METHOD__ );
+               return $job;
+       }
+
+       /**
+        * Pop a job off the front of the queue
+        *
+        * @param $offset Integer: Number of jobs to skip
+        * @return Job or false if there's no jobs
+        */
+       static function pop( $offset = 0 ) {
+               wfProfileIn( __METHOD__ );
+
+               $dbr = wfGetDB( DB_SLAVE );
+
+               /* Get a job from the slave, start with an offset,
+                       scan full set afterwards, avoid hitting purged rows
+
+                       NB: If random fetch previously was used, offset
+                               will always be ahead of few entries
+               */
+
+               $conditions = self::defaultQueueConditions();
+
+               $offset = intval( $offset );
+               $options = array( 'ORDER BY' => 'job_id', 'USE INDEX' => 'PRIMARY' );
+
+               $row = $dbr->selectRow( 'job', '*',
+                       array_merge( $conditions, array( "job_id >= $offset" ) ),
+                       __METHOD__,
+                       $options
+               );
+
+               // Refetching without offset is needed as some of job IDs could have had delayed commits
+               // and have lower IDs than jobs already executed, blame concurrency :)
+               //
+               if ( $row === false ) {
+                       if ( $offset != 0 ) {
+                               $row = $dbr->selectRow( 'job', '*', $conditions, __METHOD__, $options );
+                       }
+
+                       if ( $row === false ) {
+                               wfProfileOut( __METHOD__ );
+                               return false;
+                       }
+               }
+
+               // Try to delete it from the master
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
+               $affected = $dbw->affectedRows();
+               $dbw->commit( __METHOD__ );
+
+               if ( !$affected ) {
+                       // Failed, someone else beat us to it
+                       // Try getting a random row
+                       $row = $dbw->selectRow( 'job', array( 'MIN(job_id) as minjob',
+                               'MAX(job_id) as maxjob' ), '1=1', __METHOD__ );
+                       if ( $row === false || is_null( $row->minjob ) || is_null( $row->maxjob ) ) {
+                               // No jobs to get
+                               wfProfileOut( __METHOD__ );
+                               return false;
+                       }
+                       // Get the random row
+                       $row = $dbw->selectRow( 'job', '*',
+                               'job_id >= ' . mt_rand( $row->minjob, $row->maxjob ), __METHOD__ );
+                       if ( $row === false ) {
+                               // Random job gone before we got the chance to select it
+                               // Give up
+                               wfProfileOut( __METHOD__ );
+                               return false;
+                       }
+                       // Delete the random row
+                       $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
+                       $affected = $dbw->affectedRows();
+                       $dbw->commit( __METHOD__ );
+
+                       if ( !$affected ) {
+                               // Random job gone before we exclusively deleted it
+                               // Give up
+                               wfProfileOut( __METHOD__ );
+                               return false;
+                       }
+               }
+
+               // If execution got to here, there's a row in $row that has been deleted from the database
+               // by this thread. Hence the concurrent pop was successful.
+               wfIncrStats( 'job-pop' );
+               $namespace = $row->job_namespace;
+               $dbkey = $row->job_title;
+               $title = Title::makeTitleSafe( $namespace, $dbkey );
+
+               if ( is_null( $title ) ) {
+                       wfProfileOut( __METHOD__ );
+                       return false;
+               }
+
+               $job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ), $row->job_id );
+
+               // Remove any duplicates it may have later in the queue
+               $job->removeDuplicates();
+
+               wfProfileOut( __METHOD__ );
+               return $job;
+       }
+
+       /**
+        * Create the appropriate object to handle a specific job
+        *
+        * @param $command String: Job command
+        * @param $title Title: Associated title
+        * @param $params Array: Job parameters
+        * @param $id Int: Job identifier
+        * @return Job
+        */
+       static function factory( $command, Title $title, $params = false, $id = 0 ) {
+               global $wgJobClasses;
+               if( isset( $wgJobClasses[$command] ) ) {
+                       $class = $wgJobClasses[$command];
+                       return new $class( $title, $params, $id );
+               }
+               throw new MWException( "Invalid job command `{$command}`" );
+       }
+
+       /**
+        * @param $params
+        * @return string
+        */
+       static function makeBlob( $params ) {
+               if ( $params !== false ) {
+                       return serialize( $params );
+               } else {
+                       return '';
+               }
+       }
+
+       /**
+        * @param $blob
+        * @return bool|mixed
+        */
+       static function extractBlob( $blob ) {
+               if ( (string)$blob !== '' ) {
+                       return unserialize( $blob );
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Batch-insert a group of jobs into the queue.
+        * This will be wrapped in a transaction with a forced commit.
+        *
+        * This may add duplicate at insert time, but they will be
+        * removed later on, when the first one is popped.
+        *
+        * @param $jobs array of Job objects
+        */
+       static function batchInsert( $jobs ) {
+               if ( !count( $jobs ) ) {
+                       return;
+               }
+               $dbw = wfGetDB( DB_MASTER );
+               $rows = array();
+
+               /**
+                * @var $job Job
+                */
+               foreach ( $jobs as $job ) {
+                       $rows[] = $job->insertFields();
+                       if ( count( $rows ) >= 50 ) {
+                               # Do a small transaction to avoid slave lag
+                               $dbw->begin( __METHOD__ );
+                               $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
+                               $dbw->commit( __METHOD__ );
+                               $rows = array();
+                       }
+               }
+               if ( $rows ) { // last chunk
+                       $dbw->begin( __METHOD__ );
+                       $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
+                       $dbw->commit( __METHOD__ );
+               }
+               wfIncrStats( 'job-insert', count( $jobs ) );
+       }
+
+       /**
+        * Insert a group of jobs into the queue.
+        *
+        * Same as batchInsert() but does not commit and can thus
+        * be rolled-back as part of a larger transaction. However,
+        * large batches of jobs can cause slave lag.
+        *
+        * @param $jobs array of Job objects
+        */
+       static function safeBatchInsert( $jobs ) {
+               if ( !count( $jobs ) ) {
+                       return;
+               }
+               $dbw = wfGetDB( DB_MASTER );
+               $rows = array();
+               foreach ( $jobs as $job ) {
+                       $rows[] = $job->insertFields();
+                       if ( count( $rows ) >= 500 ) {
+                               $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
+                               $rows = array();
+                       }
+               }
+               if ( $rows ) { // last chunk
+                       $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
+               }
+               wfIncrStats( 'job-insert', count( $jobs ) );
+       }
+
+
+       /**
+        * SQL conditions to apply on most JobQueue queries
+        *
+        * Whenever we exclude jobs types from the default queue, we want to make
+        * sure that queries to the job queue actually ignore them.
+        *
+        * @return array SQL conditions suitable for Database:: methods
+        */
+       static function defaultQueueConditions( ) {
+               global $wgJobTypesExcludedFromDefaultQueue;
+               $conditions = array();
+               if ( count( $wgJobTypesExcludedFromDefaultQueue ) > 0 ) {
+                       $dbr = wfGetDB( DB_SLAVE );
+                       foreach ( $wgJobTypesExcludedFromDefaultQueue as $cmdType ) {
+                               $conditions[] = "job_cmd != " . $dbr->addQuotes( $cmdType );
+                       }
+               }
+               return $conditions;
+       }
+
+       /*-------------------------------------------------------------------------
+        * Non-static functions
+        *------------------------------------------------------------------------*/
+
+       /**
+        * @param $command
+        * @param $title
+        * @param $params array
+        * @param int $id
+        */
+       function __construct( $command, $title, $params = false, $id = 0 ) {
+               $this->command = $command;
+               $this->title = $title;
+               $this->params = $params;
+               $this->id = $id;
+
+               // A bit of premature generalisation
+               // Oh well, the whole class is premature generalisation really
+               $this->removeDuplicates = true;
+       }
+
+       /**
+        * Insert a single job into the queue.
+        * @return bool true on success
+        */
+       function insert() {
+               $fields = $this->insertFields();
+
+               $dbw = wfGetDB( DB_MASTER );
+
+               if ( $this->removeDuplicates ) {
+                       $res = $dbw->select( 'job', array( '1' ), $fields, __METHOD__ );
+                       if ( $dbw->numRows( $res ) ) {
+                               return true;
+                       }
+               }
+               wfIncrStats( 'job-insert' );
+               return $dbw->insert( 'job', $fields, __METHOD__ );
+       }
+
+       /**
+        * @return array
+        */
+       protected function insertFields() {
+               $dbw = wfGetDB( DB_MASTER );
+               return array(
+                       'job_id' => $dbw->nextSequenceValue( 'job_job_id_seq' ),
+                       'job_cmd' => $this->command,
+                       'job_namespace' => $this->title->getNamespace(),
+                       'job_title' => $this->title->getDBkey(),
+                       'job_timestamp' => $dbw->timestamp(),
+                       'job_params' => Job::makeBlob( $this->params )
+               );
+       }
+
+       /**
+        * Remove jobs in the job queue which are duplicates of this job.
+        * This is deadlock-prone and so starts its own transaction.
+        */
+       function removeDuplicates() {
+               if ( !$this->removeDuplicates ) {
+                       return;
+               }
+
+               $fields = $this->insertFields();
+               unset( $fields['job_id'] );
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->begin( __METHOD__ );
+               $dbw->delete( 'job', $fields, __METHOD__ );
+               $affected = $dbw->affectedRows();
+               $dbw->commit( __METHOD__ );
+               if ( $affected ) {
+                       wfIncrStats( 'job-dup-delete', $affected );
+               }
+       }
+
+       /**
+        * @return string
+        */
+       function toString() {
+               $paramString = '';
+               if ( $this->params ) {
+                       foreach ( $this->params as $key => $value ) {
+                               if ( $paramString != '' ) {
+                                       $paramString .= ' ';
+                               }
+                               $paramString .= "$key=$value";
+                       }
+               }
+
+               if ( is_object( $this->title ) ) {
+                       $s = "{$this->command} " . $this->title->getPrefixedDBkey();
+                       if ( $paramString !== '' ) {
+                               $s .= ' ' . $paramString;
+                       }
+                       return $s;
+               } else {
+                       return "{$this->command} $paramString";
+               }
+       }
+
+       protected function setLastError( $error ) {
+               $this->error = $error;
+       }
+
+       function getLastError() {
+               return $this->error;
+       }
+}
diff --git a/includes/job/JobQueue.php b/includes/job/JobQueue.php
deleted file mode 100644 (file)
index 7b7ec0c..0000000
+++ /dev/null
@@ -1,447 +0,0 @@
-<?php
-/**
- * Job queue base code.
- *
- * 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
- * @defgroup JobQueue JobQueue
- */
-
-/**
- * Class to both describe a background job and handle jobs.
- *
- * @ingroup JobQueue
- */
-abstract class Job {
-
-       /**
-        * @var Title
-        */
-       var $title;
-
-       var $command,
-               $params,
-               $id,
-               $removeDuplicates,
-               $error;
-
-       /*-------------------------------------------------------------------------
-        * Abstract functions
-        *------------------------------------------------------------------------*/
-
-       /**
-        * Run the job
-        * @return boolean success
-        */
-       abstract function run();
-
-       /*-------------------------------------------------------------------------
-        * Static functions
-        *------------------------------------------------------------------------*/
-
-       /**
-        * Pop a job of a certain type.  This tries less hard than pop() to
-        * actually find a job; it may be adversely affected by concurrent job
-        * runners.
-        *
-        * @param $type string
-        *
-        * @return Job
-        */
-       static function pop_type( $type ) {
-               wfProfilein( __METHOD__ );
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               $dbw->begin( __METHOD__ );
-
-               $row = $dbw->selectRow(
-                       'job',
-                       '*',
-                       array( 'job_cmd' => $type ),
-                       __METHOD__,
-                       array( 'LIMIT' => 1, 'FOR UPDATE' )
-               );
-
-               if ( $row === false ) {
-                       $dbw->commit( __METHOD__ );
-                       wfProfileOut( __METHOD__ );
-                       return false;
-               }
-
-               /* Ensure we "own" this row */
-               $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
-               $affected = $dbw->affectedRows();
-               $dbw->commit( __METHOD__ );
-
-               if ( $affected == 0 ) {
-                       wfProfileOut( __METHOD__ );
-                       return false;
-               }
-
-               wfIncrStats( 'job-pop' );
-               $namespace = $row->job_namespace;
-               $dbkey = $row->job_title;
-               $title = Title::makeTitleSafe( $namespace, $dbkey );
-               $job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ),
-                       $row->job_id );
-
-               $job->removeDuplicates();
-
-               wfProfileOut( __METHOD__ );
-               return $job;
-       }
-
-       /**
-        * Pop a job off the front of the queue
-        *
-        * @param $offset Integer: Number of jobs to skip
-        * @return Job or false if there's no jobs
-        */
-       static function pop( $offset = 0 ) {
-               wfProfileIn( __METHOD__ );
-
-               $dbr = wfGetDB( DB_SLAVE );
-
-               /* Get a job from the slave, start with an offset,
-                       scan full set afterwards, avoid hitting purged rows
-
-                       NB: If random fetch previously was used, offset
-                               will always be ahead of few entries
-               */
-
-               $conditions = self::defaultQueueConditions();
-
-               $offset = intval( $offset );
-               $options = array( 'ORDER BY' => 'job_id', 'USE INDEX' => 'PRIMARY' );
-
-               $row = $dbr->selectRow( 'job', '*',
-                       array_merge( $conditions, array( "job_id >= $offset" ) ),
-                       __METHOD__,
-                       $options
-               );
-
-               // Refetching without offset is needed as some of job IDs could have had delayed commits
-               // and have lower IDs than jobs already executed, blame concurrency :)
-               //
-               if ( $row === false ) {
-                       if ( $offset != 0 ) {
-                               $row = $dbr->selectRow( 'job', '*', $conditions, __METHOD__, $options );
-                       }
-
-                       if ( $row === false ) {
-                               wfProfileOut( __METHOD__ );
-                               return false;
-                       }
-               }
-
-               // Try to delete it from the master
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
-               $affected = $dbw->affectedRows();
-               $dbw->commit( __METHOD__ );
-
-               if ( !$affected ) {
-                       // Failed, someone else beat us to it
-                       // Try getting a random row
-                       $row = $dbw->selectRow( 'job', array( 'MIN(job_id) as minjob',
-                               'MAX(job_id) as maxjob' ), '1=1', __METHOD__ );
-                       if ( $row === false || is_null( $row->minjob ) || is_null( $row->maxjob ) ) {
-                               // No jobs to get
-                               wfProfileOut( __METHOD__ );
-                               return false;
-                       }
-                       // Get the random row
-                       $row = $dbw->selectRow( 'job', '*',
-                               'job_id >= ' . mt_rand( $row->minjob, $row->maxjob ), __METHOD__ );
-                       if ( $row === false ) {
-                               // Random job gone before we got the chance to select it
-                               // Give up
-                               wfProfileOut( __METHOD__ );
-                               return false;
-                       }
-                       // Delete the random row
-                       $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
-                       $affected = $dbw->affectedRows();
-                       $dbw->commit( __METHOD__ );
-
-                       if ( !$affected ) {
-                               // Random job gone before we exclusively deleted it
-                               // Give up
-                               wfProfileOut( __METHOD__ );
-                               return false;
-                       }
-               }
-
-               // If execution got to here, there's a row in $row that has been deleted from the database
-               // by this thread. Hence the concurrent pop was successful.
-               wfIncrStats( 'job-pop' );
-               $namespace = $row->job_namespace;
-               $dbkey = $row->job_title;
-               $title = Title::makeTitleSafe( $namespace, $dbkey );
-
-               if ( is_null( $title ) ) {
-                       wfProfileOut( __METHOD__ );
-                       return false;
-               }
-
-               $job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ), $row->job_id );
-
-               // Remove any duplicates it may have later in the queue
-               $job->removeDuplicates();
-
-               wfProfileOut( __METHOD__ );
-               return $job;
-       }
-
-       /**
-        * Create the appropriate object to handle a specific job
-        *
-        * @param $command String: Job command
-        * @param $title Title: Associated title
-        * @param $params Array: Job parameters
-        * @param $id Int: Job identifier
-        * @return Job
-        */
-       static function factory( $command, Title $title, $params = false, $id = 0 ) {
-               global $wgJobClasses;
-               if( isset( $wgJobClasses[$command] ) ) {
-                       $class = $wgJobClasses[$command];
-                       return new $class( $title, $params, $id );
-               }
-               throw new MWException( "Invalid job command `{$command}`" );
-       }
-
-       /**
-        * @param $params
-        * @return string
-        */
-       static function makeBlob( $params ) {
-               if ( $params !== false ) {
-                       return serialize( $params );
-               } else {
-                       return '';
-               }
-       }
-
-       /**
-        * @param $blob
-        * @return bool|mixed
-        */
-       static function extractBlob( $blob ) {
-               if ( (string)$blob !== '' ) {
-                       return unserialize( $blob );
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Batch-insert a group of jobs into the queue.
-        * This will be wrapped in a transaction with a forced commit.
-        *
-        * This may add duplicate at insert time, but they will be
-        * removed later on, when the first one is popped.
-        *
-        * @param $jobs array of Job objects
-        */
-       static function batchInsert( $jobs ) {
-               if ( !count( $jobs ) ) {
-                       return;
-               }
-               $dbw = wfGetDB( DB_MASTER );
-               $rows = array();
-
-               /**
-                * @var $job Job
-                */
-               foreach ( $jobs as $job ) {
-                       $rows[] = $job->insertFields();
-                       if ( count( $rows ) >= 50 ) {
-                               # Do a small transaction to avoid slave lag
-                               $dbw->begin( __METHOD__ );
-                               $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
-                               $dbw->commit( __METHOD__ );
-                               $rows = array();
-                       }
-               }
-               if ( $rows ) { // last chunk
-                       $dbw->begin( __METHOD__ );
-                       $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
-                       $dbw->commit( __METHOD__ );
-               }
-               wfIncrStats( 'job-insert', count( $jobs ) );
-       }
-
-       /**
-        * Insert a group of jobs into the queue.
-        *
-        * Same as batchInsert() but does not commit and can thus
-        * be rolled-back as part of a larger transaction. However,
-        * large batches of jobs can cause slave lag.
-        *
-        * @param $jobs array of Job objects
-        */
-       static function safeBatchInsert( $jobs ) {
-               if ( !count( $jobs ) ) {
-                       return;
-               }
-               $dbw = wfGetDB( DB_MASTER );
-               $rows = array();
-               foreach ( $jobs as $job ) {
-                       $rows[] = $job->insertFields();
-                       if ( count( $rows ) >= 500 ) {
-                               $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
-                               $rows = array();
-                       }
-               }
-               if ( $rows ) { // last chunk
-                       $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
-               }
-               wfIncrStats( 'job-insert', count( $jobs ) );
-       }
-
-
-       /**
-        * SQL conditions to apply on most JobQueue queries
-        *
-        * Whenever we exclude jobs types from the default queue, we want to make
-        * sure that queries to the job queue actually ignore them.
-        *
-        * @return array SQL conditions suitable for Database:: methods
-        */
-       static function defaultQueueConditions( ) {
-               global $wgJobTypesExcludedFromDefaultQueue;
-               $conditions = array();
-               if ( count( $wgJobTypesExcludedFromDefaultQueue ) > 0 ) {
-                       $dbr = wfGetDB( DB_SLAVE );
-                       foreach ( $wgJobTypesExcludedFromDefaultQueue as $cmdType ) {
-                               $conditions[] = "job_cmd != " . $dbr->addQuotes( $cmdType );
-                       }
-               }
-               return $conditions;
-       }
-
-       /*-------------------------------------------------------------------------
-        * Non-static functions
-        *------------------------------------------------------------------------*/
-
-       /**
-        * @param $command
-        * @param $title
-        * @param $params array
-        * @param int $id
-        */
-       function __construct( $command, $title, $params = false, $id = 0 ) {
-               $this->command = $command;
-               $this->title = $title;
-               $this->params = $params;
-               $this->id = $id;
-
-               // A bit of premature generalisation
-               // Oh well, the whole class is premature generalisation really
-               $this->removeDuplicates = true;
-       }
-
-       /**
-        * Insert a single job into the queue.
-        * @return bool true on success
-        */
-       function insert() {
-               $fields = $this->insertFields();
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               if ( $this->removeDuplicates ) {
-                       $res = $dbw->select( 'job', array( '1' ), $fields, __METHOD__ );
-                       if ( $dbw->numRows( $res ) ) {
-                               return true;
-                       }
-               }
-               wfIncrStats( 'job-insert' );
-               return $dbw->insert( 'job', $fields, __METHOD__ );
-       }
-
-       /**
-        * @return array
-        */
-       protected function insertFields() {
-               $dbw = wfGetDB( DB_MASTER );
-               return array(
-                       'job_id' => $dbw->nextSequenceValue( 'job_job_id_seq' ),
-                       'job_cmd' => $this->command,
-                       'job_namespace' => $this->title->getNamespace(),
-                       'job_title' => $this->title->getDBkey(),
-                       'job_timestamp' => $dbw->timestamp(),
-                       'job_params' => Job::makeBlob( $this->params )
-               );
-       }
-
-       /**
-        * Remove jobs in the job queue which are duplicates of this job.
-        * This is deadlock-prone and so starts its own transaction.
-        */
-       function removeDuplicates() {
-               if ( !$this->removeDuplicates ) {
-                       return;
-               }
-
-               $fields = $this->insertFields();
-               unset( $fields['job_id'] );
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
-               $dbw->delete( 'job', $fields, __METHOD__ );
-               $affected = $dbw->affectedRows();
-               $dbw->commit( __METHOD__ );
-               if ( $affected ) {
-                       wfIncrStats( 'job-dup-delete', $affected );
-               }
-       }
-
-       /**
-        * @return string
-        */
-       function toString() {
-               $paramString = '';
-               if ( $this->params ) {
-                       foreach ( $this->params as $key => $value ) {
-                               if ( $paramString != '' ) {
-                                       $paramString .= ' ';
-                               }
-                               $paramString .= "$key=$value";
-                       }
-               }
-
-               if ( is_object( $this->title ) ) {
-                       $s = "{$this->command} " . $this->title->getPrefixedDBkey();
-                       if ( $paramString !== '' ) {
-                               $s .= ' ' . $paramString;
-                       }
-                       return $s;
-               } else {
-                       return "{$this->command} $paramString";
-               }
-       }
-
-       protected function setLastError( $error ) {
-               $this->error = $error;
-       }
-
-       function getLastError() {
-               return $this->error;
-       }
-}
index d30e441..0e85050 100644 (file)
@@ -696,12 +696,9 @@ return array(
                        'mk' => 'resources/mediawiki.language/languages/mk.js',
                        'mo' => 'resources/mediawiki.language/languages/mo.js',
                        'mt' => 'resources/mediawiki.language/languages/mt.js',
-                       'nl' => 'resources/mediawiki.language/languages/nl.js',
                        'nso' => 'resources/mediawiki.language/languages/nso.js',
                        'os' => 'resources/mediawiki.language/languages/os.js',
                        'pl' => 'resources/mediawiki.language/languages/pl.js',
-                       'pt' => 'resources/mediawiki.language/languages/pt.js',
-                       'pt-br' => 'resources/mediawiki.language/languages/pt-br.js',
                        'ro' => 'resources/mediawiki.language/languages/ro.js',
                        'ru' => 'resources/mediawiki.language/languages/ru.js',
                        'se' => 'resources/mediawiki.language/languages/se.js',
index e8a0118..b871ac7 100644 (file)
                                if ( typeof uri === 'string' ) {
                                        this.parse( uri, options );
                                } else if ( typeof uri === 'object' ) {
-                                       var uriObj = this;
-                                       $.each( properties, function ( i, property ) {
-                                               uriObj[property] = uri[property];
-                                       } );
-                                       if ( this.query === undefined ) {
+                                       // Copy data over from existing URI object
+                                       for ( var prop in uri ) {
+                                               // Only copy direct properties, not inherited ones
+                                               if ( uri.hasOwnProperty( prop ) ) {
+                                                       // Deep copy object properties
+                                                       if ( $.isArray( uri[prop] ) || $.isPlainObject( uri[prop] ) ) {
+                                                               this[prop] = $.extend( true, {}, uri[prop] );
+                                                       } else {
+                                                               this[prop] = uri[prop];
+                                                       }
+                                               }
+                                       }
+                                       if ( !this.query ) {
                                                this.query = {};
                                        }
                                }
index 903a4f7..02fd29f 100644 (file)
@@ -198,12 +198,6 @@ pre, .mw-code {
        border: 1px dashed #2f6fab;
        color: black;
        background-color: #f9f9f9;
-       /* Handle overflow (bug 260) */
-       overflow: auto;
-       /* IE 7 is the first IE to support overflow but got it wrong */
-       /* IE 8+ is fine */
-       *padding-bottom: 21px;
-       *overflow-y: hidden;
 }
 
 /* Tables */
index c8606cc..b72a5cd 100644 (file)
@@ -769,6 +769,7 @@ more stuff
         * @dataProvider dataPreSaveTransform
         */
        public function testPreSaveTransform( $text, $expected ) {
+               $this->hideDeprecated( 'WikiPage::preSaveTransform' );
                $user = new User();
                $user->setName("127.0.0.1");
 
diff --git a/tests/phpunit/includes/parser/ParserMethodsTest.php b/tests/phpunit/includes/parser/ParserMethodsTest.php
new file mode 100644 (file)
index 0000000..10b57eb
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+class ParserLesserFunctionsTest extends MediaWikiLangTestCase {
+
+       public function dataPreSaveTransform() {
+               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 dataPreSaveTransform
+        */
+       public function testPreSaveTransform( $text, $expected ) {
+               global $wgParser;
+
+               $title = Title::newFromText( str_replace( '::', '__', __METHOD__ ) );
+               $user = new User();
+               $user->setName( "127.0.0.1" );
+               $popts = ParserOptions::newFromUser( $user );
+               $text = $wgParser->preSaveTransform( $text, $title, $user, $popts );
+
+               $this->assertEquals( $expected, $text );
+       }
+
+       // TODO: Add tests for cleanSig() / cleanSigInSig(), getSection(), replaceSection(), getPreloadText()
+}
+
index b834aeb..9913fb9 100644 (file)
@@ -229,19 +229,27 @@ test( 'All-dressed URI with everything', function () {
 
 test( 'Cloning', function () {
        var original, clone;
-       expect( 5 );
+       expect( 6 );
 
-       original = new mw.Uri( 'http://en.wiki.local/w/api.php?action=query&foo=bar' );
+       original = new mw.Uri( 'http://foo.example.org/index.php?one=1&two=2' );
        clone = original.clone();
 
        deepEqual( clone, original, 'clone has equivalent properties' );
        equal( original.toString(), clone.toString(), 'toString matches original' );
 
-       notStrictEqual( clone, original, 'clone is not the same when compared by reference' );
+       notStrictEqual( clone, original, 'clone is a different object when compared by reference' );
 
-       clone.host = 'fr.wiki.local';
+       clone.host = 'bar.example.org';
        notEqual( original.host, clone.host, 'manipulating clone did not effect original' );
-       notEqual( original.toString(), clone.toString(), 'toString no longer matches original' );
+       notEqual( original.toString(), clone.toString(), 'Stringified url no longer matches original' );
+
+       clone.query.three = 3;
+
+       deepEqual(
+               original.query,
+               { 'one': '1', 'two': '2' },
+               'Properties is deep cloned (bug 37708)'
+       );
 } );
 
 test( 'Constructing mw.Uri from plain object', function () {