* Etemplate: Add context menu action on linked files to copy them somewhere else

Also, update vfsSelect to use request(), so it processes the additional stuff in response so we can have a success message.
This commit is contained in:
nathangray 2020-12-21 13:53:24 -07:00
parent c731ee1a38
commit b6036de8e2
6 changed files with 152 additions and 24 deletions

View File

@ -38,6 +38,7 @@ var et2_core_inheritance_1 = require("./et2_core_inheritance");
var et2_core_valueWidget_1 = require("./et2_core_valueWidget"); var et2_core_valueWidget_1 = require("./et2_core_valueWidget");
var et2_core_inputWidget_1 = require("./et2_core_inputWidget"); var et2_core_inputWidget_1 = require("./et2_core_inputWidget");
var et2_widget_selectbox_1 = require("./et2_widget_selectbox"); var et2_widget_selectbox_1 = require("./et2_widget_selectbox");
var et2_widget_dialog_1 = require("./et2_widget_dialog");
/** /**
* UI widgets for Egroupware linking system * UI widgets for Egroupware linking system
*/ */
@ -131,7 +132,7 @@ var et2_link_to = /** @class */ (function (_super) {
select: function () { self.link_button.show(); return true; }, select: function () { self.link_button.show(); return true; },
readonly: this.options.readonly readonly: this.options.readonly
}; };
this.link_entry = et2_createWidget("link-entry", link_entry_attrs, this); this.link_entry = et2_core_widget_1.et2_createWidget("link-entry", link_entry_attrs, this);
// Filemanager select // Filemanager select
var select_attrs = { var select_attrs = {
button_label: egw.lang('Link'), button_label: egw.lang('Link'),
@ -169,7 +170,7 @@ var et2_link_to = /** @class */ (function (_super) {
select_attrs.method = 'EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_existing'; select_attrs.method = 'EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_existing';
select_attrs.method_id = self.options.value.to_app + ':' + self.options.value.to_id; select_attrs.method_id = self.options.value.to_app + ':' + self.options.value.to_id;
} }
this.vfs_select = et2_createWidget("vfs-select", select_attrs, this); this.vfs_select = et2_core_widget_1.et2_createWidget("vfs-select", select_attrs, this);
this.vfs_select.set_readonly(this.options.readonly); this.vfs_select.set_readonly(this.options.readonly);
// File upload // File upload
var file_attrs = { var file_attrs = {
@ -198,7 +199,7 @@ var et2_link_to = /** @class */ (function (_super) {
self.createLink(event); self.createLink(event);
} }
}; };
this.file_upload = et2_createWidget("file", file_attrs, this); this.file_upload = et2_core_widget_1.et2_createWidget("file", file_attrs, this);
this.file_upload.set_readonly(this.options.readonly); this.file_upload.set_readonly(this.options.readonly);
return true; return true;
}; };
@ -1479,8 +1480,8 @@ var et2_link_list = /** @class */ (function (_super) {
_this.context = new egwMenu(); _this.context = new egwMenu();
_this.context.addItem("comment", _this.egw().lang("Comment"), "", function () { _this.context.addItem("comment", _this.egw().lang("Comment"), "", function () {
var link_id = typeof self.context.data.link_id == 'number' ? self.context.data.link_id : self.context.data.link_id.replace(/[:\.]/g, '_'); var link_id = typeof self.context.data.link_id == 'number' ? self.context.data.link_id : self.context.data.link_id.replace(/[:\.]/g, '_');
et2_dialog.show_prompt(function (button, comment) { et2_widget_dialog_1.et2_dialog.show_prompt(function (button, comment) {
if (button != et2_dialog.OK_BUTTON) if (button != et2_widget_dialog_1.et2_dialog.OK_BUTTON)
return; return;
var remark = jQuery('#link_' + (self.context.data.dom_id ? self.context.data.dom_id : link_id), self.list).children('.remark'); var remark = jQuery('#link_' + (self.context.data.dom_id ? self.context.data.dom_id : link_id), self.list).children('.remark');
if (isNaN(self.context.data.link_id)) // new entry, not yet stored if (isNaN(self.context.data.link_id)) // new entry, not yet stored
@ -1561,11 +1562,33 @@ var et2_link_list = /** @class */ (function (_super) {
id: self.value.to_id id: self.value.to_id
}); });
}); });
// Only allow this option if the entry has been saved, and has a real ID
if (self.options.value && self.options.value.to_id && typeof self.options.value.to_id != 'object') {
_this.context.addItem("copy_to", _this.egw().lang("Copy to"), _this.egw().image('copy'), function (menu_item) {
// Highlight files for nice UI indicating what will be copied
jQuery('[id="link_' + self.context.data.link_id + ']', this.list).effect('highlight', {}, 2000);
// Get target
var select_attrs = {
mode: "select-dir",
button_caption: '',
button_icon: 'copy',
button_label: egw.lang("copy"),
//extra_buttons: [{text: egw.lang("link"), id:"link", image: "link"}],
dialog_title: egw.lang('Copy to'),
method: "EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_copy_to",
method_id: self.context.data
};
var vfs_select = et2_core_widget_1.et2_createWidget("vfs-select", select_attrs, self);
// No button, just open it
vfs_select.button.hide();
vfs_select.click(null);
});
}
_this.context.addItem("-", "-"); _this.context.addItem("-", "-");
_this.context.addItem("delete", _this.egw().lang("Delete link"), _this.egw().image("delete"), function (menu_item) { _this.context.addItem("delete", _this.egw().lang("Delete link"), _this.egw().image("delete"), function (menu_item) {
var link_id = isNaN(self.context.data.link_id) ? self.context.data : self.context.data.link_id; var link_id = isNaN(self.context.data.link_id) ? self.context.data : self.context.data.link_id;
var row = jQuery('#link_' + (self.context.data.dom_id ? self.context.data.dom_id : self.context.data.link_id), self.list); var row = jQuery('#link_' + (self.context.data.dom_id ? self.context.data.dom_id : self.context.data.link_id), self.list);
et2_dialog.show_dialog(function (button) { if (button == et2_dialog.YES_BUTTON) et2_widget_dialog_1.et2_dialog.show_dialog(function (button) { if (button == et2_widget_dialog_1.et2_dialog.YES_BUTTON)
self._delete_link(link_id, row); }, egw.lang('Delete link?')); self._delete_link(link_id, row); }, egw.lang('Delete link?'));
}); });
// Native DnD - Doesn't play nice with jQueryUI Sortable // Native DnD - Doesn't play nice with jQueryUI Sortable
@ -1637,12 +1660,12 @@ var et2_link_list = /** @class */ (function (_super) {
.appendTo(row) .appendTo(row)
.addClass("icon"); .addClass("icon");
if (_link_data.icon) { if (_link_data.icon) {
var icon_widget = et2_createWidget("image", {}); var icon_widget = et2_core_widget_1.et2_createWidget("image", {});
var src = ''; var src = '';
// Creat a mime widget if the link has type // Creat a mime widget if the link has type
if (_link_data.type) { if (_link_data.type) {
// VFS - file // VFS - file
var vfs_widget = et2_createWidget('vfs-mime', {}); var vfs_widget = et2_core_widget_1.et2_createWidget('vfs-mime', {});
vfs_widget.set_value({ vfs_widget.set_value({
download_url: _link_data.download_url, download_url: _link_data.download_url,
name: _link_data.title, name: _link_data.title,
@ -1717,8 +1740,8 @@ var et2_link_list = /** @class */ (function (_super) {
// We don't use ui-icon because it assigns a bg image // We don't use ui-icon because it assigns a bg image
.addClass("delete icon") .addClass("delete icon")
.bind('click', function () { .bind('click', function () {
et2_dialog.show_dialog(function (button) { et2_widget_dialog_1.et2_dialog.show_dialog(function (button) {
if (button == et2_dialog.YES_BUTTON) { if (button == et2_widget_dialog_1.et2_dialog.YES_BUTTON) {
self._delete_link(self.value && typeof self.value.to_id != 'object' && _link_data.link_id ? _link_data.link_id : _link_data, row); self._delete_link(self.value && typeof self.value.to_id != 'object' && _link_data.link_id ? _link_data.link_id : _link_data, row);
} }
}, egw.lang('Delete link?')); }, egw.lang('Delete link?'));
@ -1923,13 +1946,13 @@ var et2_link_add = /** @class */ (function (_super) {
// Already done // Already done
return false; return false;
} }
this.app_select = et2_createWidget("link-apps", jQuery.extend({}, this.options, { this.app_select = et2_core_widget_1.et2_createWidget("link-apps", jQuery.extend({}, this.options, {
'id': this.options.id + 'app', 'id': this.options.id + 'app',
value: this.options.application ? this.options.application : this.options.value && this.options.value.add_app ? this.options.value.add_app : null, value: this.options.application ? this.options.application : this.options.value && this.options.value.add_app ? this.options.value.add_app : null,
application_list: this.options.application ? this.options.application : null application_list: this.options.application ? this.options.application : null
}), this); }), this);
this.div.append(this.app_select.getDOMNode()); this.div.append(this.app_select.getDOMNode());
this.button = et2_createWidget("button", { id: this.options.id + "_add", label: this.egw().lang("add") }, this); this.button = et2_core_widget_1.et2_createWidget("button", { id: this.options.id + "_add", label: this.egw().lang("add") }, this);
this.button.set_label(this.egw().lang("add")); this.button.set_label(this.egw().lang("add"));
var self = this; var self = this;
this.button.click = function () { this.button.click = function () {

View File

@ -14,17 +14,22 @@
/vendor/bower-asset/jquery-ui/jquery-ui.js; /vendor/bower-asset/jquery-ui/jquery-ui.js;
et2_core_inputWidget; et2_core_inputWidget;
et2_core_valueWidget; et2_core_valueWidget;
et2_widget_selectbox;
expose;
// Include menu system for list context menu // Include menu system for list context menu
egw_action.egw_menu_dhtmlx; egw_action.egw_menu_dhtmlx;
*/ */
import {et2_register_widget, et2_widget, WidgetConfig} from "./et2_core_widget"; import {et2_createWidget, et2_register_widget, et2_widget, WidgetConfig} from "./et2_core_widget";
import {ClassWithAttributes} from "./et2_core_inheritance"; import {ClassWithAttributes} from "./et2_core_inheritance";
import {et2_valueWidget} from "./et2_core_valueWidget"; import {et2_valueWidget} from "./et2_core_valueWidget";
import {et2_inputWidget} from "./et2_core_inputWidget"; import {et2_inputWidget} from "./et2_core_inputWidget";
import {et2_selectbox} from "./et2_widget_selectbox"; import {et2_selectbox} from "./et2_widget_selectbox";
import {et2_button} from "./et2_widget_button"; import {et2_button} from "./et2_widget_button";
import {et2_dialog} from "./et2_widget_dialog";
import {et2_file} from "./et2_widget_file";
import {et2_vfsSelect} from "./et2_widget_vfs";
import {et2_vfs_select} from "./et2_widget_vfs"; import {et2_vfs_select} from "./et2_widget_vfs";
/** /**
@ -594,11 +599,11 @@ export class et2_link_entry extends et2_inputWidget
private cache: any = {}; private cache: any = {};
private div: JQuery; protected div: JQuery;
private app_select: JQuery; protected app_select: JQuery;
private search: JQuery; protected search: JQuery;
private clear: JQuery; protected clear: JQuery;
private link_button: JQuery; protected link_button: JQuery;
private response: any; private response: any;
private request: any; private request: any;
private last_search: string; private last_search: string;
@ -1923,6 +1928,33 @@ export class et2_link_list extends et2_link_string
id: self.value.to_id id: self.value.to_id
}); });
}); });
// Only allow this option if the entry has been saved, and has a real ID
if (self.options.value && self.options.value.to_id && typeof self.options.value.to_id != 'object')
{
this.context.addItem("copy_to", this.egw().lang("Copy to"), this.egw().image('copy'), function (menu_item)
{
// Highlight files for nice UI indicating what will be copied
jQuery('[id="link_' + self.context.data.link_id+']', this.list).effect('highlight', {}, 2000);
// Get target
var select_attrs: any = {
mode: "select-dir",
button_caption: '',
button_icon: 'copy',
button_label: egw.lang("copy"),
//extra_buttons: [{text: egw.lang("link"), id:"link", image: "link"}],
dialog_title: egw.lang('Copy to'),
method: "EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_copy_to",
method_id: self.context.data
};
let vfs_select = <et2_vfsSelect> et2_createWidget("vfs-select", select_attrs, self);
// No button, just open it
vfs_select.button.hide();
vfs_select.click(null);
});
}
this.context.addItem("-", "-"); this.context.addItem("-", "-");
this.context.addItem("delete", this.egw().lang("Delete link"), this.egw().image("delete"), function(menu_item) this.context.addItem("delete", this.egw().lang("Delete link"), this.egw().image("delete"), function(menu_item)
{ {

View File

@ -1156,9 +1156,9 @@ var et2_vfsSelect = /** @class */ (function (_super) {
et2_vfsSelect._setRecentPaths(submit_value.path); et2_vfsSelect._setRecentPaths(submit_value.path);
self.value = files; self.value = files;
if (self.options.method && self.options.method !== 'download') { if (self.options.method && self.options.method !== 'download') {
egw(window).json(self.options.method, [self.options.method_id, files, submit_button_id, savemode], function () { egw(window).request(self.options.method, [self.options.method_id, files, submit_button_id, savemode]).then(function (data) {
jQuery(self.node).change(); jQuery(self.node).change();
}).sendRequest(true); });
} }
else { else {
jQuery(self.node).change(); jQuery(self.node).change();

View File

@ -1413,13 +1413,13 @@ export class et2_vfsSelect extends et2_inputWidget
self.value = files; self.value = files;
if (self.options.method && self.options.method !== 'download') if (self.options.method && self.options.method !== 'download')
{ {
egw(window).json( egw(window).request(
self.options.method, self.options.method,
[self.options.method_id, files, submit_button_id, savemode], [self.options.method_id, files, submit_button_id, savemode]
function(){ ).then(function(data){
jQuery(self.node).change(); jQuery(self.node).change();
} }
).sendRequest(true); );
} }
else else
{ {

View File

@ -736,6 +736,62 @@ declare interface IegwWndLocal extends IegwGlobal
* @param _sender is a parameter being passed to the _callback function * @param _sender is a parameter being passed to the _callback function
*/ */
json(_menuaction : string, _parameters? : any[], _callback? : Function, _context? : object, _async? : boolean|"keepalive", _sender?) : JsonRequest; json(_menuaction : string, _parameters? : any[], _callback? : Function, _context? : object, _async? : boolean|"keepalive", _sender?) : JsonRequest;
/**
* Do an AJAX call and get a javascript promise, which will be resolved with the returned data.
*
* egw.request() returns immediately with a Promise. The promise will be resolved with just the returned data,
* any other "piggybacked" responses will be handled by registered handlers. The data will also be passed to
* any registered data handlers (egw.data) before it is passed to your handler.
*
* To use:
* @example
* egw.request(
* "EGroupware\\Api\\Etemplate\\Widget\\Select::ajax_get_options",
* ["select-cat"]
* )
* .then(function(data) {
* // Deal with the returned data here. data may be undefined if no data was returned.
* console.log("Here's the categories:",data);
* });
*
*
* The return is a Promise, so multiple .then() can be chained in the usual ways:
* @example
* egw.request(...)
* .then(function(data) {
* if(debug) console.log("Requested data", data);
* }
* .then(function(data) {
* // Change the data for the rest of the chain
* if(typeof data === "undefined") return [];
* }
* .then(function(data) {
* // data is never undefined now, if it was before it's an empty array now
* for(let i = 0; i < data.length; i++)
* {
* ...
* }
* }
*
*
* You can also fire off multiple requests, and wait for them to all be answered:
* @example
* let first = egw.request(...);
* let second = egw.request(...);
* Promise.all([first, second])
* .then(function(values) {
* console.log("First:", values[0], "Second:", values[1]);
* }
*
*
* @param {string} _menuaction
* @param {any[]} _parameters
*
* @return Promise
*/
request(_menuaction: string, param2: any[]): Promise<any>;
/** /**
* Registers a new handler plugin. * Registers a new handler plugin.
* *

View File

@ -286,6 +286,23 @@ class Link extends Etemplate\Widget
} }
} }
/**
* Copy a linked file to somewhere else in the VFS
*
* @param $file Array of file information
* @param $target Target path
* @param $button_id
*/
public static function ajax_copy_to($file_info, $target, $button_id)
{
$error_count = 0;
$copied = array();
Api\Vfs::copy_files([Api\Link::vfs_path($file_info['app2'], $file_info['id2'], $file_info['id'], true)], $target,$error_count,$copied);
$response = Api\Json\Response::get();
$response->message(lang('%1 files copied',count($copied)));
}
public static function ajax_delete($value) public static function ajax_delete($value)
{ {
$response = Api\Json\Response::get(); $response = Api\Json\Response::get();