Index: trunk/phase3/tests/LocalFileTest.php |
— | — | @@ -68,17 +68,17 @@ |
69 | 69 | } |
70 | 70 | |
71 | 71 | function testGetArchiveVirtualUrl() { |
72 | | - $this->assertEquals( 'mwrepo:///public/archive', $this->file_hl0->getArchiveVirtualUrl() ); |
73 | | - $this->assertEquals( 'mwrepo:///public/archive/a/a2', $this->file_hl2->getArchiveVirtualUrl() ); |
74 | | - $this->assertEquals( 'mwrepo:///public/archive/%21', $this->file_hl0->getArchiveVirtualUrl( '!' ) ); |
75 | | - $this->assertEquals( 'mwrepo:///public/archive/a/a2/%21', $this->file_hl2->getArchiveVirtualUrl( '!' ) ); |
| 72 | + $this->assertEquals( 'mwrepo://test/public/archive', $this->file_hl0->getArchiveVirtualUrl() ); |
| 73 | + $this->assertEquals( 'mwrepo://test/public/archive/a/a2', $this->file_hl2->getArchiveVirtualUrl() ); |
| 74 | + $this->assertEquals( 'mwrepo://test/public/archive/%21', $this->file_hl0->getArchiveVirtualUrl( '!' ) ); |
| 75 | + $this->assertEquals( 'mwrepo://test/public/archive/a/a2/%21', $this->file_hl2->getArchiveVirtualUrl( '!' ) ); |
76 | 76 | } |
77 | 77 | |
78 | 78 | function testGetThumbVirtualUrl() { |
79 | | - $this->assertEquals( 'mwrepo:///public/thumb/Test%21', $this->file_hl0->getThumbVirtualUrl() ); |
80 | | - $this->assertEquals( 'mwrepo:///public/thumb/a/a2/Test%21', $this->file_hl2->getThumbVirtualUrl() ); |
81 | | - $this->assertEquals( 'mwrepo:///public/thumb/Test%21/%21', $this->file_hl0->getThumbVirtualUrl( '!' ) ); |
82 | | - $this->assertEquals( 'mwrepo:///public/thumb/a/a2/Test%21/%21', $this->file_hl2->getThumbVirtualUrl( '!' ) ); |
| 79 | + $this->assertEquals( 'mwrepo://test/public/thumb/Test%21', $this->file_hl0->getThumbVirtualUrl() ); |
| 80 | + $this->assertEquals( 'mwrepo://test/public/thumb/a/a2/Test%21', $this->file_hl2->getThumbVirtualUrl() ); |
| 81 | + $this->assertEquals( 'mwrepo://test/public/thumb/Test%21/%21', $this->file_hl0->getThumbVirtualUrl( '!' ) ); |
| 82 | + $this->assertEquals( 'mwrepo://test/public/thumb/a/a2/Test%21/%21', $this->file_hl2->getThumbVirtualUrl( '!' ) ); |
83 | 83 | } |
84 | 84 | |
85 | 85 | function testGetUrl() { |
Index: trunk/phase3/includes/ImagePage.php |
— | — | @@ -664,7 +664,8 @@ |
665 | 665 | } |
666 | 666 | |
667 | 667 | $sourcePath = $this->img->getArchiveVirtualUrl( $oldimage ); |
668 | | - $result = $this->img->publish( $sourcePath ); |
| 668 | + $comment = wfMsg( "reverted" ); |
| 669 | + $result = $this->img->upload( $sourcePath, $comment, $comment ); |
669 | 670 | |
670 | 671 | if ( WikiError::isError( $result ) ) { |
671 | 672 | $this->showError( $result ); |
Index: trunk/phase3/includes/filerepo/LocalFile.php |
— | — | @@ -150,58 +150,7 @@ |
151 | 151 | * Load metadata from the file itself |
152 | 152 | */ |
153 | 153 | function loadFromFile() { |
154 | | - wfProfileIn( __METHOD__ ); |
155 | | - $path = $this->getPath(); |
156 | | - $this->fileExists = file_exists( $path ); |
157 | | - $gis = array(); |
158 | | - |
159 | | - if ( $this->fileExists ) { |
160 | | - $magic=& MimeMagic::singleton(); |
161 | | - |
162 | | - $this->mime = $magic->guessMimeType($path,true); |
163 | | - list( $this->major_mime, $this->minor_mime ) = self::splitMime( $this->mime ); |
164 | | - $this->media_type = $magic->getMediaType($path,$this->mime); |
165 | | - $handler = MediaHandler::getHandler( $this->mime ); |
166 | | - |
167 | | - # Get size in bytes |
168 | | - $this->size = filesize( $path ); |
169 | | - |
170 | | - # Height, width and metadata |
171 | | - if ( $handler ) { |
172 | | - $gis = $handler->getImageSize( $this, $path ); |
173 | | - $this->metadata = $handler->getMetadata( $this, $path ); |
174 | | - } else { |
175 | | - $gis = false; |
176 | | - $this->metadata = ''; |
177 | | - } |
178 | | - |
179 | | - wfDebug(__METHOD__.": $path loaded, {$this->size} bytes, {$this->mime}.\n"); |
180 | | - } else { |
181 | | - $this->mime = NULL; |
182 | | - $this->media_type = MEDIATYPE_UNKNOWN; |
183 | | - $this->metadata = ''; |
184 | | - wfDebug(__METHOD__.": $path NOT FOUND!\n"); |
185 | | - } |
186 | | - |
187 | | - if( $gis ) { |
188 | | - $this->width = $gis[0]; |
189 | | - $this->height = $gis[1]; |
190 | | - } else { |
191 | | - $this->width = 0; |
192 | | - $this->height = 0; |
193 | | - } |
194 | | - |
195 | | - #NOTE: $gis[2] contains a code for the image type. This is no longer used. |
196 | | - |
197 | | - #NOTE: we have to set this flag early to avoid load() to be called |
198 | | - # be some of the functions below. This may lead to recursion or other bad things! |
199 | | - # as ther's only one thread of execution, this should be safe anyway. |
200 | | - $this->dataLoaded = true; |
201 | | - |
202 | | - if ( isset( $gis['bits'] ) ) $this->bits = $gis['bits']; |
203 | | - else $this->bits = 0; |
204 | | - |
205 | | - wfProfileOut( __METHOD__ ); |
| 154 | + $this->setProps( self::getInfoFromPath( $this->getPath() ) ); |
206 | 155 | } |
207 | 156 | |
208 | 157 | function getCacheFields( $prefix = 'img_' ) { |
— | — | @@ -349,6 +298,17 @@ |
350 | 299 | wfProfileOut( __METHOD__ ); |
351 | 300 | } |
352 | 301 | |
| 302 | + function setProps( $info ) { |
| 303 | + $this->dataLoaded = true; |
| 304 | + $fields = $this->getCacheFields( '' ); |
| 305 | + $fields[] = 'fileExists'; |
| 306 | + foreach ( $fields as $field ) { |
| 307 | + if ( isset( $info[$field] ) ) { |
| 308 | + $this->$field = $info[$field]; |
| 309 | + } |
| 310 | + } |
| 311 | + } |
| 312 | + |
353 | 313 | /** splitMime inherited */ |
354 | 314 | /** getName inherited */ |
355 | 315 | /** getTitle inherited */ |
— | — | @@ -517,21 +477,30 @@ |
518 | 478 | * Refresh metadata in memcached, but don't touch thumbnails or squid |
519 | 479 | */ |
520 | 480 | function purgeMetadataCache() { |
521 | | - clearstatcache(); |
522 | | - $this->loadFromFile(); |
| 481 | + $this->loadFromDB(); |
523 | 482 | $this->saveToCache(); |
524 | 483 | } |
525 | 484 | |
526 | 485 | /** |
527 | 486 | * Delete all previously generated thumbnails, refresh metadata in memcached and purge the squid |
528 | 487 | */ |
529 | | - function purgeCache( $archiveFiles = array() ) { |
530 | | - global $wgUseSquid; |
531 | | - |
| 488 | + function purgeCache() { |
532 | 489 | // Refresh metadata cache |
533 | 490 | $this->purgeMetadataCache(); |
534 | 491 | |
535 | 492 | // Delete thumbnails |
| 493 | + $this->purgeThumbnails(); |
| 494 | + |
| 495 | + // Purge squid cache for this file |
| 496 | + wfPurgeSquidServers( array( $this->getURL() ) ); |
| 497 | + } |
| 498 | + |
| 499 | + /** |
| 500 | + * Delete cached transformed files |
| 501 | + */ |
| 502 | + function purgeThumbnails() { |
| 503 | + global $wgUseSquid; |
| 504 | + // Delete thumbnails |
536 | 505 | $files = $this->getThumbnails(); |
537 | 506 | $dir = $this->getThumbPath(); |
538 | 507 | $urls = array(); |
— | — | @@ -548,10 +517,6 @@ |
549 | 518 | |
550 | 519 | // Purge the squid |
551 | 520 | if ( $wgUseSquid ) { |
552 | | - $urls[] = $this->getURL(); |
553 | | - foreach ( $archiveFiles as $file ) { |
554 | | - $urls[] = $this->getArchiveUrl( $file ); |
555 | | - } |
556 | 521 | wfPurgeSquidServers( $urls ); |
557 | 522 | } |
558 | 523 | } |
— | — | @@ -632,17 +597,66 @@ |
633 | 598 | /** isHashed inherited */ |
634 | 599 | |
635 | 600 | /** |
| 601 | + * Upload a file and record it in the DB |
| 602 | + * @param string $srcPath Source path or virtual URL |
| 603 | + * @param string $comment Upload description |
| 604 | + * @param string $pageText Text to use for the new description page, if a new description page is created |
| 605 | + * @param integer $flags Flags for publish() |
| 606 | + * @param array $props File properties, if known. This can be used to reduce the |
| 607 | + * upload time when uploading virtual URLs for which the file info |
| 608 | + * is already known |
| 609 | + * @param string $timestamp Timestamp for img_timestamp, or false to use the current time |
| 610 | + * |
| 611 | + * @return Wikitext-formatted WikiError or true on success |
| 612 | + */ |
| 613 | + function upload( $srcPath, $comment, $pageText, $flags = 0, $props = false, $timestamp = false ) { |
| 614 | + $archive = $this->publish( $srcPath, $flags ); |
| 615 | + if ( WikiError::isError( $archive ) ){ |
| 616 | + return $archive; |
| 617 | + } |
| 618 | + if ( !$this->recordUpload2( $archive, $comment, $pageText, $props, $timestamp ) ) { |
| 619 | + return new WikiErrorMsg( 'filenotfound', wfEscapeWikiText( $srcPath ) ); |
| 620 | + } |
| 621 | + return true; |
| 622 | + } |
| 623 | + |
| 624 | + /** |
636 | 625 | * Record a file upload in the upload log and the image table |
| 626 | + * @deprecated use upload() |
637 | 627 | */ |
638 | 628 | function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', |
639 | 629 | $watch = false, $timestamp = false ) |
640 | 630 | { |
641 | | - global $wgUser, $wgUseCopyrightUpload; |
| 631 | + $pageText = UploadForm::getInitialPageText( $desc, $license, $copyStatus, $source ); |
| 632 | + if ( !$this->recordUpload2( $oldver, $desc, $pageText ) ) { |
| 633 | + return false; |
| 634 | + } |
| 635 | + if ( $watch ) { |
| 636 | + global $wgUser; |
| 637 | + $wgUser->addWatch( $this->getTitle() ); |
| 638 | + } |
| 639 | + return true; |
642 | 640 | |
| 641 | + } |
| 642 | + |
| 643 | + /** |
| 644 | + * Record a file upload in the upload log and the image table |
| 645 | + */ |
| 646 | + function recordUpload2( $oldver, $comment, $pageText, $props = false, $timestamp = false ) |
| 647 | + { |
| 648 | + global $wgUser; |
| 649 | + |
643 | 650 | $dbw = $this->repo->getMasterDB(); |
644 | 651 | |
| 652 | + if ( !$props ) { |
| 653 | + $props = $this->repo->getFileProps( $this->getVirtualUrl() ); |
| 654 | + } |
| 655 | + $this->setProps( $props ); |
| 656 | + |
645 | 657 | // Delete thumbnails and refresh the metadata cache |
646 | | - $this->purgeCache(); |
| 658 | + $this->purgeThumbnails(); |
| 659 | + $this->saveToCache(); |
| 660 | + wfPurgeSquidServers( array( $this->getURL() ) ); |
647 | 661 | |
648 | 662 | // Fail now if the file isn't there |
649 | 663 | if ( !$this->fileExists ) { |
— | — | @@ -650,37 +664,10 @@ |
651 | 665 | return false; |
652 | 666 | } |
653 | 667 | |
654 | | - if ( $wgUseCopyrightUpload ) { |
655 | | - if ( $license != '' ) { |
656 | | - $licensetxt = '== ' . wfMsgForContent( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n"; |
657 | | - } |
658 | | - $textdesc = '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $desc . "\n" . |
659 | | - '== ' . wfMsgForContent ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" . |
660 | | - "$licensetxt" . |
661 | | - '== ' . wfMsgForContent ( 'filesource' ) . " ==\n" . $source ; |
662 | | - } else { |
663 | | - if ( $license != '' ) { |
664 | | - $filedesc = $desc == '' ? '' : '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $desc . "\n"; |
665 | | - $textdesc = $filedesc . |
666 | | - '== ' . wfMsgForContent ( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n"; |
667 | | - } else { |
668 | | - $textdesc = $desc; |
669 | | - } |
670 | | - } |
671 | | - |
672 | 668 | if ( $timestamp === false ) { |
673 | 669 | $timestamp = $dbw->timestamp(); |
674 | 670 | } |
675 | 671 | |
676 | | - #split mime type |
677 | | - if (strpos($this->mime,'/')!==false) { |
678 | | - list($major,$minor)= explode('/',$this->mime,2); |
679 | | - } |
680 | | - else { |
681 | | - $major= $this->mime; |
682 | | - $minor= "unknown"; |
683 | | - } |
684 | | - |
685 | 672 | # Test to see if the row exists using INSERT IGNORE |
686 | 673 | # This avoids race conditions by locking the row until the commit, and also |
687 | 674 | # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition. |
— | — | @@ -692,10 +679,10 @@ |
693 | 680 | 'img_height' => intval( $this->height ), |
694 | 681 | 'img_bits' => $this->bits, |
695 | 682 | 'img_media_type' => $this->media_type, |
696 | | - 'img_major_mime' => $major, |
697 | | - 'img_minor_mime' => $minor, |
| 683 | + 'img_major_mime' => $this->major_mime, |
| 684 | + 'img_minor_mime' => $this->minor_mime, |
698 | 685 | 'img_timestamp' => $timestamp, |
699 | | - 'img_description' => $desc, |
| 686 | + 'img_description' => $comment, |
700 | 687 | 'img_user' => $wgUser->getID(), |
701 | 688 | 'img_user_text' => $wgUser->getName(), |
702 | 689 | 'img_metadata' => $this->metadata, |
— | — | @@ -730,10 +717,10 @@ |
731 | 718 | 'img_height' => intval( $this->height ), |
732 | 719 | 'img_bits' => $this->bits, |
733 | 720 | 'img_media_type' => $this->media_type, |
734 | | - 'img_major_mime' => $major, |
735 | | - 'img_minor_mime' => $minor, |
| 721 | + 'img_major_mime' => $this->major_mime, |
| 722 | + 'img_minor_mime' => $this->minor_mime, |
736 | 723 | 'img_timestamp' => $timestamp, |
737 | | - 'img_description' => $desc, |
| 724 | + 'img_description' => $comment, |
738 | 725 | 'img_user' => $wgUser->getID(), |
739 | 726 | 'img_user_text' => $wgUser->getName(), |
740 | 727 | 'img_metadata' => $this->metadata, |
— | — | @@ -750,31 +737,28 @@ |
751 | 738 | |
752 | 739 | $descTitle = $this->getTitle(); |
753 | 740 | $article = new Article( $descTitle ); |
754 | | - $minor = false; |
755 | | - $watch = $watch || $wgUser->isWatched( $descTitle ); |
756 | | - $suppressRC = true; // There's already a log entry, so don't double the RC load |
757 | 741 | |
| 742 | + # Add the log entry |
| 743 | + $log = new LogPage( 'upload' ); |
| 744 | + $log->addEntry( 'upload', $descTitle, $comment ); |
| 745 | + |
758 | 746 | if( $descTitle->exists() ) { |
759 | | - // TODO: insert a null revision into the page history for this update. |
760 | | - if( $watch ) { |
761 | | - $wgUser->addWatch( $descTitle ); |
762 | | - } |
| 747 | + # Create a null revision |
| 748 | + $nullRevision = Revision::newNullRevision( $dbw, $descTitle->getArticleId(), $log->getRcComment(), false ); |
| 749 | + $nullRevision->insertOn( $dbw ); |
763 | 750 | |
764 | 751 | # Invalidate the cache for the description page |
765 | 752 | $descTitle->invalidateCache(); |
766 | 753 | $descTitle->purgeSquid(); |
767 | 754 | } else { |
768 | 755 | // New file; create the description page. |
769 | | - $article->insertNewArticle( $textdesc, $desc, $minor, $watch, $suppressRC ); |
| 756 | + // There's already a log entry, so don't make a second RC entry |
| 757 | + $article->doEdit( $pageText, $comment, EDIT_NEW | EDIT_SUPPRESS_RC ); |
770 | 758 | } |
771 | 759 | |
772 | 760 | # Hooks, hooks, the magic of hooks... |
773 | 761 | wfRunHooks( 'FileUpload', array( $this ) ); |
774 | 762 | |
775 | | - # Add the log entry |
776 | | - $log = new LogPage( 'upload' ); |
777 | | - $log->addEntry( 'upload', $descTitle, $desc ); |
778 | | - |
779 | 763 | # Commit the transaction now, in case something goes wrong later |
780 | 764 | # The most important thing is that files don't get lost, especially archives |
781 | 765 | $dbw->immediateCommit(); |
— | — | @@ -803,11 +787,11 @@ |
804 | 788 | * file, and a wikitext-formatted WikiError object on failure. |
805 | 789 | */ |
806 | 790 | function publish( $srcPath, $flags = 0 ) { |
807 | | - $dstPath = $this->getFullPath(); |
| 791 | + $dstRel = $this->getRel(); |
808 | 792 | $archiveName = gmdate( 'YmdHis' ) . '!'. $this->getName(); |
809 | | - $archivePath = $this->getArchivePath( $archiveName ); |
| 793 | + $archiveRel = 'archive/' . $this->getHashPath() . $archiveName; |
810 | 794 | $flags = $flags & File::DELETE_SOURCE ? LocalRepo::DELETE_SOURCE : 0; |
811 | | - $status = $this->repo->publish( $srcPath, $dstPath, $archivePath, $flags ); |
| 795 | + $status = $this->repo->publish( $srcPath, $dstRel, $archiveRel, $flags ); |
812 | 796 | if ( WikiError::isError( $status ) ) { |
813 | 797 | return $status; |
814 | 798 | } elseif ( $status == 'new' ) { |
Index: trunk/phase3/includes/filerepo/UnregisteredLocalFile.php |
— | — | @@ -92,7 +92,7 @@ |
93 | 93 | |
94 | 94 | function getURL() { |
95 | 95 | if ( $this->repo ) { |
96 | | - return $this->repo->getZoneUrl( 'public' ) . $this->repo->getHashPath( $this->name ) . urlencode( $this->name ); |
| 96 | + return $this->repo->getZoneUrl( 'public' ) . '/' . $this->repo->getHashPath( $this->name ) . urlencode( $this->name ); |
97 | 97 | } else { |
98 | 98 | return false; |
99 | 99 | } |
Index: trunk/phase3/includes/filerepo/FSRepo.php |
— | — | @@ -3,88 +3,23 @@ |
4 | 4 | /** |
5 | 5 | * A repository for files accessible via the local filesystem. Does not support |
6 | 6 | * database access or registration. |
7 | | - * |
8 | | - * TODO: split off abstract base FileRepo |
9 | 7 | */ |
10 | 8 | |
11 | | -class FSRepo { |
12 | | - const DELETE_SOURCE = 1; |
13 | | - |
14 | | - var $directory, $url, $hashLevels, $thumbScriptUrl, $transformVia404; |
15 | | - var $descBaseUrl, $scriptDirUrl, $articleUrl, $fetchDescription, $initialCapital; |
| 9 | +class FSRepo extends FileRepo { |
| 10 | + var $directory, $url, $hashLevels; |
16 | 11 | var $fileFactory = array( 'UnregisteredLocalFile', 'newFromTitle' ); |
17 | 12 | var $oldFileFactory = false; |
18 | 13 | |
19 | 14 | function __construct( $info ) { |
| 15 | + parent::__construct( $info ); |
| 16 | + |
20 | 17 | // Required settings |
21 | | - $this->name = $info['name']; |
22 | 18 | $this->directory = $info['directory']; |
23 | 19 | $this->url = $info['url']; |
24 | 20 | $this->hashLevels = $info['hashLevels']; |
25 | | - $this->transformVia404 = !empty( $info['transformVia404'] ); |
26 | | - |
27 | | - // Optional settings |
28 | | - $this->initialCapital = true; // by default |
29 | | - foreach ( array( 'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription', |
30 | | - 'thumbScriptUrl', 'initialCapital' ) as $var ) |
31 | | - { |
32 | | - if ( isset( $info[$var] ) ) { |
33 | | - $this->$var = $info[$var]; |
34 | | - } |
35 | | - } |
36 | 21 | } |
37 | 22 | |
38 | 23 | /** |
39 | | - * Create a new File object from the local repository |
40 | | - * @param mixed $title Title object or string |
41 | | - * @param mixed $time Time at which the image is supposed to have existed. |
42 | | - * If this is specified, the returned object will be an |
43 | | - * instance of the repository's old file class instead of |
44 | | - * a current file. Repositories not supporting version |
45 | | - * control should return false if this parameter is set. |
46 | | - */ |
47 | | - function newFile( $title, $time = false ) { |
48 | | - if ( !($title instanceof Title) ) { |
49 | | - $title = Title::makeTitleSafe( NS_IMAGE, $title ); |
50 | | - if ( !is_object( $title ) ) { |
51 | | - return null; |
52 | | - } |
53 | | - } |
54 | | - if ( $time ) { |
55 | | - if ( $this->oldFileFactory ) { |
56 | | - return call_user_func( $this->oldFileFactory, $title, $this, $time ); |
57 | | - } else { |
58 | | - return false; |
59 | | - } |
60 | | - } else { |
61 | | - return call_user_func( $this->fileFactory, $title, $this ); |
62 | | - } |
63 | | - } |
64 | | - |
65 | | - /** |
66 | | - * Find an instance of the named file that existed at the specified time |
67 | | - * Returns false if the file did not exist. Repositories not supporting |
68 | | - * version control should return false if the time is specified. |
69 | | - * |
70 | | - * @param mixed $time 14-character timestamp, or false for the current version |
71 | | - */ |
72 | | - function findFile( $title, $time = false ) { |
73 | | - # First try the current version of the file to see if it precedes the timestamp |
74 | | - $img = $this->newFile( $title ); |
75 | | - if ( !$img ) { |
76 | | - return false; |
77 | | - } |
78 | | - if ( $img->exists() && ( !$time || $img->getTimestamp() <= $time ) ) { |
79 | | - return $img; |
80 | | - } |
81 | | - # Now try an old version of the file |
82 | | - $img = $this->newFile( $title, $time ); |
83 | | - if ( $img->exists() ) { |
84 | | - return $img; |
85 | | - } |
86 | | - } |
87 | | - |
88 | | - /** |
89 | 24 | * Get the public root directory of the repository. |
90 | 25 | */ |
91 | 26 | function getRootDirectory() { |
— | — | @@ -106,20 +41,6 @@ |
107 | 42 | } |
108 | 43 | |
109 | 44 | /** |
110 | | - * Get the URL of thumb.php |
111 | | - */ |
112 | | - function getThumbScriptUrl() { |
113 | | - return $this->thumbScriptUrl; |
114 | | - } |
115 | | - |
116 | | - /** |
117 | | - * Returns true if the repository can transform files via a 404 handler |
118 | | - */ |
119 | | - function canTransformVia404() { |
120 | | - return $this->transformVia404; |
121 | | - } |
122 | | - |
123 | | - /** |
124 | 45 | * Get the local directory corresponding to one of the three basic zones |
125 | 46 | */ |
126 | 47 | function getZonePath( $zone ) { |
— | — | @@ -153,11 +74,13 @@ |
154 | 75 | |
155 | 76 | /** |
156 | 77 | * Get a URL referring to this repository, with the private mwrepo protocol. |
| 78 | + * The suffix, if supplied, is considered to be unencoded, and will be |
| 79 | + * URL-encoded before being returned. |
157 | 80 | */ |
158 | 81 | function getVirtualUrl( $suffix = false ) { |
159 | | - $path = 'mwrepo://'; |
| 82 | + $path = 'mwrepo://' . $this->name; |
160 | 83 | if ( $suffix !== false ) { |
161 | | - $path .= '/' . $suffix; |
| 84 | + $path .= '/' . rawurlencode( $suffix ); |
162 | 85 | } |
163 | 86 | return $path; |
164 | 87 | } |
— | — | @@ -174,21 +97,24 @@ |
175 | 98 | if ( count( $bits ) != 3 ) { |
176 | 99 | throw new MWException( __METHOD__.": invalid mwrepo URL: $url" ); |
177 | 100 | } |
178 | | - list( $host, $zone, $rel ) = $bits; |
179 | | - if ( $host !== '' ) { |
| 101 | + list( $repo, $zone, $rel ) = $bits; |
| 102 | + if ( $repo !== $this->name ) { |
180 | 103 | throw new MWException( __METHOD__.": fetching from a foreign repo is not supported" ); |
181 | 104 | } |
182 | 105 | $base = $this->getZonePath( $zone ); |
183 | 106 | if ( !$base ) { |
184 | 107 | throw new MWException( __METHOD__.": invalid zone: $zone" ); |
185 | 108 | } |
186 | | - return $base . '/' . urldecode( $rel ); |
| 109 | + return $base . '/' . rawurldecode( $rel ); |
187 | 110 | } |
188 | 111 | |
189 | 112 | /** |
190 | 113 | * Store a file to a given destination. |
191 | 114 | */ |
192 | 115 | function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) { |
| 116 | + if ( !is_writable( $this->directory ) ) { |
| 117 | + return new WikiErrorMsg( 'upload_directory_read_only', wfEscapeWikiText( $this->directory ) ); |
| 118 | + } |
193 | 119 | $root = $this->getZonePath( $dstZone ); |
194 | 120 | if ( !$root ) { |
195 | 121 | throw new MWException( "Invalid zone: $dstZone" ); |
— | — | @@ -198,8 +124,8 @@ |
199 | 125 | if ( !is_dir( dirname( $dstPath ) ) ) { |
200 | 126 | wfMkdirParents( dirname( $dstPath ) ); |
201 | 127 | } |
202 | | - |
203 | | - if ( substr( $srcPath, 0, 9 ) == 'mwrepo://' ) { |
| 128 | + |
| 129 | + if ( self::isVirtualUrl( $srcPath ) ) { |
204 | 130 | $srcPath = $this->resolveVirtualUrl( $srcPath ); |
205 | 131 | } |
206 | 132 | |
— | — | @@ -245,7 +171,7 @@ |
246 | 172 | * @return boolean True on success, false on failure |
247 | 173 | */ |
248 | 174 | function freeTemp( $virtualUrl ) { |
249 | | - $temp = 'mwrepo:///temp'; |
| 175 | + $temp = "mwrepo://{$this->name}/temp"; |
250 | 176 | if ( substr( $virtualUrl, 0, strlen( $temp ) ) != $temp ) { |
251 | 177 | wfDebug( __METHOD__.": Invalid virtual URL\n" ); |
252 | 178 | return false; |
— | — | @@ -263,16 +189,28 @@ |
264 | 190 | * virtual URL, into this repository at the specified destination location. |
265 | 191 | * |
266 | 192 | * @param string $srcPath The source path or URL |
267 | | - * @param string $dstPath The destination relative path |
268 | | - * @param string $archivePath The relative path where the existing file is to |
269 | | - * be archived, if there is one. |
270 | | - * @param integer $flags Bitfield, may be FSRepo::DELETE_SOURCE to indicate |
| 193 | + * @param string $dstRel The destination relative path |
| 194 | + * @param string $archiveRel The relative path where the existing file is to |
| 195 | + * be archived, if there is one. Relative to the public zone root. |
| 196 | + * @param integer $flags Bitfield, may be FileRepo::DELETE_SOURCE to indicate |
271 | 197 | * that the source file should be deleted if possible |
272 | 198 | */ |
273 | | - function publish( $srcPath, $dstPath, $archivePath, $flags = 0 ) { |
| 199 | + function publish( $srcPath, $dstRel, $archiveRel, $flags = 0 ) { |
| 200 | + if ( !is_writable( $this->directory ) ) { |
| 201 | + return new WikiErrorMsg( 'upload_directory_read_only', wfEscapeWikiText( $this->directory ) ); |
| 202 | + } |
274 | 203 | if ( substr( $srcPath, 0, 9 ) == 'mwrepo://' ) { |
275 | 204 | $srcPath = $this->resolveVirtualUrl( $srcPath ); |
276 | 205 | } |
| 206 | + if ( !$this->validateFilename( $dstRel ) ) { |
| 207 | + throw new MWException( 'Validation error in $dstRel' ); |
| 208 | + } |
| 209 | + if ( !$this->validateFilename( $archiveRel ) ) { |
| 210 | + throw new MWException( 'Validation error in $archiveRel' ); |
| 211 | + } |
| 212 | + $dstPath = "{$this->directory}/$dstRel"; |
| 213 | + $archivePath = "{$this->directory}/$archiveRel"; |
| 214 | + |
277 | 215 | $dstDir = dirname( $dstPath ); |
278 | 216 | if ( !is_dir( $dstDir ) ) wfMkdirParents( $dstDir ); |
279 | 217 | |
— | — | @@ -324,84 +262,10 @@ |
325 | 263 | * If the repo is not hashed, returns an empty string |
326 | 264 | */ |
327 | 265 | function getHashPath( $name ) { |
328 | | - if ( $this->isHashed() ) { |
329 | | - $hash = md5( $name ); |
330 | | - $path = ''; |
331 | | - for ( $i = 1; $i <= $this->hashLevels; $i++ ) { |
332 | | - $path .= substr( $hash, 0, $i ) . '/'; |
333 | | - } |
334 | | - return $path; |
335 | | - } else { |
336 | | - return ''; |
337 | | - } |
| 266 | + return FileRepo::getHashPathForLevel( $name, $this->hashLevels ); |
338 | 267 | } |
339 | 268 | |
340 | 269 | /** |
341 | | - * Get the name of this repository, as specified by $info['name]' to the constructor |
342 | | - */ |
343 | | - function getName() { |
344 | | - return $this->name; |
345 | | - } |
346 | | - |
347 | | - /** |
348 | | - * Get the file description page base URL, or false if there isn't one. |
349 | | - * @private |
350 | | - */ |
351 | | - function getDescBaseUrl() { |
352 | | - if ( is_null( $this->descBaseUrl ) ) { |
353 | | - if ( !is_null( $this->articleUrl ) ) { |
354 | | - $this->descBaseUrl = str_replace( '$1', |
355 | | - urlencode( Namespace::getCanonicalName( NS_IMAGE ) ) . ':', $this->articleUrl ); |
356 | | - } elseif ( !is_null( $this->scriptDirUrl ) ) { |
357 | | - $this->descBaseUrl = $this->scriptDirUrl . '/index.php?title=' . |
358 | | - urlencode( Namespace::getCanonicalName( NS_IMAGE ) ) . ':'; |
359 | | - } else { |
360 | | - $this->descBaseUrl = false; |
361 | | - } |
362 | | - } |
363 | | - return $this->descBaseUrl; |
364 | | - } |
365 | | - |
366 | | - /** |
367 | | - * Get the URL of an image description page. May return false if it is |
368 | | - * unknown or not applicable. In general this should only be called by the |
369 | | - * File class, since it may return invalid results for certain kinds of |
370 | | - * repositories. Use File::getDescriptionUrl() in user code. |
371 | | - * |
372 | | - * In particular, it uses the article paths as specified to the repository |
373 | | - * constructor, whereas local repositories use the local Title functions. |
374 | | - */ |
375 | | - function getDescriptionUrl( $name ) { |
376 | | - $base = $this->getDescBaseUrl(); |
377 | | - if ( $base ) { |
378 | | - return $base . wfUrlencode( $name ); |
379 | | - } else { |
380 | | - return false; |
381 | | - } |
382 | | - } |
383 | | - |
384 | | - /** |
385 | | - * Get the URL of the content-only fragment of the description page. For |
386 | | - * MediaWiki this means action=render. This should only be called by the |
387 | | - * repository's file class, since it may return invalid results. User code |
388 | | - * should use File::getDescriptionText(). |
389 | | - */ |
390 | | - function getDescriptionRenderUrl( $name ) { |
391 | | - if ( isset( $this->scriptDirUrl ) ) { |
392 | | - return $this->scriptDirUrl . '/index.php?title=' . |
393 | | - wfUrlencode( Namespace::getCanonicalName( NS_IMAGE ) . ':' . $name ) . |
394 | | - '&action=render'; |
395 | | - } else { |
396 | | - $descBase = $this->getDescBaseUrl(); |
397 | | - if ( $descBase ) { |
398 | | - return wfAppendQuery( $descBase . wfUrlencode( $name ), 'action=render' ); |
399 | | - } else { |
400 | | - return false; |
401 | | - } |
402 | | - } |
403 | | - } |
404 | | - |
405 | | - /** |
406 | 270 | * Call a callback function for every file in the repository. |
407 | 271 | * Uses the filesystem even in child classes. |
408 | 272 | */ |
— | — | @@ -424,7 +288,7 @@ |
425 | 289 | } |
426 | 290 | |
427 | 291 | /** |
428 | | - * Call a callaback function for every file in the repository |
| 292 | + * Call a callback function for every file in the repository |
429 | 293 | * May use either the database or the filesystem |
430 | 294 | */ |
431 | 295 | function enumFiles( $callback ) { |
— | — | @@ -432,20 +296,12 @@ |
433 | 297 | } |
434 | 298 | |
435 | 299 | /** |
436 | | - * Get the name of an image from its title object |
| 300 | + * Get properties of a file with a given virtual URL |
| 301 | + * The virtual URL must refer to this repo |
437 | 302 | */ |
438 | | - function getNameFromTitle( $title ) { |
439 | | - global $wgCapitalLinks; |
440 | | - if ( $this->initialCapital != $wgCapitalLinks ) { |
441 | | - global $wgContLang; |
442 | | - $name = $title->getUserCaseDBKey(); |
443 | | - if ( $this->initialCapital ) { |
444 | | - $name = $wgContLang->ucfirst( $name ); |
445 | | - } |
446 | | - } else { |
447 | | - $name = $title->getDBkey(); |
448 | | - } |
449 | | - return $name; |
| 303 | + function getFileProps( $virtualUrl ) { |
| 304 | + $path = $this->resolveVirtualUrl( $virtualUrl ); |
| 305 | + return File::getPropsFromPath( $path ); |
450 | 306 | } |
451 | 307 | } |
452 | 308 | |
Index: trunk/phase3/includes/filerepo/File.php |
— | — | @@ -503,7 +503,7 @@ |
504 | 504 | break; |
505 | 505 | } |
506 | 506 | |
507 | | - wfDebug( "Doing stat for $thumbPath\n" ); |
| 507 | + wfDebug( __METHOD__.": Doing stat for $thumbPath\n" ); |
508 | 508 | $this->migrateThumbFile( $thumbName ); |
509 | 509 | if ( file_exists( $thumbPath ) ) { |
510 | 510 | $thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); |
— | — | @@ -663,7 +663,7 @@ |
664 | 664 | * Get urlencoded relative path of the file |
665 | 665 | */ |
666 | 666 | function getUrlRel() { |
667 | | - return $this->getHashPath() . urlencode( $this->getName() ); |
| 667 | + return $this->getHashPath() . rawurlencode( $this->getName() ); |
668 | 668 | } |
669 | 669 | |
670 | 670 | /** Get the path of the archive directory, or a particular file if $suffix is specified */ |
— | — | @@ -692,7 +692,7 @@ |
693 | 693 | if ( $suffix === false ) { |
694 | 694 | $path = substr( $path, 0, -1 ); |
695 | 695 | } else { |
696 | | - $path .= urlencode( $suffix ); |
| 696 | + $path .= rawurlencode( $suffix ); |
697 | 697 | } |
698 | 698 | return $path; |
699 | 699 | } |
— | — | @@ -701,7 +701,7 @@ |
702 | 702 | function getThumbUrl( $suffix = false ) { |
703 | 703 | $path = $this->repo->getZoneUrl('public') . '/thumb/' . $this->getUrlRel(); |
704 | 704 | if ( $suffix !== false ) { |
705 | | - $path .= '/' . urlencode( $suffix ); |
| 705 | + $path .= '/' . rawurlencode( $suffix ); |
706 | 706 | } |
707 | 707 | return $path; |
708 | 708 | } |
— | — | @@ -712,7 +712,7 @@ |
713 | 713 | if ( $suffix === false ) { |
714 | 714 | $path = substr( $path, 0, -1 ); |
715 | 715 | } else { |
716 | | - $path .= urlencode( $suffix ); |
| 716 | + $path .= rawurlencode( $suffix ); |
717 | 717 | } |
718 | 718 | return $path; |
719 | 719 | } |
— | — | @@ -721,11 +721,20 @@ |
722 | 722 | function getThumbVirtualUrl( $suffix = false ) { |
723 | 723 | $path = $this->repo->getVirtualUrl() . '/public/thumb/' . $this->getUrlRel(); |
724 | 724 | if ( $suffix !== false ) { |
725 | | - $path .= '/' . urlencode( $suffix ); |
| 725 | + $path .= '/' . rawurlencode( $suffix ); |
726 | 726 | } |
727 | 727 | return $path; |
728 | 728 | } |
729 | 729 | |
| 730 | + /** Get the virtual URL for the file itself */ |
| 731 | + function getVirtualUrl( $suffix = false ) { |
| 732 | + $path = $this->repo->getVirtualUrl() . '/public/' . $this->getUrlRel(); |
| 733 | + if ( $suffix !== false ) { |
| 734 | + $path .= '/' . rawurlencode( $suffix ); |
| 735 | + } |
| 736 | + return $path; |
| 737 | + } |
| 738 | + |
730 | 739 | /** |
731 | 740 | * @return bool |
732 | 741 | */ |
— | — | @@ -990,6 +999,59 @@ |
991 | 1000 | function userCan( $field ) { |
992 | 1001 | return true; |
993 | 1002 | } |
| 1003 | + |
| 1004 | + /** |
| 1005 | + * Get an associative array containing information about a file in the local filesystem |
| 1006 | + */ |
| 1007 | + static function getPropsFromPath( $path ) { |
| 1008 | + wfProfileIn( __METHOD__ ); |
| 1009 | + wfDebug( __METHOD__.": Getting file info for $path\n" ); |
| 1010 | + $info = array( 'fileExists' => file_exists( $path ) ); |
| 1011 | + $gis = false; |
| 1012 | + if ( $info['fileExists'] ) { |
| 1013 | + $magic = MimeMagic::singleton(); |
| 1014 | + |
| 1015 | + $info['mime'] = $magic->guessMimeType( $path, true ); |
| 1016 | + list( $info['major_mime'], $info['minor_mime'] ) = self::splitMime( $info['mime'] ); |
| 1017 | + $info['media_type'] = $magic->getMediaType( $path, $info['mime'] ); |
| 1018 | + |
| 1019 | + # Get size in bytes |
| 1020 | + $info['size'] = filesize( $path ); |
| 1021 | + |
| 1022 | + # Height, width and metadata |
| 1023 | + $handler = MediaHandler::getHandler( $info['mime'] ); |
| 1024 | + if ( $handler ) { |
| 1025 | + $tempImage = (object)array(); |
| 1026 | + $gis = $handler->getImageSize( $tempImage, $path ); |
| 1027 | + $info['metadata'] = $handler->getMetadata( $tempImage, $path ); |
| 1028 | + } else { |
| 1029 | + $gis = false; |
| 1030 | + $info['metadata'] = ''; |
| 1031 | + } |
| 1032 | + |
| 1033 | + wfDebug(__METHOD__.": $path loaded, {$info['size']} bytes, {$info['mime']}.\n"); |
| 1034 | + } else { |
| 1035 | + $info['mime'] = NULL; |
| 1036 | + $info['media_type'] = MEDIATYPE_UNKNOWN; |
| 1037 | + $info['metadata'] = ''; |
| 1038 | + wfDebug(__METHOD__.": $path NOT FOUND!\n"); |
| 1039 | + } |
| 1040 | + if( $gis ) { |
| 1041 | + # NOTE: $gis[2] contains a code for the image type. This is no longer used. |
| 1042 | + $info['width'] = $gis[0]; |
| 1043 | + $info['height'] = $gis[1]; |
| 1044 | + if ( isset( $gis['bits'] ) ) { |
| 1045 | + $info['bits'] = $gis['bits']; |
| 1046 | + } else { |
| 1047 | + $info['bits'] = 0; |
| 1048 | + } |
| 1049 | + } else { |
| 1050 | + $info['width'] = 0; |
| 1051 | + $info['height'] = 0; |
| 1052 | + } |
| 1053 | + wfProfileOut( __METHOD__ ); |
| 1054 | + return $info; |
| 1055 | + } |
994 | 1056 | } |
995 | 1057 | |
996 | 1058 | ?> |
Index: trunk/phase3/includes/filerepo/RepoGroup.php |
— | — | @@ -116,6 +116,35 @@ |
117 | 117 | $class = $info['class']; |
118 | 118 | return new $class( $info ); |
119 | 119 | } |
| 120 | + |
| 121 | + /** |
| 122 | + * Split a virtual URL into repo, zone and rel parts |
| 123 | + * @return an array containing repo, zone and rel |
| 124 | + */ |
| 125 | + function splitVirtualUrl( $url ) { |
| 126 | + if ( substr( $url, 0, 9 ) != 'mwrepo://' ) { |
| 127 | + throw new MWException( __METHOD__.': unknown protoocl' ); |
| 128 | + } |
| 129 | + |
| 130 | + $bits = explode( '/', substr( $url, 9 ), 3 ); |
| 131 | + if ( count( $bits ) != 3 ) { |
| 132 | + throw new MWException( __METHOD__.": invalid mwrepo URL: $url" ); |
| 133 | + } |
| 134 | + return $bits; |
| 135 | + } |
| 136 | + |
| 137 | + function getFileProps( $fileName ) { |
| 138 | + if ( FileRepo::isVirtualUrl( $fileName ) ) { |
| 139 | + list( $repoName, $zone, $rel ) = $this->splitVirtualUrl( $fileName ); |
| 140 | + if ( $repoName === '' ) { |
| 141 | + $repoName = 'local'; |
| 142 | + } |
| 143 | + $repo = $this->getRepo( $repoName ); |
| 144 | + return $repo->getFileProps( $fileName ); |
| 145 | + } else { |
| 146 | + return File::getPropsFromPath( $fileName ); |
| 147 | + } |
| 148 | + } |
120 | 149 | } |
121 | 150 | |
122 | 151 | ?> |
Index: trunk/phase3/includes/filerepo/FileRepo.php |
— | — | @@ -0,0 +1,280 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Base class for file repositories |
| 6 | + * Do not instantiate, use a derived class. |
| 7 | + */ |
| 8 | +abstract class FileRepo { |
| 9 | + const DELETE_SOURCE = 1; |
| 10 | + |
| 11 | + var $thumbScriptUrl, $transformVia404; |
| 12 | + var $descBaseUrl, $scriptDirUrl, $articleUrl, $fetchDescription, $initialCapital; |
| 13 | + |
| 14 | + /** |
| 15 | + * Factory functions for creating new files |
| 16 | + * Override these in the base class |
| 17 | + */ |
| 18 | + var $fileFactory = false, $oldFileFactory = false; |
| 19 | + |
| 20 | + function __construct( $info ) { |
| 21 | + // Required settings |
| 22 | + $this->name = $info['name']; |
| 23 | + |
| 24 | + // Optional settings |
| 25 | + $this->initialCapital = true; // by default |
| 26 | + foreach ( array( 'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription', |
| 27 | + 'thumbScriptUrl', 'initialCapital' ) as $var ) |
| 28 | + { |
| 29 | + if ( isset( $info[$var] ) ) { |
| 30 | + $this->$var = $info[$var]; |
| 31 | + } |
| 32 | + } |
| 33 | + $this->transformVia404 = !empty( $info['transformVia404'] ); |
| 34 | + } |
| 35 | + |
| 36 | + /** |
| 37 | + * Determine if a string is an mwrepo:// URL |
| 38 | + */ |
| 39 | + static function isVirtualUrl( $url ) { |
| 40 | + return substr( $url, 0, 9 ) == 'mwrepo://'; |
| 41 | + } |
| 42 | + |
| 43 | + /** |
| 44 | + * Create a new File object from the local repository |
| 45 | + * @param mixed $title Title object or string |
| 46 | + * @param mixed $time Time at which the image is supposed to have existed. |
| 47 | + * If this is specified, the returned object will be an |
| 48 | + * instance of the repository's old file class instead of |
| 49 | + * a current file. Repositories not supporting version |
| 50 | + * control should return false if this parameter is set. |
| 51 | + */ |
| 52 | + function newFile( $title, $time = false ) { |
| 53 | + if ( !($title instanceof Title) ) { |
| 54 | + $title = Title::makeTitleSafe( NS_IMAGE, $title ); |
| 55 | + if ( !is_object( $title ) ) { |
| 56 | + return null; |
| 57 | + } |
| 58 | + } |
| 59 | + if ( $time ) { |
| 60 | + if ( $this->oldFileFactory ) { |
| 61 | + return call_user_func( $this->oldFileFactory, $title, $this, $time ); |
| 62 | + } else { |
| 63 | + return false; |
| 64 | + } |
| 65 | + } else { |
| 66 | + return call_user_func( $this->fileFactory, $title, $this ); |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * Find an instance of the named file that existed at the specified time |
| 72 | + * Returns false if the file did not exist. Repositories not supporting |
| 73 | + * version control should return false if the time is specified. |
| 74 | + * |
| 75 | + * @param mixed $time 14-character timestamp, or false for the current version |
| 76 | + */ |
| 77 | + function findFile( $title, $time = false ) { |
| 78 | + # First try the current version of the file to see if it precedes the timestamp |
| 79 | + $img = $this->newFile( $title ); |
| 80 | + if ( !$img ) { |
| 81 | + return false; |
| 82 | + } |
| 83 | + if ( $img->exists() && ( !$time || $img->getTimestamp() <= $time ) ) { |
| 84 | + return $img; |
| 85 | + } |
| 86 | + # Now try an old version of the file |
| 87 | + $img = $this->newFile( $title, $time ); |
| 88 | + if ( $img->exists() ) { |
| 89 | + return $img; |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + /** |
| 94 | + * Get the URL of thumb.php |
| 95 | + */ |
| 96 | + function getThumbScriptUrl() { |
| 97 | + return $this->thumbScriptUrl; |
| 98 | + } |
| 99 | + |
| 100 | + /** |
| 101 | + * Returns true if the repository can transform files via a 404 handler |
| 102 | + */ |
| 103 | + function canTransformVia404() { |
| 104 | + return $this->transformVia404; |
| 105 | + } |
| 106 | + |
| 107 | + /** |
| 108 | + * Get the name of an image from its title object |
| 109 | + */ |
| 110 | + function getNameFromTitle( $title ) { |
| 111 | + global $wgCapitalLinks; |
| 112 | + if ( $this->initialCapital != $wgCapitalLinks ) { |
| 113 | + global $wgContLang; |
| 114 | + $name = $title->getUserCaseDBKey(); |
| 115 | + if ( $this->initialCapital ) { |
| 116 | + $name = $wgContLang->ucfirst( $name ); |
| 117 | + } |
| 118 | + } else { |
| 119 | + $name = $title->getDBkey(); |
| 120 | + } |
| 121 | + return $name; |
| 122 | + } |
| 123 | + |
| 124 | + static function getHashPathForLevel( $name, $levels ) { |
| 125 | + if ( $levels == 0 ) { |
| 126 | + return ''; |
| 127 | + } else { |
| 128 | + $hash = md5( $name ); |
| 129 | + $path = ''; |
| 130 | + for ( $i = 1; $i <= $levels; $i++ ) { |
| 131 | + $path .= substr( $hash, 0, $i ) . '/'; |
| 132 | + } |
| 133 | + return $path; |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + /** |
| 138 | + * Get the name of this repository, as specified by $info['name]' to the constructor |
| 139 | + */ |
| 140 | + function getName() { |
| 141 | + return $this->name; |
| 142 | + } |
| 143 | + |
| 144 | + /** |
| 145 | + * Get the file description page base URL, or false if there isn't one. |
| 146 | + * @private |
| 147 | + */ |
| 148 | + function getDescBaseUrl() { |
| 149 | + if ( is_null( $this->descBaseUrl ) ) { |
| 150 | + if ( !is_null( $this->articleUrl ) ) { |
| 151 | + $this->descBaseUrl = str_replace( '$1', |
| 152 | + wfUrlencode( Namespace::getCanonicalName( NS_IMAGE ) ) . ':', $this->articleUrl ); |
| 153 | + } elseif ( !is_null( $this->scriptDirUrl ) ) { |
| 154 | + $this->descBaseUrl = $this->scriptDirUrl . '/index.php?title=' . |
| 155 | + wfUrlencode( Namespace::getCanonicalName( NS_IMAGE ) ) . ':'; |
| 156 | + } else { |
| 157 | + $this->descBaseUrl = false; |
| 158 | + } |
| 159 | + } |
| 160 | + return $this->descBaseUrl; |
| 161 | + } |
| 162 | + |
| 163 | + /** |
| 164 | + * Get the URL of an image description page. May return false if it is |
| 165 | + * unknown or not applicable. In general this should only be called by the |
| 166 | + * File class, since it may return invalid results for certain kinds of |
| 167 | + * repositories. Use File::getDescriptionUrl() in user code. |
| 168 | + * |
| 169 | + * In particular, it uses the article paths as specified to the repository |
| 170 | + * constructor, whereas local repositories use the local Title functions. |
| 171 | + */ |
| 172 | + function getDescriptionUrl( $name ) { |
| 173 | + $base = $this->getDescBaseUrl(); |
| 174 | + if ( $base ) { |
| 175 | + return $base . wfUrlencode( $name ); |
| 176 | + } else { |
| 177 | + return false; |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + /** |
| 182 | + * Get the URL of the content-only fragment of the description page. For |
| 183 | + * MediaWiki this means action=render. This should only be called by the |
| 184 | + * repository's file class, since it may return invalid results. User code |
| 185 | + * should use File::getDescriptionText(). |
| 186 | + */ |
| 187 | + function getDescriptionRenderUrl( $name ) { |
| 188 | + if ( isset( $this->scriptDirUrl ) ) { |
| 189 | + return $this->scriptDirUrl . '/index.php?title=' . |
| 190 | + wfUrlencode( Namespace::getCanonicalName( NS_IMAGE ) . ':' . $name ) . |
| 191 | + '&action=render'; |
| 192 | + } else { |
| 193 | + $descBase = $this->getDescBaseUrl(); |
| 194 | + if ( $descBase ) { |
| 195 | + return wfAppendQuery( $descBase . wfUrlencode( $name ), 'action=render' ); |
| 196 | + } else { |
| 197 | + return false; |
| 198 | + } |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + /** |
| 203 | + * Store a file to a given destination. |
| 204 | + */ |
| 205 | + abstract function store( $srcPath, $dstZone, $dstRel, $flags = 0 ); |
| 206 | + |
| 207 | + /** |
| 208 | + * Pick a random name in the temp zone and store a file to it. |
| 209 | + * Returns the URL, or a WikiError on failure. |
| 210 | + * @param string $originalName The base name of the file as specified |
| 211 | + * by the user. The file extension will be maintained. |
| 212 | + * @param string $srcPath The current location of the file. |
| 213 | + */ |
| 214 | + abstract function storeTemp( $originalName, $srcPath ); |
| 215 | + |
| 216 | + /** |
| 217 | + * Remove a temporary file or mark it for garbage collection |
| 218 | + * @param string $virtualUrl The virtual URL returned by storeTemp |
| 219 | + * @return boolean True on success, false on failure |
| 220 | + * STUB |
| 221 | + */ |
| 222 | + function freeTemp( $virtualUrl ) { |
| 223 | + return true; |
| 224 | + } |
| 225 | + |
| 226 | + /** |
| 227 | + * Copy or move a file either from the local filesystem or from an mwrepo:// |
| 228 | + * virtual URL, into this repository at the specified destination location. |
| 229 | + * |
| 230 | + * @param string $srcPath The source path or URL |
| 231 | + * @param string $dstRel The destination relative path |
| 232 | + * @param string $archiveRel The relative path where the existing file is to |
| 233 | + * be archived, if there is one. Relative to the public zone root. |
| 234 | + * @param integer $flags Bitfield, may be FileRepo::DELETE_SOURCE to indicate |
| 235 | + * that the source file should be deleted if possible |
| 236 | + */ |
| 237 | + abstract function publish( $srcPath, $dstRel, $archiveRel, $flags = 0 ); |
| 238 | + |
| 239 | + /** |
| 240 | + * Get properties of a file with a given virtual URL |
| 241 | + * The virtual URL must refer to this repo |
| 242 | + * Properties should ultimately be obtained via File::getPropsFromPath() |
| 243 | + */ |
| 244 | + abstract function getFileProps( $virtualUrl ); |
| 245 | + |
| 246 | + /** |
| 247 | + * Call a callback function for every file in the repository |
| 248 | + * May use either the database or the filesystem |
| 249 | + * STUB |
| 250 | + */ |
| 251 | + function enumFiles( $callback ) { |
| 252 | + throw new MWException( 'enumFiles is not supported by ' . get_class( $this ) ); |
| 253 | + } |
| 254 | + |
| 255 | + /** |
| 256 | + * Determine if a relative path is valid, i.e. not blank or involving directory traveral |
| 257 | + */ |
| 258 | + function validateFilename( $filename ) { |
| 259 | + if ( strval( $filename ) == '' ) { |
| 260 | + return false; |
| 261 | + } |
| 262 | + if ( wfIsWindows() ) { |
| 263 | + $filename = strtr( $filename, '\\', '/' ); |
| 264 | + } |
| 265 | + /** |
| 266 | + * Use the same traversal protection as Title::secureAndSplit() |
| 267 | + */ |
| 268 | + if ( strpos( $filename, '.' ) !== false && |
| 269 | + ( $filename === '.' || $filename === '..' || |
| 270 | + strpos( $filename, './' ) === 0 || |
| 271 | + strpos( $filename, '../' ) === 0 || |
| 272 | + strpos( $filename, '/./' ) !== false || |
| 273 | + strpos( $filename, '/../' ) !== false ) ) |
| 274 | + { |
| 275 | + return false; |
| 276 | + } else { |
| 277 | + return true; |
| 278 | + } |
| 279 | + } |
| 280 | +} |
| 281 | +?> |
Property changes on: trunk/phase3/includes/filerepo/FileRepo.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 282 | + native |
Index: trunk/phase3/includes/filerepo/ForeignDBRepo.php |
— | — | @@ -43,7 +43,7 @@ |
44 | 44 | return $this->hasSharedCache; |
45 | 45 | } |
46 | 46 | |
47 | | - function store( /*...*/ ) { |
| 47 | + function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) { |
48 | 48 | throw new MWException( get_class($this) . ': write operations are not supported' ); |
49 | 49 | } |
50 | 50 | } |
Index: trunk/phase3/includes/SpecialUpload.php |
— | — | @@ -22,18 +22,19 @@ |
23 | 23 | /**#@+ |
24 | 24 | * @access private |
25 | 25 | */ |
26 | | - var $mUploadFile, $mUploadDescription, $mLicense ,$mIgnoreWarning, $mUploadError; |
27 | | - var $mUploadSaveName, $mUploadTempName, $mUploadSize, $mUploadOldVersion; |
28 | | - var $mUploadCopyStatus, $mUploadSource, $mReUpload, $mAction, $mUpload; |
29 | | - var $mOname, $mSessionKey, $mStashed, $mDestFile, $mRemoveTempFile, $mSourceType; |
30 | | - var $mUploadTempFileSize = 0; |
31 | | - var $mImage; |
| 26 | + var $mComment, $mLicense, $mIgnoreWarning, $mCurlError; |
| 27 | + var $mDestName, $mTempPath, $mFileSize, $mFileProps; |
| 28 | + var $mCopyrightStatus, $mCopyrightSource, $mReUpload, $mAction, $mUploadClicked; |
| 29 | + var $mSrcName, $mSessionKey, $mStashed, $mDesiredDestName, $mRemoveTempFile, $mSourceType; |
| 30 | + var $mCurlDestHandle; |
| 31 | + var $mLocalFile; |
32 | 32 | |
33 | 33 | # Placeholders for text injection by hooks (must be HTML) |
34 | 34 | # extensions should take care to _append_ to the present value |
35 | 35 | var $uploadFormTextTop; |
36 | 36 | var $uploadFormTextAfterSummary; |
37 | 37 | |
| 38 | + const SESSION_VERSION = 1; |
38 | 39 | /**#@-*/ |
39 | 40 | |
40 | 41 | /** |
— | — | @@ -43,7 +44,7 @@ |
44 | 45 | */ |
45 | 46 | function UploadForm( &$request ) { |
46 | 47 | global $wgAllowCopyUploads; |
47 | | - $this->mDestFile = $request->getText( 'wpDestFile' ); |
| 48 | + $this->mDesiredDestName = $request->getText( 'wpDestFile' ); |
48 | 49 | |
49 | 50 | if( !$request->wasPosted() ) { |
50 | 51 | # GET requests just give the main form; no data except wpDestfile. |
— | — | @@ -56,21 +57,22 @@ |
57 | 58 | |
58 | 59 | $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ); |
59 | 60 | $this->mReUpload = $request->getCheck( 'wpReUpload' ); |
60 | | - $this->mUpload = $request->getCheck( 'wpUpload' ); |
| 61 | + $this->mUploadClicked = $request->getCheck( 'wpUpload' ); |
61 | 62 | |
62 | | - $this->mUploadDescription = $request->getText( 'wpUploadDescription' ); |
| 63 | + $this->mComment = $request->getText( 'wpUploadDescription' ); |
63 | 64 | $this->mLicense = $request->getText( 'wpLicense' ); |
64 | | - $this->mUploadCopyStatus = $request->getText( 'wpUploadCopyStatus' ); |
65 | | - $this->mUploadSource = $request->getText( 'wpUploadSource' ); |
| 65 | + $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' ); |
| 66 | + $this->mCopyrightSource = $request->getText( 'wpUploadSource' ); |
66 | 67 | $this->mWatchthis = $request->getBool( 'wpWatchthis' ); |
67 | | - $this->mSourceType = $request->getText( 'wpSourceType' ); |
| 68 | + $this->mSourceType = $request->getText( 'wpSourceType' ); |
68 | 69 | wfDebug( "UploadForm: watchthis is: '$this->mWatchthis'\n" ); |
69 | 70 | |
70 | 71 | $this->mAction = $request->getVal( 'action' ); |
71 | 72 | |
72 | 73 | $this->mSessionKey = $request->getInt( 'wpSessionKey' ); |
73 | 74 | if( !empty( $this->mSessionKey ) && |
74 | | - isset( $_SESSION['wsUploadData'][$this->mSessionKey] ) ) { |
| 75 | + isset( $_SESSION['wsUploadData'][$this->mSessionKey]['version'] ) && |
| 76 | + $_SESSION['wsUploadData'][$this->mSessionKey]['version'] == self::SESSION_VERSION ) { |
75 | 77 | /** |
76 | 78 | * Confirming a temporarily stashed upload. |
77 | 79 | * We don't want path names to be forged, so we keep |
— | — | @@ -78,10 +80,11 @@ |
79 | 81 | * an opaque key to the user agent. |
80 | 82 | */ |
81 | 83 | $data = $_SESSION['wsUploadData'][$this->mSessionKey]; |
82 | | - $this->mUploadTempName = $data['mUploadTempName']; |
83 | | - $this->mUploadSize = $data['mUploadSize']; |
84 | | - $this->mOname = $data['mOname']; |
85 | | - $this->mUploadError = 0/*UPLOAD_ERR_OK*/; |
| 84 | + $this->mTempPath = $data['mTempPath']; |
| 85 | + $this->mFileSize = $data['mFileSize']; |
| 86 | + $this->mSrcName = $data['mSrcName']; |
| 87 | + $this->mFileProps = $data['mFileProps']; |
| 88 | + $this->mCurlError = 0/*UPLOAD_ERR_OK*/; |
86 | 89 | $this->mStashed = true; |
87 | 90 | $this->mRemoveTempFile = false; |
88 | 91 | } else { |
— | — | @@ -101,10 +104,11 @@ |
102 | 105 | * @access private |
103 | 106 | */ |
104 | 107 | function initializeFromUpload( $request ) { |
105 | | - $this->mUploadTempName = $request->getFileTempName( 'wpUploadFile' ); |
106 | | - $this->mUploadSize = $request->getFileSize( 'wpUploadFile' ); |
107 | | - $this->mOname = $request->getFileName( 'wpUploadFile' ); |
108 | | - $this->mUploadError = $request->getUploadError( 'wpUploadFile' ); |
| 108 | + $this->mTempPath = $request->getFileTempName( 'wpUploadFile' ); |
| 109 | + $this->mFileSize = $request->getFileSize( 'wpUploadFile' ); |
| 110 | + $this->mSrcName = $request->getFileName( 'wpUploadFile' ); |
| 111 | + $this->mCurlError = $request->getUploadError( 'wpUploadFile' ); |
| 112 | + $this->mFileProps = File::getPropsFromPath( $this->mTempPath ); |
109 | 113 | $this->mSessionKey = false; |
110 | 114 | $this->mStashed = false; |
111 | 115 | $this->mRemoveTempFile = false; // PHP will handle this |
— | — | @@ -119,10 +123,10 @@ |
120 | 124 | $url = $request->getText( 'wpUploadFileURL' ); |
121 | 125 | $local_file = tempnam( $wgTmpDirectory, 'WEBUPLOAD' ); |
122 | 126 | |
123 | | - $this->mUploadTempName = $local_file; |
124 | | - $this->mUploadError = $this->curlCopy( $url, $local_file ); |
125 | | - $this->mUploadSize = $this->mUploadTempFileSize; |
126 | | - $this->mOname = array_pop( explode( '/', $url ) ); |
| 127 | + $this->mTempPath = $local_file; |
| 128 | + $this->mFileSize = 0; # Will be set by curlCopy |
| 129 | + $this->mCurlError = $this->curlCopy( $url, $local_file ); |
| 130 | + $this->mSrcName = array_pop( explode( '/', $url ) ); |
127 | 131 | $this->mSessionKey = false; |
128 | 132 | $this->mStashed = false; |
129 | 133 | |
— | — | @@ -151,8 +155,8 @@ |
152 | 156 | } |
153 | 157 | |
154 | 158 | # Open temporary file |
155 | | - $this->mUploadTempFile = @fopen( $this->mUploadTempName, "wb" ); |
156 | | - if( $this->mUploadTempFile === false ) { |
| 159 | + $this->mCurlDestHandle = @fopen( $this->mTempPath, "wb" ); |
| 160 | + if( $this->mCurlDestHandle === false ) { |
157 | 161 | # Could not open temporary file to write in |
158 | 162 | $wgOut->errorPage( 'upload-file-error', 'upload-file-error-text'); |
159 | 163 | return true; |
— | — | @@ -170,8 +174,8 @@ |
171 | 175 | // if ( $error ) print curl_error ( $ch ) ; # Debugging output |
172 | 176 | curl_close( $ch ); |
173 | 177 | |
174 | | - fclose( $this->mUploadTempFile ); |
175 | | - unset( $this->mUploadTempFile ); |
| 178 | + fclose( $this->mCurlDestHandle ); |
| 179 | + unset( $this->mCurlDestHandle ); |
176 | 180 | if( $error ) { |
177 | 181 | unlink( $dest ); |
178 | 182 | if( wfEmptyMsg( "upload-curl-error$errornum", wfMsg("upload-curl-error$errornum") ) ) |
— | — | @@ -192,11 +196,11 @@ |
193 | 197 | function uploadCurlCallback( $ch, $data ) { |
194 | 198 | global $wgMaxUploadSize; |
195 | 199 | $length = strlen( $data ); |
196 | | - $this->mUploadTempFileSize += $length; |
197 | | - if( $this->mUploadTempFileSize > $wgMaxUploadSize ) { |
| 200 | + $this->mFileSize += $length; |
| 201 | + if( $this->mFileSize > $wgMaxUploadSize ) { |
198 | 202 | return 0; |
199 | 203 | } |
200 | | - fwrite( $this->mUploadTempFile, $data ); |
| 204 | + fwrite( $this->mCurlDestHandle, $data ); |
201 | 205 | return $length; |
202 | 206 | } |
203 | 207 | |
— | — | @@ -206,11 +210,11 @@ |
207 | 211 | */ |
208 | 212 | function execute() { |
209 | 213 | global $wgUser, $wgOut; |
210 | | - global $wgEnableUploads, $wgUploadDirectory; |
| 214 | + global $wgEnableUploads; |
211 | 215 | |
212 | 216 | # Check uploading enabled |
213 | 217 | if( !$wgEnableUploads ) { |
214 | | - $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext', array( $this->mDestFile ) ); |
| 218 | + $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext', array( $this->mDesiredDestName ) ); |
215 | 219 | return; |
216 | 220 | } |
217 | 221 | |
— | — | @@ -235,18 +239,12 @@ |
236 | 240 | return; |
237 | 241 | } |
238 | 242 | |
239 | | - /** Check if the image directory is writeable, this is a common mistake */ |
240 | | - if( !is_writeable( $wgUploadDirectory ) ) { |
241 | | - $wgOut->addWikiText( wfMsg( 'upload_directory_read_only', $wgUploadDirectory ) ); |
242 | | - return; |
243 | | - } |
244 | | - |
245 | 243 | if( $this->mReUpload ) { |
246 | 244 | if( !$this->unsaveUploadedFile() ) { |
247 | 245 | return; |
248 | 246 | } |
249 | 247 | $this->mainUploadForm(); |
250 | | - } else if( 'submit' == $this->mAction || $this->mUpload ) { |
| 248 | + } else if( 'submit' == $this->mAction || $this->mUploadClicked ) { |
251 | 249 | $this->processUpload(); |
252 | 250 | } else { |
253 | 251 | $this->mainUploadForm(); |
— | — | @@ -272,7 +270,7 @@ |
273 | 271 | } |
274 | 272 | |
275 | 273 | /* Check for PHP error if any, requires php 4.2 or newer */ |
276 | | - if( $this->mUploadError == 1/*UPLOAD_ERR_INI_SIZE*/ ) { |
| 274 | + if( $this->mCurlError == 1/*UPLOAD_ERR_INI_SIZE*/ ) { |
277 | 275 | $this->mainUploadForm( wfMsgHtml( 'largefileserver' ) ); |
278 | 276 | return; |
279 | 277 | } |
— | — | @@ -280,16 +278,16 @@ |
281 | 279 | /** |
282 | 280 | * If there was no filename or a zero size given, give up quick. |
283 | 281 | */ |
284 | | - if( trim( $this->mOname ) == '' || empty( $this->mUploadSize ) ) { |
| 282 | + if( trim( $this->mSrcName ) == '' || empty( $this->mFileSize ) ) { |
285 | 283 | $this->mainUploadForm( wfMsgHtml( 'emptyfile' ) ); |
286 | 284 | return; |
287 | 285 | } |
288 | 286 | |
289 | 287 | # Chop off any directories in the given filename |
290 | | - if( $this->mDestFile ) { |
291 | | - $basename = wfBaseName( $this->mDestFile ); |
| 288 | + if( $this->mDesiredDestName ) { |
| 289 | + $basename = wfBaseName( $this->mDesiredDestName ); |
292 | 290 | } else { |
293 | | - $basename = wfBaseName( $this->mOname ); |
| 291 | + $basename = wfBaseName( $this->mSrcName ); |
294 | 292 | } |
295 | 293 | |
296 | 294 | /** |
— | — | @@ -321,13 +319,13 @@ |
322 | 320 | * out of it. We'll strip some silently that Title would die on. |
323 | 321 | */ |
324 | 322 | $filtered = preg_replace ( "/[^".Title::legalChars()."]|:/", '-', $basename ); |
325 | | - $nt = Title::newFromText( $filtered ); |
| 323 | + $nt = Title::makeTitleSafe( NS_IMAGE, $filtered ); |
326 | 324 | if( is_null( $nt ) ) { |
327 | 325 | $this->uploadError( wfMsgWikiHtml( 'illegalfilename', htmlspecialchars( $filtered ) ) ); |
328 | 326 | return; |
329 | 327 | } |
330 | | - $nt =& Title::makeTitle( NS_IMAGE, $nt->getDBkey() ); |
331 | | - $this->mUploadSaveName = $nt->getDBkey(); |
| 328 | + $this->mLocalFile = wfLocalFile( $nt ); |
| 329 | + $this->mDestName = $this->mLocalFile->getName(); |
332 | 330 | |
333 | 331 | /** |
334 | 332 | * If the image is protected, non-sysop users won't be able |
— | — | @@ -340,7 +338,7 @@ |
341 | 339 | /** |
342 | 340 | * In some cases we may forbid overwriting of existing files. |
343 | 341 | */ |
344 | | - $overwrite = $this->checkOverwrite( $this->mUploadSaveName ); |
| 342 | + $overwrite = $this->checkOverwrite( $this->mDestName ); |
345 | 343 | if( WikiError::isError( $overwrite ) ) { |
346 | 344 | return $this->uploadError( $overwrite->toString() ); |
347 | 345 | } |
— | — | @@ -351,9 +349,9 @@ |
352 | 350 | if ($finalExt == '') { |
353 | 351 | return $this->uploadError( wfMsgExt( 'filetype-missing', array ( 'parseinline' ) ) ); |
354 | 352 | } elseif ( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) || |
355 | | - ($wgStrictFileExtensions && |
356 | | - !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) ) { |
357 | | - return $this->uploadError( wfMsgExt( 'filetype-badtype', array ( 'parseinline' ), htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ) ); |
| 353 | + ($wgStrictFileExtensions && !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) ) { |
| 354 | + return $this->uploadError( wfMsgExt( 'filetype-badtype', array ( 'parseinline' ), |
| 355 | + htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ) ); |
358 | 356 | } |
359 | 357 | |
360 | 358 | /** |
— | — | @@ -363,7 +361,7 @@ |
364 | 362 | */ |
365 | 363 | if( !$this->mStashed ) { |
366 | 364 | $this->checkMacBinary(); |
367 | | - $veri = $this->verify( $this->mUploadTempName, $finalExt ); |
| 365 | + $veri = $this->verify( $this->mTempPath, $finalExt ); |
368 | 366 | |
369 | 367 | if( $veri !== true ) { //it's a wiki error... |
370 | 368 | return $this->uploadError( $veri->toString() ); |
— | — | @@ -374,7 +372,7 @@ |
375 | 373 | */ |
376 | 374 | $error = ''; |
377 | 375 | if( !wfRunHooks( 'UploadVerification', |
378 | | - array( $this->mUploadSaveName, $this->mUploadTempName, &$error ) ) ) { |
| 376 | + array( $this->mDestName, $this->mTempPath, &$error ) ) ) { |
379 | 377 | return $this->uploadError( $error ); |
380 | 378 | } |
381 | 379 | } |
— | — | @@ -390,31 +388,31 @@ |
391 | 389 | if( $wgCapitalLinks ) { |
392 | 390 | $filtered = ucfirst( $filtered ); |
393 | 391 | } |
394 | | - if( $this->mUploadSaveName != $filtered ) { |
395 | | - $warning .= '<li>'.wfMsgHtml( 'badfilename', htmlspecialchars( $this->mUploadSaveName ) ).'</li>'; |
| 392 | + if( $this->mDestName != $filtered ) { |
| 393 | + $warning .= '<li>'.wfMsgHtml( 'badfilename', htmlspecialchars( $this->mDestName ) ).'</li>'; |
396 | 394 | } |
397 | 395 | |
398 | 396 | global $wgCheckFileExtensions; |
399 | 397 | if ( $wgCheckFileExtensions ) { |
400 | 398 | if ( ! $this->checkFileExtension( $finalExt, $wgFileExtensions ) ) { |
401 | | - $warning .= '<li>'.wfMsgExt( 'filetype-badtype', array ( 'parseinline' ), htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ).'</li>'; |
| 399 | + $warning .= '<li>'.wfMsgExt( 'filetype-badtype', array ( 'parseinline' ), |
| 400 | + htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ).'</li>'; |
402 | 401 | } |
403 | 402 | } |
404 | 403 | |
405 | 404 | global $wgUploadSizeWarning; |
406 | | - if ( $wgUploadSizeWarning && ( $this->mUploadSize > $wgUploadSizeWarning ) ) { |
| 405 | + if ( $wgUploadSizeWarning && ( $this->mFileSize > $wgUploadSizeWarning ) ) { |
407 | 406 | $skin = $wgUser->getSkin(); |
408 | 407 | $wsize = $skin->formatSize( $wgUploadSizeWarning ); |
409 | | - $asize = $skin->formatSize( $this->mUploadSize ); |
| 408 | + $asize = $skin->formatSize( $this->mFileSize ); |
410 | 409 | $warning .= '<li>' . wfMsgHtml( 'large-file', $wsize, $asize ) . '</li>'; |
411 | 410 | } |
412 | | - if ( $this->mUploadSize == 0 ) { |
| 411 | + if ( $this->mFileSize == 0 ) { |
413 | 412 | $warning .= '<li>'.wfMsgHtml( 'emptyfile' ).'</li>'; |
414 | 413 | } |
415 | 414 | |
416 | 415 | global $wgUser; |
417 | 416 | $sk = $wgUser->getSkin(); |
418 | | - $image = wfLocalFile( $nt ); |
419 | 417 | |
420 | 418 | // Check for uppercase extension. We allow these filenames but check if an image |
421 | 419 | // with lowercase extension exists already |
— | — | @@ -423,13 +421,15 @@ |
424 | 422 | $image_lc = wfLocalFile( $nt_lc ); |
425 | 423 | } |
426 | 424 | |
427 | | - if( $image->exists() ) { |
| 425 | + if( $this->mLocalFile->exists() ) { |
428 | 426 | $dlink = $sk->makeKnownLinkObj( $nt ); |
429 | | - if ( $image->allowInlineDisplay() ) { |
430 | | - $dlink2 = $sk->makeImageLinkObj( $nt, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), $nt->getText(), 'right', array(), false, true ); |
431 | | - } elseif ( !$image->allowInlineDisplay() && $image->isSafeFile() ) { |
432 | | - $icon = $image->iconThumb(); |
433 | | - $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $image->getURL() . '">' . $icon->toHtml() . '</a><br />' . $dlink . '</div>'; |
| 427 | + if ( $this->mLocalFile->allowInlineDisplay() ) { |
| 428 | + $dlink2 = $sk->makeImageLinkObj( $nt, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), |
| 429 | + $nt->getText(), 'right', array(), false, true ); |
| 430 | + } elseif ( !$this->mLocalFile->allowInlineDisplay() && $this->mLocalFile->isSafeFile() ) { |
| 431 | + $icon = $this->mLocalFile->iconThumb(); |
| 432 | + $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $this->mLocalFile->getURL() . '">' . |
| 433 | + $icon->toHtml() . '</a><br />' . $dlink . '</div>'; |
434 | 434 | } else { |
435 | 435 | $dlink2 = ''; |
436 | 436 | } |
— | — | @@ -441,17 +441,22 @@ |
442 | 442 | # It's not forbidden but in 99% it makes no sense to upload the same filename with uppercase extension |
443 | 443 | $dlink = $sk->makeKnownLinkObj( $nt_lc ); |
444 | 444 | if ( $image_lc->allowInlineDisplay() ) { |
445 | | - $dlink2 = $sk->makeImageLinkObj( $nt_lc, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), $nt_lc->getText(), 'right', array(), false, true ); |
| 445 | + $dlink2 = $sk->makeImageLinkObj( $nt_lc, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), |
| 446 | + $nt_lc->getText(), 'right', array(), false, true ); |
446 | 447 | } elseif ( !$image_lc->allowInlineDisplay() && $image_lc->isSafeFile() ) { |
447 | 448 | $icon = $image_lc->iconThumb(); |
448 | | - $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $image_lc->getURL() . '">' . $icon->toHtml() . '</a><br />' . $dlink . '</div>'; |
| 449 | + $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $image_lc->getURL() . '">' . |
| 450 | + $icon->toHtml() . '</a><br />' . $dlink . '</div>'; |
449 | 451 | } else { |
450 | 452 | $dlink2 = ''; |
451 | 453 | } |
452 | 454 | |
453 | | - $warning .= '<li>' . wfMsgExt( 'fileexists-extension', 'parsemag' , $partname . '.' . $finalExt , $dlink ) . '</li>' . $dlink2; |
| 455 | + $warning .= '<li>' . wfMsgExt( 'fileexists-extension', 'parsemag' , $partname . '.' |
| 456 | + . $finalExt , $dlink ) . '</li>' . $dlink2; |
454 | 457 | |
455 | | - } elseif ( ( substr( $partname , 3, 3 ) == 'px-' || substr( $partname , 2, 3 ) == 'px-' ) && ereg( "[0-9]{2}" , substr( $partname , 0, 2) ) ) { |
| 458 | + } elseif ( ( substr( $partname , 3, 3 ) == 'px-' || substr( $partname , 2, 3 ) == 'px-' ) |
| 459 | + && ereg( "[0-9]{2}" , substr( $partname , 0, 2) ) ) |
| 460 | + { |
456 | 461 | # Check for filenames like 50px- or 180px-, these are mostly thumbnails |
457 | 462 | $nt_thb = Title::newFromText( substr( $partname , strpos( $partname , '-' ) +1 ) . '.' . $finalExt ); |
458 | 463 | $image_thb = wfLocalFile( $nt_thb ); |
— | — | @@ -459,26 +464,34 @@ |
460 | 465 | # Check if an image without leading '180px-' (or similiar) exists |
461 | 466 | $dlink = $sk->makeKnownLinkObj( $nt_thb); |
462 | 467 | if ( $image_thb->allowInlineDisplay() ) { |
463 | | - $dlink2 = $sk->makeImageLinkObj( $nt_thb, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), $nt_thb->getText(), 'right', array(), false, true ); |
| 468 | + $dlink2 = $sk->makeImageLinkObj( $nt_thb, |
| 469 | + wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), |
| 470 | + $nt_thb->getText(), 'right', array(), false, true ); |
464 | 471 | } elseif ( !$image_thb->allowInlineDisplay() && $image_thb->isSafeFile() ) { |
465 | 472 | $icon = $image_thb->iconThumb(); |
466 | | - $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $image_thb->getURL() . '">' . $icon->toHtml() . '</a><br />' . $dlink . '</div>'; |
| 473 | + $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . |
| 474 | + $image_thb->getURL() . '">' . $icon->toHtml() . '</a><br />' . |
| 475 | + $dlink . '</div>'; |
467 | 476 | } else { |
468 | 477 | $dlink2 = ''; |
469 | 478 | } |
470 | 479 | |
471 | | - $warning .= '<li>' . wfMsgExt( 'fileexists-thumbnail-yes', 'parsemag', $dlink ) . '</li>' . $dlink2; |
| 480 | + $warning .= '<li>' . wfMsgExt( 'fileexists-thumbnail-yes', 'parsemag', $dlink ) . |
| 481 | + '</li>' . $dlink2; |
472 | 482 | } else { |
473 | 483 | # Image w/o '180px-' does not exists, but we do not like these filenames |
474 | | - $warning .= '<li>' . wfMsgExt( 'file-thumbnail-no', 'parseinline' , substr( $partname , 0, strpos( $partname , '-' ) +1 ) ) . '</li>'; |
| 484 | + $warning .= '<li>' . wfMsgExt( 'file-thumbnail-no', 'parseinline' , |
| 485 | + substr( $partname , 0, strpos( $partname , '-' ) +1 ) ) . '</li>'; |
475 | 486 | } |
476 | 487 | } |
477 | | - if ( $image->wasDeleted() ) { |
| 488 | + if ( $this->mLocalFile->wasDeleted() ) { |
478 | 489 | # If the file existed before and was deleted, warn the user of this |
479 | 490 | # Don't bother doing so if the image exists now, however |
480 | 491 | $ltitle = SpecialPage::getTitleFor( 'Log' ); |
481 | | - $llink = $sk->makeKnownLinkObj( $ltitle, wfMsgHtml( 'deletionlog' ), 'type=delete&page=' . $nt->getPrefixedUrl() ); |
482 | | - $warning .= wfOpenElement( 'li' ) . wfMsgWikiHtml( 'filewasdeleted', $llink ) . wfCloseElement( 'li' ); |
| 492 | + $llink = $sk->makeKnownLinkObj( $ltitle, wfMsgHtml( 'deletionlog' ), |
| 493 | + 'type=delete&page=' . $nt->getPrefixedUrl() ); |
| 494 | + $warning .= wfOpenElement( 'li' ) . wfMsgWikiHtml( 'filewasdeleted', $llink ) . |
| 495 | + wfCloseElement( 'li' ); |
483 | 496 | } |
484 | 497 | |
485 | 498 | if( $warning != '' ) { |
— | — | @@ -494,59 +507,24 @@ |
495 | 508 | * Try actually saving the thing... |
496 | 509 | * It will show an error form on failure. |
497 | 510 | */ |
498 | | - $hasBeenMunged = !empty( $this->mSessionKey ) || $this->mRemoveTempFile; |
499 | | - if( $this->saveUploadedFile( $this->mUploadSaveName, |
500 | | - $this->mUploadTempName, |
501 | | - $hasBeenMunged ) ) { |
502 | | - /** |
503 | | - * Update the upload log and create the description page |
504 | | - * if it's a new file. |
505 | | - */ |
506 | | - $this->mImage = wfLocalFile( $this->mUploadSaveName ); |
507 | | - $success = $this->mImage->recordUpload( $this->mUploadOldVersion, |
508 | | - $this->mUploadDescription, |
509 | | - $this->mLicense, |
510 | | - $this->mUploadCopyStatus, |
511 | | - $this->mUploadSource, |
512 | | - $this->mWatchthis ); |
| 511 | + $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, |
| 512 | + $this->mCopyrightStatus, $this->mCopyrightSource ); |
513 | 513 | |
514 | | - if ( $success ) { |
515 | | - $this->showSuccess(); |
516 | | - wfRunHooks( 'UploadComplete', array( &$img ) ); |
517 | | - } else { |
518 | | - // File::recordUpload() fails if the image went missing, which is |
519 | | - // unlikely, hence the lack of a specialised message |
520 | | - $wgOut->showFileNotFoundError( $this->mUploadSaveName ); |
| 514 | + $error = $this->mLocalFile->upload( $this->mTempPath, $this->mComment, $pageText, |
| 515 | + File::DELETE_SOURCE, $this->mFileProps ); |
| 516 | + if ( WikiError::isError( $error ) ) { |
| 517 | + $this->showError( $error ); |
| 518 | + } else { |
| 519 | + if ( $this->mWatchthis ) { |
| 520 | + global $wgUser; |
| 521 | + $wgUser->addWatch( $this->mLocalFile->getTitle() ); |
521 | 522 | } |
| 523 | + $this->showSuccess(); |
| 524 | + wfRunHooks( 'UploadComplete', array( &$img ) ); |
522 | 525 | } |
523 | 526 | } |
524 | 527 | |
525 | 528 | /** |
526 | | - * Move the uploaded file from its temporary location to the final |
527 | | - * destination. If a previous version of the file exists, move |
528 | | - * it into the archive subdirectory. |
529 | | - * |
530 | | - * @todo If the later save fails, we may have disappeared the original file. |
531 | | - * |
532 | | - * @param string $saveName |
533 | | - * @param string $tempName full path to the temporary file |
534 | | - * @param bool $useRename if true, doesn't check that the source file |
535 | | - * is a PHP-managed upload temporary |
536 | | - */ |
537 | | - function saveUploadedFile( $saveName, $tempName, $useRename = false ) { |
538 | | - global $wgOut, $wgAllowCopyUploads; |
539 | | - |
540 | | - $image = wfLocalFile( $saveName ); |
541 | | - $archiveName = $image->publish( $tempName, File::DELETE_SOURCE ); |
542 | | - if ( WikiError::isError( $archiveName ) ) { |
543 | | - $this->showError( $archiveName ); |
544 | | - return false; |
545 | | - } |
546 | | - $this->mUploadOldVersion = $archiveName; |
547 | | - return true; |
548 | | - } |
549 | | - |
550 | | - /** |
551 | 529 | * Stash a file in a temporary directory for later processing |
552 | 530 | * after the user has confirmed it. |
553 | 531 | * |
— | — | @@ -580,8 +558,7 @@ |
581 | 559 | * @access private |
582 | 560 | */ |
583 | 561 | function stashSession() { |
584 | | - $stash = $this->saveTempUploadedFile( |
585 | | - $this->mUploadSaveName, $this->mUploadTempName ); |
| 562 | + $stash = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath ); |
586 | 563 | |
587 | 564 | if( !$stash ) { |
588 | 565 | # Couldn't save the file. |
— | — | @@ -590,9 +567,12 @@ |
591 | 568 | |
592 | 569 | $key = mt_rand( 0, 0x7fffffff ); |
593 | 570 | $_SESSION['wsUploadData'][$key] = array( |
594 | | - 'mUploadTempName' => $stash, |
595 | | - 'mUploadSize' => $this->mUploadSize, |
596 | | - 'mOname' => $this->mOname ); |
| 571 | + 'mTempPath' => $stash, |
| 572 | + 'mFileSize' => $this->mFileSize, |
| 573 | + 'mSrcName' => $this->mSrcName, |
| 574 | + 'mFileProps' => $this->mFileProps, |
| 575 | + 'version' => self::SESSION_VERSION, |
| 576 | + ); |
597 | 577 | return $key; |
598 | 578 | } |
599 | 579 | |
— | — | @@ -604,9 +584,9 @@ |
605 | 585 | function unsaveUploadedFile() { |
606 | 586 | global $wgOut; |
607 | 587 | $repo = RepoGroup::singleton()->getLocalRepo(); |
608 | | - $success = $repo->freeTemp( $this->mUploadTempName ); |
| 588 | + $success = $repo->freeTemp( $this->mTempPath ); |
609 | 589 | if ( ! $success ) { |
610 | | - $wgOut->showFileDeleteError( $this->mUploadTempName ); |
| 590 | + $wgOut->showFileDeleteError( $this->mTempPath ); |
611 | 591 | return false; |
612 | 592 | } else { |
613 | 593 | return true; |
— | — | @@ -623,8 +603,8 @@ |
624 | 604 | global $wgUser, $wgOut, $wgContLang; |
625 | 605 | |
626 | 606 | $sk = $wgUser->getSkin(); |
627 | | - $ilink = $sk->makeMediaLinkObj( $this->mImage->getTitle() ); |
628 | | - $dname = $wgContLang->getNsText( NS_IMAGE ) . ':'.$this->mUploadSaveName; |
| 607 | + $ilink = $sk->makeMediaLinkObj( $this->mLocalFile->getTitle() ); |
| 608 | + $dname = $wgContLang->getNsText( NS_IMAGE ) . ':'.$this->mDestName; |
629 | 609 | $dlink = $sk->makeKnownLink( $dname, $dname ); |
630 | 610 | |
631 | 611 | $wgOut->addHTML( '<h2>' . wfMsgHtml( 'successfulupload' ) . "</h2>\n" ); |
— | — | @@ -674,8 +654,8 @@ |
675 | 655 | if ( $wgUseCopyrightUpload ) |
676 | 656 | { |
677 | 657 | $copyright = " |
678 | | - <input type='hidden' name='wpUploadCopyStatus' value=\"" . htmlspecialchars( $this->mUploadCopyStatus ) . "\" /> |
679 | | - <input type='hidden' name='wpUploadSource' value=\"" . htmlspecialchars( $this->mUploadSource ) . "\" /> |
| 658 | + <input type='hidden' name='wpUploadCopyStatus' value=\"" . htmlspecialchars( $this->mCopyrightStatus ) . "\" /> |
| 659 | + <input type='hidden' name='wpUploadSource' value=\"" . htmlspecialchars( $this->mCopyrightSource ) . "\" /> |
680 | 660 | "; |
681 | 661 | } else { |
682 | 662 | $copyright = ""; |
— | — | @@ -685,9 +665,9 @@ |
686 | 666 | <form id='uploadwarning' method='post' enctype='multipart/form-data' action='$action'> |
687 | 667 | <input type='hidden' name='wpIgnoreWarning' value='1' /> |
688 | 668 | <input type='hidden' name='wpSessionKey' value=\"" . htmlspecialchars( $this->mSessionKey ) . "\" /> |
689 | | - <input type='hidden' name='wpUploadDescription' value=\"" . htmlspecialchars( $this->mUploadDescription ) . "\" /> |
| 669 | + <input type='hidden' name='wpUploadDescription' value=\"" . htmlspecialchars( $this->mComment ) . "\" /> |
690 | 670 | <input type='hidden' name='wpLicense' value=\"" . htmlspecialchars( $this->mLicense ) . "\" /> |
691 | | - <input type='hidden' name='wpDestFile' value=\"" . htmlspecialchars( $this->mDestFile ) . "\" /> |
| 671 | + <input type='hidden' name='wpDestFile' value=\"" . htmlspecialchars( $this->mDesiredDestName ) . "\" /> |
692 | 672 | <input type='hidden' name='wpWatchthis' value=\"" . htmlspecialchars( intval( $this->mWatchthis ) ) . "\" /> |
693 | 673 | {$copyright} |
694 | 674 | <table border='0'> |
— | — | @@ -737,7 +717,7 @@ |
738 | 718 | "<span class='error'>{$msg}</span>\n" ); |
739 | 719 | } |
740 | 720 | $wgOut->addHTML( '<div id="uploadtext">' ); |
741 | | - $wgOut->addWikiText( wfMsgNoTrans( 'uploadtext', $this->mDestFile ) ); |
| 721 | + $wgOut->addWikiText( wfMsgNoTrans( 'uploadtext', $this->mDesiredDestName ) ); |
742 | 722 | $wgOut->addHTML( '</div>' ); |
743 | 723 | |
744 | 724 | $sourcefilename = wfMsgHtml( 'sourcefilename' ); |
— | — | @@ -755,35 +735,44 @@ |
756 | 736 | $titleObj = SpecialPage::getTitleFor( 'Upload' ); |
757 | 737 | $action = $titleObj->escapeLocalURL(); |
758 | 738 | |
759 | | - $encDestFile = htmlspecialchars( $this->mDestFile ); |
| 739 | + $encDestName = htmlspecialchars( $this->mDesiredDestName ); |
760 | 740 | |
761 | 741 | $watchChecked = |
762 | 742 | ( $wgUser->getOption( 'watchdefault' ) || |
763 | | - ( $wgUser->getOption( 'watchcreations' ) && $this->mDestFile == '' ) ) |
| 743 | + ( $wgUser->getOption( 'watchcreations' ) && $this->mDesiredDestName == '' ) ) |
764 | 744 | ? 'checked="checked"' |
765 | 745 | : ''; |
766 | 746 | |
767 | 747 | // Prepare form for upload or upload/copy |
768 | 748 | if( $wgAllowCopyUploads && $wgUser->isAllowed( 'upload_by_url' ) ) { |
769 | 749 | $filename_form = |
770 | | - "<input type='radio' id='wpSourceTypeFile' name='wpSourceType' value='file' onchange='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\")' checked />" . |
771 | | - "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' onfocus='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\");toggle_element_check(\"wpSourceTypeFile\",\"wpSourceTypeURL\")'" . |
772 | | - ($this->mDestFile?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") . "size='40' />" . |
| 750 | + "<input type='radio' id='wpSourceTypeFile' name='wpSourceType' value='file' " . |
| 751 | + "onchange='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\")' checked />" . |
| 752 | + "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " . |
| 753 | + "onfocus='" . |
| 754 | + "toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\");" . |
| 755 | + "toggle_element_check(\"wpSourceTypeFile\",\"wpSourceTypeURL\")'" . |
| 756 | + ($this->mDesiredDestName?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") . "size='40' />" . |
773 | 757 | wfMsgHTML( 'upload_source_file' ) . "<br/>" . |
774 | | - "<input type='radio' id='wpSourceTypeURL' name='wpSourceType' value='web' onchange='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\")' />" . |
775 | | - "<input tabindex='1' type='text' name='wpUploadFileURL' id='wpUploadFileURL' onfocus='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\");toggle_element_check(\"wpSourceTypeURL\",\"wpSourceTypeFile\")'" . |
776 | | - ($this->mDestFile?"":"onchange='fillDestFilename(\"wpUploadFileURL\")' ") . "size='40' DISABLED />" . |
| 758 | + "<input type='radio' id='wpSourceTypeURL' name='wpSourceType' value='web' " . |
| 759 | + "onchange='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\")' />" . |
| 760 | + "<input tabindex='1' type='text' name='wpUploadFileURL' id='wpUploadFileURL' " . |
| 761 | + "onfocus='" . |
| 762 | + "toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\");" . |
| 763 | + "toggle_element_check(\"wpSourceTypeURL\",\"wpSourceTypeFile\")'" . |
| 764 | + ($this->mDesiredDestName?"":"onchange='fillDestFilename(\"wpUploadFileURL\")' ") . "size='40' DISABLED />" . |
777 | 765 | wfMsgHtml( 'upload_source_url' ) ; |
778 | 766 | } else { |
779 | 767 | $filename_form = |
780 | 768 | "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " . |
781 | | - ($this->mDestFile?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") . |
| 769 | + ($this->mDesiredDestName?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") . |
782 | 770 | "size='40' />" . |
783 | 771 | "<input type='hidden' name='wpSourceType' value='file' />" ; |
784 | 772 | } |
| 773 | + $encComment = htmlspecialchars( $this->mComment ); |
785 | 774 | |
786 | | - $wgOut->addHTML( " |
787 | | - <form id='upload' method='post' enctype='multipart/form-data' action=\"$action\"> |
| 775 | + $wgOut->addHTML( <<<EOT |
| 776 | + <form id='upload' method='post' enctype='multipart/form-data' action="$action"> |
788 | 777 | <table border='0'> |
789 | 778 | <tr> |
790 | 779 | {$this->uploadFormTextTop} |
— | — | @@ -795,17 +784,20 @@ |
796 | 785 | <tr> |
797 | 786 | <td align='right'><label for='wpDestFile'>{$destfilename}:</label></td> |
798 | 787 | <td align='left'> |
799 | | - <input tabindex='2' type='text' name='wpDestFile' id='wpDestFile' size='40' value=\"$encDestFile\" /> |
| 788 | + <input tabindex='2' type='text' name='wpDestFile' id='wpDestFile' size='40' value="$encDestName" /> |
800 | 789 | </td> |
801 | 790 | </tr> |
802 | 791 | <tr> |
803 | 792 | <td align='right'><label for='wpUploadDescription'>{$summary}</label></td> |
804 | 793 | <td align='left'> |
805 | | - <textarea tabindex='3' name='wpUploadDescription' id='wpUploadDescription' rows='6' cols='{$cols}'{$ew}>" . htmlspecialchars( $this->mUploadDescription ) . "</textarea> |
| 794 | + <textarea tabindex='3' name='wpUploadDescription' id='wpUploadDescription' rows='6' |
| 795 | + cols='{$cols}'{$ew}>$encComment</textarea> |
806 | 796 | {$this->uploadFormTextAfterSummary} |
807 | 797 | </td> |
808 | 798 | </tr> |
809 | | - <tr>" ); |
| 799 | + <tr> |
| 800 | +EOT |
| 801 | + ); |
810 | 802 | |
811 | 803 | if ( $licenseshtml != '' ) { |
812 | 804 | global $wgStylePath; |
— | — | @@ -826,17 +818,19 @@ |
827 | 819 | |
828 | 820 | if ( $wgUseCopyrightUpload ) { |
829 | 821 | $filestatus = wfMsgHtml ( 'filestatus' ); |
830 | | - $copystatus = htmlspecialchars( $this->mUploadCopyStatus ); |
| 822 | + $copystatus = htmlspecialchars( $this->mCopyrightStatus ); |
831 | 823 | $filesource = wfMsgHtml ( 'filesource' ); |
832 | | - $uploadsource = htmlspecialchars( $this->mUploadSource ); |
| 824 | + $uploadsource = htmlspecialchars( $this->mCopyrightSource ); |
833 | 825 | |
834 | 826 | $wgOut->addHTML( " |
835 | 827 | <td align='right' nowrap='nowrap'><label for='wpUploadCopyStatus'>$filestatus:</label></td> |
836 | | - <td><input tabindex='5' type='text' name='wpUploadCopyStatus' id='wpUploadCopyStatus' value=\"$copystatus\" size='40' /></td> |
| 828 | + <td><input tabindex='5' type='text' name='wpUploadCopyStatus' id='wpUploadCopyStatus' |
| 829 | + value=\"$copystatus\" size='40' /></td> |
837 | 830 | </tr> |
838 | 831 | <tr> |
839 | 832 | <td align='right'><label for='wpUploadCopyStatus'>$filesource:</label></td> |
840 | | - <td><input tabindex='6' type='text' name='wpUploadSource' id='wpUploadCopyStatus' value=\"$uploadsource\" size='40' /></td> |
| 833 | + <td><input tabindex='6' type='text' name='wpUploadSource' id='wpUploadCopyStatus' |
| 834 | + value=\"$uploadsource\" size='40' /></td> |
841 | 835 | </tr> |
842 | 836 | <tr> |
843 | 837 | "); |
— | — | @@ -927,8 +921,6 @@ |
928 | 922 | $magic=& MimeMagic::singleton(); |
929 | 923 | $mime= $magic->guessMimeType($tmpfile,false); |
930 | 924 | |
931 | | - $fname= "SpecialUpload::verify"; |
932 | | - |
933 | 925 | #check mime type, if desired |
934 | 926 | global $wgVerifyMimeType; |
935 | 927 | if ($wgVerifyMimeType) { |
— | — | @@ -959,7 +951,7 @@ |
960 | 952 | return new WikiErrorMsg( 'uploadvirus', htmlspecialchars($virus) ); |
961 | 953 | } |
962 | 954 | |
963 | | - wfDebug( "$fname: all clear; passing.\n" ); |
| 955 | + wfDebug( __METHOD__.": all clear; passing.\n" ); |
964 | 956 | return true; |
965 | 957 | } |
966 | 958 | |
— | — | @@ -971,45 +963,46 @@ |
972 | 964 | * @return bool |
973 | 965 | */ |
974 | 966 | function verifyExtension( $mime, $extension ) { |
975 | | - $fname = 'SpecialUpload::verifyExtension'; |
976 | | - |
977 | 967 | $magic =& MimeMagic::singleton(); |
978 | 968 | |
979 | 969 | if ( ! $mime || $mime == 'unknown' || $mime == 'unknown/unknown' ) |
980 | 970 | if ( ! $magic->isRecognizableExtension( $extension ) ) { |
981 | | - wfDebug( "$fname: passing file with unknown detected mime type; unrecognized extension '$extension', can't verify\n" ); |
| 971 | + wfDebug( __METHOD__.": passing file with unknown detected mime type; " . |
| 972 | + "unrecognized extension '$extension', can't verify\n" ); |
982 | 973 | return true; |
983 | 974 | } else { |
984 | | - wfDebug( "$fname: rejecting file with unknown detected mime type; recognized extension '$extension', so probably invalid file\n" ); |
| 975 | + wfDebug( __METHOD__.": rejecting file with unknown detected mime type; ". |
| 976 | + "recognized extension '$extension', so probably invalid file\n" ); |
985 | 977 | return false; |
986 | 978 | } |
987 | 979 | |
988 | 980 | $match= $magic->isMatchingExtension($extension,$mime); |
989 | 981 | |
990 | 982 | if ($match===NULL) { |
991 | | - wfDebug( "$fname: no file extension known for mime type $mime, passing file\n" ); |
| 983 | + wfDebug( __METHOD__.": no file extension known for mime type $mime, passing file\n" ); |
992 | 984 | return true; |
993 | 985 | } elseif ($match===true) { |
994 | | - wfDebug( "$fname: mime type $mime matches extension $extension, passing file\n" ); |
| 986 | + wfDebug( __METHOD__.": mime type $mime matches extension $extension, passing file\n" ); |
995 | 987 | |
996 | 988 | #TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it! |
997 | 989 | return true; |
998 | 990 | |
999 | 991 | } else { |
1000 | | - wfDebug( "$fname: mime type $mime mismatches file extension $extension, rejecting file\n" ); |
| 992 | + wfDebug( __METHOD__.": mime type $mime mismatches file extension $extension, rejecting file\n" ); |
1001 | 993 | return false; |
1002 | 994 | } |
1003 | 995 | } |
1004 | 996 | |
1005 | | - /** Heuristig for detecting files that *could* contain JavaScript instructions or |
1006 | | - * things that may look like HTML to a browser and are thus |
1007 | | - * potentially harmful. The present implementation will produce false positives in some situations. |
1008 | | - * |
1009 | | - * @param string $file Pathname to the temporary upload file |
1010 | | - * @param string $mime The mime type of the file |
1011 | | - * @param string $extension The extension of the file |
1012 | | - * @return bool true if the file contains something looking like embedded scripts |
1013 | | - */ |
| 997 | + /** |
| 998 | + * Heuristic for detecting files that *could* contain JavaScript instructions or |
| 999 | + * things that may look like HTML to a browser and are thus |
| 1000 | + * potentially harmful. The present implementation will produce false positives in some situations. |
| 1001 | + * |
| 1002 | + * @param string $file Pathname to the temporary upload file |
| 1003 | + * @param string $mime The mime type of the file |
| 1004 | + * @param string $extension The extension of the file |
| 1005 | + * @return bool true if the file contains something looking like embedded scripts |
| 1006 | + */ |
1014 | 1007 | function detectScript($file, $mime, $extension) { |
1015 | 1008 | global $wgAllowTitlesInSVG; |
1016 | 1009 | |
— | — | @@ -1098,93 +1091,103 @@ |
1099 | 1092 | return false; |
1100 | 1093 | } |
1101 | 1094 | |
1102 | | - /** Generic wrapper function for a virus scanner program. |
1103 | | - * This relies on the $wgAntivirus and $wgAntivirusSetup variables. |
1104 | | - * $wgAntivirusRequired may be used to deny upload if the scan fails. |
1105 | | - * |
1106 | | - * @param string $file Pathname to the temporary upload file |
1107 | | - * @return mixed false if not virus is found, NULL if the scan fails or is disabled, |
1108 | | - * or a string containing feedback from the virus scanner if a virus was found. |
1109 | | - * If textual feedback is missing but a virus was found, this function returns true. |
1110 | | - */ |
| 1095 | + /** |
| 1096 | + * Generic wrapper function for a virus scanner program. |
| 1097 | + * This relies on the $wgAntivirus and $wgAntivirusSetup variables. |
| 1098 | + * $wgAntivirusRequired may be used to deny upload if the scan fails. |
| 1099 | + * |
| 1100 | + * @param string $file Pathname to the temporary upload file |
| 1101 | + * @return mixed false if not virus is found, NULL if the scan fails or is disabled, |
| 1102 | + * or a string containing feedback from the virus scanner if a virus was found. |
| 1103 | + * If textual feedback is missing but a virus was found, this function returns true. |
| 1104 | + */ |
1111 | 1105 | function detectVirus($file) { |
1112 | 1106 | global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired, $wgOut; |
1113 | 1107 | |
1114 | | - $fname= "SpecialUpload::detectVirus"; |
1115 | | - |
1116 | | - if (!$wgAntivirus) { #disabled? |
1117 | | - wfDebug("$fname: virus scanner disabled\n"); |
1118 | | - |
| 1108 | + if ( !$wgAntivirus ) { |
| 1109 | + wfDebug( __METHOD__.": virus scanner disabled\n"); |
1119 | 1110 | return NULL; |
1120 | 1111 | } |
1121 | 1112 | |
1122 | | - if (!$wgAntivirusSetup[$wgAntivirus]) { |
1123 | | - wfDebug("$fname: unknown virus scanner: $wgAntivirus\n"); |
1124 | | - |
1125 | | - $wgOut->addHTML( "<div class='error'>Bad configuration: unknown virus scanner: <i>$wgAntivirus</i></div>\n" ); #LOCALIZE |
1126 | | - |
| 1113 | + if ( !$wgAntivirusSetup[$wgAntivirus] ) { |
| 1114 | + wfDebug( __METHOD__.": unknown virus scanner: $wgAntivirus\n" ); |
| 1115 | + # @TODO: localise |
| 1116 | + $wgOut->addHTML( "<div class='error'>Bad configuration: unknown virus scanner: <i>$wgAntivirus</i></div>\n" ); |
1127 | 1117 | return "unknown antivirus: $wgAntivirus"; |
1128 | 1118 | } |
1129 | 1119 | |
1130 | | - #look up scanner configuration |
1131 | | - $virus_scanner= $wgAntivirusSetup[$wgAntivirus]["command"]; #command pattern |
1132 | | - $virus_scanner_codes= $wgAntivirusSetup[$wgAntivirus]["codemap"]; #exit-code map |
1133 | | - $msg_pattern= $wgAntivirusSetup[$wgAntivirus]["messagepattern"]; #message pattern |
| 1120 | + # look up scanner configuration |
| 1121 | + $command = $wgAntivirusSetup[$wgAntivirus]["command"]; |
| 1122 | + $exitCodeMap = $wgAntivirusSetup[$wgAntivirus]["codemap"]; |
| 1123 | + $msgPattern = isset( $wgAntivirusSetup[$wgAntivirus]["messagepattern"] ) ? |
| 1124 | + $wgAntivirusSetup[$wgAntivirus]["messagepattern"] : null; |
1134 | 1125 | |
1135 | | - $scanner= $virus_scanner; #copy, so we can resolve the pattern |
| 1126 | + if ( strpos( $command,"%f" ) === false ) { |
| 1127 | + # simple pattern: append file to scan |
| 1128 | + $command .= " " . wfEscapeShellArg( $file ); |
| 1129 | + } else { |
| 1130 | + # complex pattern: replace "%f" with file to scan |
| 1131 | + $command = str_replace( "%f", wfEscapeShellArg( $file ), $command ); |
| 1132 | + } |
1136 | 1133 | |
1137 | | - if (strpos($scanner,"%f")===false) $scanner.= " ".wfEscapeShellArg($file); #simple pattern: append file to scan |
1138 | | - else $scanner= str_replace("%f",wfEscapeShellArg($file),$scanner); #complex pattern: replace "%f" with file to scan |
| 1134 | + wfDebug( __METHOD__.": running virus scan: $command \n" ); |
1139 | 1135 | |
1140 | | - wfDebug("$fname: running virus scan: $scanner \n"); |
| 1136 | + # execute virus scanner |
| 1137 | + $exitCode = false; |
1141 | 1138 | |
1142 | | - #execute virus scanner |
1143 | | - $code= false; |
1144 | | - |
1145 | 1139 | #NOTE: there's a 50 line workaround to make stderr redirection work on windows, too. |
1146 | 1140 | # that does not seem to be worth the pain. |
1147 | 1141 | # Ask me (Duesentrieb) about it if it's ever needed. |
1148 | 1142 | $output = array(); |
1149 | | - if (wfIsWindows()) exec("$scanner",$output,$code); |
1150 | | - else exec("$scanner 2>&1",$output,$code); |
| 1143 | + if ( wfIsWindows() ) { |
| 1144 | + exec( "$command", $output, $exitCode ); |
| 1145 | + } else { |
| 1146 | + exec( "$command 2>&1", $output, $exitCode ); |
| 1147 | + } |
1151 | 1148 | |
1152 | | - $exit_code= $code; #remember for user feedback |
1153 | | - |
1154 | | - if ($virus_scanner_codes) { #map exit code to AV_xxx constants. |
1155 | | - if (isset($virus_scanner_codes[$code])) { |
1156 | | - $code= $virus_scanner_codes[$code]; # explicit mapping |
1157 | | - } else if (isset($virus_scanner_codes["*"])) { |
1158 | | - $code= $virus_scanner_codes["*"]; # fallback mapping |
| 1149 | + # map exit code to AV_xxx constants. |
| 1150 | + $mappedCode = $exitCode; |
| 1151 | + if ( $exitCodeMap ) { |
| 1152 | + if ( isset( $exitCodeMap[$exitCode] ) ) { |
| 1153 | + $mappedCode = $exitCodeMap[$exitCode]; |
| 1154 | + } elseif ( isset( $exitCodeMap["*"] ) ) { |
| 1155 | + $mappedCode = $exitCodeMap["*"]; |
1159 | 1156 | } |
1160 | 1157 | } |
1161 | 1158 | |
1162 | | - if ($code===AV_SCAN_FAILED) { #scan failed (code was mapped to false by $virus_scanner_codes) |
1163 | | - wfDebug("$fname: failed to scan $file (code $exit_code).\n"); |
| 1159 | + if ( $mappedCode === AV_SCAN_FAILED ) { |
| 1160 | + # scan failed (code was mapped to false by $exitCodeMap) |
| 1161 | + wfDebug( __METHOD__.": failed to scan $file (code $exitCode).\n" ); |
1164 | 1162 | |
1165 | | - if ($wgAntivirusRequired) { return "scan failed (code $exit_code)"; } |
1166 | | - else { return NULL; } |
1167 | | - } |
1168 | | - else if ($code===AV_SCAN_ABORTED) { #scan failed because filetype is unknown (probably imune) |
1169 | | - wfDebug("$fname: unsupported file type $file (code $exit_code).\n"); |
| 1163 | + if ( $wgAntivirusRequired ) { |
| 1164 | + return "scan failed (code $exitCode)"; |
| 1165 | + } else { |
| 1166 | + return NULL; |
| 1167 | + } |
| 1168 | + } else if ( $mappedCode === AV_SCAN_ABORTED ) { |
| 1169 | + # scan failed because filetype is unknown (probably imune) |
| 1170 | + wfDebug( __METHOD__.": unsupported file type $file (code $exitCode).\n" ); |
1170 | 1171 | return NULL; |
1171 | | - } |
1172 | | - else if ($code===AV_NO_VIRUS) { |
1173 | | - wfDebug("$fname: file passed virus scan.\n"); |
1174 | | - return false; #no virus found |
1175 | | - } |
1176 | | - else { |
1177 | | - $output= join("\n",$output); |
1178 | | - $output= trim($output); |
| 1172 | + } else if ( $mappedCode === AV_NO_VIRUS ) { |
| 1173 | + # no virus found |
| 1174 | + wfDebug( __METHOD__.": file passed virus scan.\n" ); |
| 1175 | + return false; |
| 1176 | + } else { |
| 1177 | + $output = join( "\n", $output ); |
| 1178 | + $output = trim( $output ); |
1179 | 1179 | |
1180 | | - if (!$output) $output= true; #if there's no output, return true |
1181 | | - else if ($msg_pattern) { |
1182 | | - $groups= array(); |
1183 | | - if (preg_match($msg_pattern,$output,$groups)) { |
1184 | | - if ($groups[1]) $output= $groups[1]; |
| 1180 | + if ( !$output ) { |
| 1181 | + $output = true; #if there's no output, return true |
| 1182 | + } elseif ( $msgPattern ) { |
| 1183 | + $groups = array(); |
| 1184 | + if ( preg_match( $msgPattern, $output, $groups ) ) { |
| 1185 | + if ( $groups[1] ) { |
| 1186 | + $output = $groups[1]; |
| 1187 | + } |
1185 | 1188 | } |
1186 | 1189 | } |
1187 | 1190 | |
1188 | | - wfDebug("$fname: FOUND VIRUS! scanner feedback: $output"); |
| 1191 | + wfDebug( __METHOD__.": FOUND VIRUS! scanner feedback: $output" ); |
1189 | 1192 | return $output; |
1190 | 1193 | } |
1191 | 1194 | } |
— | — | @@ -1198,7 +1201,7 @@ |
1199 | 1202 | * @access private |
1200 | 1203 | */ |
1201 | 1204 | function checkMacBinary() { |
1202 | | - $macbin = new MacBinary( $this->mUploadTempName ); |
| 1205 | + $macbin = new MacBinary( $this->mTempPath ); |
1203 | 1206 | if( $macbin->isValid() ) { |
1204 | 1207 | $dataFile = tempnam( wfTempDir(), "WikiMacBinary" ); |
1205 | 1208 | $dataHandle = fopen( $dataFile, 'wb' ); |
— | — | @@ -1206,8 +1209,8 @@ |
1207 | 1210 | wfDebug( "SpecialUpload::checkMacBinary: Extracting MacBinary data fork to $dataFile\n" ); |
1208 | 1211 | $macbin->extractData( $dataHandle ); |
1209 | 1212 | |
1210 | | - $this->mUploadTempName = $dataFile; |
1211 | | - $this->mUploadSize = $macbin->dataForkLength(); |
| 1213 | + $this->mTempPath = $dataFile; |
| 1214 | + $this->mFileSize = $macbin->dataForkLength(); |
1212 | 1215 | |
1213 | 1216 | // We'll have to manually remove the new file if it's not kept. |
1214 | 1217 | $this->mRemoveTempFile = true; |
— | — | @@ -1221,9 +1224,9 @@ |
1222 | 1225 | * @access private |
1223 | 1226 | */ |
1224 | 1227 | function cleanupTempFile() { |
1225 | | - if( $this->mRemoveTempFile && file_exists( $this->mUploadTempName ) ) { |
1226 | | - wfDebug( "SpecialUpload::cleanupTempFile: Removing temporary file $this->mUploadTempName\n" ); |
1227 | | - unlink( $this->mUploadTempName ); |
| 1228 | + if ( $this->mRemoveTempFile && file_exists( $this->mTempPath ) ) { |
| 1229 | + wfDebug( "SpecialUpload::cleanupTempFile: Removing temporary file {$this->mTempPath}\n" ); |
| 1230 | + unlink( $this->mTempPath ); |
1228 | 1231 | } |
1229 | 1232 | } |
1230 | 1233 | |
— | — | @@ -1296,5 +1299,30 @@ |
1297 | 1300 | $wgOut->enableClientCache( false ); |
1298 | 1301 | $wgOut->addWikiText( $error->getMessage() ); |
1299 | 1302 | } |
| 1303 | + |
| 1304 | + /** |
| 1305 | + * Get the initial image page text based on a comment and optional file status information |
| 1306 | + */ |
| 1307 | + static function getInitialPageText( $comment, $license, $copyStatus, $source ) { |
| 1308 | + global $wgUseCopyrightUpload; |
| 1309 | + if ( $wgUseCopyrightUpload ) { |
| 1310 | + if ( $license != '' ) { |
| 1311 | + $licensetxt = '== ' . wfMsgForContent( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n"; |
| 1312 | + } |
| 1313 | + $pageText = '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $comment . "\n" . |
| 1314 | + '== ' . wfMsgForContent ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" . |
| 1315 | + "$licensetxt" . |
| 1316 | + '== ' . wfMsgForContent ( 'filesource' ) . " ==\n" . $source ; |
| 1317 | + } else { |
| 1318 | + if ( $license != '' ) { |
| 1319 | + $filedesc = $comment == '' ? '' : '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $comment . "\n"; |
| 1320 | + $pageText = $filedesc . |
| 1321 | + '== ' . wfMsgForContent ( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n"; |
| 1322 | + } else { |
| 1323 | + $pageText = $comment; |
| 1324 | + } |
| 1325 | + } |
| 1326 | + return $pageText; |
| 1327 | + } |
1300 | 1328 | } |
1301 | 1329 | ?> |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -254,6 +254,7 @@ |
255 | 255 | # filerepo |
256 | 256 | 'ArchivedFile' => 'includes/filerepo/ArchivedFile.php', |
257 | 257 | 'File' => 'includes/filerepo/File.php', |
| 258 | + 'FileRepo' => 'includes/filerepo/FileRepo.php', |
258 | 259 | 'ForeignDBFile' => 'includes/filerepo/ForeignDBFile.php', |
259 | 260 | 'ForeignDBRepo' => 'includes/filerepo/ForeignDBRepo.php', |
260 | 261 | 'FSRepo' => 'includes/filerepo/FSRepo.php', |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -308,34 +308,34 @@ |
309 | 309 | * |
310 | 310 | * @global array $wgAntivirusSetup |
311 | 311 | */ |
312 | | -$wgAntivirusSetup= array( |
| 312 | +$wgAntivirusSetup = array( |
313 | 313 | |
314 | 314 | #setup for clamav |
315 | 315 | 'clamav' => array ( |
316 | 316 | 'command' => "clamscan --no-summary ", |
317 | 317 | |
318 | | - 'codemap'=> array ( |
319 | | - "0"=> AV_NO_VIRUS, #no virus |
320 | | - "1"=> AV_VIRUS_FOUND, #virus found |
321 | | - "52"=> AV_SCAN_ABORTED, #unsupported file format (probably imune) |
322 | | - "*"=> AV_SCAN_FAILED, #else scan failed |
| 318 | + 'codemap' => array ( |
| 319 | + "0" => AV_NO_VIRUS, # no virus |
| 320 | + "1" => AV_VIRUS_FOUND, # virus found |
| 321 | + "52" => AV_SCAN_ABORTED, # unsupported file format (probably imune) |
| 322 | + "*" => AV_SCAN_FAILED, # else scan failed |
323 | 323 | ), |
324 | 324 | |
325 | | - 'messagepattern'=> '/.*?:(.*)/sim', |
| 325 | + 'messagepattern' => '/.*?:(.*)/sim', |
326 | 326 | ), |
327 | 327 | |
328 | 328 | #setup for f-prot |
329 | 329 | 'f-prot' => array ( |
330 | 330 | 'command' => "f-prot ", |
331 | 331 | |
332 | | - 'codemap'=> array ( |
333 | | - "0"=> AV_NO_VIRUS, #no virus |
334 | | - "3"=> AV_VIRUS_FOUND, #virus found |
335 | | - "6"=> AV_VIRUS_FOUND, #virus found |
336 | | - "*"=> AV_SCAN_FAILED, #else scan failed |
| 332 | + 'codemap' => array ( |
| 333 | + "0" => AV_NO_VIRUS, # no virus |
| 334 | + "3" => AV_VIRUS_FOUND, # virus found |
| 335 | + "6" => AV_VIRUS_FOUND, # virus found |
| 336 | + "*" => AV_SCAN_FAILED, # else scan failed |
337 | 337 | ), |
338 | 338 | |
339 | | - 'messagepattern'=> '/.*?Infection:(.*)$/m', |
| 339 | + 'messagepattern' => '/.*?Infection:(.*)$/m', |
340 | 340 | ), |
341 | 341 | ); |
342 | 342 | |
Index: trunk/phase3/includes/LogPage.php |
— | — | @@ -79,20 +79,24 @@ |
80 | 80 | # And update recentchanges |
81 | 81 | if ( $this->updateRecentChanges ) { |
82 | 82 | $titleObj = SpecialPage::getTitleFor( 'Log', $this->type ); |
83 | | - $rcComment = $this->actionText; |
84 | | - if( '' != $this->comment ) { |
85 | | - if ($rcComment == '') |
86 | | - $rcComment = $this->comment; |
87 | | - else |
88 | | - $rcComment .= ': ' . $this->comment; |
89 | | - } |
90 | | - |
| 83 | + $rcComment = $this->getRcComment(); |
91 | 84 | RecentChange::notifyLog( $now, $titleObj, $wgUser, $rcComment, '', |
92 | 85 | $this->type, $this->action, $this->target, $this->comment, $this->params ); |
93 | 86 | } |
94 | 87 | return true; |
95 | 88 | } |
96 | 89 | |
| 90 | + public function getRcComment() { |
| 91 | + $rcComment = $this->actionText; |
| 92 | + if( '' != $this->comment ) { |
| 93 | + if ($rcComment == '') |
| 94 | + $rcComment = $this->comment; |
| 95 | + else |
| 96 | + $rcComment .= ': ' . $this->comment; |
| 97 | + } |
| 98 | + return $rcComment; |
| 99 | + } |
| 100 | + |
97 | 101 | /** |
98 | 102 | * @static |
99 | 103 | */ |