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