Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 99 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
ImportFiles | |
0.00% |
0 / 96 |
|
0.00% |
0 / 5 |
552 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
30 | |||
getFileList | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
30 | |||
error | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
processFile | |
0.00% |
0 / 45 |
|
0.00% |
0 / 1 |
132 |
1 | <?php |
2 | |
3 | /** |
4 | * Import images as NSFileRepo file. |
5 | * Therefor the part of the filename till first underscore |
6 | * is used as namespace. |
7 | * |
8 | * Expampe: |
9 | * A file 'ABC_My_File.png' will be uploaded to the wiki as 'ABC:My File.png' |
10 | */ |
11 | |
12 | require_once dirname( dirname( dirname( __DIR__ ) ) ) . '/maintenance/Maintenance.php'; |
13 | |
14 | use MediaWiki\MediaWikiServices; |
15 | |
16 | class ImportFiles extends Maintenance { |
17 | |
18 | /** |
19 | * @var string |
20 | */ |
21 | protected $src = ''; |
22 | |
23 | /** |
24 | * @var array |
25 | */ |
26 | protected $errors = []; |
27 | |
28 | /** |
29 | * |
30 | */ |
31 | public function __construct() { |
32 | $this->addOption( 'overwrite', 'Overwrite existing files?' ); |
33 | $this->addOption( 'dry', 'Dry run. Do not actually upload files to the repo' ); |
34 | $this->addOption( 'summary', 'A summary for all file uploads' ); |
35 | $this->addOption( 'comment', 'A comment for all file uploads' ); |
36 | $this->addOption( 'verbose', 'More verbose output' ); |
37 | $this->addArg( 'dir', 'Path to the directory containing images to be imported' ); |
38 | } |
39 | |
40 | /** |
41 | * @return void |
42 | */ |
43 | public function execute() { |
44 | $this->src = $this->getArg( 0 ); |
45 | |
46 | $files = $this->getFileList(); |
47 | |
48 | $processedFiles = 0; |
49 | foreach ( $files as $fileName => $file ) { |
50 | if ( $file instanceof SplFileInfo !== true ) { |
51 | $this->error( 'Could not process list item: ' |
52 | . $fileName . ' ' |
53 | . var_export( $file, true ) |
54 | ); |
55 | continue; |
56 | } |
57 | $this->output( 'Processing ' . $file->getPathname() . " ... \n" ); |
58 | $mResult = $this->processFile( $file ); |
59 | if ( $mResult !== true ) { |
60 | $this->error( " ... error: $mResult\n\n" ); |
61 | } else { |
62 | $this->output( " ... done.\n\n" ); |
63 | $processedFiles++; |
64 | } |
65 | } |
66 | |
67 | $this->output( "$processedFiles file(s) processed.\n" ); |
68 | $this->output( count( $this->errors ) . " errors(s) occurred.\n" ); |
69 | if ( count( $this->errors ) > 0 ) { |
70 | $this->output( |
71 | implode( "\n", $this->errors ) |
72 | ); |
73 | } |
74 | } |
75 | |
76 | /** |
77 | * @return array |
78 | */ |
79 | public function getFileList() { |
80 | global $wgFileExtensions; |
81 | $fileExtensions = array_map( 'strtolower', $wgFileExtensions ); |
82 | |
83 | $realPath = realPath( $this->src ); |
84 | $this->output( 'Fetching file list from "' . $realPath . '"' ); |
85 | |
86 | $iterator = new RecursiveIteratorIterator( |
87 | new RecursiveDirectoryIterator( $realPath ), |
88 | RecursiveIteratorIterator::SELF_FIRST |
89 | ); |
90 | |
91 | $files = []; |
92 | foreach ( $iterator as $realPath => $file ) { |
93 | if ( $file instanceof SplFileInfo === false ) { |
94 | $this->error( 'Not a valid SplFileInfo object: ' . $realPath ); |
95 | } |
96 | if ( !empty( $fileExtensions ) ) { |
97 | $fileExt = strtolower( $file->getExtension() ); |
98 | if ( !in_array( $fileExt, $fileExtensions ) ) { |
99 | continue; |
100 | } |
101 | } |
102 | $files[$file->getPathname()] = $file; |
103 | } |
104 | |
105 | ksort( $files, SORT_NATURAL ); |
106 | $fileCount = count( $files ); |
107 | $this->output( " ... found $fileCount file(s)\n" ); |
108 | |
109 | return $files; |
110 | } |
111 | |
112 | /** |
113 | * Throw an error to the user. Doesn't respect --quiet, so don't use |
114 | * this for non-error output |
115 | * |
116 | * @param string $error String: the error to display |
117 | * @param int $die Int: if > 0, go ahead and die out using this int as the code |
118 | */ |
119 | public function error( $error, $die = 0 ) { |
120 | $this->errors[] = $error; |
121 | parent::error( $error, $die ); |
122 | } |
123 | |
124 | /** |
125 | * @param SplFileInfo $file |
126 | * @return bool |
127 | */ |
128 | public function processFile( $file ) { |
129 | $filename = $file->getFileName(); |
130 | $services = MediaWikiServices::getInstance(); |
131 | |
132 | // NSFileRep: Use the text till first '_' as namespace |
133 | $pos = strpos( $filename, '_' ); |
134 | if ( $pos !== false ) { |
135 | $namespace = substr( $filename, 0, $pos ); |
136 | if ( $namespace !== false ) { |
137 | $filename = str_replace( $namespace . '_', $namespace . ':', $filename ); |
138 | } |
139 | } |
140 | |
141 | // MediaWiki normalizes multiple spaces/undescores into one single score/underscore |
142 | $filename = str_replace( ' ', '_', $filename ); |
143 | $filename = preg_replace( '#(_)+#si', '_', $filename ); |
144 | |
145 | $targetTitle = Title::makeTitle( NS_FILE, $filename ); |
146 | $repo = $services->getRepoGroup()->getLocalRepo(); |
147 | |
148 | $this->output( "Using target title {$targetTitle->getPrefixedDBkey()} " ); |
149 | |
150 | $repoFile = $repo->newFile( $targetTitle ); |
151 | if ( $repoFile->exists() ) { |
152 | if ( !$this->hasOption( 'overwrite' ) ) { |
153 | $this->output( "File '{$repoFile->getName()}' already exists. Skipping...\n" ); |
154 | return true; |
155 | } else { |
156 | $this->output( "File '{$repoFile->getName()}' already exists. Overwriting...\n" ); |
157 | } |
158 | } |
159 | |
160 | /* |
161 | * The following code is almost a dirext copy of |
162 | * <mediawiki>/maintenance/importImages.php |
163 | */ |
164 | $commentText = $this->getOption( 'comment', '' ); |
165 | |
166 | if ( !$this->hasOption( 'dry' ) ) { |
167 | $mwProps = new MWFileProps( $services->getMimeAnalyzer() ); |
168 | $props = $mwProps->getPropsFromPath( $file->getPathname(), true ); |
169 | $flags = 0; |
170 | $publishOptions = []; |
171 | $handler = MediaHandler::getHandler( $props['mime'] ); |
172 | if ( $handler ) { |
173 | $metadata = \Wikimedia\AtEase\AtEase::quietCall( 'unserialize', $props['metadata'] ); |
174 | |
175 | $publishOptions['headers'] = $handler->getContentHeaders( $metadata ); |
176 | } else { |
177 | $publishOptions['headers'] = []; |
178 | } |
179 | $archive = $repoFile->publish( $file->getPathname(), $flags, $publishOptions ); |
180 | if ( !$archive->isGood() ) { |
181 | $this->output( "failed. (" . |
182 | $archive->getMessage( false, false, 'en' )->text() . |
183 | ")\n" ); |
184 | } |
185 | } |
186 | |
187 | $commentText = SpecialUpload::getInitialPageText( $commentText, '' ); |
188 | $summary = $this->getOption( 'summary', '' ); |
189 | $user = User::newSystemUser( 'Maintenance script', [ 'steal' => true ] ); |
190 | |
191 | if ( $this->hasOption( 'dry' ) ) { |
192 | $this->output( "done.\n" ); |
193 | } elseif ( $repoFile->recordUpload3( $archive->value, $summary, $commentText, $user, $props ) ) { |
194 | $this->output( "done.\n" ); |
195 | } |
196 | |
197 | if ( $this->hasOption( 'verbose' ) ) { |
198 | $this->output( "Canonical Filename: {$repoFile->getName()}\n" ); |
199 | $this->output( "Canonical URL: {$repoFile->getCanonicalUrl()}" ); |
200 | } |
201 | |
202 | return true; |
203 | } |
204 | } |
205 | |
206 | $maintClass = ImportFiles::class; |
207 | require_once RUN_MAINTENANCE_IF_MAIN; |