Merge "Improve docs for Title::getInternalURL/getCanonicalURL"
[lhc/web/wiklou.git] / includes / specials / forms / UploadForm.php
1 <?php
2 /**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21 use MediaWiki\Linker\LinkRenderer;
22 use MediaWiki\MediaWikiServices;
23
24 /**
25 * Sub class of HTMLForm that provides the form section of SpecialUpload
26 */
27 class UploadForm extends HTMLForm {
28 protected $mWatch;
29 protected $mForReUpload;
30 protected $mSessionKey;
31 protected $mHideIgnoreWarning;
32 protected $mDestWarningAck;
33 protected $mDestFile;
34
35 protected $mComment;
36 protected $mTextTop;
37 protected $mTextAfterSummary;
38
39 protected $mSourceIds;
40
41 protected $mMaxFileSize = [];
42
43 protected $mMaxUploadSize = [];
44
45 public function __construct( array $options = [], IContextSource $context = null,
46 LinkRenderer $linkRenderer = null
47 ) {
48 if ( $context instanceof IContextSource ) {
49 $this->setContext( $context );
50 }
51
52 if ( !$linkRenderer ) {
53 $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
54 }
55
56 $this->mWatch = !empty( $options['watch'] );
57 $this->mForReUpload = !empty( $options['forreupload'] );
58 $this->mSessionKey = $options['sessionkey'] ?? '';
59 $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] );
60 $this->mDestWarningAck = !empty( $options['destwarningack'] );
61 $this->mDestFile = $options['destfile'] ?? '';
62
63 $this->mComment = $options['description'] ?? '';
64
65 $this->mTextTop = $options['texttop'] ?? '';
66
67 $this->mTextAfterSummary = $options['textaftersummary'] ?? '';
68
69 $sourceDescriptor = $this->getSourceSection();
70 $descriptor = $sourceDescriptor
71 + $this->getDescriptionSection()
72 + $this->getOptionsSection();
73
74 Hooks::run( 'UploadFormInitDescriptor', [ &$descriptor ] );
75 parent::__construct( $descriptor, $context, 'upload' );
76
77 # Add a link to edit MediaWiki:Licenses
78 if ( $this->getUser()->isAllowed( 'editinterface' ) ) {
79 $this->getOutput()->addModuleStyles( 'mediawiki.special' );
80 $licensesLink = $linkRenderer->makeKnownLink(
81 $this->msg( 'licenses' )->inContentLanguage()->getTitle(),
82 $this->msg( 'licenses-edit' )->text(),
83 [],
84 [ 'action' => 'edit' ]
85 );
86 $editLicenses = '<p class="mw-upload-editlicenses">' . $licensesLink . '</p>';
87 $this->addFooterText( $editLicenses, 'description' );
88 }
89
90 # Set some form properties
91 $this->setSubmitText( $this->msg( 'uploadbtn' )->text() );
92 $this->setSubmitName( 'wpUpload' );
93 # Used message keys: 'accesskey-upload', 'tooltip-upload'
94 $this->setSubmitTooltip( 'upload' );
95 $this->setId( 'mw-upload-form' );
96
97 # Build a list of IDs for javascript insertion
98 $this->mSourceIds = [];
99 foreach ( $sourceDescriptor as $field ) {
100 if ( !empty( $field['id'] ) ) {
101 $this->mSourceIds[] = $field['id'];
102 }
103 }
104 }
105
106 /**
107 * Get the descriptor of the fieldset that contains the file source
108 * selection. The section is 'source'
109 *
110 * @return array Descriptor array
111 */
112 protected function getSourceSection() {
113 if ( $this->mSessionKey ) {
114 return [
115 'SessionKey' => [
116 'type' => 'hidden',
117 'default' => $this->mSessionKey,
118 ],
119 'SourceType' => [
120 'type' => 'hidden',
121 'default' => 'Stash',
122 ],
123 ];
124 }
125
126 $canUploadByUrl = UploadFromUrl::isEnabled()
127 && ( UploadFromUrl::isAllowed( $this->getUser() ) === true )
128 && $this->getConfig()->get( 'CopyUploadsFromSpecialUpload' );
129 $radio = $canUploadByUrl;
130 $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) );
131
132 $descriptor = [];
133 if ( $this->mTextTop ) {
134 $descriptor['UploadFormTextTop'] = [
135 'type' => 'info',
136 'section' => 'source',
137 'default' => $this->mTextTop,
138 'raw' => true,
139 ];
140 }
141
142 $this->mMaxUploadSize['file'] = min(
143 UploadBase::getMaxUploadSize( 'file' ),
144 UploadBase::getMaxPhpUploadSize()
145 );
146
147 $help = $this->msg( 'upload-maxfilesize',
148 $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['file'] )
149 )->parse();
150
151 // If the user can also upload by URL, there are 2 different file size limits.
152 // This extra message helps stress which limit corresponds to what.
153 if ( $canUploadByUrl ) {
154 $help .= $this->msg( 'word-separator' )->escaped();
155 $help .= $this->msg( 'upload_source_file' )->parse();
156 }
157
158 $descriptor['UploadFile'] = [
159 'class' => UploadSourceField::class,
160 'section' => 'source',
161 'type' => 'file',
162 'id' => 'wpUploadFile',
163 'radio-id' => 'wpSourceTypeFile',
164 'label-message' => 'sourcefilename',
165 'upload-type' => 'File',
166 'radio' => &$radio,
167 'help' => $help,
168 'checked' => $selectedSourceType == 'file',
169 ];
170
171 if ( $canUploadByUrl ) {
172 $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' );
173 $descriptor['UploadFileURL'] = [
174 'class' => UploadSourceField::class,
175 'section' => 'source',
176 'id' => 'wpUploadFileURL',
177 'radio-id' => 'wpSourceTypeurl',
178 'label-message' => 'sourceurl',
179 'upload-type' => 'url',
180 'radio' => &$radio,
181 'help' => $this->msg( 'upload-maxfilesize',
182 $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['url'] )
183 )->parse() .
184 $this->msg( 'word-separator' )->escaped() .
185 $this->msg( 'upload_source_url' )->parse(),
186 'checked' => $selectedSourceType == 'url',
187 ];
188 }
189 Hooks::run( 'UploadFormSourceDescriptors', [ &$descriptor, &$radio, $selectedSourceType ] );
190
191 $descriptor['Extensions'] = [
192 'type' => 'info',
193 'section' => 'source',
194 'default' => $this->getExtensionsMessage(),
195 'raw' => true,
196 ];
197
198 return $descriptor;
199 }
200
201 /**
202 * Get the messages indicating which extensions are preferred and prohibitted.
203 *
204 * @return string HTML string containing the message
205 */
206 protected function getExtensionsMessage() {
207 # Print a list of allowed file extensions, if so configured. We ignore
208 # MIME type here, it's incomprehensible to most people and too long.
209 $config = $this->getConfig();
210
211 if ( $config->get( 'CheckFileExtensions' ) ) {
212 $fileExtensions = array_unique( $config->get( 'FileExtensions' ) );
213 if ( $config->get( 'StrictFileExtensions' ) ) {
214 # Everything not permitted is banned
215 $extensionsList =
216 '<div id="mw-upload-permitted">' .
217 $this->msg( 'upload-permitted' )
218 ->params( $this->getLanguage()->commaList( $fileExtensions ) )
219 ->numParams( count( $fileExtensions ) )
220 ->parseAsBlock() .
221 "</div>\n";
222 } else {
223 # We have to list both preferred and prohibited
224 $fileBlacklist = array_unique( $config->get( 'FileBlacklist' ) );
225 $extensionsList =
226 '<div id="mw-upload-preferred">' .
227 $this->msg( 'upload-preferred' )
228 ->params( $this->getLanguage()->commaList( $fileExtensions ) )
229 ->numParams( count( $fileExtensions ) )
230 ->parseAsBlock() .
231 "</div>\n" .
232 '<div id="mw-upload-prohibited">' .
233 $this->msg( 'upload-prohibited' )
234 ->params( $this->getLanguage()->commaList( $fileBlacklist ) )
235 ->numParams( count( $fileBlacklist ) )
236 ->parseAsBlock() .
237 "</div>\n";
238 }
239 } else {
240 # Everything is permitted.
241 $extensionsList = '';
242 }
243
244 return $extensionsList;
245 }
246
247 /**
248 * Get the descriptor of the fieldset that contains the file description
249 * input. The section is 'description'
250 *
251 * @return array Descriptor array
252 */
253 protected function getDescriptionSection() {
254 $config = $this->getConfig();
255 if ( $this->mSessionKey ) {
256 $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash( $this->getUser() );
257 try {
258 $file = $stash->getFile( $this->mSessionKey );
259 } catch ( Exception $e ) {
260 $file = null;
261 }
262 if ( $file ) {
263 $mto = $file->transform( [ 'width' => 120 ] );
264 if ( $mto ) {
265 $this->addHeaderText(
266 '<div class="thumb t' .
267 MediaWikiServices::getInstance()->getContentLanguage()->alignEnd() . '">' .
268 Html::element( 'img', [
269 'src' => $mto->getUrl(),
270 'class' => 'thumbimage',
271 ] ) . '</div>', 'description' );
272 }
273 }
274 }
275
276 $descriptor = [
277 'DestFile' => [
278 'type' => 'text',
279 'section' => 'description',
280 'id' => 'wpDestFile',
281 'label-message' => 'destfilename',
282 'size' => 60,
283 'default' => $this->mDestFile,
284 # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm
285 'nodata' => strval( $this->mDestFile ) !== '',
286 ],
287 'UploadDescription' => [
288 'type' => 'textarea',
289 'section' => 'description',
290 'id' => 'wpUploadDescription',
291 'label-message' => $this->mForReUpload
292 ? 'filereuploadsummary'
293 : 'fileuploadsummary',
294 'default' => $this->mComment,
295 'cols' => 80,
296 'rows' => 8,
297 ]
298 ];
299 if ( $this->mTextAfterSummary ) {
300 $descriptor['UploadFormTextAfterSummary'] = [
301 'type' => 'info',
302 'section' => 'description',
303 'default' => $this->mTextAfterSummary,
304 'raw' => true,
305 ];
306 }
307
308 $descriptor += [
309 'EditTools' => [
310 'type' => 'edittools',
311 'section' => 'description',
312 'message' => 'edittools-upload',
313 ]
314 ];
315
316 if ( $this->mForReUpload ) {
317 $descriptor['DestFile']['readonly'] = true;
318 } else {
319 $descriptor['License'] = [
320 'type' => 'select',
321 'class' => Licenses::class,
322 'section' => 'description',
323 'id' => 'wpLicense',
324 'label-message' => 'license',
325 ];
326 }
327
328 if ( $config->get( 'UseCopyrightUpload' ) ) {
329 $descriptor['UploadCopyStatus'] = [
330 'type' => 'text',
331 'section' => 'description',
332 'id' => 'wpUploadCopyStatus',
333 'label-message' => 'filestatus',
334 ];
335 $descriptor['UploadSource'] = [
336 'type' => 'text',
337 'section' => 'description',
338 'id' => 'wpUploadSource',
339 'label-message' => 'filesource',
340 ];
341 }
342
343 return $descriptor;
344 }
345
346 /**
347 * Get the descriptor of the fieldset that contains the upload options,
348 * such as "watch this file". The section is 'options'
349 *
350 * @return array Descriptor array
351 */
352 protected function getOptionsSection() {
353 $user = $this->getUser();
354 if ( $user->isLoggedIn() ) {
355 $descriptor = [
356 'Watchthis' => [
357 'type' => 'check',
358 'id' => 'wpWatchthis',
359 'label-message' => 'watchthisupload',
360 'section' => 'options',
361 'default' => $this->mWatch,
362 ]
363 ];
364 }
365 if ( !$this->mHideIgnoreWarning ) {
366 $descriptor['IgnoreWarning'] = [
367 'type' => 'check',
368 'id' => 'wpIgnoreWarning',
369 'label-message' => 'ignorewarnings',
370 'section' => 'options',
371 ];
372 }
373
374 $descriptor['DestFileWarningAck'] = [
375 'type' => 'hidden',
376 'id' => 'wpDestFileWarningAck',
377 'default' => $this->mDestWarningAck ? '1' : '',
378 ];
379
380 if ( $this->mForReUpload ) {
381 $descriptor['ForReUpload'] = [
382 'type' => 'hidden',
383 'id' => 'wpForReUpload',
384 'default' => '1',
385 ];
386 }
387
388 return $descriptor;
389 }
390
391 /**
392 * Add the upload JS and show the form.
393 */
394 public function show() {
395 $this->addUploadJS();
396 return parent::show();
397 }
398
399 /**
400 * Add upload JS to the OutputPage
401 */
402 protected function addUploadJS() {
403 $config = $this->getConfig();
404
405 $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize();
406
407 $scriptVars = [
408 'wgAjaxUploadDestCheck' => $config->get( 'AjaxUploadDestCheck' ),
409 'wgAjaxLicensePreview' => $config->get( 'AjaxLicensePreview' ),
410 'wgUploadAutoFill' => !$this->mForReUpload &&
411 // If we received mDestFile from the request, don't autofill
412 // the wpDestFile textbox
413 $this->mDestFile === '',
414 'wgUploadSourceIds' => $this->mSourceIds,
415 'wgCheckFileExtensions' => $config->get( 'CheckFileExtensions' ),
416 'wgStrictFileExtensions' => $config->get( 'StrictFileExtensions' ),
417 'wgFileExtensions' => array_values( array_unique( $config->get( 'FileExtensions' ) ) ),
418 'wgCapitalizeUploads' => MWNamespace::isCapitalized( NS_FILE ),
419 'wgMaxUploadSize' => $this->mMaxUploadSize,
420 'wgFileCanRotate' => SpecialUpload::rotationEnabled(),
421 ];
422
423 $out = $this->getOutput();
424 $out->addJsConfigVars( $scriptVars );
425
426 $out->addModules( [
427 'mediawiki.special.upload', // Extras for thumbnail and license preview.
428 ] );
429 }
430
431 /**
432 * Empty function; submission is handled elsewhere.
433 *
434 * @return bool False
435 */
436 function trySubmit() {
437 return false;
438 }
439 }