Bartosz Dziewoński has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/377652 )
Change subject: Split off remaining helper classes for special pages to separate files ...................................................................... Split off remaining helper classes for special pages to separate files includes/specials/formfields/ * EditWatchlistCheckboxSeriesField (for SpecialEditWatchlist) * UploadSourceField (for SpecialUpload) * Licenses (for SpecialUpload) includes/specials/forms/ * EditWatchlistNormalHTMLForm (for SpecialEditWatchlist) * PreferencesForm (for SpecialPreferences) * UploadForm (for SpecialUpload) includes/specials/helpers/ * ImportReporter (for SpecialImport) * License (for SpecialUpload) Change-Id: I58abcbb44dbf9bf1762b4252555f7552bfa7c253 --- M autoload.php M includes/Preferences.php M includes/specials/SpecialEditWatchlist.php M includes/specials/SpecialImport.php M includes/specials/SpecialUpload.php A includes/specials/formfields/EditWatchlistCheckboxSeriesField.php R includes/specials/formfields/Licenses.php A includes/specials/formfields/UploadSourceField.php A includes/specials/forms/EditWatchlistNormalHTMLForm.php A includes/specials/forms/PreferencesForm.php A includes/specials/forms/UploadForm.php A includes/specials/helpers/ImportReporter.php A includes/specials/helpers/License.php 13 files changed, 967 insertions(+), 828 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/52/377652/1 diff --git a/autoload.php b/autoload.php index 4448204..61fd192 100644 --- a/autoload.php +++ b/autoload.php @@ -421,8 +421,8 @@ 'EditAction' => __DIR__ . '/includes/actions/EditAction.php', 'EditCLI' => __DIR__ . '/maintenance/edit.php', 'EditPage' => __DIR__ . '/includes/EditPage.php', - 'EditWatchlistCheckboxSeriesField' => __DIR__ . '/includes/specials/SpecialEditWatchlist.php', - 'EditWatchlistNormalHTMLForm' => __DIR__ . '/includes/specials/SpecialEditWatchlist.php', + 'EditWatchlistCheckboxSeriesField' => __DIR__ . '/includes/specials/formfields/EditWatchlistCheckboxSeriesField.php', + 'EditWatchlistNormalHTMLForm' => __DIR__ . '/includes/specials/forms/EditWatchlistNormalHTMLForm.php', 'EmailConfirmation' => __DIR__ . '/includes/specials/SpecialConfirmemail.php', 'EmailInvalidation' => __DIR__ . '/includes/specials/SpecialEmailInvalidate.php', 'EmailNotification' => __DIR__ . '/includes/mail/EmailNotification.php', @@ -633,7 +633,7 @@ 'ImageQueryPage' => __DIR__ . '/includes/specialpage/ImageQueryPage.php', 'ImportImages' => __DIR__ . '/maintenance/importImages.php', 'ImportLogFormatter' => __DIR__ . '/includes/logging/ImportLogFormatter.php', - 'ImportReporter' => __DIR__ . '/includes/specials/SpecialImport.php', + 'ImportReporter' => __DIR__ . '/includes/specials/helpers/ImportReporter.php', 'ImportSiteScripts' => __DIR__ . '/maintenance/importSiteScripts.php', 'ImportSites' => __DIR__ . '/maintenance/importSites.php', 'ImportSource' => __DIR__ . '/includes/import/ImportSource.php', @@ -746,8 +746,8 @@ 'Languages' => __DIR__ . '/maintenance/language/languages.inc', 'LayeredParameterizedPassword' => __DIR__ . '/includes/password/LayeredParameterizedPassword.php', 'LegacyLogFormatter' => __DIR__ . '/includes/logging/LogFormatter.php', - 'License' => __DIR__ . '/includes/Licenses.php', - 'Licenses' => __DIR__ . '/includes/Licenses.php', + 'License' => __DIR__ . '/includes/specials/helpers/License.php', + 'Licenses' => __DIR__ . '/includes/specials/formfields/Licenses.php', 'LinkBatch' => __DIR__ . '/includes/cache/LinkBatch.php', 'LinkCache' => __DIR__ . '/includes/cache/LinkCache.php', 'LinkFilter' => __DIR__ . '/includes/LinkFilter.php', @@ -1135,7 +1135,7 @@ 'PostgresInstaller' => __DIR__ . '/includes/installer/PostgresInstaller.php', 'PostgresUpdater' => __DIR__ . '/includes/installer/PostgresUpdater.php', 'Preferences' => __DIR__ . '/includes/Preferences.php', - 'PreferencesForm' => __DIR__ . '/includes/Preferences.php', + 'PreferencesForm' => __DIR__ . '/includes/specials/forms/PreferencesForm.php', 'PrefixSearch' => __DIR__ . '/includes/PrefixSearch.php', 'PreprocessDump' => __DIR__ . '/maintenance/preprocessDump.php', 'Preprocessor' => __DIR__ . '/includes/parser/Preprocessor.php', @@ -1532,14 +1532,14 @@ 'UploadChunkVerificationException' => __DIR__ . '/includes/upload/UploadFromChunks.php', 'UploadChunkZeroLengthFileException' => __DIR__ . '/includes/upload/UploadFromChunks.php', 'UploadDumper' => __DIR__ . '/maintenance/dumpUploads.php', - 'UploadForm' => __DIR__ . '/includes/specials/SpecialUpload.php', + 'UploadForm' => __DIR__ . '/includes/specials/forms/UploadForm.php', 'UploadFromChunks' => __DIR__ . '/includes/upload/UploadFromChunks.php', 'UploadFromFile' => __DIR__ . '/includes/upload/UploadFromFile.php', 'UploadFromStash' => __DIR__ . '/includes/upload/UploadFromStash.php', 'UploadFromUrl' => __DIR__ . '/includes/upload/UploadFromUrl.php', 'UploadLogFormatter' => __DIR__ . '/includes/logging/UploadLogFormatter.php', 'UploadSourceAdapter' => __DIR__ . '/includes/import/UploadSourceAdapter.php', - 'UploadSourceField' => __DIR__ . '/includes/specials/SpecialUpload.php', + 'UploadSourceField' => __DIR__ . '/includes/specials/formfields/UploadSourceField.php', 'UploadStash' => __DIR__ . '/includes/upload/UploadStash.php', 'UploadStashBadPathException' => __DIR__ . '/includes/upload/UploadStash.php', 'UploadStashCleanup' => __DIR__ . '/maintenance/cleanupUploadStash.php', diff --git a/includes/Preferences.php b/includes/Preferences.php index c29c4b9..0bb1d28 100644 --- a/includes/Preferences.php +++ b/includes/Preferences.php @@ -1,7 +1,5 @@ <?php /** - * Form to edit user preferences. - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -1633,125 +1631,5 @@ } return $timeZoneList; - } -} - -/** Some tweaks to allow js prefs to work */ -class PreferencesForm extends HTMLForm { - // Override default value from HTMLForm - protected $mSubSectionBeforeFields = false; - - private $modifiedUser; - - /** - * @param User $user - */ - public function setModifiedUser( $user ) { - $this->modifiedUser = $user; - } - - /** - * @return User - */ - public function getModifiedUser() { - if ( $this->modifiedUser === null ) { - return $this->getUser(); - } else { - return $this->modifiedUser; - } - } - - /** - * Get extra parameters for the query string when redirecting after - * successful save. - * - * @return array - */ - public function getExtraSuccessRedirectParameters() { - return []; - } - - /** - * @param string $html - * @return string - */ - function wrapForm( $html ) { - $html = Xml::tags( 'div', [ 'id' => 'preferences' ], $html ); - - return parent::wrapForm( $html ); - } - - /** - * @return string - */ - function getButtons() { - $attrs = [ 'id' => 'mw-prefs-restoreprefs' ]; - - if ( !$this->getModifiedUser()->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' ) ) { - return ''; - } - - $html = parent::getButtons(); - - if ( $this->getModifiedUser()->isAllowed( 'editmyoptions' ) ) { - $t = $this->getTitle()->getSubpage( 'reset' ); - - $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); - $html .= "\n" . $linkRenderer->makeLink( $t, $this->msg( 'restoreprefs' )->text(), - Html::buttonAttributes( $attrs, [ 'mw-ui-quiet' ] ) ); - - $html = Xml::tags( 'div', [ 'class' => 'mw-prefs-buttons' ], $html ); - } - - return $html; - } - - /** - * Separate multi-option preferences into multiple preferences, since we - * have to store them separately - * @param array $data - * @return array - */ - function filterDataForSubmit( $data ) { - foreach ( $this->mFlatFields as $fieldname => $field ) { - if ( $field instanceof HTMLNestedFilterable ) { - $info = $field->mParams; - $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $fieldname; - foreach ( $field->filterDataForSubmit( $data[$fieldname] ) as $key => $value ) { - $data["$prefix$key"] = $value; - } - unset( $data[$fieldname] ); - } - } - - return $data; - } - - /** - * Get the whole body of the form. - * @return string - */ - function getBody() { - return $this->displaySection( $this->mFieldTree, '', 'mw-prefsection-' ); - } - - /** - * Get the "<legend>" for a given section key. Normally this is the - * prefs-$key message but we'll allow extensions to override it. - * @param string $key - * @return string - */ - function getLegend( $key ) { - $legend = parent::getLegend( $key ); - Hooks::run( 'PreferencesGetLegend', [ $this, $key, &$legend ] ); - return $legend; - } - - /** - * Get the keys of each top level preference section. - * @return array of section keys - */ - function getPreferenceSections() { - return array_keys( array_filter( $this->mFieldTree, 'is_array' ) ); } } diff --git a/includes/specials/SpecialEditWatchlist.php b/includes/specials/SpecialEditWatchlist.php index e1ecfe8..d2940e4 100644 --- a/includes/specials/SpecialEditWatchlist.php +++ b/includes/specials/SpecialEditWatchlist.php @@ -770,38 +770,3 @@ ); } } - -/** - * Extend HTMLForm purely so we can have a more sane way of getting the section headers - */ -class EditWatchlistNormalHTMLForm extends HTMLForm { - public function getLegend( $namespace ) { - $namespace = substr( $namespace, 2 ); - - return $namespace == NS_MAIN - ? $this->msg( 'blanknamespace' )->escaped() - : htmlspecialchars( $this->getContext()->getLanguage()->getFormattedNsText( $namespace ) ); - } - - public function getBody() { - return $this->displaySection( $this->mFieldTree, '', 'editwatchlist-' ); - } -} - -class EditWatchlistCheckboxSeriesField extends HTMLMultiSelectField { - /** - * HTMLMultiSelectField throws validation errors if we get input data - * that doesn't match the data set in the form setup. This causes - * problems if something gets removed from the watchlist while the - * form is open (T34126), but we know that invalid items will - * be harmless so we can override it here. - * - * @param string $value The value the field was submitted with - * @param array $alldata The data collected from the form - * @return bool|string Bool true on success, or String error to display. - */ - function validate( $value, $alldata ) { - // Need to call into grandparent to be a good citizen. :) - return HTMLFormField::validate( $value, $alldata ); - } -} diff --git a/includes/specials/SpecialImport.php b/includes/specials/SpecialImport.php index beb454d..5d79b66 100644 --- a/includes/specials/SpecialImport.php +++ b/includes/specials/SpecialImport.php @@ -524,172 +524,3 @@ return 'pagetools'; } } - -/** - * Reporting callback - * @ingroup SpecialPage - */ -class ImportReporter extends ContextSource { - private $reason = false; - private $logTags = []; - private $mOriginalLogCallback = null; - private $mOriginalPageOutCallback = null; - private $mLogItemCount = 0; - - /** - * @param WikiImporter $importer - * @param bool $upload - * @param string $interwiki - * @param string|bool $reason - */ - function __construct( $importer, $upload, $interwiki, $reason = false ) { - $this->mOriginalPageOutCallback = - $importer->setPageOutCallback( [ $this, 'reportPage' ] ); - $this->mOriginalLogCallback = - $importer->setLogItemCallback( [ $this, 'reportLogItem' ] ); - $importer->setNoticeCallback( [ $this, 'reportNotice' ] ); - $this->mPageCount = 0; - $this->mIsUpload = $upload; - $this->mInterwiki = $interwiki; - $this->reason = $reason; - } - - /** - * Sets change tags to apply to the import log entry and null revision. - * - * @param array $tags - * @since 1.29 - */ - public function setChangeTags( array $tags ) { - $this->logTags = $tags; - } - - function open() { - $this->getOutput()->addHTML( "<ul>\n" ); - } - - function reportNotice( $msg, array $params ) { - $this->getOutput()->addHTML( - Html::element( 'li', [], $this->msg( $msg, $params )->text() ) - ); - } - - function reportLogItem( /* ... */ ) { - $this->mLogItemCount++; - if ( is_callable( $this->mOriginalLogCallback ) ) { - call_user_func_array( $this->mOriginalLogCallback, func_get_args() ); - } - } - - /** - * @param Title $title - * @param ForeignTitle $foreignTitle - * @param int $revisionCount - * @param int $successCount - * @param array $pageInfo - * @return void - */ - public function reportPage( $title, $foreignTitle, $revisionCount, - $successCount, $pageInfo ) { - $args = func_get_args(); - call_user_func_array( $this->mOriginalPageOutCallback, $args ); - - if ( $title === null ) { - # Invalid or non-importable title; a notice is already displayed - return; - } - - $this->mPageCount++; - $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); - if ( $successCount > 0 ) { - // <bdi> prevents jumbling of the versions count - // in RTL wikis in case the page title is LTR - $this->getOutput()->addHTML( - "<li>" . $linkRenderer->makeLink( $title ) . " " . - "<bdi>" . - $this->msg( 'import-revision-count' )->numParams( $successCount )->escaped() . - "</bdi>" . - "</li>\n" - ); - - $logParams = [ '4:number:count' => $successCount ]; - if ( $this->mIsUpload ) { - $detail = $this->msg( 'import-logentry-upload-detail' )->numParams( - $successCount )->inContentLanguage()->text(); - $action = 'upload'; - } else { - $pageTitle = $foreignTitle->getFullText(); - $fullInterwikiPrefix = $this->mInterwiki; - Hooks::run( 'ImportLogInterwikiLink', [ &$fullInterwikiPrefix, &$pageTitle ] ); - - $interwikiTitleStr = $fullInterwikiPrefix . ':' . $pageTitle; - $interwiki = '[[:' . $interwikiTitleStr . ']]'; - $detail = $this->msg( 'import-logentry-interwiki-detail' )->numParams( - $successCount )->params( $interwiki )->inContentLanguage()->text(); - $action = 'interwiki'; - $logParams['5:title-link:interwiki'] = $interwikiTitleStr; - } - if ( $this->reason ) { - $detail .= $this->msg( 'colon-separator' )->inContentLanguage()->text() - . $this->reason; - } - - $comment = $detail; // quick - $dbw = wfGetDB( DB_MASTER ); - $latest = $title->getLatestRevID(); - $nullRevision = Revision::newNullRevision( - $dbw, - $title->getArticleID(), - $comment, - true, - $this->getUser() - ); - - $nullRevId = null; - if ( !is_null( $nullRevision ) ) { - $nullRevId = $nullRevision->insertOn( $dbw ); - $page = WikiPage::factory( $title ); - # Update page record - $page->updateRevisionOn( $dbw, $nullRevision ); - Hooks::run( - 'NewRevisionFromEditComplete', - [ $page, $nullRevision, $latest, $this->getUser() ] - ); - } - - // Create the import log entry - $logEntry = new ManualLogEntry( 'import', $action ); - $logEntry->setTarget( $title ); - $logEntry->setComment( $this->reason ); - $logEntry->setPerformer( $this->getUser() ); - $logEntry->setParameters( $logParams ); - $logid = $logEntry->insert(); - if ( count( $this->logTags ) ) { - $logEntry->setTags( $this->logTags ); - } - // Make sure the null revision will be tagged as well - $logEntry->setAssociatedRevId( $nullRevId ); - - $logEntry->publish( $logid ); - - } else { - $this->getOutput()->addHTML( "<li>" . $linkRenderer->makeKnownLink( $title ) . " " . - $this->msg( 'import-nonewrevisions' )->escaped() . "</li>\n" ); - } - } - - function close() { - $out = $this->getOutput(); - if ( $this->mLogItemCount > 0 ) { - $msg = $this->msg( 'imported-log-entries' )->numParams( $this->mLogItemCount )->parse(); - $out->addHTML( Xml::tags( 'li', null, $msg ) ); - } elseif ( $this->mPageCount == 0 && $this->mLogItemCount == 0 ) { - $out->addHTML( "</ul>\n" ); - - return Status::newFatal( 'importnopages' ); - } - $out->addHTML( "</ul>\n" ); - - return Status::newGood( $this->mPageCount ); - } -} diff --git a/includes/specials/SpecialUpload.php b/includes/specials/SpecialUpload.php index b98fad1..bbec122 100644 --- a/includes/specials/SpecialUpload.php +++ b/includes/specials/SpecialUpload.php @@ -846,476 +846,3 @@ return $bitmapHandler->autoRotateEnabled(); } } - -/** - * Sub class of HTMLForm that provides the form section of SpecialUpload - */ -class UploadForm extends HTMLForm { - protected $mWatch; - protected $mForReUpload; - protected $mSessionKey; - protected $mHideIgnoreWarning; - protected $mDestWarningAck; - protected $mDestFile; - - protected $mComment; - protected $mTextTop; - protected $mTextAfterSummary; - - protected $mSourceIds; - - protected $mMaxFileSize = []; - - protected $mMaxUploadSize = []; - - public function __construct( array $options = [], IContextSource $context = null, - LinkRenderer $linkRenderer = null - ) { - if ( $context instanceof IContextSource ) { - $this->setContext( $context ); - } - - if ( !$linkRenderer ) { - $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); - } - - $this->mWatch = !empty( $options['watch'] ); - $this->mForReUpload = !empty( $options['forreupload'] ); - $this->mSessionKey = isset( $options['sessionkey'] ) ? $options['sessionkey'] : ''; - $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] ); - $this->mDestWarningAck = !empty( $options['destwarningack'] ); - $this->mDestFile = isset( $options['destfile'] ) ? $options['destfile'] : ''; - - $this->mComment = isset( $options['description'] ) ? - $options['description'] : ''; - - $this->mTextTop = isset( $options['texttop'] ) - ? $options['texttop'] : ''; - - $this->mTextAfterSummary = isset( $options['textaftersummary'] ) - ? $options['textaftersummary'] : ''; - - $sourceDescriptor = $this->getSourceSection(); - $descriptor = $sourceDescriptor - + $this->getDescriptionSection() - + $this->getOptionsSection(); - - Hooks::run( 'UploadFormInitDescriptor', [ &$descriptor ] ); - parent::__construct( $descriptor, $context, 'upload' ); - - # Add a link to edit MediaWiki:Licenses - if ( $this->getUser()->isAllowed( 'editinterface' ) ) { - $this->getOutput()->addModuleStyles( 'mediawiki.special.upload.styles' ); - $licensesLink = $linkRenderer->makeKnownLink( - $this->msg( 'licenses' )->inContentLanguage()->getTitle(), - $this->msg( 'licenses-edit' )->text(), - [], - [ 'action' => 'edit' ] - ); - $editLicenses = '<p class="mw-upload-editlicenses">' . $licensesLink . '</p>'; - $this->addFooterText( $editLicenses, 'description' ); - } - - # Set some form properties - $this->setSubmitText( $this->msg( 'uploadbtn' )->text() ); - $this->setSubmitName( 'wpUpload' ); - # Used message keys: 'accesskey-upload', 'tooltip-upload' - $this->setSubmitTooltip( 'upload' ); - $this->setId( 'mw-upload-form' ); - - # Build a list of IDs for javascript insertion - $this->mSourceIds = []; - foreach ( $sourceDescriptor as $field ) { - if ( !empty( $field['id'] ) ) { - $this->mSourceIds[] = $field['id']; - } - } - } - - /** - * Get the descriptor of the fieldset that contains the file source - * selection. The section is 'source' - * - * @return array Descriptor array - */ - protected function getSourceSection() { - if ( $this->mSessionKey ) { - return [ - 'SessionKey' => [ - 'type' => 'hidden', - 'default' => $this->mSessionKey, - ], - 'SourceType' => [ - 'type' => 'hidden', - 'default' => 'Stash', - ], - ]; - } - - $canUploadByUrl = UploadFromUrl::isEnabled() - && ( UploadFromUrl::isAllowed( $this->getUser() ) === true ) - && $this->getConfig()->get( 'CopyUploadsFromSpecialUpload' ); - $radio = $canUploadByUrl; - $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) ); - - $descriptor = []; - if ( $this->mTextTop ) { - $descriptor['UploadFormTextTop'] = [ - 'type' => 'info', - 'section' => 'source', - 'default' => $this->mTextTop, - 'raw' => true, - ]; - } - - $this->mMaxUploadSize['file'] = min( - UploadBase::getMaxUploadSize( 'file' ), - UploadBase::getMaxPhpUploadSize() - ); - - $help = $this->msg( 'upload-maxfilesize', - $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['file'] ) - )->parse(); - - // If the user can also upload by URL, there are 2 different file size limits. - // This extra message helps stress which limit corresponds to what. - if ( $canUploadByUrl ) { - $help .= $this->msg( 'word-separator' )->escaped(); - $help .= $this->msg( 'upload_source_file' )->parse(); - } - - $descriptor['UploadFile'] = [ - 'class' => 'UploadSourceField', - 'section' => 'source', - 'type' => 'file', - 'id' => 'wpUploadFile', - 'radio-id' => 'wpSourceTypeFile', - 'label-message' => 'sourcefilename', - 'upload-type' => 'File', - 'radio' => &$radio, - 'help' => $help, - 'checked' => $selectedSourceType == 'file', - ]; - - if ( $canUploadByUrl ) { - $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' ); - $descriptor['UploadFileURL'] = [ - 'class' => 'UploadSourceField', - 'section' => 'source', - 'id' => 'wpUploadFileURL', - 'radio-id' => 'wpSourceTypeurl', - 'label-message' => 'sourceurl', - 'upload-type' => 'url', - 'radio' => &$radio, - 'help' => $this->msg( 'upload-maxfilesize', - $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['url'] ) - )->parse() . - $this->msg( 'word-separator' )->escaped() . - $this->msg( 'upload_source_url' )->parse(), - 'checked' => $selectedSourceType == 'url', - ]; - } - Hooks::run( 'UploadFormSourceDescriptors', [ &$descriptor, &$radio, $selectedSourceType ] ); - - $descriptor['Extensions'] = [ - 'type' => 'info', - 'section' => 'source', - 'default' => $this->getExtensionsMessage(), - 'raw' => true, - ]; - - return $descriptor; - } - - /** - * Get the messages indicating which extensions are preferred and prohibitted. - * - * @return string HTML string containing the message - */ - protected function getExtensionsMessage() { - # Print a list of allowed file extensions, if so configured. We ignore - # MIME type here, it's incomprehensible to most people and too long. - $config = $this->getConfig(); - - if ( $config->get( 'CheckFileExtensions' ) ) { - $fileExtensions = array_unique( $config->get( 'FileExtensions' ) ); - if ( $config->get( 'StrictFileExtensions' ) ) { - # Everything not permitted is banned - $extensionsList = - '<div id="mw-upload-permitted">' . - $this->msg( 'upload-permitted' ) - ->params( $this->getLanguage()->commaList( $fileExtensions ) ) - ->numParams( count( $fileExtensions ) ) - ->parseAsBlock() . - "</div>\n"; - } else { - # We have to list both preferred and prohibited - $fileBlacklist = array_unique( $config->get( 'FileBlacklist' ) ); - $extensionsList = - '<div id="mw-upload-preferred">' . - $this->msg( 'upload-preferred' ) - ->params( $this->getLanguage()->commaList( $fileExtensions ) ) - ->numParams( count( $fileExtensions ) ) - ->parseAsBlock() . - "</div>\n" . - '<div id="mw-upload-prohibited">' . - $this->msg( 'upload-prohibited' ) - ->params( $this->getLanguage()->commaList( $fileBlacklist ) ) - ->numParams( count( $fileBlacklist ) ) - ->parseAsBlock() . - "</div>\n"; - } - } else { - # Everything is permitted. - $extensionsList = ''; - } - - return $extensionsList; - } - - /** - * Get the descriptor of the fieldset that contains the file description - * input. The section is 'description' - * - * @return array Descriptor array - */ - protected function getDescriptionSection() { - $config = $this->getConfig(); - if ( $this->mSessionKey ) { - $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash( $this->getUser() ); - try { - $file = $stash->getFile( $this->mSessionKey ); - } catch ( Exception $e ) { - $file = null; - } - if ( $file ) { - global $wgContLang; - - $mto = $file->transform( [ 'width' => 120 ] ); - if ( $mto ) { - $this->addHeaderText( - '<div class="thumb t' . $wgContLang->alignEnd() . '">' . - Html::element( 'img', [ - 'src' => $mto->getUrl(), - 'class' => 'thumbimage', - ] ) . '</div>', 'description' ); - } - } - } - - $descriptor = [ - 'DestFile' => [ - 'type' => 'text', - 'section' => 'description', - 'id' => 'wpDestFile', - 'label-message' => 'destfilename', - 'size' => 60, - 'default' => $this->mDestFile, - # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm - 'nodata' => strval( $this->mDestFile ) !== '', - ], - 'UploadDescription' => [ - 'type' => 'textarea', - 'section' => 'description', - 'id' => 'wpUploadDescription', - 'label-message' => $this->mForReUpload - ? 'filereuploadsummary' - : 'fileuploadsummary', - 'default' => $this->mComment, - 'cols' => 80, - 'rows' => 8, - ] - ]; - if ( $this->mTextAfterSummary ) { - $descriptor['UploadFormTextAfterSummary'] = [ - 'type' => 'info', - 'section' => 'description', - 'default' => $this->mTextAfterSummary, - 'raw' => true, - ]; - } - - $descriptor += [ - 'EditTools' => [ - 'type' => 'edittools', - 'section' => 'description', - 'message' => 'edittools-upload', - ] - ]; - - if ( $this->mForReUpload ) { - $descriptor['DestFile']['readonly'] = true; - } else { - $descriptor['License'] = [ - 'type' => 'select', - 'class' => 'Licenses', - 'section' => 'description', - 'id' => 'wpLicense', - 'label-message' => 'license', - ]; - } - - if ( $config->get( 'UseCopyrightUpload' ) ) { - $descriptor['UploadCopyStatus'] = [ - 'type' => 'text', - 'section' => 'description', - 'id' => 'wpUploadCopyStatus', - 'label-message' => 'filestatus', - ]; - $descriptor['UploadSource'] = [ - 'type' => 'text', - 'section' => 'description', - 'id' => 'wpUploadSource', - 'label-message' => 'filesource', - ]; - } - - return $descriptor; - } - - /** - * Get the descriptor of the fieldset that contains the upload options, - * such as "watch this file". The section is 'options' - * - * @return array Descriptor array - */ - protected function getOptionsSection() { - $user = $this->getUser(); - if ( $user->isLoggedIn() ) { - $descriptor = [ - 'Watchthis' => [ - 'type' => 'check', - 'id' => 'wpWatchthis', - 'label-message' => 'watchthisupload', - 'section' => 'options', - 'default' => $this->mWatch, - ] - ]; - } - if ( !$this->mHideIgnoreWarning ) { - $descriptor['IgnoreWarning'] = [ - 'type' => 'check', - 'id' => 'wpIgnoreWarning', - 'label-message' => 'ignorewarnings', - 'section' => 'options', - ]; - } - - $descriptor['DestFileWarningAck'] = [ - 'type' => 'hidden', - 'id' => 'wpDestFileWarningAck', - 'default' => $this->mDestWarningAck ? '1' : '', - ]; - - if ( $this->mForReUpload ) { - $descriptor['ForReUpload'] = [ - 'type' => 'hidden', - 'id' => 'wpForReUpload', - 'default' => '1', - ]; - } - - return $descriptor; - } - - /** - * Add the upload JS and show the form. - */ - public function show() { - $this->addUploadJS(); - parent::show(); - } - - /** - * Add upload JS to the OutputPage - */ - protected function addUploadJS() { - $config = $this->getConfig(); - - $useAjaxDestCheck = $config->get( 'UseAjax' ) && $config->get( 'AjaxUploadDestCheck' ); - $useAjaxLicensePreview = $config->get( 'UseAjax' ) && - $config->get( 'AjaxLicensePreview' ) && $config->get( 'EnableAPI' ); - $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize(); - - $scriptVars = [ - 'wgAjaxUploadDestCheck' => $useAjaxDestCheck, - 'wgAjaxLicensePreview' => $useAjaxLicensePreview, - 'wgUploadAutoFill' => !$this->mForReUpload && - // If we received mDestFile from the request, don't autofill - // the wpDestFile textbox - $this->mDestFile === '', - 'wgUploadSourceIds' => $this->mSourceIds, - 'wgCheckFileExtensions' => $config->get( 'CheckFileExtensions' ), - 'wgStrictFileExtensions' => $config->get( 'StrictFileExtensions' ), - 'wgFileExtensions' => array_values( array_unique( $config->get( 'FileExtensions' ) ) ), - 'wgCapitalizeUploads' => MWNamespace::isCapitalized( NS_FILE ), - 'wgMaxUploadSize' => $this->mMaxUploadSize, - 'wgFileCanRotate' => SpecialUpload::rotationEnabled(), - ]; - - $out = $this->getOutput(); - $out->addJsConfigVars( $scriptVars ); - - $out->addModules( [ - 'mediawiki.special.upload', // Extras for thumbnail and license preview. - ] ); - } - - /** - * Empty function; submission is handled elsewhere. - * - * @return bool False - */ - function trySubmit() { - return false; - } -} - -/** - * A form field that contains a radio box in the label - */ -class UploadSourceField extends HTMLTextField { - - /** - * @param array $cellAttributes - * @return string - */ - function getLabelHtml( $cellAttributes = [] ) { - $id = $this->mParams['id']; - $label = Html::rawElement( 'label', [ 'for' => $id ], $this->mLabel ); - - if ( !empty( $this->mParams['radio'] ) ) { - if ( isset( $this->mParams['radio-id'] ) ) { - $radioId = $this->mParams['radio-id']; - } else { - // Old way. For the benefit of extensions that do not define - // the 'radio-id' key. - $radioId = 'wpSourceType' . $this->mParams['upload-type']; - } - - $attribs = [ - 'name' => 'wpSourceType', - 'type' => 'radio', - 'id' => $radioId, - 'value' => $this->mParams['upload-type'], - ]; - - if ( !empty( $this->mParams['checked'] ) ) { - $attribs['checked'] = 'checked'; - } - - $label .= Html::element( 'input', $attribs ); - } - - return Html::rawElement( 'td', [ 'class' => 'mw-label' ] + $cellAttributes, $label ); - } - - /** - * @return int - */ - function getSize() { - return isset( $this->mParams['size'] ) - ? $this->mParams['size'] - : 60; - } -} diff --git a/includes/specials/formfields/EditWatchlistCheckboxSeriesField.php b/includes/specials/formfields/EditWatchlistCheckboxSeriesField.php new file mode 100644 index 0000000..cb93bb2 --- /dev/null +++ b/includes/specials/formfields/EditWatchlistCheckboxSeriesField.php @@ -0,0 +1,37 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +class EditWatchlistCheckboxSeriesField extends HTMLMultiSelectField { + /** + * HTMLMultiSelectField throws validation errors if we get input data + * that doesn't match the data set in the form setup. This causes + * problems if something gets removed from the watchlist while the + * form is open (T34126), but we know that invalid items will + * be harmless so we can override it here. + * + * @param string $value The value the field was submitted with + * @param array $alldata The data collected from the form + * @return bool|string Bool true on success, or String error to display. + */ + function validate( $value, $alldata ) { + // Need to call into grandparent to be a good citizen. :) + return HTMLFormField::validate( $value, $alldata ); + } +} diff --git a/includes/Licenses.php b/includes/specials/formfields/Licenses.php similarity index 91% rename from includes/Licenses.php rename to includes/specials/formfields/Licenses.php index 6467777..f499cc1 100644 --- a/includes/Licenses.php +++ b/includes/specials/formfields/Licenses.php @@ -187,24 +187,3 @@ return Html::rawElement( 'select', $attribs, $this->html ); } } - -/** - * A License class for use on Special:Upload (represents a single type of license). - */ -class License { - /** @var string */ - public $template; - - /** @var string */ - public $text; - - /** - * @param string $str License name?? - */ - function __construct( $str ) { - list( $text, $template ) = explode( '|', strrev( $str ), 2 ); - - $this->template = strrev( $template ); - $this->text = strrev( $text ); - } -} diff --git a/includes/specials/formfields/UploadSourceField.php b/includes/specials/formfields/UploadSourceField.php new file mode 100644 index 0000000..251a286 --- /dev/null +++ b/includes/specials/formfields/UploadSourceField.php @@ -0,0 +1,68 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +/** + * A form field that contains a radio box in the label + */ +class UploadSourceField extends HTMLTextField { + + /** + * @param array $cellAttributes + * @return string + */ + function getLabelHtml( $cellAttributes = [] ) { + $id = $this->mParams['id']; + $label = Html::rawElement( 'label', [ 'for' => $id ], $this->mLabel ); + + if ( !empty( $this->mParams['radio'] ) ) { + if ( isset( $this->mParams['radio-id'] ) ) { + $radioId = $this->mParams['radio-id']; + } else { + // Old way. For the benefit of extensions that do not define + // the 'radio-id' key. + $radioId = 'wpSourceType' . $this->mParams['upload-type']; + } + + $attribs = [ + 'name' => 'wpSourceType', + 'type' => 'radio', + 'id' => $radioId, + 'value' => $this->mParams['upload-type'], + ]; + + if ( !empty( $this->mParams['checked'] ) ) { + $attribs['checked'] = 'checked'; + } + + $label .= Html::element( 'input', $attribs ); + } + + return Html::rawElement( 'td', [ 'class' => 'mw-label' ] + $cellAttributes, $label ); + } + + /** + * @return int + */ + function getSize() { + return isset( $this->mParams['size'] ) + ? $this->mParams['size'] + : 60; + } +} diff --git a/includes/specials/forms/EditWatchlistNormalHTMLForm.php b/includes/specials/forms/EditWatchlistNormalHTMLForm.php new file mode 100644 index 0000000..723093a --- /dev/null +++ b/includes/specials/forms/EditWatchlistNormalHTMLForm.php @@ -0,0 +1,36 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +/** + * Extend HTMLForm purely so we can have a more sane way of getting the section headers + */ +class EditWatchlistNormalHTMLForm extends HTMLForm { + public function getLegend( $namespace ) { + $namespace = substr( $namespace, 2 ); + + return $namespace == NS_MAIN + ? $this->msg( 'blanknamespace' )->escaped() + : htmlspecialchars( $this->getContext()->getLanguage()->getFormattedNsText( $namespace ) ); + } + + public function getBody() { + return $this->displaySection( $this->mFieldTree, '', 'editwatchlist-' ); + } +} diff --git a/includes/specials/forms/PreferencesForm.php b/includes/specials/forms/PreferencesForm.php new file mode 100644 index 0000000..591625d --- /dev/null +++ b/includes/specials/forms/PreferencesForm.php @@ -0,0 +1,141 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +/** + * Form to edit user preferences. + */ +class PreferencesForm extends HTMLForm { + // Override default value from HTMLForm + protected $mSubSectionBeforeFields = false; + + private $modifiedUser; + + /** + * @param User $user + */ + public function setModifiedUser( $user ) { + $this->modifiedUser = $user; + } + + /** + * @return User + */ + public function getModifiedUser() { + if ( $this->modifiedUser === null ) { + return $this->getUser(); + } else { + return $this->modifiedUser; + } + } + + /** + * Get extra parameters for the query string when redirecting after + * successful save. + * + * @return array + */ + public function getExtraSuccessRedirectParameters() { + return []; + } + + /** + * @param string $html + * @return string + */ + function wrapForm( $html ) { + $html = Xml::tags( 'div', [ 'id' => 'preferences' ], $html ); + + return parent::wrapForm( $html ); + } + + /** + * @return string + */ + function getButtons() { + $attrs = [ 'id' => 'mw-prefs-restoreprefs' ]; + + if ( !$this->getModifiedUser()->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' ) ) { + return ''; + } + + $html = parent::getButtons(); + + if ( $this->getModifiedUser()->isAllowed( 'editmyoptions' ) ) { + $t = $this->getTitle()->getSubpage( 'reset' ); + + $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); + $html .= "\n" . $linkRenderer->makeLink( $t, $this->msg( 'restoreprefs' )->text(), + Html::buttonAttributes( $attrs, [ 'mw-ui-quiet' ] ) ); + + $html = Xml::tags( 'div', [ 'class' => 'mw-prefs-buttons' ], $html ); + } + + return $html; + } + + /** + * Separate multi-option preferences into multiple preferences, since we + * have to store them separately + * @param array $data + * @return array + */ + function filterDataForSubmit( $data ) { + foreach ( $this->mFlatFields as $fieldname => $field ) { + if ( $field instanceof HTMLNestedFilterable ) { + $info = $field->mParams; + $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $fieldname; + foreach ( $field->filterDataForSubmit( $data[$fieldname] ) as $key => $value ) { + $data["$prefix$key"] = $value; + } + unset( $data[$fieldname] ); + } + } + + return $data; + } + + /** + * Get the whole body of the form. + * @return string + */ + function getBody() { + return $this->displaySection( $this->mFieldTree, '', 'mw-prefsection-' ); + } + + /** + * Get the "<legend>" for a given section key. Normally this is the + * prefs-$key message but we'll allow extensions to override it. + * @param string $key + * @return string + */ + function getLegend( $key ) { + $legend = parent::getLegend( $key ); + Hooks::run( 'PreferencesGetLegend', [ $this, $key, &$legend ] ); + return $legend; + } + + /** + * Get the keys of each top level preference section. + * @return array of section keys + */ + function getPreferenceSections() { + return array_keys( array_filter( $this->mFieldTree, 'is_array' ) ); + } +} diff --git a/includes/specials/forms/UploadForm.php b/includes/specials/forms/UploadForm.php new file mode 100644 index 0000000..07290b2 --- /dev/null +++ b/includes/specials/forms/UploadForm.php @@ -0,0 +1,443 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +/** + * Sub class of HTMLForm that provides the form section of SpecialUpload + */ +class UploadForm extends HTMLForm { + protected $mWatch; + protected $mForReUpload; + protected $mSessionKey; + protected $mHideIgnoreWarning; + protected $mDestWarningAck; + protected $mDestFile; + + protected $mComment; + protected $mTextTop; + protected $mTextAfterSummary; + + protected $mSourceIds; + + protected $mMaxFileSize = []; + + protected $mMaxUploadSize = []; + + public function __construct( array $options = [], IContextSource $context = null, + LinkRenderer $linkRenderer = null + ) { + if ( $context instanceof IContextSource ) { + $this->setContext( $context ); + } + + if ( !$linkRenderer ) { + $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); + } + + $this->mWatch = !empty( $options['watch'] ); + $this->mForReUpload = !empty( $options['forreupload'] ); + $this->mSessionKey = isset( $options['sessionkey'] ) ? $options['sessionkey'] : ''; + $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] ); + $this->mDestWarningAck = !empty( $options['destwarningack'] ); + $this->mDestFile = isset( $options['destfile'] ) ? $options['destfile'] : ''; + + $this->mComment = isset( $options['description'] ) ? + $options['description'] : ''; + + $this->mTextTop = isset( $options['texttop'] ) + ? $options['texttop'] : ''; + + $this->mTextAfterSummary = isset( $options['textaftersummary'] ) + ? $options['textaftersummary'] : ''; + + $sourceDescriptor = $this->getSourceSection(); + $descriptor = $sourceDescriptor + + $this->getDescriptionSection() + + $this->getOptionsSection(); + + Hooks::run( 'UploadFormInitDescriptor', [ &$descriptor ] ); + parent::__construct( $descriptor, $context, 'upload' ); + + # Add a link to edit MediaWiki:Licenses + if ( $this->getUser()->isAllowed( 'editinterface' ) ) { + $this->getOutput()->addModuleStyles( 'mediawiki.special.upload.styles' ); + $licensesLink = $linkRenderer->makeKnownLink( + $this->msg( 'licenses' )->inContentLanguage()->getTitle(), + $this->msg( 'licenses-edit' )->text(), + [], + [ 'action' => 'edit' ] + ); + $editLicenses = '<p class="mw-upload-editlicenses">' . $licensesLink . '</p>'; + $this->addFooterText( $editLicenses, 'description' ); + } + + # Set some form properties + $this->setSubmitText( $this->msg( 'uploadbtn' )->text() ); + $this->setSubmitName( 'wpUpload' ); + # Used message keys: 'accesskey-upload', 'tooltip-upload' + $this->setSubmitTooltip( 'upload' ); + $this->setId( 'mw-upload-form' ); + + # Build a list of IDs for javascript insertion + $this->mSourceIds = []; + foreach ( $sourceDescriptor as $field ) { + if ( !empty( $field['id'] ) ) { + $this->mSourceIds[] = $field['id']; + } + } + } + + /** + * Get the descriptor of the fieldset that contains the file source + * selection. The section is 'source' + * + * @return array Descriptor array + */ + protected function getSourceSection() { + if ( $this->mSessionKey ) { + return [ + 'SessionKey' => [ + 'type' => 'hidden', + 'default' => $this->mSessionKey, + ], + 'SourceType' => [ + 'type' => 'hidden', + 'default' => 'Stash', + ], + ]; + } + + $canUploadByUrl = UploadFromUrl::isEnabled() + && ( UploadFromUrl::isAllowed( $this->getUser() ) === true ) + && $this->getConfig()->get( 'CopyUploadsFromSpecialUpload' ); + $radio = $canUploadByUrl; + $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) ); + + $descriptor = []; + if ( $this->mTextTop ) { + $descriptor['UploadFormTextTop'] = [ + 'type' => 'info', + 'section' => 'source', + 'default' => $this->mTextTop, + 'raw' => true, + ]; + } + + $this->mMaxUploadSize['file'] = min( + UploadBase::getMaxUploadSize( 'file' ), + UploadBase::getMaxPhpUploadSize() + ); + + $help = $this->msg( 'upload-maxfilesize', + $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['file'] ) + )->parse(); + + // If the user can also upload by URL, there are 2 different file size limits. + // This extra message helps stress which limit corresponds to what. + if ( $canUploadByUrl ) { + $help .= $this->msg( 'word-separator' )->escaped(); + $help .= $this->msg( 'upload_source_file' )->parse(); + } + + $descriptor['UploadFile'] = [ + 'class' => 'UploadSourceField', + 'section' => 'source', + 'type' => 'file', + 'id' => 'wpUploadFile', + 'radio-id' => 'wpSourceTypeFile', + 'label-message' => 'sourcefilename', + 'upload-type' => 'File', + 'radio' => &$radio, + 'help' => $help, + 'checked' => $selectedSourceType == 'file', + ]; + + if ( $canUploadByUrl ) { + $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' ); + $descriptor['UploadFileURL'] = [ + 'class' => 'UploadSourceField', + 'section' => 'source', + 'id' => 'wpUploadFileURL', + 'radio-id' => 'wpSourceTypeurl', + 'label-message' => 'sourceurl', + 'upload-type' => 'url', + 'radio' => &$radio, + 'help' => $this->msg( 'upload-maxfilesize', + $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['url'] ) + )->parse() . + $this->msg( 'word-separator' )->escaped() . + $this->msg( 'upload_source_url' )->parse(), + 'checked' => $selectedSourceType == 'url', + ]; + } + Hooks::run( 'UploadFormSourceDescriptors', [ &$descriptor, &$radio, $selectedSourceType ] ); + + $descriptor['Extensions'] = [ + 'type' => 'info', + 'section' => 'source', + 'default' => $this->getExtensionsMessage(), + 'raw' => true, + ]; + + return $descriptor; + } + + /** + * Get the messages indicating which extensions are preferred and prohibitted. + * + * @return string HTML string containing the message + */ + protected function getExtensionsMessage() { + # Print a list of allowed file extensions, if so configured. We ignore + # MIME type here, it's incomprehensible to most people and too long. + $config = $this->getConfig(); + + if ( $config->get( 'CheckFileExtensions' ) ) { + $fileExtensions = array_unique( $config->get( 'FileExtensions' ) ); + if ( $config->get( 'StrictFileExtensions' ) ) { + # Everything not permitted is banned + $extensionsList = + '<div id="mw-upload-permitted">' . + $this->msg( 'upload-permitted' ) + ->params( $this->getLanguage()->commaList( $fileExtensions ) ) + ->numParams( count( $fileExtensions ) ) + ->parseAsBlock() . + "</div>\n"; + } else { + # We have to list both preferred and prohibited + $fileBlacklist = array_unique( $config->get( 'FileBlacklist' ) ); + $extensionsList = + '<div id="mw-upload-preferred">' . + $this->msg( 'upload-preferred' ) + ->params( $this->getLanguage()->commaList( $fileExtensions ) ) + ->numParams( count( $fileExtensions ) ) + ->parseAsBlock() . + "</div>\n" . + '<div id="mw-upload-prohibited">' . + $this->msg( 'upload-prohibited' ) + ->params( $this->getLanguage()->commaList( $fileBlacklist ) ) + ->numParams( count( $fileBlacklist ) ) + ->parseAsBlock() . + "</div>\n"; + } + } else { + # Everything is permitted. + $extensionsList = ''; + } + + return $extensionsList; + } + + /** + * Get the descriptor of the fieldset that contains the file description + * input. The section is 'description' + * + * @return array Descriptor array + */ + protected function getDescriptionSection() { + $config = $this->getConfig(); + if ( $this->mSessionKey ) { + $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash( $this->getUser() ); + try { + $file = $stash->getFile( $this->mSessionKey ); + } catch ( Exception $e ) { + $file = null; + } + if ( $file ) { + global $wgContLang; + + $mto = $file->transform( [ 'width' => 120 ] ); + if ( $mto ) { + $this->addHeaderText( + '<div class="thumb t' . $wgContLang->alignEnd() . '">' . + Html::element( 'img', [ + 'src' => $mto->getUrl(), + 'class' => 'thumbimage', + ] ) . '</div>', 'description' ); + } + } + } + + $descriptor = [ + 'DestFile' => [ + 'type' => 'text', + 'section' => 'description', + 'id' => 'wpDestFile', + 'label-message' => 'destfilename', + 'size' => 60, + 'default' => $this->mDestFile, + # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm + 'nodata' => strval( $this->mDestFile ) !== '', + ], + 'UploadDescription' => [ + 'type' => 'textarea', + 'section' => 'description', + 'id' => 'wpUploadDescription', + 'label-message' => $this->mForReUpload + ? 'filereuploadsummary' + : 'fileuploadsummary', + 'default' => $this->mComment, + 'cols' => 80, + 'rows' => 8, + ] + ]; + if ( $this->mTextAfterSummary ) { + $descriptor['UploadFormTextAfterSummary'] = [ + 'type' => 'info', + 'section' => 'description', + 'default' => $this->mTextAfterSummary, + 'raw' => true, + ]; + } + + $descriptor += [ + 'EditTools' => [ + 'type' => 'edittools', + 'section' => 'description', + 'message' => 'edittools-upload', + ] + ]; + + if ( $this->mForReUpload ) { + $descriptor['DestFile']['readonly'] = true; + } else { + $descriptor['License'] = [ + 'type' => 'select', + 'class' => 'Licenses', + 'section' => 'description', + 'id' => 'wpLicense', + 'label-message' => 'license', + ]; + } + + if ( $config->get( 'UseCopyrightUpload' ) ) { + $descriptor['UploadCopyStatus'] = [ + 'type' => 'text', + 'section' => 'description', + 'id' => 'wpUploadCopyStatus', + 'label-message' => 'filestatus', + ]; + $descriptor['UploadSource'] = [ + 'type' => 'text', + 'section' => 'description', + 'id' => 'wpUploadSource', + 'label-message' => 'filesource', + ]; + } + + return $descriptor; + } + + /** + * Get the descriptor of the fieldset that contains the upload options, + * such as "watch this file". The section is 'options' + * + * @return array Descriptor array + */ + protected function getOptionsSection() { + $user = $this->getUser(); + if ( $user->isLoggedIn() ) { + $descriptor = [ + 'Watchthis' => [ + 'type' => 'check', + 'id' => 'wpWatchthis', + 'label-message' => 'watchthisupload', + 'section' => 'options', + 'default' => $this->mWatch, + ] + ]; + } + if ( !$this->mHideIgnoreWarning ) { + $descriptor['IgnoreWarning'] = [ + 'type' => 'check', + 'id' => 'wpIgnoreWarning', + 'label-message' => 'ignorewarnings', + 'section' => 'options', + ]; + } + + $descriptor['DestFileWarningAck'] = [ + 'type' => 'hidden', + 'id' => 'wpDestFileWarningAck', + 'default' => $this->mDestWarningAck ? '1' : '', + ]; + + if ( $this->mForReUpload ) { + $descriptor['ForReUpload'] = [ + 'type' => 'hidden', + 'id' => 'wpForReUpload', + 'default' => '1', + ]; + } + + return $descriptor; + } + + /** + * Add the upload JS and show the form. + */ + public function show() { + $this->addUploadJS(); + parent::show(); + } + + /** + * Add upload JS to the OutputPage + */ + protected function addUploadJS() { + $config = $this->getConfig(); + + $useAjaxDestCheck = $config->get( 'UseAjax' ) && $config->get( 'AjaxUploadDestCheck' ); + $useAjaxLicensePreview = $config->get( 'UseAjax' ) && + $config->get( 'AjaxLicensePreview' ) && $config->get( 'EnableAPI' ); + $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize(); + + $scriptVars = [ + 'wgAjaxUploadDestCheck' => $useAjaxDestCheck, + 'wgAjaxLicensePreview' => $useAjaxLicensePreview, + 'wgUploadAutoFill' => !$this->mForReUpload && + // If we received mDestFile from the request, don't autofill + // the wpDestFile textbox + $this->mDestFile === '', + 'wgUploadSourceIds' => $this->mSourceIds, + 'wgCheckFileExtensions' => $config->get( 'CheckFileExtensions' ), + 'wgStrictFileExtensions' => $config->get( 'StrictFileExtensions' ), + 'wgFileExtensions' => array_values( array_unique( $config->get( 'FileExtensions' ) ) ), + 'wgCapitalizeUploads' => MWNamespace::isCapitalized( NS_FILE ), + 'wgMaxUploadSize' => $this->mMaxUploadSize, + 'wgFileCanRotate' => SpecialUpload::rotationEnabled(), + ]; + + $out = $this->getOutput(); + $out->addJsConfigVars( $scriptVars ); + + $out->addModules( [ + 'mediawiki.special.upload', // Extras for thumbnail and license preview. + ] ); + } + + /** + * Empty function; submission is handled elsewhere. + * + * @return bool False + */ + function trySubmit() { + return false; + } +} diff --git a/includes/specials/helpers/ImportReporter.php b/includes/specials/helpers/ImportReporter.php new file mode 100644 index 0000000..325c6732 --- /dev/null +++ b/includes/specials/helpers/ImportReporter.php @@ -0,0 +1,188 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +/** + * Reporting callback + * @ingroup SpecialPage + */ +class ImportReporter extends ContextSource { + private $reason = false; + private $logTags = []; + private $mOriginalLogCallback = null; + private $mOriginalPageOutCallback = null; + private $mLogItemCount = 0; + + /** + * @param WikiImporter $importer + * @param bool $upload + * @param string $interwiki + * @param string|bool $reason + */ + function __construct( $importer, $upload, $interwiki, $reason = false ) { + $this->mOriginalPageOutCallback = + $importer->setPageOutCallback( [ $this, 'reportPage' ] ); + $this->mOriginalLogCallback = + $importer->setLogItemCallback( [ $this, 'reportLogItem' ] ); + $importer->setNoticeCallback( [ $this, 'reportNotice' ] ); + $this->mPageCount = 0; + $this->mIsUpload = $upload; + $this->mInterwiki = $interwiki; + $this->reason = $reason; + } + + /** + * Sets change tags to apply to the import log entry and null revision. + * + * @param array $tags + * @since 1.29 + */ + public function setChangeTags( array $tags ) { + $this->logTags = $tags; + } + + function open() { + $this->getOutput()->addHTML( "<ul>\n" ); + } + + function reportNotice( $msg, array $params ) { + $this->getOutput()->addHTML( + Html::element( 'li', [], $this->msg( $msg, $params )->text() ) + ); + } + + function reportLogItem( /* ... */ ) { + $this->mLogItemCount++; + if ( is_callable( $this->mOriginalLogCallback ) ) { + call_user_func_array( $this->mOriginalLogCallback, func_get_args() ); + } + } + + /** + * @param Title $title + * @param ForeignTitle $foreignTitle + * @param int $revisionCount + * @param int $successCount + * @param array $pageInfo + * @return void + */ + public function reportPage( $title, $foreignTitle, $revisionCount, + $successCount, $pageInfo ) { + $args = func_get_args(); + call_user_func_array( $this->mOriginalPageOutCallback, $args ); + + if ( $title === null ) { + # Invalid or non-importable title; a notice is already displayed + return; + } + + $this->mPageCount++; + $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); + if ( $successCount > 0 ) { + // <bdi> prevents jumbling of the versions count + // in RTL wikis in case the page title is LTR + $this->getOutput()->addHTML( + "<li>" . $linkRenderer->makeLink( $title ) . " " . + "<bdi>" . + $this->msg( 'import-revision-count' )->numParams( $successCount )->escaped() . + "</bdi>" . + "</li>\n" + ); + + $logParams = [ '4:number:count' => $successCount ]; + if ( $this->mIsUpload ) { + $detail = $this->msg( 'import-logentry-upload-detail' )->numParams( + $successCount )->inContentLanguage()->text(); + $action = 'upload'; + } else { + $pageTitle = $foreignTitle->getFullText(); + $fullInterwikiPrefix = $this->mInterwiki; + Hooks::run( 'ImportLogInterwikiLink', [ &$fullInterwikiPrefix, &$pageTitle ] ); + + $interwikiTitleStr = $fullInterwikiPrefix . ':' . $pageTitle; + $interwiki = '[[:' . $interwikiTitleStr . ']]'; + $detail = $this->msg( 'import-logentry-interwiki-detail' )->numParams( + $successCount )->params( $interwiki )->inContentLanguage()->text(); + $action = 'interwiki'; + $logParams['5:title-link:interwiki'] = $interwikiTitleStr; + } + if ( $this->reason ) { + $detail .= $this->msg( 'colon-separator' )->inContentLanguage()->text() + . $this->reason; + } + + $comment = $detail; // quick + $dbw = wfGetDB( DB_MASTER ); + $latest = $title->getLatestRevID(); + $nullRevision = Revision::newNullRevision( + $dbw, + $title->getArticleID(), + $comment, + true, + $this->getUser() + ); + + $nullRevId = null; + if ( !is_null( $nullRevision ) ) { + $nullRevId = $nullRevision->insertOn( $dbw ); + $page = WikiPage::factory( $title ); + # Update page record + $page->updateRevisionOn( $dbw, $nullRevision ); + Hooks::run( + 'NewRevisionFromEditComplete', + [ $page, $nullRevision, $latest, $this->getUser() ] + ); + } + + // Create the import log entry + $logEntry = new ManualLogEntry( 'import', $action ); + $logEntry->setTarget( $title ); + $logEntry->setComment( $this->reason ); + $logEntry->setPerformer( $this->getUser() ); + $logEntry->setParameters( $logParams ); + $logid = $logEntry->insert(); + if ( count( $this->logTags ) ) { + $logEntry->setTags( $this->logTags ); + } + // Make sure the null revision will be tagged as well + $logEntry->setAssociatedRevId( $nullRevId ); + + $logEntry->publish( $logid ); + + } else { + $this->getOutput()->addHTML( "<li>" . $linkRenderer->makeKnownLink( $title ) . " " . + $this->msg( 'import-nonewrevisions' )->escaped() . "</li>\n" ); + } + } + + function close() { + $out = $this->getOutput(); + if ( $this->mLogItemCount > 0 ) { + $msg = $this->msg( 'imported-log-entries' )->numParams( $this->mLogItemCount )->parse(); + $out->addHTML( Xml::tags( 'li', null, $msg ) ); + } elseif ( $this->mPageCount == 0 && $this->mLogItemCount == 0 ) { + $out->addHTML( "</ul>\n" ); + + return Status::newFatal( 'importnopages' ); + } + $out->addHTML( "</ul>\n" ); + + return Status::newGood( $this->mPageCount ); + } +} diff --git a/includes/specials/helpers/License.php b/includes/specials/helpers/License.php new file mode 100644 index 0000000..4f94b4d --- /dev/null +++ b/includes/specials/helpers/License.php @@ -0,0 +1,46 @@ +<?php +/** + * License selector for use on Special:Upload. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup SpecialPage + * @author Ævar Arnfjörð Bjarmason <ava...@gmail.com> + * @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later + */ + +/** + * A License class for use on Special:Upload (represents a single type of license). + */ +class License { + /** @var string */ + public $template; + + /** @var string */ + public $text; + + /** + * @param string $str License name?? + */ + function __construct( $str ) { + list( $text, $template ) = explode( '|', strrev( $str ), 2 ); + + $this->template = strrev( $template ); + $this->text = strrev( $text ); + } +} -- To view, visit https://gerrit.wikimedia.org/r/377652 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I58abcbb44dbf9bf1762b4252555f7552bfa7c253 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Bartosz Dziewoński <matma....@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits