This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Implements Special:Upload |
||
4 | * |
||
5 | * This program is free software; you can redistribute it and/or modify |
||
6 | * it under the terms of the GNU General Public License as published by |
||
7 | * the Free Software Foundation; either version 2 of the License, or |
||
8 | * (at your option) any later version. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | * GNU General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU General Public License along |
||
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
18 | * http://www.gnu.org/copyleft/gpl.html |
||
19 | * |
||
20 | * @file |
||
21 | * @ingroup SpecialPage |
||
22 | * @ingroup Upload |
||
23 | */ |
||
24 | |||
25 | use MediaWiki\Linker\LinkRenderer; |
||
26 | use MediaWiki\MediaWikiServices; |
||
27 | |||
28 | /** |
||
29 | * Form for handling uploads and special page. |
||
30 | * |
||
31 | * @ingroup SpecialPage |
||
32 | * @ingroup Upload |
||
33 | */ |
||
34 | class SpecialUpload extends SpecialPage { |
||
35 | /** |
||
36 | * Constructor : initialise object |
||
37 | * Get data POSTed through the form and assign them to the object |
||
38 | * @param WebRequest $request Data posted. |
||
39 | */ |
||
40 | public function __construct( $request = null ) { |
||
41 | parent::__construct( 'Upload', 'upload' ); |
||
42 | } |
||
43 | |||
44 | public function doesWrites() { |
||
45 | return true; |
||
46 | } |
||
47 | |||
48 | /** Misc variables **/ |
||
49 | |||
50 | /** @var WebRequest|FauxRequest The request this form is supposed to handle */ |
||
51 | public $mRequest; |
||
52 | public $mSourceType; |
||
53 | |||
54 | /** @var UploadBase */ |
||
55 | public $mUpload; |
||
56 | |||
57 | /** @var LocalFile */ |
||
58 | public $mLocalFile; |
||
59 | public $mUploadClicked; |
||
60 | |||
61 | /** User input variables from the "description" section **/ |
||
62 | |||
63 | /** @var string The requested target file name */ |
||
64 | public $mDesiredDestName; |
||
65 | public $mComment; |
||
66 | public $mLicense; |
||
67 | |||
68 | /** User input variables from the root section **/ |
||
69 | |||
70 | public $mIgnoreWarning; |
||
71 | public $mWatchthis; |
||
72 | public $mCopyrightStatus; |
||
73 | public $mCopyrightSource; |
||
74 | |||
75 | /** Hidden variables **/ |
||
76 | |||
77 | public $mDestWarningAck; |
||
78 | |||
79 | /** @var bool The user followed an "overwrite this file" link */ |
||
80 | public $mForReUpload; |
||
81 | |||
82 | /** @var bool The user clicked "Cancel and return to upload form" button */ |
||
83 | public $mCancelUpload; |
||
84 | public $mTokenOk; |
||
85 | |||
86 | /** @var bool Subclasses can use this to determine whether a file was uploaded */ |
||
87 | public $mUploadSuccessful = false; |
||
88 | |||
89 | /** Text injection points for hooks not using HTMLForm **/ |
||
90 | public $uploadFormTextTop; |
||
91 | public $uploadFormTextAfterSummary; |
||
92 | |||
93 | /** |
||
94 | * Initialize instance variables from request and create an Upload handler |
||
95 | */ |
||
96 | protected function loadRequest() { |
||
97 | $this->mRequest = $request = $this->getRequest(); |
||
98 | $this->mSourceType = $request->getVal( 'wpSourceType', 'file' ); |
||
99 | $this->mUpload = UploadBase::createFromRequest( $request ); |
||
100 | $this->mUploadClicked = $request->wasPosted() |
||
101 | && ( $request->getCheck( 'wpUpload' ) |
||
102 | || $request->getCheck( 'wpUploadIgnoreWarning' ) ); |
||
103 | |||
104 | // Guess the desired name from the filename if not provided |
||
105 | $this->mDesiredDestName = $request->getText( 'wpDestFile' ); |
||
106 | if ( !$this->mDesiredDestName && $request->getFileName( 'wpUploadFile' ) !== null ) { |
||
107 | $this->mDesiredDestName = $request->getFileName( 'wpUploadFile' ); |
||
108 | } |
||
109 | $this->mLicense = $request->getText( 'wpLicense' ); |
||
110 | |||
111 | $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' ); |
||
112 | $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ) |
||
113 | || $request->getCheck( 'wpUploadIgnoreWarning' ); |
||
114 | $this->mWatchthis = $request->getBool( 'wpWatchthis' ) && $this->getUser()->isLoggedIn(); |
||
115 | $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' ); |
||
116 | $this->mCopyrightSource = $request->getText( 'wpUploadSource' ); |
||
117 | |||
118 | $this->mForReUpload = $request->getBool( 'wpForReUpload' ); // updating a file |
||
119 | |||
120 | $commentDefault = ''; |
||
121 | $commentMsg = wfMessage( 'upload-default-description' )->inContentLanguage(); |
||
122 | if ( !$this->mForReUpload && !$commentMsg->isDisabled() ) { |
||
123 | $commentDefault = $commentMsg->plain(); |
||
124 | } |
||
125 | $this->mComment = $request->getText( 'wpUploadDescription', $commentDefault ); |
||
126 | |||
127 | $this->mCancelUpload = $request->getCheck( 'wpCancelUpload' ) |
||
128 | || $request->getCheck( 'wpReUpload' ); // b/w compat |
||
129 | |||
130 | // If it was posted check for the token (no remote POST'ing with user credentials) |
||
131 | $token = $request->getVal( 'wpEditToken' ); |
||
132 | $this->mTokenOk = $this->getUser()->matchEditToken( $token ); |
||
133 | |||
134 | $this->uploadFormTextTop = ''; |
||
135 | $this->uploadFormTextAfterSummary = ''; |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * This page can be shown if uploading is enabled. |
||
140 | * Handle permission checking elsewhere in order to be able to show |
||
141 | * custom error messages. |
||
142 | * |
||
143 | * @param User $user |
||
144 | * @return bool |
||
145 | */ |
||
146 | public function userCanExecute( User $user ) { |
||
147 | return UploadBase::isEnabled() && parent::userCanExecute( $user ); |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * Special page entry point |
||
152 | * @param string $par |
||
153 | * @throws ErrorPageError |
||
154 | * @throws Exception |
||
155 | * @throws FatalError |
||
156 | * @throws MWException |
||
157 | * @throws PermissionsError |
||
158 | * @throws ReadOnlyError |
||
159 | * @throws UserBlockedError |
||
160 | */ |
||
161 | public function execute( $par ) { |
||
162 | $this->useTransactionalTimeLimit(); |
||
163 | |||
164 | $this->setHeaders(); |
||
165 | $this->outputHeader(); |
||
166 | |||
167 | # Check uploading enabled |
||
168 | if ( !UploadBase::isEnabled() ) { |
||
169 | throw new ErrorPageError( 'uploaddisabled', 'uploaddisabledtext' ); |
||
170 | } |
||
171 | |||
172 | $this->addHelpLink( 'Help:Managing files' ); |
||
173 | |||
174 | # Check permissions |
||
175 | $user = $this->getUser(); |
||
176 | $permissionRequired = UploadBase::isAllowed( $user ); |
||
177 | if ( $permissionRequired !== true ) { |
||
178 | throw new PermissionsError( $permissionRequired ); |
||
0 ignored issues
–
show
|
|||
179 | } |
||
180 | |||
181 | # Check blocks |
||
182 | if ( $user->isBlocked() ) { |
||
183 | throw new UserBlockedError( $user->getBlock() ); |
||
0 ignored issues
–
show
It seems like
$user->getBlock() can be null ; however, __construct() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
184 | } |
||
185 | |||
186 | // Global blocks |
||
187 | if ( $user->isBlockedGlobally() ) { |
||
188 | throw new UserBlockedError( $user->getGlobalBlock() ); |
||
0 ignored issues
–
show
It seems like
$user->getGlobalBlock() can be null ; however, __construct() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
189 | } |
||
190 | |||
191 | # Check whether we actually want to allow changing stuff |
||
192 | $this->checkReadOnly(); |
||
193 | |||
194 | $this->loadRequest(); |
||
195 | |||
196 | # Unsave the temporary file in case this was a cancelled upload |
||
197 | if ( $this->mCancelUpload ) { |
||
198 | if ( !$this->unsaveUploadedFile() ) { |
||
199 | # Something went wrong, so unsaveUploadedFile showed a warning |
||
200 | return; |
||
201 | } |
||
202 | } |
||
203 | |||
204 | # Process upload or show a form |
||
205 | if ( |
||
206 | $this->mTokenOk && !$this->mCancelUpload && |
||
207 | ( $this->mUpload && $this->mUploadClicked ) |
||
208 | ) { |
||
209 | $this->processUpload(); |
||
210 | } else { |
||
211 | # Backwards compatibility hook |
||
212 | if ( !Hooks::run( 'UploadForm:initial', [ &$this ] ) ) { |
||
213 | wfDebug( "Hook 'UploadForm:initial' broke output of the upload form\n" ); |
||
214 | |||
215 | return; |
||
216 | } |
||
217 | $this->showUploadForm( $this->getUploadForm() ); |
||
218 | } |
||
219 | |||
220 | # Cleanup |
||
221 | if ( $this->mUpload ) { |
||
222 | $this->mUpload->cleanupTempFile(); |
||
223 | } |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Show the main upload form |
||
228 | * |
||
229 | * @param HTMLForm|string $form An HTMLForm instance or HTML string to show |
||
230 | */ |
||
231 | protected function showUploadForm( $form ) { |
||
232 | # Add links if file was previously deleted |
||
233 | if ( $this->mDesiredDestName ) { |
||
234 | $this->showViewDeletedLinks(); |
||
235 | } |
||
236 | |||
237 | if ( $form instanceof HTMLForm ) { |
||
238 | $form->show(); |
||
239 | } else { |
||
240 | $this->getOutput()->addHTML( $form ); |
||
241 | } |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * Get an UploadForm instance with title and text properly set. |
||
246 | * |
||
247 | * @param string $message HTML string to add to the form |
||
248 | * @param string $sessionKey Session key in case this is a stashed upload |
||
249 | * @param bool $hideIgnoreWarning Whether to hide "ignore warning" check box |
||
250 | * @return UploadForm |
||
251 | */ |
||
252 | protected function getUploadForm( $message = '', $sessionKey = '', $hideIgnoreWarning = false ) { |
||
253 | # Initialize form |
||
254 | $context = new DerivativeContext( $this->getContext() ); |
||
255 | $context->setTitle( $this->getPageTitle() ); // Remove subpage |
||
256 | $form = new UploadForm( [ |
||
257 | 'watch' => $this->getWatchCheck(), |
||
258 | 'forreupload' => $this->mForReUpload, |
||
259 | 'sessionkey' => $sessionKey, |
||
260 | 'hideignorewarning' => $hideIgnoreWarning, |
||
261 | 'destwarningack' => (bool)$this->mDestWarningAck, |
||
262 | |||
263 | 'description' => $this->mComment, |
||
264 | 'texttop' => $this->uploadFormTextTop, |
||
265 | 'textaftersummary' => $this->uploadFormTextAfterSummary, |
||
266 | 'destfile' => $this->mDesiredDestName, |
||
267 | ], $context, $this->getLinkRenderer() ); |
||
268 | |||
269 | # Check the token, but only if necessary |
||
270 | if ( |
||
271 | !$this->mTokenOk && !$this->mCancelUpload && |
||
272 | ( $this->mUpload && $this->mUploadClicked ) |
||
273 | ) { |
||
274 | $form->addPreText( $this->msg( 'session_fail_preview' )->parse() ); |
||
275 | } |
||
276 | |||
277 | # Give a notice if the user is uploading a file that has been deleted or moved |
||
278 | # Note that this is independent from the message 'filewasdeleted' |
||
279 | $desiredTitleObj = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); |
||
280 | $delNotice = ''; // empty by default |
||
281 | if ( $desiredTitleObj instanceof Title && !$desiredTitleObj->exists() ) { |
||
282 | LogEventsList::showLogExtract( $delNotice, [ 'delete', 'move' ], |
||
283 | $desiredTitleObj, |
||
284 | '', [ 'lim' => 10, |
||
285 | 'conds' => [ "log_action != 'revision'" ], |
||
286 | 'showIfEmpty' => false, |
||
287 | 'msgKey' => [ 'upload-recreate-warning' ] ] |
||
288 | ); |
||
289 | } |
||
290 | $form->addPreText( $delNotice ); |
||
0 ignored issues
–
show
It seems like
$delNotice can also be of type object<OutputPage> ; however, HTMLForm::addPreText() does only seem to accept string , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
291 | |||
292 | # Add text to form |
||
293 | $form->addPreText( '<div id="uploadtext">' . |
||
294 | $this->msg( 'uploadtext', [ $this->mDesiredDestName ] )->parseAsBlock() . |
||
295 | '</div>' ); |
||
296 | # Add upload error message |
||
297 | $form->addPreText( $message ); |
||
298 | |||
299 | # Add footer to form |
||
300 | $uploadFooter = $this->msg( 'uploadfooter' ); |
||
301 | if ( !$uploadFooter->isDisabled() ) { |
||
302 | $form->addPostText( '<div id="mw-upload-footer-message">' |
||
303 | . $uploadFooter->parseAsBlock() . "</div>\n" ); |
||
304 | } |
||
305 | |||
306 | return $form; |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * Shows the "view X deleted revivions link"" |
||
311 | */ |
||
312 | protected function showViewDeletedLinks() { |
||
313 | $title = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); |
||
314 | $user = $this->getUser(); |
||
315 | // Show a subtitle link to deleted revisions (to sysops et al only) |
||
316 | if ( $title instanceof Title ) { |
||
317 | $count = $title->isDeleted(); |
||
318 | if ( $count > 0 && $user->isAllowed( 'deletedhistory' ) ) { |
||
319 | $restorelink = $this->getLinkRenderer()->makeKnownLink( |
||
320 | SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ), |
||
321 | $this->msg( 'restorelink' )->numParams( $count )->text() |
||
322 | ); |
||
323 | $link = $this->msg( $user->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted' ) |
||
324 | ->rawParams( $restorelink )->parseAsBlock(); |
||
325 | $this->getOutput()->addHTML( "<div id=\"contentSub2\">{$link}</div>" ); |
||
326 | } |
||
327 | } |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * Stashes the upload and shows the main upload form. |
||
332 | * |
||
333 | * Note: only errors that can be handled by changing the name or |
||
334 | * description should be redirected here. It should be assumed that the |
||
335 | * file itself is sane and has passed UploadBase::verifyFile. This |
||
336 | * essentially means that UploadBase::VERIFICATION_ERROR and |
||
337 | * UploadBase::EMPTY_FILE should not be passed here. |
||
338 | * |
||
339 | * @param string $message HTML message to be passed to mainUploadForm |
||
340 | */ |
||
341 | protected function showRecoverableUploadError( $message ) { |
||
342 | $stashStatus = $this->mUpload->tryStashFile( $this->getUser() ); |
||
343 | if ( $stashStatus->isGood() ) { |
||
344 | $sessionKey = $stashStatus->getValue()->getFileKey(); |
||
345 | } else { |
||
346 | $sessionKey = null; |
||
347 | // TODO Add a warning message about the failure to stash here? |
||
348 | } |
||
349 | $message = '<h2>' . $this->msg( 'uploaderror' )->escaped() . "</h2>\n" . |
||
350 | '<div class="error">' . $message . "</div>\n"; |
||
351 | |||
352 | $form = $this->getUploadForm( $message, $sessionKey ); |
||
353 | $form->setSubmitText( $this->msg( 'upload-tryagain' )->escaped() ); |
||
354 | $this->showUploadForm( $form ); |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * Stashes the upload, shows the main form, but adds a "continue anyway button". |
||
359 | * Also checks whether there are actually warnings to display. |
||
360 | * |
||
361 | * @param array $warnings |
||
362 | * @return bool True if warnings were displayed, false if there are no |
||
363 | * warnings and it should continue processing |
||
364 | */ |
||
365 | protected function showUploadWarning( $warnings ) { |
||
366 | # If there are no warnings, or warnings we can ignore, return early. |
||
367 | # mDestWarningAck is set when some javascript has shown the warning |
||
368 | # to the user. mForReUpload is set when the user clicks the "upload a |
||
369 | # new version" link. |
||
370 | if ( !$warnings || ( count( $warnings ) == 1 |
||
371 | && isset( $warnings['exists'] ) |
||
372 | && ( $this->mDestWarningAck || $this->mForReUpload ) ) |
||
373 | ) { |
||
374 | return false; |
||
375 | } |
||
376 | |||
377 | $stashStatus = $this->mUpload->tryStashFile( $this->getUser() ); |
||
378 | if ( $stashStatus->isGood() ) { |
||
379 | $sessionKey = $stashStatus->getValue()->getFileKey(); |
||
380 | } else { |
||
381 | $sessionKey = null; |
||
382 | // TODO Add a warning message about the failure to stash here? |
||
383 | } |
||
384 | |||
385 | // Add styles for the warning, reused from the live preview |
||
386 | $this->getOutput()->addModuleStyles( 'mediawiki.special.upload.styles' ); |
||
387 | |||
388 | $linkRenderer = $this->getLinkRenderer(); |
||
389 | $warningHtml = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" |
||
390 | . '<div class="mw-destfile-warning"><ul>'; |
||
391 | foreach ( $warnings as $warning => $args ) { |
||
392 | if ( $warning == 'badfilename' ) { |
||
393 | $this->mDesiredDestName = Title::makeTitle( NS_FILE, $args )->getText(); |
||
394 | } |
||
395 | if ( $warning == 'exists' ) { |
||
396 | $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n"; |
||
397 | } elseif ( $warning == 'no-change' ) { |
||
398 | $file = $args; |
||
399 | $filename = $file->getTitle()->getPrefixedText(); |
||
400 | $msg = "\t<li>" . wfMessage( 'fileexists-no-change', $filename )->parse() . "</li>\n"; |
||
401 | } elseif ( $warning == 'duplicate-version' ) { |
||
402 | $file = $args[0]; |
||
403 | $count = count( $args ); |
||
404 | $filename = $file->getTitle()->getPrefixedText(); |
||
405 | $message = wfMessage( 'fileexists-duplicate-version' ) |
||
406 | ->params( $filename ) |
||
407 | ->numParams( $count ); |
||
408 | $msg = "\t<li>" . $message->parse() . "</li>\n"; |
||
409 | } elseif ( $warning == 'was-deleted' ) { |
||
410 | # If the file existed before and was deleted, warn the user of this |
||
411 | $ltitle = SpecialPage::getTitleFor( 'Log' ); |
||
412 | $llink = $linkRenderer->makeKnownLink( |
||
413 | $ltitle, |
||
414 | wfMessage( 'deletionlog' )->text(), |
||
415 | [], |
||
416 | [ |
||
417 | 'type' => 'delete', |
||
418 | 'page' => Title::makeTitle( NS_FILE, $args )->getPrefixedText(), |
||
419 | ] |
||
420 | ); |
||
421 | $msg = "\t<li>" . wfMessage( 'filewasdeleted' )->rawParams( $llink )->parse() . "</li>\n"; |
||
422 | } elseif ( $warning == 'duplicate' ) { |
||
423 | $msg = $this->getDupeWarning( $args ); |
||
424 | } elseif ( $warning == 'duplicate-archive' ) { |
||
425 | if ( $args === '' ) { |
||
426 | $msg = "\t<li>" . $this->msg( 'file-deleted-duplicate-notitle' )->parse() |
||
427 | . "</li>\n"; |
||
428 | } else { |
||
429 | $msg = "\t<li>" . $this->msg( 'file-deleted-duplicate', |
||
430 | Title::makeTitle( NS_FILE, $args )->getPrefixedText() )->parse() |
||
431 | . "</li>\n"; |
||
432 | } |
||
433 | } else { |
||
434 | View Code Duplication | if ( $args === true ) { |
|
435 | $args = []; |
||
436 | } elseif ( !is_array( $args ) ) { |
||
437 | $args = [ $args ]; |
||
438 | } |
||
439 | $msg = "\t<li>" . $this->msg( $warning, $args )->parse() . "</li>\n"; |
||
440 | } |
||
441 | $warningHtml .= $msg; |
||
442 | } |
||
443 | $warningHtml .= "</ul></div>\n"; |
||
444 | $warningHtml .= $this->msg( 'uploadwarning-text' )->parseAsBlock(); |
||
445 | |||
446 | $form = $this->getUploadForm( $warningHtml, $sessionKey, /* $hideIgnoreWarning */ true ); |
||
447 | $form->setSubmitText( $this->msg( 'upload-tryagain' )->text() ); |
||
448 | $form->addButton( [ |
||
449 | 'name' => 'wpUploadIgnoreWarning', |
||
450 | 'value' => $this->msg( 'ignorewarning' )->text() |
||
451 | ] ); |
||
452 | $form->addButton( [ |
||
453 | 'name' => 'wpCancelUpload', |
||
454 | 'value' => $this->msg( 'reuploaddesc' )->text() |
||
455 | ] ); |
||
456 | |||
457 | $this->showUploadForm( $form ); |
||
458 | |||
459 | # Indicate that we showed a form |
||
460 | return true; |
||
461 | } |
||
462 | |||
463 | /** |
||
464 | * Show the upload form with error message, but do not stash the file. |
||
465 | * |
||
466 | * @param string $message HTML string |
||
467 | */ |
||
468 | protected function showUploadError( $message ) { |
||
469 | $message = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" . |
||
470 | '<div class="error">' . $message . "</div>\n"; |
||
471 | $this->showUploadForm( $this->getUploadForm( $message ) ); |
||
472 | } |
||
473 | |||
474 | /** |
||
475 | * Do the upload. |
||
476 | * Checks are made in SpecialUpload::execute() |
||
477 | */ |
||
478 | protected function processUpload() { |
||
479 | // Fetch the file if required |
||
480 | $status = $this->mUpload->fetchFile(); |
||
481 | if ( !$status->isOK() ) { |
||
482 | $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) ); |
||
483 | |||
484 | return; |
||
485 | } |
||
486 | |||
487 | if ( !Hooks::run( 'UploadForm:BeforeProcessing', [ &$this ] ) ) { |
||
488 | wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" ); |
||
489 | // This code path is deprecated. If you want to break upload processing |
||
490 | // do so by hooking into the appropriate hooks in UploadBase::verifyUpload |
||
491 | // and UploadBase::verifyFile. |
||
492 | // If you use this hook to break uploading, the user will be returned |
||
493 | // an empty form with no error message whatsoever. |
||
494 | return; |
||
495 | } |
||
496 | |||
497 | // Upload verification |
||
498 | $details = $this->mUpload->verifyUpload(); |
||
499 | if ( $details['status'] != UploadBase::OK ) { |
||
500 | $this->processVerificationError( $details ); |
||
0 ignored issues
–
show
It seems like
$details defined by $this->mUpload->verifyUpload() on line 498 can also be of type boolean ; however, SpecialUpload::processVerificationError() does only seem to accept array , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
501 | |||
502 | return; |
||
503 | } |
||
504 | |||
505 | // Verify permissions for this title |
||
506 | $permErrors = $this->mUpload->verifyTitlePermissions( $this->getUser() ); |
||
507 | if ( $permErrors !== true ) { |
||
508 | $code = array_shift( $permErrors[0] ); |
||
509 | $this->showRecoverableUploadError( $this->msg( $code, $permErrors[0] )->parse() ); |
||
510 | |||
511 | return; |
||
512 | } |
||
513 | |||
514 | $this->mLocalFile = $this->mUpload->getLocalFile(); |
||
515 | |||
516 | // Check warnings if necessary |
||
517 | if ( !$this->mIgnoreWarning ) { |
||
518 | $warnings = $this->mUpload->checkWarnings(); |
||
519 | if ( $this->showUploadWarning( $warnings ) ) { |
||
520 | return; |
||
521 | } |
||
522 | } |
||
523 | |||
524 | // This is as late as we can throttle, after expected issues have been handled |
||
525 | if ( UploadBase::isThrottled( $this->getUser() ) ) { |
||
526 | $this->showRecoverableUploadError( |
||
527 | $this->msg( 'actionthrottledtext' )->escaped() |
||
528 | ); |
||
529 | return; |
||
530 | } |
||
531 | |||
532 | // Get the page text if this is not a reupload |
||
533 | if ( !$this->mForReUpload ) { |
||
534 | $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, |
||
535 | $this->mCopyrightStatus, $this->mCopyrightSource, $this->getConfig() ); |
||
536 | } else { |
||
537 | $pageText = false; |
||
538 | } |
||
539 | |||
540 | $changeTags = $this->getRequest()->getVal( 'wpChangeTags' ); |
||
541 | if ( is_null( $changeTags ) || $changeTags === '' ) { |
||
542 | $changeTags = []; |
||
543 | } else { |
||
544 | $changeTags = array_filter( array_map( 'trim', explode( ',', $changeTags ) ) ); |
||
545 | } |
||
546 | |||
547 | if ( $changeTags ) { |
||
548 | $changeTagsStatus = ChangeTags::canAddTagsAccompanyingChange( |
||
549 | $changeTags, $this->getUser() ); |
||
550 | if ( !$changeTagsStatus->isOK() ) { |
||
551 | $this->showUploadError( $this->getOutput()->parse( $changeTagsStatus->getWikiText() ) ); |
||
552 | |||
553 | return; |
||
554 | } |
||
555 | } |
||
556 | |||
557 | $status = $this->mUpload->performUpload( |
||
558 | $this->mComment, |
||
559 | $pageText, |
||
0 ignored issues
–
show
It seems like
$pageText defined by false on line 537 can also be of type false ; however, UploadBase::performUpload() does only seem to accept string , did you maybe forget to handle an error condition?
This check looks for type mismatches where the missing type is Consider the follow example <?php
function getDate($date)
{
if ($date !== null) {
return new DateTime($date);
}
return false;
}
This function either returns a new ![]() |
|||
560 | $this->mWatchthis, |
||
561 | $this->getUser(), |
||
562 | $changeTags |
||
563 | ); |
||
564 | |||
565 | if ( !$status->isGood() ) { |
||
566 | $this->showRecoverableUploadError( $this->getOutput()->parse( $status->getWikiText() ) ); |
||
567 | |||
568 | return; |
||
569 | } |
||
570 | |||
571 | // Success, redirect to description page |
||
572 | $this->mUploadSuccessful = true; |
||
573 | Hooks::run( 'SpecialUploadComplete', [ &$this ] ); |
||
574 | $this->getOutput()->redirect( $this->mLocalFile->getTitle()->getFullURL() ); |
||
575 | } |
||
576 | |||
577 | /** |
||
578 | * Get the initial image page text based on a comment and optional file status information |
||
579 | * @param string $comment |
||
580 | * @param string $license |
||
581 | * @param string $copyStatus |
||
582 | * @param string $source |
||
583 | * @param Config $config Configuration object to load data from |
||
584 | * @return string |
||
585 | */ |
||
586 | public static function getInitialPageText( $comment = '', $license = '', |
||
587 | $copyStatus = '', $source = '', Config $config = null |
||
588 | ) { |
||
589 | View Code Duplication | if ( $config === null ) { |
|
590 | wfDebug( __METHOD__ . ' called without a Config instance passed to it' ); |
||
591 | $config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' ); |
||
592 | } |
||
593 | |||
594 | $msg = []; |
||
595 | $forceUIMsgAsContentMsg = (array)$config->get( 'ForceUIMsgAsContentMsg' ); |
||
596 | /* These messages are transcluded into the actual text of the description page. |
||
597 | * Thus, forcing them as content messages makes the upload to produce an int: template |
||
598 | * instead of hardcoding it there in the uploader language. |
||
599 | */ |
||
600 | foreach ( [ 'license-header', 'filedesc', 'filestatus', 'filesource' ] as $msgName ) { |
||
601 | if ( in_array( $msgName, $forceUIMsgAsContentMsg ) ) { |
||
602 | $msg[$msgName] = "{{int:$msgName}}"; |
||
603 | } else { |
||
604 | $msg[$msgName] = wfMessage( $msgName )->inContentLanguage()->text(); |
||
605 | } |
||
606 | } |
||
607 | |||
608 | if ( $config->get( 'UseCopyrightUpload' ) ) { |
||
609 | $licensetxt = ''; |
||
610 | if ( $license != '' ) { |
||
611 | $licensetxt = '== ' . $msg['license-header'] . " ==\n" . '{{' . $license . '}}' . "\n"; |
||
612 | } |
||
613 | $pageText = '== ' . $msg['filedesc'] . " ==\n" . $comment . "\n" . |
||
614 | '== ' . $msg['filestatus'] . " ==\n" . $copyStatus . "\n" . |
||
615 | "$licensetxt" . |
||
616 | '== ' . $msg['filesource'] . " ==\n" . $source; |
||
617 | } else { |
||
618 | if ( $license != '' ) { |
||
619 | $filedesc = $comment == '' ? '' : '== ' . $msg['filedesc'] . " ==\n" . $comment . "\n"; |
||
620 | $pageText = $filedesc . |
||
621 | '== ' . $msg['license-header'] . " ==\n" . '{{' . $license . '}}' . "\n"; |
||
622 | } else { |
||
623 | $pageText = $comment; |
||
624 | } |
||
625 | } |
||
626 | |||
627 | return $pageText; |
||
628 | } |
||
629 | |||
630 | /** |
||
631 | * See if we should check the 'watch this page' checkbox on the form |
||
632 | * based on the user's preferences and whether we're being asked |
||
633 | * to create a new file or update an existing one. |
||
634 | * |
||
635 | * In the case where 'watch edits' is off but 'watch creations' is on, |
||
636 | * we'll leave the box unchecked. |
||
637 | * |
||
638 | * Note that the page target can be changed *on the form*, so our check |
||
639 | * state can get out of sync. |
||
640 | * @return bool|string |
||
641 | */ |
||
642 | protected function getWatchCheck() { |
||
643 | if ( $this->getUser()->getOption( 'watchdefault' ) ) { |
||
644 | // Watch all edits! |
||
645 | return true; |
||
646 | } |
||
647 | |||
648 | $desiredTitleObj = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); |
||
649 | if ( $desiredTitleObj instanceof Title && $this->getUser()->isWatched( $desiredTitleObj ) ) { |
||
650 | // Already watched, don't change that |
||
651 | return true; |
||
652 | } |
||
653 | |||
654 | $local = wfLocalFile( $this->mDesiredDestName ); |
||
655 | if ( $local && $local->exists() ) { |
||
656 | // We're uploading a new version of an existing file. |
||
657 | // No creation, so don't watch it if we're not already. |
||
658 | return false; |
||
659 | } else { |
||
660 | // New page should get watched if that's our option. |
||
661 | return $this->getUser()->getOption( 'watchcreations' ) || |
||
662 | $this->getUser()->getOption( 'watchuploads' ); |
||
663 | } |
||
664 | } |
||
665 | |||
666 | /** |
||
667 | * Provides output to the user for a result of UploadBase::verifyUpload |
||
668 | * |
||
669 | * @param array $details Result of UploadBase::verifyUpload |
||
670 | * @throws MWException |
||
671 | */ |
||
672 | protected function processVerificationError( $details ) { |
||
673 | switch ( $details['status'] ) { |
||
674 | |||
675 | /** Statuses that only require name changing **/ |
||
676 | case UploadBase::MIN_LENGTH_PARTNAME: |
||
677 | $this->showRecoverableUploadError( $this->msg( 'minlength1' )->escaped() ); |
||
678 | break; |
||
679 | case UploadBase::ILLEGAL_FILENAME: |
||
680 | $this->showRecoverableUploadError( $this->msg( 'illegalfilename', |
||
681 | $details['filtered'] )->parse() ); |
||
682 | break; |
||
683 | case UploadBase::FILENAME_TOO_LONG: |
||
684 | $this->showRecoverableUploadError( $this->msg( 'filename-toolong' )->escaped() ); |
||
685 | break; |
||
686 | case UploadBase::FILETYPE_MISSING: |
||
687 | $this->showRecoverableUploadError( $this->msg( 'filetype-missing' )->parse() ); |
||
688 | break; |
||
689 | case UploadBase::WINDOWS_NONASCII_FILENAME: |
||
690 | $this->showRecoverableUploadError( $this->msg( 'windows-nonascii-filename' )->parse() ); |
||
691 | break; |
||
692 | |||
693 | /** Statuses that require reuploading **/ |
||
694 | case UploadBase::EMPTY_FILE: |
||
695 | $this->showUploadError( $this->msg( 'emptyfile' )->escaped() ); |
||
696 | break; |
||
697 | case UploadBase::FILE_TOO_LARGE: |
||
698 | $this->showUploadError( $this->msg( 'largefileserver' )->escaped() ); |
||
699 | break; |
||
700 | case UploadBase::FILETYPE_BADTYPE: |
||
701 | $msg = $this->msg( 'filetype-banned-type' ); |
||
702 | if ( isset( $details['blacklistedExt'] ) ) { |
||
703 | $msg->params( $this->getLanguage()->commaList( $details['blacklistedExt'] ) ); |
||
704 | } else { |
||
705 | $msg->params( $details['finalExt'] ); |
||
706 | } |
||
707 | $extensions = array_unique( $this->getConfig()->get( 'FileExtensions' ) ); |
||
708 | $msg->params( $this->getLanguage()->commaList( $extensions ), |
||
709 | count( $extensions ) ); |
||
710 | |||
711 | // Add PLURAL support for the first parameter. This results |
||
712 | // in a bit unlogical parameter sequence, but does not break |
||
713 | // old translations |
||
714 | if ( isset( $details['blacklistedExt'] ) ) { |
||
715 | $msg->params( count( $details['blacklistedExt'] ) ); |
||
716 | } else { |
||
717 | $msg->params( 1 ); |
||
718 | } |
||
719 | |||
720 | $this->showUploadError( $msg->parse() ); |
||
721 | break; |
||
722 | case UploadBase::VERIFICATION_ERROR: |
||
723 | unset( $details['status'] ); |
||
724 | $code = array_shift( $details['details'] ); |
||
725 | $this->showUploadError( $this->msg( $code, $details['details'] )->parse() ); |
||
726 | break; |
||
727 | case UploadBase::HOOK_ABORTED: |
||
728 | if ( is_array( $details['error'] ) ) { # allow hooks to return error details in an array |
||
729 | $args = $details['error']; |
||
730 | $error = array_shift( $args ); |
||
731 | } else { |
||
732 | $error = $details['error']; |
||
733 | $args = null; |
||
734 | } |
||
735 | |||
736 | $this->showUploadError( $this->msg( $error, $args )->parse() ); |
||
737 | break; |
||
738 | default: |
||
739 | throw new MWException( __METHOD__ . ": Unknown value `{$details['status']}`" ); |
||
740 | } |
||
741 | } |
||
742 | |||
743 | /** |
||
744 | * Remove a temporarily kept file stashed by saveTempUploadedFile(). |
||
745 | * |
||
746 | * @return bool Success |
||
747 | */ |
||
748 | protected function unsaveUploadedFile() { |
||
749 | if ( !( $this->mUpload instanceof UploadFromStash ) ) { |
||
750 | return true; |
||
751 | } |
||
752 | $success = $this->mUpload->unsaveUploadedFile(); |
||
753 | if ( !$success ) { |
||
754 | $this->getOutput()->showFileDeleteError( $this->mUpload->getTempPath() ); |
||
755 | |||
756 | return false; |
||
757 | } else { |
||
758 | return true; |
||
759 | } |
||
760 | } |
||
761 | |||
762 | /*** Functions for formatting warnings ***/ |
||
763 | |||
764 | /** |
||
765 | * Formats a result of UploadBase::getExistsWarning as HTML |
||
766 | * This check is static and can be done pre-upload via AJAX |
||
767 | * |
||
768 | * @param array $exists The result of UploadBase::getExistsWarning |
||
769 | * @return string Empty string if there is no warning or an HTML fragment |
||
770 | */ |
||
771 | public static function getExistsWarning( $exists ) { |
||
772 | if ( !$exists ) { |
||
773 | return ''; |
||
774 | } |
||
775 | |||
776 | $file = $exists['file']; |
||
777 | $filename = $file->getTitle()->getPrefixedText(); |
||
778 | $warnMsg = null; |
||
779 | |||
780 | if ( $exists['warning'] == 'exists' ) { |
||
781 | // Exact match |
||
782 | $warnMsg = wfMessage( 'fileexists', $filename ); |
||
783 | } elseif ( $exists['warning'] == 'page-exists' ) { |
||
784 | // Page exists but file does not |
||
785 | $warnMsg = wfMessage( 'filepageexists', $filename ); |
||
786 | } elseif ( $exists['warning'] == 'exists-normalized' ) { |
||
787 | $warnMsg = wfMessage( 'fileexists-extension', $filename, |
||
788 | $exists['normalizedFile']->getTitle()->getPrefixedText() ); |
||
789 | } elseif ( $exists['warning'] == 'thumb' ) { |
||
790 | // Swapped argument order compared with other messages for backwards compatibility |
||
791 | $warnMsg = wfMessage( 'fileexists-thumbnail-yes', |
||
792 | $exists['thumbFile']->getTitle()->getPrefixedText(), $filename ); |
||
793 | } elseif ( $exists['warning'] == 'thumb-name' ) { |
||
794 | // Image w/o '180px-' does not exists, but we do not like these filenames |
||
795 | $name = $file->getName(); |
||
796 | $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 ); |
||
797 | $warnMsg = wfMessage( 'file-thumbnail-no', $badPart ); |
||
798 | } elseif ( $exists['warning'] == 'bad-prefix' ) { |
||
799 | $warnMsg = wfMessage( 'filename-bad-prefix', $exists['prefix'] ); |
||
800 | } |
||
801 | |||
802 | return $warnMsg ? $warnMsg->title( $file->getTitle() )->parse() : ''; |
||
803 | } |
||
804 | |||
805 | /** |
||
806 | * Construct a warning and a gallery from an array of duplicate files. |
||
807 | * @param array $dupes |
||
808 | * @return string |
||
809 | */ |
||
810 | public function getDupeWarning( $dupes ) { |
||
811 | if ( !$dupes ) { |
||
812 | return ''; |
||
813 | } |
||
814 | |||
815 | $gallery = ImageGalleryBase::factory( false, $this->getContext() ); |
||
816 | $gallery->setShowBytes( false ); |
||
817 | foreach ( $dupes as $file ) { |
||
818 | $gallery->add( $file->getTitle() ); |
||
819 | } |
||
820 | |||
821 | return '<li>' . |
||
822 | $this->msg( 'file-exists-duplicate' )->numParams( count( $dupes ) )->parse() . |
||
823 | $gallery->toHTML() . "</li>\n"; |
||
824 | } |
||
825 | |||
826 | protected function getGroupName() { |
||
827 | return 'media'; |
||
828 | } |
||
829 | |||
830 | /** |
||
831 | * Should we rotate images in the preview on Special:Upload. |
||
832 | * |
||
833 | * This controls js: mw.config.get( 'wgFileCanRotate' ) |
||
834 | * |
||
835 | * @todo What about non-BitmapHandler handled files? |
||
836 | */ |
||
837 | public static function rotationEnabled() { |
||
838 | $bitmapHandler = new BitmapHandler(); |
||
839 | return $bitmapHandler->autoRotateEnabled(); |
||
840 | } |
||
841 | } |
||
842 | |||
843 | /** |
||
844 | * Sub class of HTMLForm that provides the form section of SpecialUpload |
||
845 | */ |
||
846 | class UploadForm extends HTMLForm { |
||
847 | protected $mWatch; |
||
848 | protected $mForReUpload; |
||
849 | protected $mSessionKey; |
||
850 | protected $mHideIgnoreWarning; |
||
851 | protected $mDestWarningAck; |
||
852 | protected $mDestFile; |
||
853 | |||
854 | protected $mComment; |
||
855 | protected $mTextTop; |
||
856 | protected $mTextAfterSummary; |
||
857 | |||
858 | protected $mSourceIds; |
||
859 | |||
860 | protected $mMaxFileSize = []; |
||
861 | |||
862 | protected $mMaxUploadSize = []; |
||
863 | |||
864 | public function __construct( array $options = [], IContextSource $context = null, |
||
865 | LinkRenderer $linkRenderer = null |
||
866 | ) { |
||
867 | if ( $context instanceof IContextSource ) { |
||
868 | $this->setContext( $context ); |
||
869 | } |
||
870 | |||
871 | if ( !$linkRenderer ) { |
||
872 | $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); |
||
873 | } |
||
874 | |||
875 | $this->mWatch = !empty( $options['watch'] ); |
||
876 | $this->mForReUpload = !empty( $options['forreupload'] ); |
||
877 | $this->mSessionKey = isset( $options['sessionkey'] ) ? $options['sessionkey'] : ''; |
||
878 | $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] ); |
||
879 | $this->mDestWarningAck = !empty( $options['destwarningack'] ); |
||
880 | $this->mDestFile = isset( $options['destfile'] ) ? $options['destfile'] : ''; |
||
881 | |||
882 | $this->mComment = isset( $options['description'] ) ? |
||
883 | $options['description'] : ''; |
||
884 | |||
885 | $this->mTextTop = isset( $options['texttop'] ) |
||
886 | ? $options['texttop'] : ''; |
||
887 | |||
888 | $this->mTextAfterSummary = isset( $options['textaftersummary'] ) |
||
889 | ? $options['textaftersummary'] : ''; |
||
890 | |||
891 | $sourceDescriptor = $this->getSourceSection(); |
||
892 | $descriptor = $sourceDescriptor |
||
893 | + $this->getDescriptionSection() |
||
894 | + $this->getOptionsSection(); |
||
895 | |||
896 | Hooks::run( 'UploadFormInitDescriptor', [ &$descriptor ] ); |
||
897 | parent::__construct( $descriptor, $context, 'upload' ); |
||
898 | |||
899 | # Add a link to edit MediaWiki:Licenses |
||
900 | if ( $this->getUser()->isAllowed( 'editinterface' ) ) { |
||
901 | $this->getOutput()->addModuleStyles( 'mediawiki.special.upload.styles' ); |
||
902 | $licensesLink = $linkRenderer->makeKnownLink( |
||
903 | $this->msg( 'licenses' )->inContentLanguage()->getTitle(), |
||
904 | $this->msg( 'licenses-edit' )->text(), |
||
905 | [], |
||
906 | [ 'action' => 'edit' ] |
||
907 | ); |
||
908 | $editLicenses = '<p class="mw-upload-editlicenses">' . $licensesLink . '</p>'; |
||
909 | $this->addFooterText( $editLicenses, 'description' ); |
||
910 | } |
||
911 | |||
912 | # Set some form properties |
||
913 | $this->setSubmitText( $this->msg( 'uploadbtn' )->text() ); |
||
914 | $this->setSubmitName( 'wpUpload' ); |
||
915 | # Used message keys: 'accesskey-upload', 'tooltip-upload' |
||
916 | $this->setSubmitTooltip( 'upload' ); |
||
917 | $this->setId( 'mw-upload-form' ); |
||
918 | |||
919 | # Build a list of IDs for javascript insertion |
||
920 | $this->mSourceIds = []; |
||
921 | foreach ( $sourceDescriptor as $field ) { |
||
922 | if ( !empty( $field['id'] ) ) { |
||
923 | $this->mSourceIds[] = $field['id']; |
||
924 | } |
||
925 | } |
||
926 | } |
||
927 | |||
928 | /** |
||
929 | * Get the descriptor of the fieldset that contains the file source |
||
930 | * selection. The section is 'source' |
||
931 | * |
||
932 | * @return array Descriptor array |
||
933 | */ |
||
934 | protected function getSourceSection() { |
||
935 | if ( $this->mSessionKey ) { |
||
936 | return [ |
||
937 | 'SessionKey' => [ |
||
938 | 'type' => 'hidden', |
||
939 | 'default' => $this->mSessionKey, |
||
940 | ], |
||
941 | 'SourceType' => [ |
||
942 | 'type' => 'hidden', |
||
943 | 'default' => 'Stash', |
||
944 | ], |
||
945 | ]; |
||
946 | } |
||
947 | |||
948 | $canUploadByUrl = UploadFromUrl::isEnabled() |
||
949 | && ( UploadFromUrl::isAllowed( $this->getUser() ) === true ) |
||
950 | && $this->getConfig()->get( 'CopyUploadsFromSpecialUpload' ); |
||
951 | $radio = $canUploadByUrl; |
||
952 | $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) ); |
||
953 | |||
954 | $descriptor = []; |
||
955 | if ( $this->mTextTop ) { |
||
956 | $descriptor['UploadFormTextTop'] = [ |
||
957 | 'type' => 'info', |
||
958 | 'section' => 'source', |
||
959 | 'default' => $this->mTextTop, |
||
960 | 'raw' => true, |
||
961 | ]; |
||
962 | } |
||
963 | |||
964 | $this->mMaxUploadSize['file'] = min( |
||
965 | UploadBase::getMaxUploadSize( 'file' ), |
||
966 | UploadBase::getMaxPhpUploadSize() |
||
967 | ); |
||
968 | |||
969 | $help = $this->msg( 'upload-maxfilesize', |
||
970 | $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['file'] ) |
||
971 | )->parse(); |
||
972 | |||
973 | // If the user can also upload by URL, there are 2 different file size limits. |
||
974 | // This extra message helps stress which limit corresponds to what. |
||
975 | if ( $canUploadByUrl ) { |
||
976 | $help .= $this->msg( 'word-separator' )->escaped(); |
||
977 | $help .= $this->msg( 'upload_source_file' )->parse(); |
||
978 | } |
||
979 | |||
980 | $descriptor['UploadFile'] = [ |
||
981 | 'class' => 'UploadSourceField', |
||
982 | 'section' => 'source', |
||
983 | 'type' => 'file', |
||
984 | 'id' => 'wpUploadFile', |
||
985 | 'radio-id' => 'wpSourceTypeFile', |
||
986 | 'label-message' => 'sourcefilename', |
||
987 | 'upload-type' => 'File', |
||
988 | 'radio' => &$radio, |
||
989 | 'help' => $help, |
||
990 | 'checked' => $selectedSourceType == 'file', |
||
991 | ]; |
||
992 | |||
993 | if ( $canUploadByUrl ) { |
||
994 | $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' ); |
||
995 | $descriptor['UploadFileURL'] = [ |
||
996 | 'class' => 'UploadSourceField', |
||
997 | 'section' => 'source', |
||
998 | 'id' => 'wpUploadFileURL', |
||
999 | 'radio-id' => 'wpSourceTypeurl', |
||
1000 | 'label-message' => 'sourceurl', |
||
1001 | 'upload-type' => 'url', |
||
1002 | 'radio' => &$radio, |
||
1003 | 'help' => $this->msg( 'upload-maxfilesize', |
||
1004 | $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['url'] ) |
||
1005 | )->parse() . |
||
1006 | $this->msg( 'word-separator' )->escaped() . |
||
1007 | $this->msg( 'upload_source_url' )->parse(), |
||
1008 | 'checked' => $selectedSourceType == 'url', |
||
1009 | ]; |
||
1010 | } |
||
1011 | Hooks::run( 'UploadFormSourceDescriptors', [ &$descriptor, &$radio, $selectedSourceType ] ); |
||
1012 | |||
1013 | $descriptor['Extensions'] = [ |
||
1014 | 'type' => 'info', |
||
1015 | 'section' => 'source', |
||
1016 | 'default' => $this->getExtensionsMessage(), |
||
1017 | 'raw' => true, |
||
1018 | ]; |
||
1019 | |||
1020 | return $descriptor; |
||
1021 | } |
||
1022 | |||
1023 | /** |
||
1024 | * Get the messages indicating which extensions are preferred and prohibitted. |
||
1025 | * |
||
1026 | * @return string HTML string containing the message |
||
1027 | */ |
||
1028 | protected function getExtensionsMessage() { |
||
1029 | # Print a list of allowed file extensions, if so configured. We ignore |
||
1030 | # MIME type here, it's incomprehensible to most people and too long. |
||
1031 | $config = $this->getConfig(); |
||
1032 | |||
1033 | if ( $config->get( 'CheckFileExtensions' ) ) { |
||
1034 | $fileExtensions = array_unique( $config->get( 'FileExtensions' ) ); |
||
1035 | if ( $config->get( 'StrictFileExtensions' ) ) { |
||
1036 | # Everything not permitted is banned |
||
1037 | $extensionsList = |
||
1038 | '<div id="mw-upload-permitted">' . |
||
1039 | $this->msg( 'upload-permitted' ) |
||
1040 | ->params( $this->getLanguage()->commaList( $fileExtensions ) ) |
||
1041 | ->numParams( count( $fileExtensions ) ) |
||
1042 | ->parseAsBlock() . |
||
1043 | "</div>\n"; |
||
1044 | } else { |
||
1045 | # We have to list both preferred and prohibited |
||
1046 | $fileBlacklist = array_unique( $config->get( 'FileBlacklist' ) ); |
||
1047 | $extensionsList = |
||
1048 | '<div id="mw-upload-preferred">' . |
||
1049 | $this->msg( 'upload-preferred' ) |
||
1050 | ->params( $this->getLanguage()->commaList( $fileExtensions ) ) |
||
1051 | ->numParams( count( $fileExtensions ) ) |
||
1052 | ->parseAsBlock() . |
||
1053 | "</div>\n" . |
||
1054 | '<div id="mw-upload-prohibited">' . |
||
1055 | $this->msg( 'upload-prohibited' ) |
||
1056 | ->params( $this->getLanguage()->commaList( $fileBlacklist ) ) |
||
1057 | ->numParams( count( $fileBlacklist ) ) |
||
1058 | ->parseAsBlock() . |
||
1059 | "</div>\n"; |
||
1060 | } |
||
1061 | } else { |
||
1062 | # Everything is permitted. |
||
1063 | $extensionsList = ''; |
||
1064 | } |
||
1065 | |||
1066 | return $extensionsList; |
||
1067 | } |
||
1068 | |||
1069 | /** |
||
1070 | * Get the descriptor of the fieldset that contains the file description |
||
1071 | * input. The section is 'description' |
||
1072 | * |
||
1073 | * @return array Descriptor array |
||
1074 | */ |
||
1075 | protected function getDescriptionSection() { |
||
1076 | $config = $this->getConfig(); |
||
1077 | if ( $this->mSessionKey ) { |
||
1078 | $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash( $this->getUser() ); |
||
1079 | try { |
||
1080 | $file = $stash->getFile( $this->mSessionKey ); |
||
1081 | } catch ( Exception $e ) { |
||
1082 | $file = null; |
||
1083 | } |
||
1084 | if ( $file ) { |
||
1085 | global $wgContLang; |
||
1086 | |||
1087 | $mto = $file->transform( [ 'width' => 120 ] ); |
||
1088 | $this->addHeaderText( |
||
1089 | '<div class="thumb t' . $wgContLang->alignEnd() . '">' . |
||
1090 | Html::element( 'img', [ |
||
1091 | 'src' => $mto->getUrl(), |
||
1092 | 'class' => 'thumbimage', |
||
1093 | ] ) . '</div>', 'description' ); |
||
1094 | } |
||
1095 | } |
||
1096 | |||
1097 | $descriptor = [ |
||
1098 | 'DestFile' => [ |
||
1099 | 'type' => 'text', |
||
1100 | 'section' => 'description', |
||
1101 | 'id' => 'wpDestFile', |
||
1102 | 'label-message' => 'destfilename', |
||
1103 | 'size' => 60, |
||
1104 | 'default' => $this->mDestFile, |
||
1105 | # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm |
||
1106 | 'nodata' => strval( $this->mDestFile ) !== '', |
||
1107 | ], |
||
1108 | 'UploadDescription' => [ |
||
1109 | 'type' => 'textarea', |
||
1110 | 'section' => 'description', |
||
1111 | 'id' => 'wpUploadDescription', |
||
1112 | 'label-message' => $this->mForReUpload |
||
1113 | ? 'filereuploadsummary' |
||
1114 | : 'fileuploadsummary', |
||
1115 | 'default' => $this->mComment, |
||
1116 | 'cols' => $this->getUser()->getIntOption( 'cols' ), |
||
1117 | 'rows' => 8, |
||
1118 | ] |
||
1119 | ]; |
||
1120 | if ( $this->mTextAfterSummary ) { |
||
1121 | $descriptor['UploadFormTextAfterSummary'] = [ |
||
1122 | 'type' => 'info', |
||
1123 | 'section' => 'description', |
||
1124 | 'default' => $this->mTextAfterSummary, |
||
1125 | 'raw' => true, |
||
1126 | ]; |
||
1127 | } |
||
1128 | |||
1129 | $descriptor += [ |
||
1130 | 'EditTools' => [ |
||
1131 | 'type' => 'edittools', |
||
1132 | 'section' => 'description', |
||
1133 | 'message' => 'edittools-upload', |
||
1134 | ] |
||
1135 | ]; |
||
1136 | |||
1137 | if ( $this->mForReUpload ) { |
||
1138 | $descriptor['DestFile']['readonly'] = true; |
||
1139 | } else { |
||
1140 | $descriptor['License'] = [ |
||
1141 | 'type' => 'select', |
||
1142 | 'class' => 'Licenses', |
||
1143 | 'section' => 'description', |
||
1144 | 'id' => 'wpLicense', |
||
1145 | 'label-message' => 'license', |
||
1146 | ]; |
||
1147 | } |
||
1148 | |||
1149 | if ( $config->get( 'UseCopyrightUpload' ) ) { |
||
1150 | $descriptor['UploadCopyStatus'] = [ |
||
1151 | 'type' => 'text', |
||
1152 | 'section' => 'description', |
||
1153 | 'id' => 'wpUploadCopyStatus', |
||
1154 | 'label-message' => 'filestatus', |
||
1155 | ]; |
||
1156 | $descriptor['UploadSource'] = [ |
||
1157 | 'type' => 'text', |
||
1158 | 'section' => 'description', |
||
1159 | 'id' => 'wpUploadSource', |
||
1160 | 'label-message' => 'filesource', |
||
1161 | ]; |
||
1162 | } |
||
1163 | |||
1164 | return $descriptor; |
||
1165 | } |
||
1166 | |||
1167 | /** |
||
1168 | * Get the descriptor of the fieldset that contains the upload options, |
||
1169 | * such as "watch this file". The section is 'options' |
||
1170 | * |
||
1171 | * @return array Descriptor array |
||
1172 | */ |
||
1173 | protected function getOptionsSection() { |
||
1174 | $user = $this->getUser(); |
||
1175 | if ( $user->isLoggedIn() ) { |
||
1176 | $descriptor = [ |
||
1177 | 'Watchthis' => [ |
||
1178 | 'type' => 'check', |
||
1179 | 'id' => 'wpWatchthis', |
||
1180 | 'label-message' => 'watchthisupload', |
||
1181 | 'section' => 'options', |
||
1182 | 'default' => $this->mWatch, |
||
1183 | ] |
||
1184 | ]; |
||
1185 | } |
||
1186 | if ( !$this->mHideIgnoreWarning ) { |
||
1187 | $descriptor['IgnoreWarning'] = [ |
||
0 ignored issues
–
show
The variable
$descriptor does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1188 | 'type' => 'check', |
||
1189 | 'id' => 'wpIgnoreWarning', |
||
1190 | 'label-message' => 'ignorewarnings', |
||
1191 | 'section' => 'options', |
||
1192 | ]; |
||
1193 | } |
||
1194 | |||
1195 | $descriptor['DestFileWarningAck'] = [ |
||
1196 | 'type' => 'hidden', |
||
1197 | 'id' => 'wpDestFileWarningAck', |
||
1198 | 'default' => $this->mDestWarningAck ? '1' : '', |
||
1199 | ]; |
||
1200 | |||
1201 | if ( $this->mForReUpload ) { |
||
1202 | $descriptor['ForReUpload'] = [ |
||
1203 | 'type' => 'hidden', |
||
1204 | 'id' => 'wpForReUpload', |
||
1205 | 'default' => '1', |
||
1206 | ]; |
||
1207 | } |
||
1208 | |||
1209 | return $descriptor; |
||
1210 | } |
||
1211 | |||
1212 | /** |
||
1213 | * Add the upload JS and show the form. |
||
1214 | */ |
||
1215 | public function show() { |
||
1216 | $this->addUploadJS(); |
||
1217 | parent::show(); |
||
1218 | } |
||
1219 | |||
1220 | /** |
||
1221 | * Add upload JS to the OutputPage |
||
1222 | */ |
||
1223 | protected function addUploadJS() { |
||
1224 | $config = $this->getConfig(); |
||
1225 | |||
1226 | $useAjaxDestCheck = $config->get( 'UseAjax' ) && $config->get( 'AjaxUploadDestCheck' ); |
||
1227 | $useAjaxLicensePreview = $config->get( 'UseAjax' ) && |
||
1228 | $config->get( 'AjaxLicensePreview' ) && $config->get( 'EnableAPI' ); |
||
1229 | $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize(); |
||
1230 | |||
1231 | $scriptVars = [ |
||
1232 | 'wgAjaxUploadDestCheck' => $useAjaxDestCheck, |
||
1233 | 'wgAjaxLicensePreview' => $useAjaxLicensePreview, |
||
1234 | 'wgUploadAutoFill' => !$this->mForReUpload && |
||
1235 | // If we received mDestFile from the request, don't autofill |
||
1236 | // the wpDestFile textbox |
||
1237 | $this->mDestFile === '', |
||
1238 | 'wgUploadSourceIds' => $this->mSourceIds, |
||
1239 | 'wgCheckFileExtensions' => $config->get( 'CheckFileExtensions' ), |
||
1240 | 'wgStrictFileExtensions' => $config->get( 'StrictFileExtensions' ), |
||
1241 | 'wgFileExtensions' => array_values( array_unique( $config->get( 'FileExtensions' ) ) ), |
||
1242 | 'wgCapitalizeUploads' => MWNamespace::isCapitalized( NS_FILE ), |
||
1243 | 'wgMaxUploadSize' => $this->mMaxUploadSize, |
||
1244 | 'wgFileCanRotate' => SpecialUpload::rotationEnabled(), |
||
1245 | ]; |
||
1246 | |||
1247 | $out = $this->getOutput(); |
||
1248 | $out->addJsConfigVars( $scriptVars ); |
||
1249 | |||
1250 | $out->addModules( [ |
||
1251 | 'mediawiki.action.edit', // For <charinsert> support |
||
1252 | 'mediawiki.special.upload', // Extras for thumbnail and license preview. |
||
1253 | ] ); |
||
1254 | } |
||
1255 | |||
1256 | /** |
||
1257 | * Empty function; submission is handled elsewhere. |
||
1258 | * |
||
1259 | * @return bool False |
||
1260 | */ |
||
1261 | function trySubmit() { |
||
1262 | return false; |
||
1263 | } |
||
1264 | } |
||
1265 | |||
1266 | /** |
||
1267 | * A form field that contains a radio box in the label |
||
1268 | */ |
||
1269 | class UploadSourceField extends HTMLTextField { |
||
1270 | |||
1271 | /** |
||
1272 | * @param array $cellAttributes |
||
1273 | * @return string |
||
1274 | */ |
||
1275 | function getLabelHtml( $cellAttributes = [] ) { |
||
1276 | $id = $this->mParams['id']; |
||
1277 | $label = Html::rawElement( 'label', [ 'for' => $id ], $this->mLabel ); |
||
1278 | |||
1279 | if ( !empty( $this->mParams['radio'] ) ) { |
||
1280 | if ( isset( $this->mParams['radio-id'] ) ) { |
||
1281 | $radioId = $this->mParams['radio-id']; |
||
1282 | } else { |
||
1283 | // Old way. For the benefit of extensions that do not define |
||
1284 | // the 'radio-id' key. |
||
1285 | $radioId = 'wpSourceType' . $this->mParams['upload-type']; |
||
1286 | } |
||
1287 | |||
1288 | $attribs = [ |
||
1289 | 'name' => 'wpSourceType', |
||
1290 | 'type' => 'radio', |
||
1291 | 'id' => $radioId, |
||
1292 | 'value' => $this->mParams['upload-type'], |
||
1293 | ]; |
||
1294 | |||
1295 | if ( !empty( $this->mParams['checked'] ) ) { |
||
1296 | $attribs['checked'] = 'checked'; |
||
1297 | } |
||
1298 | |||
1299 | $label .= Html::element( 'input', $attribs ); |
||
1300 | } |
||
1301 | |||
1302 | return Html::rawElement( 'td', [ 'class' => 'mw-label' ] + $cellAttributes, $label ); |
||
1303 | } |
||
1304 | |||
1305 | /** |
||
1306 | * @return int |
||
1307 | */ |
||
1308 | function getSize() { |
||
1309 | return isset( $this->mParams['size'] ) |
||
1310 | ? $this->mParams['size'] |
||
1311 | : 60; |
||
1312 | } |
||
1313 | } |
||
1314 |
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.