diff --git a/api/js/egw_action/EgwPopupActionImplementation.ts b/api/js/egw_action/EgwPopupActionImplementation.ts index 17e4e01c0e..0fae61c4e2 100644 --- a/api/js/egw_action/EgwPopupActionImplementation.ts +++ b/api/js/egw_action/EgwPopupActionImplementation.ts @@ -534,8 +534,14 @@ export class EgwPopupActionImplementation implements EgwActionImplementation { // egwAction is a circular structure and can't be stringified so just take what we want // Hopefully that's enough for the action handlers for (const k in selected) { - if (selected[k].id) clipboard.selected.push({id: selected[k].id, data: selected[k].data}); - } + if(selected[k].id) + { + clipboard.selected.push({ + id: selected[k].id, + data: {...(window.egw.dataGetUIDdata(selected[k].id)?.data ?? {}), ...selected[k].data} + }); + } + } // Save it in session window.egw.setSessionItem('phpgwapi', 'egw_clipboard', JSON.stringify(clipboard)); @@ -561,8 +567,14 @@ export class EgwPopupActionImplementation implements EgwActionImplementation { // egwAction is a circular structure and can't be stringified so just take what we want // Hopefully that's enough for the action handlers for (const k in selected) { - if (selected[k].id) clipboard.selected.push({id: selected[k].id, data: selected[k].data}); - } + if(selected[k].id) + { + clipboard.selected.push({ + id: selected[k].id, + data: {...(window.egw.dataGetUIDdata(selected[k].id)?.data ?? {}), ...selected[k].data} + }); + } + } // Save it in session window.egw.setSessionItem('phpgwapi', 'egw_clipboard', JSON.stringify(clipboard)); diff --git a/api/js/etemplate/Et2Link/Et2LinkPasteDialog.ts b/api/js/etemplate/Et2Link/Et2LinkPasteDialog.ts new file mode 100644 index 0000000000..70f6707cbd --- /dev/null +++ b/api/js/etemplate/Et2Link/Et2LinkPasteDialog.ts @@ -0,0 +1,101 @@ +import {Et2VfsSelectDialog} from "../Et2Vfs/Et2VfsSelectDialog"; +import {css, html, TemplateResult} from "lit"; +import {SearchResult} from "../Et2Widget/SearchMixin"; + +/** + * Select files from the file clipboard to paste + */ +export class Et2LinkPasteDialog extends Et2VfsSelectDialog +{ + static get styles() + { + return [ + ...super.styles, + css` + /* Hide the unwanted stuff */ + + #toolbar, #path { + display: none; + } + ` + ]; + } + + constructor() + { + super(); + this.searchUrl = ""; + this.multiple = true; + this._appList = []; + } + + /** + * Override double-click on directory, can't go into it + * + * @param {MouseEvent} event + */ + handleFileDoubleClick(event : MouseEvent) + { + // just select it + this.handleFileClick(event); + } + + protected localSearch(search : string, searchOptions : object, localOptions : DataType[] = []) : Promise + { + const files = getClipboardFiles(); + + // We don't care if they're directories, treat them all as files (no double click, all selectable) + files.forEach(f => f.isDir = false); + + return super.localSearch(search, searchOptions, files); + } + + + protected noResultsTemplate() : TemplateResult + { + return html` +
+ + ${this.egw().lang("clipboard is empty!")} +
`; + } +} + +/** + * Get any files that are in the system clipboard + * + * @return {string[]} Paths + */ +export function getClipboardFiles() +{ + let clipboard_files = []; + if(typeof window.egw.getSessionItem('phpgwapi', 'egw_clipboard') != 'undefined') + { + let clipboard = JSON.parse(window.egw.getSessionItem('phpgwapi', 'egw_clipboard')) || { + type: [], + selected: [] + }; + if(clipboard.type.indexOf('file') >= 0) + { + for(let i = 0; i < clipboard.selected.length; i++) + { + let split = clipboard.selected[i].id.split('::'); + if(split.shift() == 'filemanager') + { + const data = clipboard.selected[i].data ?? {}; + clipboard_files.push({ + value: split.join("::"), + name: data.name ?? clipboard.selected[i].id, + mime: data.mime, + isDir: data.is_dir ?? false, + path: data.path ?? split.join("::"), + downloadUrl: data.download_url + }); + } + } + } + } + return clipboard_files; +} + +customElements.define("et2-link-paste-dialog", Et2LinkPasteDialog); \ No newline at end of file diff --git a/api/js/etemplate/Et2Link/Et2LinkTo.ts b/api/js/etemplate/Et2Link/Et2LinkTo.ts index b1a672198a..353cc69d85 100644 --- a/api/js/etemplate/Et2Link/Et2LinkTo.ts +++ b/api/js/etemplate/Et2Link/Et2LinkTo.ts @@ -24,6 +24,7 @@ import type {ValidationType} from "@lion/form-core/types/validate/ValidateMixinT import {ManualMessage} from "../Validators/ManualMessage"; import {Et2Tabs} from "../Layout/Et2Tabs/Et2Tabs"; import {Et2VfsSelectButton} from "../Et2Vfs/Et2VfsSelectButton"; +import {Et2LinkPasteDialog, getClipboardFiles} from "./Et2LinkPasteDialog"; /** * Choose an existing entry, VFS file or local file, and link it to the current entry. @@ -96,7 +97,8 @@ export class Et2LinkTo extends Et2InputWidget(ScopedElementsMixin(FormControlMix ...super.scopedElements, 'et2-button': Et2Button, 'et2-link-entry': Et2LinkEntry, - 'et2-vfs-select': Et2VfsSelectButton + 'et2-vfs-select': Et2VfsSelectButton, + 'et2-link-paste-dialog': Et2LinkPasteDialog }; } @@ -139,11 +141,17 @@ export class Et2LinkTo extends Et2InputWidget(ScopedElementsMixin(FormControlMix // otherwise it only gives an error on server-side let method = null; let method_id = null; + let pasteEnabled = false; + let pasteTooltip = "" if(this.value && this.value.to_id && typeof this.value.to_id != 'object') { method = 'EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_existing'; method_id = this.value.to_app + ':' + this.value.to_id; + + let clipboard_files = getClipboardFiles(); + pasteEnabled = clipboard_files.length > 0; } + return html` - `; + + + { + // Pre-select all files + let files = []; + getClipboardFiles().forEach(f => files.push(f.value)); + e.target.firstElementChild.value = files; + e.target.firstElementChild.requestUpdate(); + }} + .onchange=${this.handleVfsSelected} + > + + + `; } /** @@ -435,6 +466,27 @@ export class Et2LinkTo extends Et2InputWidget(ScopedElementsMixin(FormControlMix } } + handleFilePaste([button, files]) + { + if(!button) + { + return; + } + let values = {}; + for(var i = 0; i < files.length; i++) + { + values['link:' + files[i]] = { + app: 'link', + id: files[i], + type: 'unknown', + icon: 'link', + remark: '', + title: files[i] + }; + } + this._link_result(values); + } + handleVfsSelected(event, select : Et2VfsSelectButton) { let values = true;