Robert Vogel has uploaded a new change for review. https://gerrit.wikimedia.org/r/148956
Change subject: [WIP] Moving upload panel/dialog to BSF ...................................................................... [WIP] Moving upload panel/dialog to BSF This should make it easier to reuse the components REQUEST FOR COMMENTS: What do I do wth the 'bs-insertfile-upload*' messages? Refactor them to 'bs-extjs-upload*'? How about the already translated messages? Change-Id: I27081530c7594a9f7e70226cc133a1b2aabd229c --- M BlueSpiceFoundation.php M includes/AutoLoader.php M includes/CommonAJAXInterface.php M includes/CoreHooks.php A includes/JsonLicenses.php A resources/bluespice.extjs/BS/dialog/Base.js A resources/bluespice.extjs/BS/dialog/Upload.js A resources/bluespice.extjs/BS/panel/Base.js A resources/bluespice.extjs/BS/panel/Upload.js 9 files changed, 439 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/BlueSpiceFoundation refs/changes/56/148956/1 diff --git a/BlueSpiceFoundation.php b/BlueSpiceFoundation.php index 7e2760c..282ef22 100644 --- a/BlueSpiceFoundation.php +++ b/BlueSpiceFoundation.php @@ -63,6 +63,7 @@ $wgAjaxExportList[] = 'BsCommonAJAXInterface::getCategoryStoreData'; $wgAjaxExportList[] = 'BsCommonAJAXInterface::getAsyncCategoryTreeStoreData'; $wgAjaxExportList[] = 'BsCommonAJAXInterface::getFileUrl'; +$wgAjaxExportList[] = 'BsCommonAJAXInterface::getLicenses'; $wgAjaxExportList[] = 'BsCore::ajaxBSPing'; //I18N MW1.23+ diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 84196fb..1c8f225 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -40,6 +40,7 @@ $GLOBALS['wgAutoloadClasses']['BsCAResponse'] = __DIR__."/CAResponse.php"; $GLOBALS['wgAutoloadClasses']['BsCAIResponse'] = __DIR__."/CAResponse.php"; $GLOBALS['wgAutoloadClasses']['BsExtJSStoreParams'] = __DIR__."/ExtJSStoreParams.php"; +$GLOBALS['wgAutoloadClasses']['JsonLicenses'] = __DIR__ . '/JsonLicenses.php'; //adapter $GLOBALS['wgAutoloadClasses']['BsExtensionMW'] = __DIR__."/ExtensionMW.class.php"; diff --git a/includes/CommonAJAXInterface.php b/includes/CommonAJAXInterface.php index b6faa62..57a23fd 100644 --- a/includes/CommonAJAXInterface.php +++ b/includes/CommonAJAXInterface.php @@ -509,6 +509,11 @@ } return $image->getUrl(); } + + public static function getLicenses() { + $oLicenses = new JsonLicenses(); + return $oLicenses->getJsonOutput(); + } } //Alias diff --git a/includes/CoreHooks.php b/includes/CoreHooks.php index da7f66d..de8f33a 100755 --- a/includes/CoreHooks.php +++ b/includes/CoreHooks.php @@ -161,6 +161,10 @@ $sExtJS.= " Ext.Loader.setPath( 'BS', '$sBasePath' + '/BS');"; $sExtJS.= " Ext.Loader.setPath( 'Ext.ux', '$sBasePath' + '/Ext.ux');"; $sExtJS.= ' Ext.Loader.setPath('.FormatJson::encode( $aExtJSPaths).');'; + $sExtJS.= ''; + $sExtJS.= ' /* Maintain some downwards compatibility */'; + $sExtJS.= ' Ext.ClassManager.setAlias( "BS.panel.Upload", "BS.InsertFile.UploadPanel" );'; + $sExtJS.= ' Ext.ClassManager.setAlias( "BS.dialog.Upload", "BS.InsertFile.UploadDialog" );'; $sExtJS.= '});'; $out->addScript( diff --git a/includes/JsonLicenses.php b/includes/JsonLicenses.php new file mode 100644 index 0000000..197e37e --- /dev/null +++ b/includes/JsonLicenses.php @@ -0,0 +1,44 @@ +<?php + +/** + * JsonLicenses + * + * Extends the mediawiki licenses class to put the available licenses out in a json format for ExtJS combobox. + * + * @author Sebastian Ulbricht + */ +class JsonLicenses extends Licenses { + + protected $json; + + public function __construct() { + parent::__construct( array( 'fieldname' => 'JsonLicenses' ) ); + } + + public function getJsonOutput() { + $this->json[] = $this->outputJsonOption( wfMessage( 'nolicense' )->text(), '' ); + $this->makeJson( $this->getLicenses() ); + return json_encode(array('items' => $this->json)); + } + + protected function outputJsonOption( $text, $value, $depth = 0 ) { + return array( + 'text' => $text, + 'value' => "\n\n==". wfMessage( 'license-header' )->inContentLanguage()->text()."==\n{{".$value."}}", + 'indent'=> $depth + ); + } + + protected function makeJson( $tagset, $depth = 0 ) { + foreach ( $tagset as $key => $val ) { + if ( is_array( $val ) ) { + $this->json[] = $this->outputJsonOption( $key, '', $depth ); + $this->makeJson( $val, $depth + 1 ); + } + else { + $this->json[] = $this->outputJsonOption( $val->text, $val->template, $depth ); + } + } + } + +} \ No newline at end of file diff --git a/resources/bluespice.extjs/BS/dialog/Base.js b/resources/bluespice.extjs/BS/dialog/Base.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/resources/bluespice.extjs/BS/dialog/Base.js diff --git a/resources/bluespice.extjs/BS/dialog/Upload.js b/resources/bluespice.extjs/BS/dialog/Upload.js new file mode 100644 index 0000000..eef4b93 --- /dev/null +++ b/resources/bluespice.extjs/BS/dialog/Upload.js @@ -0,0 +1,39 @@ +Ext.define( 'BS.dialog.Upload', { + extend: 'BS.Window', + requires:[ + 'Ext.Button', 'BS.panel.Upload' + ], + minHeight:null, + padding: 0, + layout: 'fit', + title: mw.message('bs-insertfile-labelupload').plain(), + + afterInitComponent: function() { + this.upMain = new BS.panel.Upload({ + id: this.getId()+'-upload-panel', + allowedFileExtensions: this.allowedFileExtensions + }); + this.upMain.on( 'uploadcomplete', this.onUpMainUploadComplete, this ); + + this.items = [ + this.upMain + ]; + + this.callParent(arguments); + }, + + onBtnOKClick: function() { + this.upMain.uploadFile(); + }, + + onUpMainUploadComplete: function( panel, upload ) { + this.fireEvent( 'ok', this, upload ); + this.close(); + }, + + resetData: function() { + this.upMain.getForm().reset(); + + this.callParent(arguments); + } +}); \ No newline at end of file diff --git a/resources/bluespice.extjs/BS/panel/Base.js b/resources/bluespice.extjs/BS/panel/Base.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/resources/bluespice.extjs/BS/panel/Base.js diff --git a/resources/bluespice.extjs/BS/panel/Upload.js b/resources/bluespice.extjs/BS/panel/Upload.js new file mode 100644 index 0000000..b10fc70 --- /dev/null +++ b/resources/bluespice.extjs/BS/panel/Upload.js @@ -0,0 +1,345 @@ +Ext.define( 'BS.panel.Upload', { + extend: 'Ext.form.Panel', + require:[ + 'BS.form.action.MediaWikiApiCall' + ], + fieldDefaults: { + anchor: '100%', + labelWidth: 70, + labelAlign: 'right', + msgTarget: 'under' + }, + fileUpload: true, + layout: { + type: 'vbox', + align: 'stretch' // Child items are stretched to full width + }, + bodyPadding: 5, + + //Custom settings + allowedFileExtensions: mw.config.get( 'wgFileExtensions' ), + + initComponent: function() { + this.panelItems = this.makeMainItems(); + var detailsItems = this.makeDetailItems(); + + $(document).trigger( 'BSUploadPanelInitComponent', [ this, this.panelItems, detailsItems ] ); + + this.fsDetails.add( detailsItems ); + this.items = this.panelItems; + + this.addEvents( 'uploadcomplete' ); + + this.callParent(arguments); + }, + + makeMainItems: function() { + this.tfFileName = Ext.create('Ext.form.TextField', { + fieldLabel: mw.message('bs-insertfile-uploaddestfilelabel').plain(), + id: this.getId()+'-filename', + name: 'filename' + }); + this.tfFileName.on( 'change', this.tfFileNameChange, this ); + + //HINT: https://www.mediawiki.org/wiki/API:Upload#Uploading + this.fuFile = Ext.create('Ext.form.field.File', { + fieldLabel: mw.message('bs-insertfile-uploadfilefieldlabel').plain(), + buttonText: mw.message('bs-insertfile-uploadbuttontext').plain(), + id: this.getId()+'-file', + name: 'file', + emptyText: mw.message('bs-insertfile-uploadfileemptytext').plain(), + validator: this.validateFile, + validateOnChange: true + }); + this.fuFile.on( 'change', this.fuFileChange, this ); + + this.fsDetails = Ext.create( 'Ext.form.FieldSet', { + title: 'Details', + collapsed: true, + collapsible: true, + anchor: '98%', + defaults: { + anchor: '100%', + labelWidth: 90, + labelAlign: 'right' + } + }); + + return [ + this.tfFileName, + this.fuFile, + this.fsDetails + ]; + }, + + makeDetailItems: function() { + this.taDescription = Ext.create('Ext.form.field.TextArea', { + fieldLabel: mw.message('bs-insertfile-uploaddescfilelabel').plain(), + id: this.getId()+'-text', + value: '', + name: 'text' + }); + + this.storeLicenses = Ext.create( 'Ext.data.Store', { + proxy: { + type: 'ajax', + url: bs.util.getCAIUrl('getLicenses'), + reader: { + type: 'json', + root: 'items', + idProperty: 'value' + } + }, + extraParams: { + type: this.storeFileType + }, + remoteSort: true, + fields: ['text', 'value', 'indent'] + }); + + this.cbLicences = Ext.create('Ext.form.ComboBox',{ + fieldLabel: mw.message('bs-insertfile-license').plain(), + //autoSelect: true, + //forceSelection: true, + //typeAhead: true, + //triggerAction: 'all', + //lazyRender: true, + mode: 'local', + store: this.storeLicenses, + valueField: 'value', + displayField: 'text', + tpl: new Ext.XTemplate( + '<ul class="x-list-plain">', + '<tpl for=".">', + '<tpl if="this.hasValue(value) == false">', + '<li role="option" class="x-boundlist-item no-value">{text}</li>', + '</tpl>', + '<tpl if="this.hasValue(value)">', + '<li role="option" class="x-boundlist-item indent-{indent}">{text}</li>', + '</tpl>', + '</tpl>', + '</ul>', + { + compiled: true, + disableFormats: true, + // member functions: + hasValue: function(value) { + return value !== ''; + } + } + ) + }); + + this.cbxWatch = Ext.create('Ext.form.field.Checkbox', { + boxLabel: mw.message('bs-insertfile-uploadwatchthislabel').plain(), + id: this.getId()+'watch_page', + name: 'watch' + }); + + this.cbxWarnings = Ext.create('Ext.form.field.Checkbox', { + boxLabel: mw.message('bs-insertfile-uploadignorewarningslabel').plain(), + id: this.getId()+'ignorewarnings', + name: 'ignorewarnings' + }); + + this.bsCategories = Ext.create( 'BS.form.CategoryBoxSelect', { + id: this.getId()+'categories', + name: 'categories', + fieldLabel: mw.message('bs-insertfile-categories').plain() + }); + + return [ + this.bsCategories, + this.taDescription, + this.cbLicences, + this.cbxWarnings, + this.cbxWatch + ]; + }, + + fuFileChange: function(field, value, eOpts) { + //Remove path info + value = value.replace(/^.*?([^\\\/:]*?\.[a-z0-9]+)$/img, "$1"); + value = value.replace(/\s/g, "_"); + if( mw.config.get('bsIsWindows') ) { + value = value.replace(/[^\u0000-\u007F]/gmi, ''); //Replace Non-ASCII + } + + this.tfFileName.setValue(value); + this.tfFileName.fireEvent('change', this.tfFileName, value); + }, + + tfFileNameChange: function(field, value) { + Ext.Ajax.request({ + url: bs.util.getAjaxDispatcherUrl( 'InsertFileAJAXBackend::getExistsWarning', [ value ] ), + success: function(response, options) { + if(!(response.responseText.trim() === '' + || response.responseText === ' ' + || response.responseText === ' ')) { + + bs.util.alert( + this.getId()+'-existswarning', + { + titleMsg: 'bs-extjs-title-warning', + text: response.responseText + }, + { + ok: function() { + //User is noticed. Now let's set the + //ignore warnings flag automatically + this.cbxWarnings.setValue(true); + }, + scope: this + } + ); + } + }, + scope: this + }); + }, + + checkFileSize: function( ExtCmpId ) { + //No FileAPI? No love. + if(typeof window.FileReader === 'undefined') return true; + + var allowedSize = mw.config.get('bsMaxUploadSize'); + if( allowedSize === null ) return true; + + var filesize = this.fuFile.fileInputEl.dom.files[0].size; + if( filesize > allowedSize.php || filesize > allowedSize.mediawiki) { + return false; + } + return true; + }, + + uploadFile: function( sessionKeyForReupload ) { + var desc = this.taDescription.getValue(); + var licence = this.cbLicences.getValue(); + if( licence ) { + desc += licence + "\n"; + } + + var categories = this.bsCategories.getValue(); + var formattedNamespaces = mw.config.get('wgFormattedNamespaces'); + for( var i = 0; i < categories.length; i++ ) { + var categoryLink = new bs.wikiText.Link({ + title: $.ucFirst( categories[i] ), + nsText: formattedNamespaces[bs.ns.NS_CATEGORY], + link: false //TDOD: fix this in "bs.wikiText.Link" + }); + desc += "\n" + categoryLink.toString(); + } + this.taDescription.setValue( desc ); + + this.cbLicences.disable(); //To prevent the form from submitting a generated name + + var params = { + action: 'upload', + token: mw.user.tokens.get('editToken'), + //IE9 has an issue with this API call returnug a application/json + //content-type. Therefore we let the server return a "text/xml" + //content-type header + //HINT: http://stackoverflow.com/questions/18571719/extjs-file-uploading-error-on-ie8-ie9 + format: 'xml' + }; + + if( sessionKeyForReupload ) { + params.sessionkey = sessionKeyForReupload; + } + + this.getForm().doAction( Ext.create('BS.form.action.MediaWikiApiCall', { + form: this.getForm(), //Required + url: mw.util.wikiScript('api'), + params: params, + success: this.onUploadSuccess, + failure: this.onUploadFailure, + scope: this + })); + + //We mask only the FormPanel, because masking the whole document using + // "waitMsg" param on MediaWikiApiCall does no automatic unmasking. + //This is because MediaWikiApiCall overrides the onSuccess/onFailure + //methods of action "Submit" + this.getEl().mask( + mw.message('bs-insertfile-upload-waitmessage').plain(), + Ext.baseCSSPrefix + 'mask-loading' + ); + }, + + onUploadSuccess: function( response, action ) { + this.getEl().unmask(); + this.cbLicences.enable(); + + var errorTag = response.responseXML + .documentElement.getElementsByTagName('error').item(0); + + if( errorTag !== null ) { + bs.util.alert( + this.getId()+'-error', + { + title: mw.message('bs-insertfile-error').plain(), + text: errorTag.getAttribute('info') + } + ); + return; + } + + //As we process XML instead of JSON (see reason above) we have to + //create a suitable JS object from the XML response to be compatible + var uploadTag = response.responseXML + .documentElement.getElementsByTagName('upload').item(0); + var imageinfoTag = uploadTag.getElementsByTagName('imageinfo').item(0); + + var imageinfo = {}; + if( imageinfoTag.attributes ) { + for( var i = 0; i < imageinfoTag.attributes.length; i++ ) { + var attribute = imageinfoTag.attributes.item(i); + imageinfo[attribute.nodeName] = attribute.nodeValue; + } + } + var upload = { + result: uploadTag.getAttribute('result'), + filename: uploadTag.getAttribute('filename'), + imageinfo: imageinfo + }; + + this.fireEvent( 'uploadcomplete', this, upload ); + this.getForm().reset(); + }, + + onUploadFailure: function( response, action ) { + //This would only happen when a server error occurred but not when the + //MediaWiki API returns an JSON encoded error + this.getForm().reset(); + this.cbLicences.enable(); + }, + + //scope: "this" == fuFile + validateFile: function( value ) { + if( value === "" ) return true; + var me = this.up('form'); + me.allowedFileExtensions = mw.config.get( 'wgFileExtensions' ); + + var nameParts = value.split('.'); + var fileExtension = nameParts[nameParts.length-1].toLowerCase(); + var extensionFound = false; + + $.each(me.allowedFileExtensions, function(index, value) { + if (value.toLowerCase() === fileExtension.toLowerCase()) { + extensionFound = true; + return false; + } + }); + + if(!extensionFound) { + return mw.message('bs-insertfile-allowedfiletypesare').plain() + + " " + me.allowedFileExtensions.join(', '); + } + + if( me.checkFileSize() === false ) { + return mw.message( 'largefileserver' ).plain(); + } + + return true; + } +}); -- To view, visit https://gerrit.wikimedia.org/r/148956 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I27081530c7594a9f7e70226cc133a1b2aabd229c Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/BlueSpiceFoundation Gerrit-Branch: master Gerrit-Owner: Robert Vogel <vo...@hallowelt.biz> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits