Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 168
0.00% covered (danger)
0.00%
0 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 2
NSLocalFile
0.00% covered (danger)
0.00%
0 / 134
0.00% covered (danger)
0.00%
0 / 19
2162
0.00% covered (danger)
0.00%
0 / 1
 getRel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getThumbRel
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getArchiveRel
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 getUrlRel
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getThumbUrl
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 thumbName
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 generateThumbName
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 getArchiveUrl
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getArchiveThumbRel
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 getArchiveThumbUrl
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 purgeThumbList
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
 getArchiveVirtualUrl
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getThumbVirtualUrl
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getFileNameStripped
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 publishTo
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 move
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 newFromTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 newFromRow
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 newFromKey
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
NSLocalFileMoveBatch
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 3
56
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 addOlds
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
30
 getFileNameStripped
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3class NSLocalFile extends LocalFile {
4    /**
5     * Get the path of the file relative to the public zone root
6     */
7    function getRel() {
8        return $this->getHashPath() . self::getFileNameStripped( $this->getName() );
9    }
10    /**
11     * Get the path, relative to the thumbnail zone root, of the
12     * thumbnail directory or a particular file if $suffix is specified
13     *
14     * @param $suffix bool|string if not false, the name of a thumbnail file
15     *
16     * @return string
17     */
18    function getThumbRel( $suffix = false ) {
19        $path = $this->getRel();
20        if ( $suffix !== false ) {
21            /* This is the part that changed from LocalFile */
22            $path .= '/' . self::getFileNameStripped( $suffix );
23            /* End of changes */
24        }
25        return $path;
26    }
27
28    /**
29     * Get the path of an archived file relative to the public zone root
30     *
31     * @param $suffix bool|string if not false, the name of an archived thumbnail file
32     *
33     * @return string
34     */
35    function getArchiveRel( $suffix = false ) {
36        $path = 'archive/' . $this->getHashPath();
37        if ( $suffix === false ) {
38            $path = substr( $path, 0, -1 );
39        } else {
40/* This is the part that changed from LocalFile */
41            $path .= '/' . self::getFileNameStripped( $suffix );
42/* End of changes */
43        }
44        return $path;
45    }
46
47
48
49    /**
50     * Get urlencoded relative path of the file
51     */
52    function getUrlRel() {
53        return $this->getHashPath() .
54            rawurlencode( self::getFileNameStripped( $this->getName() ) );
55    }
56
57    /**
58     * Get the URL of the thumbnail directory, or a particular file if $suffix is specified
59     *
60     * @param $suffix bool|string if not false, the name of a thumbnail file
61     *
62     * @return string path
63     */
64    function getThumbUrl( $suffix = false ) {
65        $path = $this->repo->getZoneUrl('thumb') . '/' . $this->getUrlRel();
66        if ( $suffix !== false ) {
67            $path .= '/' . rawurlencode( self::getFileNameStripped( $suffix ) );
68        }
69        return $path;
70    }
71
72
73    public function thumbName( $params, $flags = 0 ) {
74        $name = ( $this->repo && !( $flags & self::THUMB_FULL_NAME ) )
75/* This is the part that changed from LocalFile */
76            ? $this->repo->nameForThumb( self::getFileNameStripped( $this->getName() ) )
77            : self::getFileNameStripped( $this->getName() );
78/* End of changes */
79        return $this->generateThumbName( $name, $params );
80    }
81
82    /**
83     * Generate a thumbnail file name from a name and specified parameters
84     *
85     * @param string $name
86     * @param array $params Parameters which will be passed to MediaHandler::makeParamString
87     *
88     * @return string
89     */
90    public function generateThumbName( $name, $params ) {
91        if ( !$this->getHandler() ) {
92            return null;
93        }
94        $extension = $this->getExtension();
95        list( $thumbExt, $thumbMime ) = $this->handler->getThumbType(
96            $extension, $this->getMimeType(), $params );
97/* This is the part that changed from LocalFile */
98        $thumbName = $this->handler->makeParamString( $params ) . '-' .
99            self::getFileNameStripped( $this->getName() );
100/* End of changes */
101        if ( $thumbExt != $extension ) {
102            $thumbName .= ".$thumbExt";
103        }
104/* And also need to retain namespace changed from LocalFile */
105        $bits = explode( ':',$this->getName() );
106        if ( count($bits) > 1 ) $thumbName = $bits[0] . ":" . $thumbName;
107/* End of changes */
108        return $thumbName;
109    }
110
111    /**
112     * Get the URL of the archive directory, or a particular file if $suffix is specified
113     *
114     * @param $suffix bool|string if not false, the name of an archived file
115     *
116     * @return string
117     */
118    function getArchiveUrl( $suffix = false ) {
119        $this->assertRepoDefined();
120        $ext = $this->getExtension();
121        $path = $this->repo->getZoneUrl( 'public', $ext ) . '/archive/' . $this->getHashPath();
122        if ( $suffix === false ) {
123            $path = substr( $path, 0, -1 );
124        } else {
125/* This is the part that changed from LocalFile */
126            $path .= rawurlencode( self::getFileNameStripped( $suffix ) );
127/* End of changes */
128        }
129        return $path;
130    }
131
132    /**
133     * Get the path, relative to the thumbnail zone root, for an archived file's thumbs directory
134     * or a specific thumb if the $suffix is given.
135     *
136     * @param string $archiveName The timestamped name of an archived image
137     * @param bool|string $suffix If not false, the name of a thumbnail file
138     * @return string
139     */
140    function getArchiveThumbRel( $archiveName, $suffix = false ) {
141        $path = 'archive/' . $this->getHashPath() . $archiveName . "/";
142        if ( $suffix === false ) {
143            $path = substr( $path, 0, -1 );
144        } else {
145/* This is the part that changed from LocalFile */
146            $path .= self::getFileNameStripped( $suffix );
147/* End of changes */
148        }
149
150        return $path;
151    }
152
153    /**
154     * Get the URL of the archived file's thumbs, or a particular thumb if $suffix is specified
155     *
156     * @param string $archiveName The timestamped name of an archived image
157     * @param bool|string $suffix If not false, the name of a thumbnail file
158     * @return string
159     */
160    function getArchiveThumbUrl( $archiveName, $suffix = false ) {
161        $this->assertRepoDefined();
162        $ext = $this->getExtension();
163        $path = $this->repo->getZoneUrl( 'thumb', $ext ) . '/archive/' .
164            $this->getHashPath() . rawurlencode( $archiveName ) . "/";
165        if ( $suffix === false ) {
166            $path = substr( $path, 0, -1 );
167        } else {
168/* This is the part that changed from LocalFile */
169            $path .= rawurlencode( self::getFileNameStripped( $suffix ) );
170/* End of changes */
171        }
172
173        return $path;
174    }
175
176    /**
177     * Delete cached transformed files for the current version only.
178     * @param array $options
179     */
180    protected function purgeThumbList( $dir, $files ) {
181
182        $purgeList = [];
183        foreach ( $files as $file ) {
184            if ( $this->repo->supportsSha1URLs() ) {
185                $reference = $this->getSha1();
186            } else {
187                //change from LocalFile.php here
188                $reference = $this->getFileNameStripped($this->getName());
189            }
190
191            # Check that the reference (filename or sha1) is part of the thumb name
192            # This is a basic sanity check to avoid erasing unrelated directories
193            if ( strpos( $file, $reference ) !== false
194                || strpos( $file, "-thumbnail" ) !== false // "short" thumb name
195            ) {
196                $purgeList[] = "{$dir}/{$file}";
197            }
198        }
199
200        # Delete the thumbnails
201        $this->repo->quickPurgeBatch( $purgeList );
202        # Clear out the thumbnail directory if empty
203        $this->repo->quickCleanDir( $dir );
204    }
205    /**
206     * Get the public zone virtual URL for an archived version source file
207     *
208     * @param $suffix bool|string if not false, the name of a thumbnail file
209     *
210     * @return string
211     */
212    function getArchiveVirtualUrl( $suffix = false ) {
213        $this->assertRepoDefined();
214        $path = $this->repo->getVirtualUrl() . '/public/archive/' . $this->getHashPath();
215        if ( $suffix === false ) {
216            $path = substr( $path, 0, -1 );
217        } else {
218/* This is the part that changed from LocalFile */
219            $path .= rawurlencode( self::getFileNameStripped( $suffix ) );
220/* End of changes */
221        }
222        return $path;
223    }
224
225    /**
226     * Get the virtual URL for a thumbnail file or directory
227     *
228     * @param $suffix bool|string if not false, the name of a thumbnail file
229     *
230     * @return string
231     */
232    function getThumbVirtualUrl( $suffix = false ) {
233        $this->assertRepoDefined();
234        $path = $this->repo->getVirtualUrl() . '/thumb/' . $this->getUrlRel();
235        if ( $suffix !== false ) {
236            $path .= '/' . rawurlencode( $suffix );
237/* This is the part that changed from LocalFile */
238            $path .= '/' . rawurlencode( self::getFileNameStripped( $suffix ) );
239/* End of changes */
240        }
241        return $path;
242    }
243
244    /**
245     * Strip namespace (if any) from file name
246     *
247     * @param $suffix the name of a thumbnail file
248     *
249     * @return string
250     */
251    public static function getFileNameStripped($suffix) {
252        $iNsEndPos = strpos( $suffix, ":");
253        if( $iNsEndPos ){
254            $sRes = '';
255            $iTsEndPos = strpos( $suffix, "!" );
256
257            if( $iTsEndPos ) {
258                $sRes = substr( $suffix, 0, $iTsEndPos +1 );
259            }
260            $sRes .= substr( $suffix,  $iNsEndPos +1, strlen( $suffix ) -1 );
261
262            return $sRes;
263        } else {
264            return $suffix;
265        }
266    }
267
268    /**
269     * Move or copy a file to a specified location. Returns a FileRepoStatus
270     * object with the archive name in the "value" member on success.
271     *
272     * The archive name should be passed through to recordUpload for database
273     * registration.
274     *
275     * @param $srcPath String: local filesystem path to the source image
276     * @param $dstRel String: target relative path
277     * @param $flags Integer: a bitwise combination of:
278     *     File::DELETE_SOURCE    Delete the source file, i.e. move rather than copy
279     * @param $options array Optional additional parameters
280     * @return Status object. On success, the value member contains the
281     *     archive name, or an empty string if it was a new file.
282     */
283    function publishTo( $srcPath, $dstRel, $flags = 0, array $options = array() ) {
284        if ( $this->getRepo()->getReadOnlyReason() !== false ) {
285            return $this->readOnlyFatalStatus();
286        }
287
288        $this->lock(); // begin
289
290        $archiveName = wfTimestamp( TS_MW ) . '!'. $this->getName();
291/* This is the part that changed from LocalFile */
292        $strippedArchiveName = wfTimestamp( TS_MW ) . '!'. self::getFileNameStripped( $this->getName() );
293        $archiveRel = 'archive/' . $this->getHashPath() . $strippedArchiveName;
294/* End of changes */
295        $flags = $flags & File::DELETE_SOURCE ? LocalRepo::DELETE_SOURCE : 0;
296        $status = $this->repo->publish( $srcPath, $dstRel, $archiveRel, $flags, $options );
297
298        if ( $status->value == 'new' ) {
299            $status->value = '';
300        } else {
301            $status->value = $archiveName;
302        }
303
304        $this->purgeThumbnails();
305        $this->unlock(); // done
306
307        return $status;
308    }
309
310    /**
311     * Move file to the new title
312     *
313     * Move current, old version and all thumbnails
314     * to the new filename. Old file is deleted.
315     *
316     * Cache purging is done; checks for validity
317     * and logging are caller's responsibility
318     *
319     * @param $target Title New file name
320     * @return Status object.
321     */
322    function move( $target ) {
323        if ( $this->getRepo()->getReadOnlyReason() !== false ) {
324            return $this->readOnlyFatalStatus();
325        }
326
327        wfDebugLog( 'imagemove', "Got request to move {$this->name} to " . $target->getText() );
328/* This is the part that changed from LocalFile */
329        $batch = new NSLocalFileMoveBatch( $this, $target );
330/* End of changes */
331
332        $this->lock(); // begin
333        $batch->addCurrent();
334        $archiveNames = $batch->addOlds();
335        $status = $batch->execute();
336        $this->unlock(); // done
337
338        wfDebugLog( 'imagemove', "Finished moving {$this->name}" );
339
340        $this->purgeEverything();
341        foreach ( $archiveNames as $archiveName ) {
342            $this->purgeOldThumbnails( $archiveName );
343        }
344        if ( $status->isOK() ) {
345            // Now switch the object
346            $this->title = $target;
347            // Force regeneration of the name and hashpath
348            unset( $this->name );
349            unset( $this->hashPath );
350            // Purge the new image
351            $this->purgeEverything();
352        }
353
354        return $status;
355    }
356
357
358    /** Instantiating this class using "self"
359     * If you're reading this, you're problably wondering why on earth are the following static functions, which are copied
360     * verbatim from the original extended class "LocalFIle" included here?
361     * The answer is that "self", will instantiate the class the code is physically in, not the class extended from it.
362     * Without the inclusion of these methods in "NSLocalFile, "self" would instantiate a "LocalFile" class, not the
363     * "NSLocalFile" class we want it to.  Since there are only two methods within the "LocalFile" class that use "self",
364     * I just copied that code into the new "NSLocalFile" extended class, and the copied code will instantiate the "NSLocalFIle"
365     * class instead of the "LocalFile" class (at least in PHP 5.2.4)
366     */
367
368    /**
369     * Create a NSLocalFile from a title
370     * Do not call this except from inside a repo class.
371     *
372     * Note: $unused param is only here to avoid an E_STRICT
373     */
374    static function newFromTitle( $title, $repo, $unused = null ) {
375        return new self( $title, $repo );
376    }
377    /**
378     * Create a NSLocalFile from a title
379     * Do not call this except from inside a repo class.
380     */
381
382    static function newFromRow( $row, $repo ) {
383        $title = Title::makeTitle( NS_FILE, $row->img_name );
384        $file = new self( $title, $repo );
385        $file->loadFromRow( $row );
386        return $file;
387    }
388
389    /**
390     * Create a NSLocalFile from a SHA-1 key
391     * Do not call this except from inside a repo class.
392     *
393     * Copy & paste from LocalFile to fix "late-static-binding" issue
394     *
395     * @param string $sha1 Base-36 SHA-1
396     * @param LocalRepo $repo
397     * @param string|bool $timestamp MW_timestamp (optional)
398     * @return bool|LocalFile
399     */
400    static function newFromKey( $sha1, $repo, $timestamp = false ) {
401        $dbr = $repo->getReplicaDB();
402
403        $conds = [ 'img_sha1' => $sha1 ];
404        if ( $timestamp ) {
405            $conds['img_timestamp'] = $dbr->timestamp( $timestamp );
406        }
407
408        $fileQuery = static::getQueryInfo();
409        $row = $dbr->selectRow(
410            $fileQuery['tables'], $fileQuery['fields'], $conds, __METHOD__, [], $fileQuery['joins']
411        );
412        if ( $row ) {
413            return static::newFromRow( $row, $repo );
414        } else {
415            return false;
416        }
417    }
418}
419
420/**
421 * Helper class for file movement
422 * @ingroup FileAbstraction
423 */
424class NSLocalFileMoveBatch extends LocalFileMoveBatch {
425    /**
426     * @param File $file
427     * @param Title $target
428     */
429    public function __construct( LocalFile $file, Title $target ) {
430        parent::__construct( $file, $target );
431        $this->oldRel = $this->oldHash . NSLocalFile::getFileNameStripped( $this->oldName );
432        $this->newRel = $this->newHash . NSLocalFile::getFileNameStripped( $this->newName );
433    }
434
435    /**
436     * Add the old versions of the image to the batch
437     * @return array List of archive names from old versions
438     */
439    function addOlds() {
440/* This is the part that changed from LocalFile */
441        $newName = $this->getFileNameStripped( $this->newName );
442/* End of changes */
443        $archiveBase = 'archive';
444        $this->olds = array();
445        $this->oldCount = 0;
446        $archiveNames = array();
447
448        $result = $this->db->select( 'oldimage',
449            array( 'oi_archive_name', 'oi_deleted' ),
450            array( 'oi_name' => $this->oldName ),
451            __METHOD__
452        );
453
454        foreach ( $result as $row ) {
455            $archiveNames[] = $row->oi_archive_name;
456            $oldName = $row->oi_archive_name;
457            $bits = explode( '!', $oldName, 2 );
458
459            if ( count( $bits ) != 2 ) {
460                wfDebug( "Old file name missing !: '$oldName' \n" );
461                continue;
462            }
463
464            list( $timestamp, $filename ) = $bits;
465
466            if ( $this->oldName != $filename ) {
467                wfDebug( "Old file name doesn't match: '$oldName' \n" );
468                continue;
469            }
470/* This is the part that changed from LocalFileMoveBatch */
471            #When file is moved within a namespace we do not want it
472            #looking to NS:Name format in FS
473            $strippedOldName = $this->file->getFileNameStripped( $oldName );
474/* End of changes */
475
476            $this->oldCount++;
477
478            // Do we want to add those to oldCount?
479            if ( $row->oi_deleted & File::DELETED_FILE ) {
480                continue;
481            }
482/* This is the part that changed from LocalFile */
483            $this->olds[] = array(
484                "{$archiveBase}/{$this->oldHash}{$strippedOldName}",
485                "{$archiveBase}/{$this->newHash}{$timestamp}!{$newName}"
486            );
487/* End of changes */
488        }
489
490        return $archiveNames;
491    }
492
493    function getFileNameStripped( $suffix ) {
494        return(NSLocalFile::getFileNameStripped($suffix));
495    }
496}