Index: trunk/phase3/includes/filerepo/OldLocalFile.php |
— | — | @@ -227,12 +227,14 @@ |
228 | 228 | * @param $archiveName string Full archive name of the file, in the form |
229 | 229 | * $timestamp!$filename, where $filename must match $this->getName() |
230 | 230 | */ |
231 | | - function uploadOld( $srcPath, $archiveName, $comment, $user ) { |
| 231 | + function uploadOld( $srcPath, $archiveName, $timestamp, $comment, $user, $flags = 0 ) { |
232 | 232 | $this->lock(); |
233 | | - $status = $this->publish( $srcPath, $flags, $archiveName ); |
| 233 | + $status = $this->publish( $srcPath, |
| 234 | + $flags & File::DELETE_SOURCE ? FileRepo::DELETE_SOURCE : 0, |
| 235 | + $archiveName ); |
234 | 236 | |
235 | 237 | if ( $status->isGood() ) { |
236 | | - if ( !$this->recordOldUpload( $srcPath, $archiveName, $comment, $user ) ) { |
| 238 | + if ( !$this->recordOldUpload( $srcPath, $archiveName, $timestamp, $comment, $user ) ) { |
237 | 239 | $status->fatal( 'filenotfound', $srcPath ); |
238 | 240 | } |
239 | 241 | } |
— | — | @@ -251,7 +253,7 @@ |
252 | 254 | * @param $user User User who did this upload |
253 | 255 | * @return bool |
254 | 256 | */ |
255 | | - function recordOldUpload( $srcPath, $archiveName, $comment, $user ) { |
| 257 | + function recordOldUpload( $srcPath, $archiveName, $timestamp, $comment, $user ) { |
256 | 258 | $dbw = $this->repo->getMasterDB(); |
257 | 259 | $dbw->begin(); |
258 | 260 | |
— | — | @@ -269,7 +271,7 @@ |
270 | 272 | 'oi_width' => intval( $props['width'] ), |
271 | 273 | 'oi_height' => intval( $props['height'] ), |
272 | 274 | 'oi_bits' => $props['bits'], |
273 | | - 'oi_timestamp' => $props['timestamp'], |
| 275 | + 'oi_timestamp' => $dbw->timestamp( $timestamp ), |
274 | 276 | 'oi_description' => $comment, |
275 | 277 | 'oi_user' => $user->getId(), |
276 | 278 | 'oi_user_text' => $user->getName(), |
Index: trunk/phase3/includes/Export.php |
— | — | @@ -35,6 +35,7 @@ |
36 | 36 | var $author_list = "" ; |
37 | 37 | |
38 | 38 | var $dumpUploads = false; |
| 39 | + var $dumpUploadFileContents = false; |
39 | 40 | |
40 | 41 | const FULL = 1; |
41 | 42 | const CURRENT = 2; |
— | — | @@ -318,7 +319,7 @@ |
319 | 320 | if ( isset( $last ) ) { |
320 | 321 | $output = ''; |
321 | 322 | if ( $this->dumpUploads ) { |
322 | | - $output .= $this->writer->writeUploads( $last ); |
| 323 | + $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents ); |
323 | 324 | } |
324 | 325 | $output .= $this->writer->closePage(); |
325 | 326 | $this->sink->writeClosePage( $output ); |
— | — | @@ -333,7 +334,7 @@ |
334 | 335 | if ( isset( $last ) ) { |
335 | 336 | $output = ''; |
336 | 337 | if ( $this->dumpUploads ) { |
337 | | - $output .= $this->writer->writeUploads( $last ); |
| 338 | + $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents ); |
338 | 339 | } |
339 | 340 | $output .= $this->author_list; |
340 | 341 | $output .= $this->writer->closePage(); |
— | — | @@ -600,29 +601,48 @@ |
601 | 602 | /** |
602 | 603 | * Warning! This data is potentially inconsistent. :( |
603 | 604 | */ |
604 | | - function writeUploads( $row ) { |
| 605 | + function writeUploads( $row, $dumpContents = false ) { |
605 | 606 | if ( $row->page_namespace == NS_IMAGE ) { |
606 | 607 | $img = wfFindFile( $row->page_title ); |
607 | 608 | if ( $img ) { |
608 | 609 | $out = ''; |
609 | 610 | foreach ( array_reverse( $img->getHistory() ) as $ver ) { |
610 | | - $out .= $this->writeUpload( $ver ); |
| 611 | + $out .= $this->writeUpload( $ver, $dumpContents ); |
611 | 612 | } |
612 | | - $out .= $this->writeUpload( $img ); |
| 613 | + $out .= $this->writeUpload( $img, $dumpContents ); |
613 | 614 | return $out; |
614 | 615 | } |
615 | 616 | } |
616 | 617 | return ''; |
617 | 618 | } |
618 | 619 | |
619 | | - function writeUpload( $file ) { |
| 620 | + function writeUpload( $file, $dumpContents = false ) { |
| 621 | + if ( $file->isOld() ) { |
| 622 | + $archiveName = " " . |
| 623 | + Xml::element( 'archivename', null, $file->getArchiveName() ) . "\n"; |
| 624 | + } else { |
| 625 | + $archiveName = ''; |
| 626 | + } |
| 627 | + if ( $dumpContents ) { |
| 628 | + # Dump file as base64 |
| 629 | + # Uses only XML-safe characters, so does not need escaping |
| 630 | + $contents = ' <contents encoding="base64">' . |
| 631 | + chunk_split( base64_encode( file_get_contents( $file->getPath() ) ) ) . |
| 632 | + " </contents>\n"; |
| 633 | + } else { |
| 634 | + $contents = ''; |
| 635 | + } |
620 | 636 | return " <upload>\n" . |
621 | 637 | $this->writeTimestamp( $file->getTimestamp() ) . |
622 | 638 | $this->writeContributor( $file->getUser( 'id' ), $file->getUser( 'text' ) ) . |
623 | 639 | " " . Xml::elementClean( 'comment', null, $file->getDescription() ) . "\n" . |
624 | 640 | " " . Xml::element( 'filename', null, $file->getName() ) . "\n" . |
| 641 | + $archiveName . |
625 | 642 | " " . Xml::element( 'src', null, $file->getFullUrl() ) . "\n" . |
626 | 643 | " " . Xml::element( 'size', null, $file->getSize() ) . "\n" . |
| 644 | + " " . Xml::element( 'sha1base36', null, $file->getSha1() ) . "\n" . |
| 645 | + " " . Xml::element( 'rel', null, $file->getRel() ) . "\n" . |
| 646 | + $contents . |
627 | 647 | " </upload>\n"; |
628 | 648 | } |
629 | 649 | |
Index: trunk/phase3/includes/Import.php |
— | — | @@ -35,6 +35,7 @@ |
36 | 36 | private $mLogItemCallback, $mUploadCallback, $mRevisionCallback, $mPageCallback; |
37 | 37 | private $mSiteInfoCallback, $mTargetNamespace, $mPageOutCallback; |
38 | 38 | private $mDebug; |
| 39 | + private $mImportUploads, $mImageBasePath; |
39 | 40 | |
40 | 41 | /** |
41 | 42 | * Creates an ImportXMLReader drawing from the source provided |
— | — | @@ -169,6 +170,13 @@ |
170 | 171 | return false; |
171 | 172 | } |
172 | 173 | } |
| 174 | + |
| 175 | + /** |
| 176 | + * |
| 177 | + */ |
| 178 | + public function setImageBasePath( $dir ) { |
| 179 | + $this->mImageBasePath = $dir; |
| 180 | + } |
173 | 181 | |
174 | 182 | /** |
175 | 183 | * Default per-revision callback, performs the import. |
— | — | @@ -192,9 +200,8 @@ |
193 | 201 | * Dummy for now... |
194 | 202 | */ |
195 | 203 | public function importUpload( $revision ) { |
196 | | - $revision->importUpload(); |
197 | | - //$dbw = wfGetDB( DB_MASTER ); |
198 | | - //return $dbw->deadlockLoop( array( $revision, 'importUpload' ) ); |
| 204 | + $dbw = wfGetDB( DB_MASTER ); |
| 205 | + return $dbw->deadlockLoop( array( $revision, 'importUpload' ) ); |
199 | 206 | return false; |
200 | 207 | } |
201 | 208 | |
— | — | @@ -582,7 +589,7 @@ |
583 | 590 | $uploadInfo = array(); |
584 | 591 | |
585 | 592 | $normalFields = array( 'timestamp', 'comment', 'filename', 'text', |
586 | | - 'src', 'size' ); |
| 593 | + 'src', 'size', 'sha1base36', 'archivename', 'rel' ); |
587 | 594 | |
588 | 595 | $skip = false; |
589 | 596 | |
— | — | @@ -601,26 +608,53 @@ |
602 | 609 | $uploadInfo[$tag] = $this->nodeContents(); |
603 | 610 | } elseif ( $tag == 'contributor' ) { |
604 | 611 | $uploadInfo['contributor'] = $this->handleContributor(); |
| 612 | + } elseif ( $tag == 'contents' ) { |
| 613 | + $contents = $this->nodeContents(); |
| 614 | + $encoding = $this->reader->getAttribute( 'encoding' ); |
| 615 | + if ( $encoding === 'base64' ) { |
| 616 | + $uploadInfo['fileSrc'] = $this->dumpTemp( base64_decode( $contents ) ); |
| 617 | + } |
605 | 618 | } elseif ( $tag != '#text' ) { |
606 | 619 | $this->warn( "Unhandled upload XML tag $tag" ); |
607 | 620 | $skip = true; |
608 | 621 | } |
609 | 622 | } |
| 623 | + |
| 624 | + if ( $this->mImageBasePath && isset( $uploadInfo['rel'] ) ) { |
| 625 | + $path = "{$this->mImageBasePath}/{$uploadInfo['rel']}"; |
| 626 | + if ( file_exists( $path ) ) { |
| 627 | + $uploadInfo['fileSrc'] = $path; |
| 628 | + } |
| 629 | + } |
610 | 630 | |
611 | | - return $this->processUpload( $pageInfo, $uploadInfo ); |
| 631 | + if ( $this->mImportUploads ) { |
| 632 | + return $this->processUpload( $pageInfo, $uploadInfo ); |
| 633 | + } |
612 | 634 | } |
| 635 | + |
| 636 | + private function dumpTemp( $contents ) { |
| 637 | + $filename = tempnam( wfTempDir(), 'importupload' ); |
| 638 | + file_put_contents( $filename, $contents ); |
| 639 | + return $filename; |
| 640 | + } |
613 | 641 | |
614 | 642 | |
615 | 643 | private function processUpload( $pageInfo, $uploadInfo ) { |
616 | 644 | $revision = new WikiRevision; |
617 | | - $text = isset( $uploadInfo['text'] ) ? $uploadInfo['text'] : ''; |
| 645 | + $text = isset( $uploadInfo['text'] ) ? $uploadInfo['text'] : ''; |
618 | 646 | |
619 | 647 | $revision->setTitle( $pageInfo['_title'] ); |
620 | | - $revision->setID( $pageInfo['id'] ); |
| 648 | + $revision->setID( $pageInfo['id'] ); |
621 | 649 | $revision->setTimestamp( $uploadInfo['timestamp'] ); |
622 | | - $revision->setText( $text ); |
| 650 | + $revision->setText( $text ); |
623 | 651 | $revision->setFilename( $uploadInfo['filename'] ); |
| 652 | + if ( isset( $uploadInfo['archivename'] ) ) { |
| 653 | + $revision->setArchiveName( $uploadInfo['archivename'] ); |
| 654 | + } |
624 | 655 | $revision->setSrc( $uploadInfo['src'] ); |
| 656 | + if ( isset( $uploadInfo['fileSrc'] ) ) { |
| 657 | + $revision->setFileSrc( $uploadInfo['fileSrc'] ); |
| 658 | + } |
625 | 659 | $revision->setSize( intval( $uploadInfo['size'] ) ); |
626 | 660 | $revision->setComment( $uploadInfo['comment'] ); |
627 | 661 | |
— | — | @@ -631,7 +665,7 @@ |
632 | 666 | $revision->setUserName( $uploadInfo['contributor']['username'] ); |
633 | 667 | } |
634 | 668 | |
635 | | - return call_user_func( $this->mUploadCallback, $revision ); |
| 669 | + return call_user_func( $this->mUploadCallback, $revision ); |
636 | 670 | } |
637 | 671 | |
638 | 672 | private function handleContributor() { |
— | — | @@ -790,6 +824,7 @@ |
791 | 825 | * @ingroup SpecialPage |
792 | 826 | */ |
793 | 827 | class WikiRevision { |
| 828 | + var $importer = null; |
794 | 829 | var $title = null; |
795 | 830 | var $id = 0; |
796 | 831 | var $timestamp = "20010115000000"; |
— | — | @@ -801,6 +836,8 @@ |
802 | 837 | var $type = ""; |
803 | 838 | var $action = ""; |
804 | 839 | var $params = ""; |
| 840 | + var $fileSrc = ''; |
| 841 | + var $archiveName = ''; |
805 | 842 | |
806 | 843 | function setTitle( $title ) { |
807 | 844 | if( is_object( $title ) ) { |
— | — | @@ -844,10 +881,16 @@ |
845 | 882 | function setSrc( $src ) { |
846 | 883 | $this->src = $src; |
847 | 884 | } |
| 885 | + function setFileSrc( $src ) { |
| 886 | + $this->fileSrc = $src; |
| 887 | + } |
848 | 888 | |
849 | 889 | function setFilename( $filename ) { |
850 | 890 | $this->filename = $filename; |
851 | 891 | } |
| 892 | + function setArchiveName( $archiveName ) { |
| 893 | + $this->archiveName = $archiveName; |
| 894 | + } |
852 | 895 | |
853 | 896 | function setSize( $size ) { |
854 | 897 | $this->size = intval( $size ); |
— | — | @@ -896,10 +939,16 @@ |
897 | 940 | function getSrc() { |
898 | 941 | return $this->src; |
899 | 942 | } |
| 943 | + function getFileSrc() { |
| 944 | + return $this->fileSrc; |
| 945 | + } |
900 | 946 | |
901 | 947 | function getFilename() { |
902 | 948 | return $this->filename; |
903 | 949 | } |
| 950 | + function getArchiveName() { |
| 951 | + return $this->archiveName; |
| 952 | + } |
904 | 953 | |
905 | 954 | function getSize() { |
906 | 955 | return $this->size; |
— | — | @@ -1044,62 +1093,55 @@ |
1045 | 1094 | } |
1046 | 1095 | |
1047 | 1096 | function importUpload() { |
1048 | | - wfDebug( __METHOD__ . ": STUB\n" ); |
1049 | | - |
1050 | | - /** |
1051 | | - // from file revert... |
1052 | | - $source = $this->file->getArchiveVirtualUrl( $this->oldimage ); |
1053 | | - $comment = $wgRequest->getText( 'wpComment' ); |
1054 | | - // TODO: Preserve file properties from database instead of reloading from file |
1055 | | - $status = $this->file->upload( $source, $comment, $comment ); |
1056 | | - if( $status->isGood() ) { |
1057 | | - */ |
1058 | | - |
1059 | | - /** |
1060 | | - // from file upload... |
1061 | | - $this->mLocalFile = wfLocalFile( $nt ); |
1062 | | - $this->mDestName = $this->mLocalFile->getName(); |
1063 | | - //.... |
1064 | | - $status = $this->mLocalFile->upload( $this->mTempPath, $this->mComment, $pageText, |
1065 | | - File::DELETE_SOURCE, $this->mFileProps ); |
1066 | | - if ( !$status->isGood() ) { |
1067 | | - $resultDetails = array( 'internal' => $status->getWikiText() ); |
1068 | | - */ |
1069 | | - |
1070 | | - // @todo Fixme: it may create a page without our desire, also wrong potentially. |
1071 | | - // and, it will record a *current* upload, but we might want an archive version here |
1072 | | - |
1073 | | - $file = wfLocalFile( $this->getTitle() ); |
| 1097 | + # Construct a file |
| 1098 | + $archiveName = $this->getArchiveName(); |
| 1099 | + if ( $archiveName ) { |
| 1100 | + wfDebug( __METHOD__ . "Importing archived file as $archiveName\n" ); |
| 1101 | + $file = OldLocalFile::newFromArchiveName( $this->getTitle(), |
| 1102 | + RepoGroup::singleton()->getLocalRepo(), $archiveName ); |
| 1103 | + } else { |
| 1104 | + $file = wfLocalFile( $this->getTitle() ); |
| 1105 | + wfDebug( __METHOD__ . 'Importing new file as ' . $file->getName() . "\n" ); |
| 1106 | + if ( $file->exists() && $file->getTimestamp() > $this->getTimestamp() ) { |
| 1107 | + $archiveName = $file->getTimestamp() . '!' . $file->getName(); |
| 1108 | + $file = OldLocalFile::newFromArchiveName( $this->getTitle(), |
| 1109 | + RepoGroup::singleton()->getLocalRepo(), $archiveName ); |
| 1110 | + wfDebug( __METHOD__ . "File already exists; importing as $archiveName\n" ); |
| 1111 | + } |
| 1112 | + } |
1074 | 1113 | if( !$file ) { |
1075 | | - wfDebug( "IMPORT: Bad file. :(\n" ); |
| 1114 | + wfDebug( __METHOD__ . ': Bad file for ' . $this->getTitle() . "\n" ); |
1076 | 1115 | return false; |
1077 | 1116 | } |
1078 | | - |
1079 | | - $source = $this->downloadSource(); |
| 1117 | + |
| 1118 | + # Get the file source or download if necessary |
| 1119 | + $source = $this->getFileSrc(); |
| 1120 | + if ( !$source ) { |
| 1121 | + $source = $this->downloadSource(); |
| 1122 | + } |
1080 | 1123 | if( !$source ) { |
1081 | | - wfDebug( "IMPORT: Could not fetch remote file. :(\n" ); |
| 1124 | + wfDebug( __METHOD__ . ": Could not fetch remote file.\n" ); |
1082 | 1125 | return false; |
1083 | 1126 | } |
1084 | 1127 | |
1085 | 1128 | $user = User::newFromName( $this->user_text ); |
1086 | | - |
1087 | | - $status = $file->upload( $source, |
1088 | | - $this->getComment(), |
1089 | | - $this->getComment(), // Initial page, if none present... |
1090 | | - File::DELETE_SOURCE, |
1091 | | - false, // props... |
1092 | | - $this->getTimestamp(), |
1093 | | - is_object( $user ) ? ( $user->isLoggedIn() ? $user : null ) : null ); |
1094 | | - |
1095 | | - if( $status->isGood() ) { |
1096 | | - // yay? |
1097 | | - wfDebug( "IMPORT: is ok?\n" ); |
| 1129 | + |
| 1130 | + # Do the actual upload |
| 1131 | + if ( $archiveName ) { |
| 1132 | + $status = $file->uploadOld( $source, $archiveName, |
| 1133 | + $this->getTimestamp(), $this->getComment(), $user, File::DELETE_SOURCE ); |
| 1134 | + } else { |
| 1135 | + $status = $file->upload( $source, $this->getComment(), $this->getComment(), |
| 1136 | + File::DELETE_SOURCE, false, $this->getTimestamp(), $user ); |
| 1137 | + } |
| 1138 | + |
| 1139 | + if ( $status->isGood() ) { |
| 1140 | + wfDebug( __METHOD__ . ": Succesful\n" ); |
1098 | 1141 | return true; |
| 1142 | + } else { |
| 1143 | + wfDebug( __METHOD__ . ': failed: ' . $status->getXml() . "\n" ); |
| 1144 | + return false; |
1099 | 1145 | } |
1100 | | - |
1101 | | - wfDebug( "IMPORT: is bad? " . $status->getXml() . "\n" ); |
1102 | | - return false; |
1103 | | - |
1104 | 1146 | } |
1105 | 1147 | |
1106 | 1148 | function downloadSource() { |