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