diff --git a/api/js/etemplate/et2_widget_vfs.ts b/api/js/etemplate/et2_widget_vfs.ts index c390f9d707..4b7a3e16a1 100644 --- a/api/js/etemplate/et2_widget_vfs.ts +++ b/api/js/etemplate/et2_widget_vfs.ts @@ -24,7 +24,6 @@ import {ClassWithAttributes} from "./et2_core_inheritance"; import {et2_textbox, et2_textbox_ro} from "./et2_widget_textbox"; import {et2_description} from "./et2_widget_description"; import {et2_file} from "./et2_widget_file"; -import {et2_inputWidget} from "./et2_core_inputWidget"; import {et2_IDetachedDOM} from "./et2_core_interfaces"; import {egw} from "../jsapi/egw_global"; import {egw_getAppObjectManager, egwActionObject} from "../egw_action/egw_action"; @@ -820,425 +819,3 @@ export class et2_vfsUpload extends et2_file } } et2_register_widget(et2_vfsUpload, ["vfs-upload"]); - - -export class et2_vfsSelect extends et2_inputWidget -{ - // Allowed mode options - modes : string[] = ['open','open-multiple','saveas','select-dir']; - - static readonly _attributes : any = { - "mode": { - name: "Dialog mode", - type: "string", - description: "One of {open|open-multiple|saveas|select-dir}", - default: "open-multiple" - }, - "method": { - name: "Server side callback", - type: "string", - description: "Server side callback to process selected value(s) in \n\ - app.class.method or class::method format. The first parameter will \n\ - be Method ID, the second the file list. 'download' is reserved and it \n\ - means it should use download_baseUrl instead of path in value (no method\n\ - will be actually executed)." - }, - "method_id": { - name: "Method ID", - type: "any", - description: "optional parameter passed to server side callback.\n\ - Can be a string or a function.", - default: "" - }, - "path": { - name: "Path", - type: "string", - description:"Start path in VFS. Leave unset to use the last used path." - }, - "mime": { - name: "Mime type", - type: "any", - description: "Limit display to the given mime-type" - }, - "button_label": { - name: "Button label", - description: "Set the label on the dialog's OK button.", - default: "open" - }, - "value": { - type: "any", // Object - description: "Array of paths (strings)" - }, - "button_caption":{ - name: "button caption", - type: "string", - default: "Select files from Filemanager ...", - description: "Caption for vfs-select button.", - translate:true - }, - "button_icon":{ - name: "button icon", - type: "string", - default: "check", - description: "Custom icon to show on submit button." - }, - "name": { - name:"File name", - type: "any", // Object - description: "file name", - default: "" - }, - "dialog_title":{ - name: "dialog title", - type: "string", - default: "Save as", - description: "Title of dialog", - translate:true - }, - "extra_buttons": { - name: "extra action buttons", - type: "any", - description: "Extra buttons passed to dialog. It's co-related to method." - } - }; - - button : JQuery; - submit_callback : any; - dialog : Et2Dialog; - private value : any; - /** - * Constructor - * - * @param _parent - * @param _attrs - * @memberOf et2_vfsSelect - */ - constructor(_parent, _attrs? : WidgetConfig, _child? : object) - { - // Call the inherited constructor - super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_vfsSelect._attributes, _child || {})); - - // Allow no child widgets - this.supportedWidgetClasses = []; - - this.button = jQuery(document.createElement("et2-button")) - .attr("title", this.egw().lang("Select file(s) from VFS")) - .attr("noSubmit", true) - .addClass("et2_vfs_btn") - .attr("image", "filemanager/navbar"); - - if(this.options.readonly) - { - this.button.hide(); - } - - if (this.options.button_caption != "") - { - this.button.text(this.options.button_caption); - } - this.setDOMNode(this.button[0]); - } - - _content(_content, _callback) - { - egw(window).loading_prompt('vfs-select', true, '', 'body'); - let self = this; - if (typeof app.vfsSelectUI !="undefined") - { - if(this.dialog) - { - this.dialog.close(); - } - app.vfsSelectUI.destroy(); - delete app.vfsSelectUI; - } - let attrs = { - mode: this.options.mode, - label: this.options.button_label, - path: this.options.path || null, - mime: this.options.mime || null, - name: this.options.name, - method: this.options.method, - recentPaths: et2_vfsSelect._getRecentPaths() - }; - var callback = _callback || this._buildDialog; - egw(window).json( - 'EGroupware\\Api\\Etemplate\\Widget\\Vfs::ajax_vfsSelect_content', - [_content, attrs], - function(_content){ - egw(window).loading_prompt('vfs-select', false); - callback.apply(self, arguments); - } - ).sendRequest(true); - } - - /** - * Builds file navigator dialog - * - * @param {object} _data content - */ - private _buildDialog(_data) - { - if (!_data.content.mode.match(/open|open-multiple|saveas|select-dir/)) { - egw.debug('warn', 'Mode is not matched!'); - return; - } - let self = this; - let buttons = [ - { - label: egw.lang(_data.content.label), - id: "submit", - image: _data.content.mode.match(/saveas|select-dir/) ? "save" : this.options.button_icon, - default: false - } - ]; - let extra_buttons_action = {}; - - if (this.options.extra_buttons && this.options.method) - { - for (let i=0; i < this.options.extra_buttons.length; i++) - { - delete(this.options.extra_buttons[i]['click']); - buttons.push(this.options.extra_buttons[i]); - extra_buttons_action[this.options.extra_buttons[i]['id']] = this.options.extra_buttons[i]['id']; - } - - } - buttons.push({label: egw.lang("Close"), id: "close", image: "cancel"}); - - - // Don't rely only on app_name to fetch et2 object as app_name may not - // always represent current app of the window, e.g.: mail admin account. - // Try to fetch et2 from its template name. - let etemplate = jQuery('form').data('etemplate'); - let et2; - let currentApp = egw(window).app_name(); - if (etemplate && etemplate.name && !app[currentApp]) - { - et2 = etemplate2.getByTemplate(etemplate.name)[0]; - currentApp = et2.name.split('.')[0]; - } - else - { - et2 = etemplate2.getByApplication(currentApp)[0]; - } - - let data = jQuery.extend(_data, {'currentapp': currentApp, etemplate_exec_id: et2.etemplate_exec_id}); - - // define a mini app object for vfs select UI - app.vfsSelectUI = et2.app_obj['vfsSelectUI'] = new app.classes.vfsSelectUI; - - // callback for dialog - this.submit_callback = function(submit_button_id, submit_value, savemode) - { - if ((submit_button_id == 'submit' || (extra_buttons_action && extra_buttons_action[submit_button_id])) && submit_value) - { - let files : any = []; - switch(_data.content.mode) - { - case 'open-multiple': - if (submit_value.dir && submit_value.dir.selected) - { - for(var key in Object.keys(submit_value.dir.selected)) - { - if (submit_value.dir.selected[key] != "") - { - files.push(submit_value.path+'/'+submit_value.dir.selected[key]); - } - } - } - break; - case 'select-dir': - files = submit_value.path; - break; - default: - if (self.options.method === 'download') submit_value.path = _data.content.download_baseUrl; - files = submit_value.path+'/'+submit_value.name; - if (self.options.mode === 'saveas' && !savemode) - { - for(var p in _data.content.dir) - { - if (_data.content.dir[p]['name'] == submit_value.name) - { - var saveModeDialogButtons = [ - {text: self.egw().lang("Yes"), id: "overwrite", class: "ui-priority-primary", "default": true, image: 'check'}, - {text: self.egw().lang("Rename"), id:"rename", image: 'edit'}, - {text: self.egw().lang("Cancel"), id:"cancel"} - ]; - return Et2Dialog.show_prompt(function(_button_id, _value) { - switch (_button_id) - { - case "overwrite": - return self.submit_callback(submit_button_id, submit_value, 'overwrite'); - case "rename": - submit_value.name = _value; - return self.submit_callback(submit_button_id, submit_value, 'rename'); - } - }, - self.egw().lang('Do you want to overwrite existing file %1 in directory %2?', submit_value.name, submit_value.path), - self.egw().lang('File %1 already exists', submit_value.name), - submit_value.name, saveModeDialogButtons, null); - } - } - } - break; - } - et2_vfsSelect._setRecentPaths(submit_value.path); - self.value = files; - if (self.options.method && self.options.method !== 'download') - { - egw(window).request( - self.options.method, - [self.options.method_id, files, submit_button_id, savemode] - ).then(function(data){ - jQuery(self.node).change(); - } - ); - } - else - { - jQuery(self.node).change(); - } - delete app.vfsSelectUI; - self.dialog.close(); - return true; - } - }; - this.dialog = new Et2Dialog('api'); - - this.dialog.transformAttributes( - { - callback: this.submit_callback, - title: this.options.dialog_title, - buttons: buttons, - minWidth: 500, - minHeight: 400, - value: data, - template: egw.webserverUrl + '/api/templates/default/vfsSelectUI.xet', - resizable: false, - // Don't destroy on close, it would take out the opening etemplate - destroy_on_close: false - }); - this.dialog.addEventListener("before-load", (ev) => - { - this.dialog.template.uniqueId = 'api.vfsSelectUI'; - }); - document.body.appendChild(this.dialog); - - // Wait for dialog to finish loading - this.dialog.updateComplete.then(() => - { - app.vfsSelectUI.et2 = self.dialog.template.widgetContainer; - app.vfsSelectUI.vfsSelectWidget = self; - app.vfsSelectUI.et2_ready(app.vfsSelectUI.et2, 'api.vfsSelectUI'); - app.vfsSelectUI.et2.getInstanceManager().app_obj['vfsSelectUI'] = app.vfsSelectUI; - }) - this.dialog.addEventListener("close", () => - { - self.dialog = undefined; - }); - } - - /** - * Set recent path into sessionStorage - * @param {string} _path - */ - private static _setRecentPaths(_path) - { - let recentPaths = egw.getSessionItem('api', 'vfsRecentPaths') ? - egw.getSessionItem('api', 'vfsRecentPaths').split(',') : []; - if (recentPaths.indexOf(_path) == -1) recentPaths.push(_path); - egw.setSessionItem('api', 'vfsRecentPaths', recentPaths); - } - - /** - * Get recent paths from sessionStorage - * @returns {Array} returns an array of recent paths - */ - private static _getRecentPaths() - { - return egw.getSessionItem('api', 'vfsRecentPaths') ? - egw.getSessionItem('api', 'vfsRecentPaths').split(',') : []; - } - - /** - * click handler - * @param {event object} e - */ - click(e) - { - this._content.call(this, null); - } - - /** - * Set the dialog's mode. - * Valid options are in et2_vfsSelect.modes - * - * @param {string} mode 'open', 'open-multiple', 'saveas' or 'select-dir' - */ - set_mode(mode) - { - // Check mode - if(jQuery.inArray(mode, this.modes) < 0) - { - this.egw().debug("warn", "Invalid mode for '%s': %s Valid options:", this.id,mode, this.modes); - return; - } - this.options.mode = mode; - } - - /** - * Set the label on the dialog's OK button. - * - * @param {string} label - */ - set_button_label(label) - { - this.options.button_label = label; - } - - /** - * Set the caption for vfs-select button - * - * @param {string} caption string value as a caption - */ - set_button_caption(caption) - { - this.options.button_caption = caption; - } - - /** - * Set the ID passed to the server side callback - * - * @param {string} id - */ - set_method_id(id) - { - this.options.method_id = id; - } - - set_readonly(readonly) - { - this.options.readonly = Boolean(readonly); - - if(this.options.readonly) - { - this.button.hide(); - } - else - { - this.button.show(); - } - } - - set_value(value) - { - this.value = value; - } - - getValue() - { - return this.value; - } -}; -et2_register_widget(et2_vfsSelect, ["vfs-select", "old-vfs-select"]); \ No newline at end of file diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts index ca4b3976ee..65d23cbbe5 100644 --- a/api/js/etemplate/etemplate2.ts +++ b/api/js/etemplate/etemplate2.ts @@ -149,7 +149,6 @@ import './et2_widget_script'; import './et2_widget_countdown'; import './et2_extension_nextmatch'; import './et2_extension_customfields'; -import './vfsSelectUI'; import {Et2Tabs} from "./Layout/Et2Tabs/Et2Tabs"; import {Et2Dialog} from "./Et2Dialog/Et2Dialog"; diff --git a/api/js/etemplate/vfsSelectUI.ts b/api/js/etemplate/vfsSelectUI.ts deleted file mode 100644 index a909ed028d..0000000000 --- a/api/js/etemplate/vfsSelectUI.ts +++ /dev/null @@ -1,385 +0,0 @@ -/** - * EGroupware - VFS SELECT Widget UI - * - * @link https://www.egroupware.org - * @package et2_vfsSelect - * @author Hadi Nategh - * @copyright (c) 2013-2021 by Hadi Nategh - * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - */ - -import {EgwApp} from "../jsapi/egw_app"; -import {et2_vfs, et2_vfsPath, et2_vfsSelect} from "./et2_widget_vfs"; -import {egw} from "../jsapi/egw_global"; -import {et2_file} from "./et2_widget_file"; -import {Et2Button} from "./Et2Button/Et2Button"; -import {Et2Select} from "./Et2Select/Et2Select"; -import {Et2Dialog} from "./Et2Dialog/Et2Dialog"; -import {Et2Checkbox} from "./Et2Checkbox/Et2Checkbox"; -import {Et2Searchbox} from "./Et2Textbox/Et2Searchbox"; - - -/** - * UI for VFS Select widget - * - */ -export class vfsSelectUI extends EgwApp -{ - readonly appname: 'filemanager'; - private dirContent: {}; - private vfsSelectWidget : et2_vfsSelect; - private path_widget: et2_vfsPath; - - /** - * Constructor - * - */ - constructor() - { - // call parent - super('vfsSelectUI'); - - this.egw.langRequireApp(this.egw.window, 'filemanager'); - } - - /** - * Destructor - */ - destroy(_app) - { - delete this.path_widget; - delete this.vfsSelectWidget; - // call parent - super.destroy(_app); - } - - /** - * This function is called when the etemplate2 object is loaded - * and ready. If you must store a reference to the et2 object, - * make sure to clean it up in destroy(). - * - * @param et2 etemplate2 Newly ready object - * @param {string} name template name - */ - et2_ready(et2,name) - { - this.path_widget = this.et2.getWidgetById('path'); - this.dirContent = this.et2.getArrayMgr('content').data.dir; - } - - /** - * Get directory of a path - * - * @param {string} _path - * @returns string - */ - dirname(_path) - { - let parts = _path.split('/'); - parts.pop(); - return parts.join('/') || '/'; - } - - /** - * Get name of a path - * - * @param {string} _path - * @returns string - */ - basename(_path) - { - return _path.split('/').pop(); - } - - /** - * Get current working directory - * - * @return string - */ - get_path() - { - return this.path_widget.get_value(); - } - - /** - * Send names of uploaded files (again) to server, - * to process them: either copy to vfs or ask overwrite/rename - * - * @param {event} _event - */ - storeFile(_event) - { - let path = this.get_path(); - - if (!jQuery.isEmptyObject(_event.data.getValue())) - { - let widget = _event.data; - egw(window).json('EGroupware\\Api\\Etemplate\\Widget\\Vfs::ajax_vfsSelect_storeFile', [widget.getValue(), path], - this._storeFile_callback, this, true, this - ).sendRequest(true); - widget.set_value(''); - } - } - - /** - * Callback for server response to storeFile request: - * - display message and refresh list - * - ask use to confirm overwritting existing files or rename upload - * - * @param {object} _data values for attributes msg, files, ... - */ - _storeFile_callback(_data : {msg : string, uploaded : any[], path : string}) - { - if (_data.msg || _data.uploaded) egw(window).message(_data.msg); - - let that = this; - for(let file in _data.uploaded) - { - if (_data.uploaded[file].confirm && !_data.uploaded[file].confirmed) - { - let buttons = [ - { - label: this.egw.lang("Yes"), - id: "overwrite", - class: "ui-priority-primary", - "default": true, - image: 'check' - }, - {label: this.egw.lang("Rename"), id: "rename", image: 'edit'}, - {label: this.egw.lang("Cancel"), id: "cancel"} - ]; - if(_data.uploaded[file].confirm === "is_dir") - { - buttons.shift(); - } - let dialog = Et2Dialog.show_prompt(function(_button_id, _value) - { - let uploaded = {}; - uploaded[this.my_data.file] = this.my_data.data; - switch(_button_id) - { - case "overwrite": - uploaded[this.my_data.file].confirmed = true; - // fall through - case "rename": - uploaded[this.my_data.file].name = _value; - delete uploaded[this.my_data.file].confirm; - // send overwrite-confirmation and/or rename request to server - egw.json('EGroupware\\Api\\Etemplate\\Widget\\Vfs::ajax_vfsSelect_storeFile', [uploaded, this.my_data.path], - that._storeFile_callback, that, true, that - ).sendRequest(); - return; - case "cancel": - // Remove that file from every file widget... - that.et2.iterateOver(function(_widget) { - _widget.remove_file(this.my_data.data.name); - }, this, et2_file); - } - }, - _data.uploaded[file].confirm === "is_dir" ? - this.egw.lang("There's already a directory with that name!") : - this.egw.lang('Do you want to overwrite existing file %1 in directory %2?', _data.uploaded[file].name, _data.path), - this.egw.lang('File %1 already exists', _data.uploaded[file].name), - _data.uploaded[file].name, buttons, file); - // setting required data for callback in as my_data - dialog.my_data = { - file: file, - path: _data.path, - data: _data.uploaded[file], - }; - } - else - { - this.submit(); - } - } - } - - /** - * Prompt user for directory to create - * - * @param {egwAction|undefined|jQuery.Event} action Action, event or undefined if called directly - * @param {egwActionObject[] | undefined} selected Selected row, or undefined if called directly - */ - createdir(action, selected) - { - let self = this; - Et2Dialog.show_prompt(function(button, dir) - { - if(button && dir) - { - let path = self.get_path(); - self.egw.json('EGroupware\\Api\\Etemplate\\Widget\\Vfs::ajax_create_dir', [dir, path], function(msg) - { - self.egw.message(msg); - self.change_dir((path == '/' ? '' : path) + '/' + dir); - }).sendRequest(false); - } - }, this.egw.lang('New directory'), this.egw.lang('Create directory')); - } - - /** - * Change directory - * - * @param {string} _dir directory to change to incl. '..' for one up - */ - change_dir(_dir : string) - { - if (_dir == '..') - { - _dir = this.dirname(this.get_path()); - } - this.path_widget.set_value(_dir); - } - - /** - * Row or filename in select-file dialog clicked - * - * @param {jQuery.event} event - * @param {et2_widget} widget - */ - select_clicked(event : JQueryEventObject, widget : et2_vfs) - { - if (!widget || typeof widget.value != 'object') - { - - } - else if (widget.value.is_dir) // true for "httpd/unix-directory" and "egw/*" - { - let path = widget.getRoot().getWidgetById("path"); - - if(path) - { - path.set_value(widget.value.path); - } - } - else if (this.et2 && this.et2.getArrayMgr('content').getEntry('mode') != 'open-multiple') - { - this.et2.setValueById('name', widget.value.name); - } - else - { - let file = widget.value.name; - widget.getParent().iterateOver(function(widget) - { - if(widget.options.selectedValue === file) - { - widget.set_value(widget.get_value() === file ? widget.options.unselectedValue : file); - } - }, null, Et2Checkbox); - - } - // Stop event or it will toggle back off - event.preventDefault(); - event.stopPropagation(); - return false; - } - - /** - * Handles action and offer it to the submit - * - * @param {string} action action name - * @param {object} widget widget which action was called from - */ - do_action(action : string, widget : Et2Button | Et2Select | et2_vfsPath) - { - if(!this.et2) - { - // Etemplate has already gone - return; - } - if(!action && widget && widget.id) - { - action = widget.id; - } - if(!action) - { - return; - } - let field = '', value : string | string[] = ''; - switch(action) - { - case 'path': - field = 'path'; - value = (widget).getValue(); - break; - case 'home': - field = 'action'; - value = 'home'; - break; - case 'app': - field = 'app'; - value = (widget).getValue(); - break; - case 'mime': - field = 'mime'; - value = (widget).getValue(); - break; - } - this.submit(field, value); - return false; - } - - /** - * Sumbits content value after modification - * - * @param {string} _field content field to be modified - * @param {any} _val value of field - * @param {function} _callback - */ - submit(_field? : string, _val? : any, _callback? : Function) - { - let arrMgrs = this.et2.getArrayMgrs(); - if (_field) - { - arrMgrs.content.data[_field] = _val; - jQuery.extend(arrMgrs.content.data, arrMgrs.modifications.data); - this.et2.setArrayMgrs(arrMgrs); - } - - // preserve value of the name - if (arrMgrs && this.et2.getWidgetById('name')) - { - arrMgrs.content.data['name'] = this.et2.getWidgetById('name').get_value(); - } - - this.vfsSelectWidget._content(arrMgrs.content.data, _callback); - } - - /** - * search through dir content and set its content base on searched query - * @returns - */ - search(_ev : Event, _widget : Et2Searchbox) - { - let dir = this.et2.getWidgetById('dir'); - let query = _widget.get_value(); - if (query == "") - { - dir.set_value({content: this.dirContent}); - return; - } - let self = this; - let searchQuery = function (_query) - { - let result = {}; - let reg = RegExp(_query, 'ig'); - let key = 0; - for (let i in self.dirContent) - { - if (typeof self.dirContent[i]['name'] != 'undefined' && self.dirContent[i]['name'].match(reg)) - { - result[key] = self.dirContent[i]; - key++; - } - else if (typeof self.dirContent[i]['name'] == 'undefined' && isNaN(i)) - { - result[i] = self.dirContent[i]; - } - } - return result; - }; - dir.set_value({content: searchQuery(query)}); - } -} -app.classes.vfsSelectUI = vfsSelectUI; \ No newline at end of file