(bug 31363) Expand description URLs for thumbnails to canonical form
[lhc/web/wiklou.git] / includes / Export.php
index 91eb7af..489a928 100644 (file)
@@ -35,6 +35,7 @@ class WikiExporter {
        var $author_list = "" ;
 
        var $dumpUploads = false;
+       var $dumpUploadFileContents = false;
 
        const FULL = 1;
        const CURRENT = 2;
@@ -157,17 +158,23 @@ class WikiExporter {
        # Generates the distinct list of authors of an article
        # Not called by default (depends on $this->list_authors)
        # Can be set by Special:Export when not exporting whole history
-       protected function do_list_authors( $page , $revision , $cond ) {
+       protected function do_list_authors( $cond ) {
                wfProfileIn( __METHOD__ );
                $this->author_list = "<contributors>";
                // rev_deleted
-               $nothidden = '(' . $this->db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ') = 0';
 
-               $sql = "SELECT DISTINCT rev_user_text,rev_user FROM {$page},{$revision}
-               WHERE page_id=rev_page AND $nothidden AND " . $cond ;
-               $result = $this->db->query( $sql, __METHOD__ );
-               $resultset = $this->db->resultObject( $result );
-               foreach ( $resultset as $row ) {
+               $res = $this->db->select(
+                       array( 'page', 'revision' ),
+                       array( 'DISTINCT rev_user_text', 'rev_user' ),
+                       array(
+                               $this->db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0',
+                               $cond,
+                               'page_id = rev_id',
+                       ),
+                       __METHOD__
+               );
+
+               foreach ( $res as $row ) {
                        $this->author_list .= "<contributor>" .
                                "<username>" .
                                htmlentities( $row->rev_user_text )  .
@@ -240,8 +247,7 @@ class WikiExporter {
                        } elseif ( $this->history & WikiExporter::CURRENT ) {
                                # Latest revision dumps...
                                if ( $this->list_authors && $cond != '' )  { // List authors, if so desired
-                                       list( $page, $revision ) = $this->db->tableNamesN( 'page', 'revision' );
-                                       $this->do_list_authors( $page, $revision, $cond );
+                                       $this->do_list_authors( $cond );
                                }
                                $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' );
                        } elseif ( $this->history & WikiExporter::STABLE ) {
@@ -313,7 +319,7 @@ class WikiExporter {
                                if ( isset( $last ) ) {
                                        $output = '';
                                        if ( $this->dumpUploads ) {
-                                               $output .= $this->writer->writeUploads( $last );
+                                               $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents );
                                        }
                                        $output .= $this->writer->closePage();
                                        $this->sink->writeClosePage( $output );
@@ -328,7 +334,7 @@ class WikiExporter {
                if ( isset( $last ) ) {
                        $output = '';
                        if ( $this->dumpUploads ) {
-                               $output .= $this->writer->writeUploads( $last );
+                               $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents );
                        }
                        $output .= $this->author_list;
                        $output .= $this->writer->closePage();
@@ -348,7 +354,6 @@ class WikiExporter {
  * @ingroup Dump
  */
 class XmlDumpWriter {
-
        /**
         * Returns the export schema version.
         * @return string
@@ -405,7 +410,7 @@ class XmlDumpWriter {
        }
 
        function homelink() {
-               return Xml::element( 'base', array(), Title::newMainPage()->getFullUrl() );
+               return Xml::element( 'base', array(), Title::newMainPage()->getCanonicalUrl() );
        }
 
        function caseSetting() {
@@ -432,12 +437,13 @@ class XmlDumpWriter {
        /**
         * Closes the output stream with the closing root element.
         * Call when finished dumping things.
+        *
+        * @return string
         */
        function closeStream() {
                return "</mediawiki>\n";
        }
 
-
        /**
         * Opens a <page> section on the output stream, with data
         * from the given database row.
@@ -595,29 +601,53 @@ class XmlDumpWriter {
        /**
         * Warning! This data is potentially inconsistent. :(
         */
-       function writeUploads( $row ) {
+       function writeUploads( $row, $dumpContents = false ) {
                if ( $row->page_namespace == NS_IMAGE ) {
-                       $img = wfFindFile( $row->page_title );
-                       if ( $img ) {
+                       $img = wfLocalFile( $row->page_title );
+                       if ( $img && $img->exists() ) {
                                $out = '';
                                foreach ( array_reverse( $img->getHistory() ) as $ver ) {
-                                       $out .= $this->writeUpload( $ver );
+                                       $out .= $this->writeUpload( $ver, $dumpContents );
                                }
-                               $out .= $this->writeUpload( $img );
+                               $out .= $this->writeUpload( $img, $dumpContents );
                                return $out;
                        }
                }
                return '';
        }
 
-       function writeUpload( $file ) {
+       /**
+        * @param $file File
+        * @param $dumpContents bool
+        * @return string
+        */
+       function writeUpload( $file, $dumpContents = false ) {
+               if ( $file->isOld() ) {
+                       $archiveName = "      " .
+                               Xml::element( 'archivename', null, $file->getArchiveName() ) . "\n";
+               } else {
+                       $archiveName = '';
+               }
+               if ( $dumpContents ) {
+                       # Dump file as base64
+                       # Uses only XML-safe characters, so does not need escaping
+                       $contents = '      <contents encoding="base64">' .
+                               chunk_split( base64_encode( file_get_contents( $file->getPath() ) ) ) .
+                               "      </contents>\n";
+               } else {
+                       $contents = '';
+               }
                return "    <upload>\n" .
                        $this->writeTimestamp( $file->getTimestamp() ) .
                        $this->writeContributor( $file->getUser( 'id' ), $file->getUser( 'text' ) ) .
                        "      " . Xml::elementClean( 'comment', null, $file->getDescription() ) . "\n" .
                        "      " . Xml::element( 'filename', null, $file->getName() ) . "\n" .
-                       "      " . Xml::element( 'src', null, $file->getFullUrl() ) . "\n" .
+                       $archiveName .
+                       "      " . Xml::element( 'src', null, $file->getCanonicalUrl() ) . "\n" .
                        "      " . Xml::element( 'size', null, $file->getSize() ) . "\n" .
+                       "      " . Xml::element( 'sha1base36', null, $file->getSha1() ) . "\n" .
+                       "      " . Xml::element( 'rel', null, $file->getRel() ) . "\n" .
+                       $contents .
                        "    </upload>\n";
        }
 
@@ -660,6 +690,36 @@ class DumpOutput {
        function write( $string ) {
                print $string;
        }
+
+       /**
+        * Close the old file, move it to a specified name,
+        * and reopen new file with the old name. Use this
+        * for writing out a file in multiple pieces
+        * at specified checkpoints (e.g. every n hours).
+        * @param $newname mixed File name. May be a string or an array with one element
+        */
+       function closeRenameAndReopen( $newname ) {
+               return;
+       }
+
+       /**
+        * Close the old file, and move it to a specified name.
+        * Use this for the last piece of a file written out
+        * at specified checkpoints (e.g. every n hours).
+        * @param $newname mixed File name. May be a string or an array with one element
+        * @param $open bool If true, a new file with the old filename will be opened again for writing (default: false)
+        */
+       function closeAndRename( $newname, $open = false ) {
+               return;
+       }
+
+       /**
+        * Returns the name of the file or files which are
+        * being written to, if there are any.
+        */
+       function getFilenames() {
+               return NULL;
+       }
 }
 
 /**
@@ -667,15 +727,43 @@ class DumpOutput {
  * @ingroup Dump
  */
 class DumpFileOutput extends DumpOutput {
-       var $handle;
+       protected $handle, $filename;
 
        function __construct( $file ) {
                $this->handle = fopen( $file, "wt" );
+               $this->filename = $file;
        }
 
        function write( $string ) {
                fputs( $this->handle, $string );
        }
+
+       function closeRenameAndReopen( $newname ) {
+               $this->closeAndRename( $newname, true );
+       }
+
+       function closeAndRename( $newname, $open = false ) {
+               if ( is_array( $newname ) ) {
+                       if ( count( $newname ) > 1 ) {
+                               throw new MWException( __METHOD__ . ": passed multiple arguments for rename of single file\n" );
+                       } else {
+                               $newname = $newname[0];
+                       }
+               }
+               if ( $newname ) {
+                       fclose( $this->handle );
+                       if (! rename( $this->filename, $newname ) ) {
+                               throw new MWException( __METHOD__ . ": rename of file {$this->filename} to $newname failed\n" );
+                       }
+                       elseif ( $open ) {
+                               $this->handle = fopen( $this->filename, "wt" );
+                       }
+               }
+       }
+
+       function getFilenames() {
+               return $this->filename;
+       }
 }
 
 /**
@@ -685,12 +773,53 @@ class DumpFileOutput extends DumpOutput {
  * @ingroup Dump
  */
 class DumpPipeOutput extends DumpFileOutput {
+       protected $command, $filename;
+
        function __construct( $command, $file = null ) {
                if ( !is_null( $file ) ) {
                        $command .=  " > " . wfEscapeShellArg( $file );
                }
-               $this->handle = popen( $command, "w" );
+
+               $this->startCommand( $command );
+               $this->command = $command;
+               $this->filename = $file;
+       }
+
+       function startCommand( $command ) {
+               $spec = array(
+                       0 => array( "pipe", "r" ),
+               );
+               $pipes = array();
+               $this->procOpenResource = proc_open( $command, $spec, $pipes );
+               $this->handle = $pipes[0];
        }
+
+       function closeRenameAndReopen( $newname ) {
+               $this->closeAndRename( $newname, true );
+       }
+
+       function closeAndRename( $newname, $open = false ) {
+               if ( is_array( $newname ) ) {
+                       if ( count( $newname ) > 1 ) {
+                               throw new MWException( __METHOD__ . ": passed multiple arguments for rename of single file\n" );
+                       } else {
+                               $newname = $newname[0];
+                       }
+               }
+               if ( $newname ) {
+                       fclose( $this->handle );
+                       proc_close( $this->procOpenResource );
+                       if (! rename( $this->filename, $newname ) ) {
+                               throw new MWException( __METHOD__ . ": rename of file {$this->filename} to $newname failed\n" );
+                       }
+                       elseif ( $open ) {
+                               $command = $this->command;
+                               $command .=  " > " . wfEscapeShellArg( $this->filename );
+                               $this->startCommand( $command );
+                       }
+               }
+       }
+
 }
 
 /**
@@ -718,12 +847,40 @@ class DumpBZip2Output extends DumpPipeOutput {
  * @ingroup Dump
  */
 class Dump7ZipOutput extends DumpPipeOutput {
+       protected $filename;
+
        function __construct( $file ) {
-               $command = "7za a -bd -si " . wfEscapeShellArg( $file );
-               // Suppress annoying useless crap from p7zip
-               // Unfortunately this could suppress real error messages too
-               $command .= ' >' . wfGetNull() . ' 2>&1';
+               $command = setup7zCommand( $file );
                parent::__construct( $command );
+               $this->filename = $file;
+       }
+
+       function closeRenameAndReopen( $newname ) {
+               $this->closeAndRename( $newname, true );
+       }
+
+       function closeAndRename( $newname, $open = false ) {
+               if ( is_array( $newname ) ) {
+                       if ( count( $newname ) > 1 ) {
+                               throw new MWException( __METHOD__ . ": passed multiple arguments for rename of single file\n" );
+                       } else {
+                               $newname = $newname[0];
+                       }
+               }
+               if ( $newname ) {
+                       fclose( $this->handle );
+                       proc_close( $this->procOpenResource );
+                       if (! rename( $this->filename, $newname ) ) {
+                               throw new MWException( __METHOD__ . ": rename of file {$this->filename} to $newname failed\n" );
+                       }
+                       elseif ( $open ) {
+                               $command = "7za a -bd -si " . wfEscapeShellArg( $file );
+                               // Suppress annoying useless crap from p7zip
+                               // Unfortunately this could suppress real error messages too
+                               $command .= ' >' . wfGetNull() . ' 2>&1';
+                               $this->startCommand( $command );
+                       }
+               }
        }
 }
 
@@ -772,6 +929,18 @@ class DumpFilter {
                $this->sink->writeRevision( $rev, $string );
        }
 
+       function closeRenameAndReopen( $newname ) {
+               $this->sink->closeRenameAndReopen( $newname );
+       }
+
+       function closeAndRename( $newname, $open = false ) {
+               $this->sink->closeAndRename( $newname, $open );
+       }
+
+       function getFilenames() {
+               return $this->sink->getFilenames();
+       }
+
        /**
         * Override for page-based filter types.
         * @return bool
@@ -919,6 +1088,25 @@ class DumpMultiWriter {
                        $this->sinks[$i]->writeRevision( $rev, $string );
                }
        }
+
+       function closeRenameAndReopen( $newnames ) {
+               $this->closeAndRename( $newnames, true );
+       }
+
+       function closeAndRename( $newnames, $open = false ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->closeAndRename( $newnames[$i], $open );
+               }
+       }
+
+       function getFilenames() {
+               $filenames = array();
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $filenames[] =  $this->sinks[$i]->getFilenames();
+               }
+               return $filenames;
+       }
+
 }
 
 function xmlsafe( $string ) {