diff --git a/api/js/jsapi/egw_app.ts b/api/js/jsapi/egw_app.ts index b309398a27..8124f6bee6 100644 --- a/api/js/jsapi/egw_app.ts +++ b/api/js/jsapi/egw_app.ts @@ -20,6 +20,9 @@ import {et2_valueWidget} from "../etemplate/et2_core_valueWidget"; import {nm_action} from "../etemplate/et2_extension_nextmatch_actions"; import {Et2Dialog} from "../etemplate/Et2Dialog/Et2Dialog"; import {Et2Favorites} from "../etemplate/Et2Favorites/Et2Favorites"; +import {loadWebComponent} from "../etemplate/Et2Widget/Et2Widget"; +import {Et2VfsSelectDialog} from "../etemplate/Et2Vfs/Et2VfsSelectDialog"; +import {Et2Checkbox} from "../etemplate/Et2Checkbox/Et2Checkbox"; /** * Type for push-message @@ -795,7 +798,7 @@ export abstract class EgwApp * @param {egwAction} _action * @param {egwActionObject[]} _selected */ - merge(_action : egwAction, _selected : egwActionObject[]) + async mergeAction(_action : egwAction, _selected : egwActionObject[]) { // Find what we need let nm = null; @@ -809,16 +812,10 @@ export abstract class EgwApp { nm = action.data.nextmatch; } - if(as_pdf === null && action.getActionById('as_pdf') !== null) - { - as_pdf = action.getActionById('as_pdf').checked; - } action = action.parent; } let all = nm?.getSelection().all || false; - as_pdf = as_pdf || false; - // Get list of entry IDs let ids = []; for(let i = 0; !all && i < _selected.length; i++) @@ -826,16 +823,123 @@ export abstract class EgwApp let split = _selected[i].id.split("::"); ids.push(split[1]); } + let document = await this._getMergeDocument(); + if(!document.document) + { + return; + } let vars = { ..._action.data.merge_data, - pdf: as_pdf, + document: document.document, + pdf: document.pdf ?? false, select_all: all, - id: JSON.stringify(ids) + id: ids }; + if(document.mime == "message/rfc822") + { + return this._mergeEmail(_action.clone(), vars); + } + else + { + vars.id = JSON.stringify(ids); + } egw.open_link(egw.link('/index.php', vars), '_blank'); } + /** + * Ask the user for a target document to merge into + * + * @returns {Promise<{document : string, pdf : boolean, mime : string}>} + * @protected + */ + + protected _getMergeDocument() : Promise<{ document : string, pdf : boolean, mime : string }> + { + let dirPref = this.egw.preference('document_dir', this.appname) ?? ""; + let dirs = dirPref.split('/[,\s]+\//'); + dirs.forEach((d, index) => + { + if(index) + { + d = "/" + d; + } + }); + let fileSelect = loadWebComponent('et2-vfs-select-dialog', { + class: "egw_app_merge_document", + title: this.egw.lang("Insert in document"), + mode: "open", + path: dirs.pop() ?? "", + open: true + }, this.et2); + let pdf = loadWebComponent("et2-checkbox", { + slot: "footer", + label: "As PDF" + }, fileSelect); + + // Disable PDF checkbox for emails + fileSelect.addEventListener("et2-select", e => + { + let canPDF = true; + fileSelect.value.forEach(path => + { + if(fileSelect.fileInfo(path).mime == "message/rfc822") + { + canPDF = false; + } + }); + pdf.disabled = !canPDF; + }); + return fileSelect.getComplete().then((values) => + { + if(!values[0]) + { + return {}; + } + + const value = values[1].pop() ?? ""; + const fileInfo = fileSelect.fileInfo(value) ?? {}; + fileSelect.remove(); + return {document: value, pdf: pdf.getValue(), mime: fileInfo.mime ?? ""}; + }); + } + + /** + * Merging into an email + * + * @param {object} data + * @protected + */ + protected _mergeEmail(action, data : object) + { + const ids = data['id']; + // egw.open() used if only 1 row selected + data['egw_open'] = 'edit-mail--'; + data['target'] = 'compose_' + data.document; + + // long_task runs menuaction once for each selected row + data['nm_action'] = 'long_task'; + data['popup'] = this.egw.link_get_registry('mail', 'edit_popup'); + data['message'] = this.egw.lang('insert in %1', data.document); + + data['menuaction'] = 'mail.mail_compose.ajax_merge'; + action.data = data; + + if(data['select_all'] || ids.length > 1) + { + data['menuaction'] += "&document=" + data.document + "&merge=" + data.merge; + nm_action(action, null, data['target'], {all: data['select_all'], ids: ids}); + } + else + { + this.egw.open(ids.pop(), 'mail', 'edit', { + from: 'merge', + document: data.document, + merge: data.merge + }, data['target']); + } + } + /** * Initializes actions and handlers on sidebox (delete) * diff --git a/api/src/Storage/Merge.php b/api/src/Storage/Merge.php index 5d8ce8f29f..410157cf00 100644 --- a/api/src/Storage/Merge.php +++ b/api/src/Storage/Merge.php @@ -2414,256 +2414,20 @@ abstract class Merge if($export_limit == null) { $export_limit = self::getExportLimit(); - } // check if there is a globalsetting - - try - { - if(class_exists('EGroupware\\collabora\\Bo') && - $GLOBALS['egw_info']['user']['apps']['collabora'] && - ($discovery = \EGroupware\collabora\Bo::discover()) && - $GLOBALS['egw_info']['user']['preferences']['filemanager']['merge_open_handler'] != 'download' - ) - { - $editable_mimes = $discovery; - } } - catch (\Exception $e) - { - // ignore failed discovery - unset($e); - } - if($default_doc && ($file = Api\Vfs::stat($default_doc))) // put default document on top - { - if(!$file['mime']) - { - $file['mime'] = Api\Vfs::mime_content_type($default_doc); - $file['path'] = $default_doc; - } - $documents['document'] = array( - 'icon' => Api\Vfs::mime_icon($file['mime']), - 'caption' => Api\Vfs::decodePath(Api\Vfs::basename($default_doc)), - 'group' => 1 - ); - self::document_editable_action($documents['document'], $file); - if($file['mime'] == 'message/rfc822') - { - self::document_mail_action($documents['document'], $file); - } - } - - $files = array(); - if($dirs) - { - // split multiple comma or whitespace separated directories - // to still allow space or comma in dirnames, we also use the trailing slash of all pathes to split - if(count($dirs = preg_split('/[,\s]+\//', $dirs)) > 1) - { - foreach($dirs as $n => &$d) - { - if($n) - { - $d = '/' . $d; - } // re-adding trailing slash removed by split - } - } - foreach($dirs as $dir) - { - $files += Api\Vfs::find($dir, array( - 'need_mime' => true, - 'order' => 'fs_name', - 'sort' => 'ASC', - ), true); - } - } - - $dircount = array(); - foreach($files as $key => $file) - { - // use only the mime-types we support - $parts = explode('.', $file['name']); - if(!self::is_implemented($file['mime'], '.' . array_pop($parts)) || - !Api\Vfs::check_access($file['path'], Api\Vfs::READABLE, $file) || // remove files not readable by user - $file['path'] === $default_doc) // default doc already added - { - unset($files[$key]); - } - else - { - $dirname = Api\Vfs::dirname($file['path']); - if(!isset($dircount[$dirname])) - { - $dircount[$dirname] = 1; - } - else - { - $dircount[$dirname]++; - } - } - } - foreach($files as $file) - { - if(count($dircount) > 1) - { - $name_arr = explode('/', $file['name']); - $current_level = &$documents; - for($count = 0; $count < count($name_arr); $count++) - { - if($count == 0) - { - $current_level = &$documents; - } - else - { - $current_level = &$current_level[$prefix . $name_arr[($count - 1)]]['children']; - } - switch($count) - { - case (count($name_arr) - 1): - if(!isset($current_level[$prefix . $file['name']])) - { - $current_level[$prefix . $file['name']] = []; - } - self::document_editable_action($current_level[$prefix . $file['name']], $file); - if($file['mime'] === 'message/rfc822') - { - self::document_mail_action($current_level[$prefix . $file['name']], $file); - } - break; - - default: - if(!isset($current_level[$prefix . $name_arr[$count]])) - { - // create parent folder - $current_level[$prefix . $name_arr[$count]] = array( - 'icon' => 'phpgwapi/foldertree_folder', - 'caption' => Api\Vfs::decodePath($name_arr[$count]), - 'group' => 2, - 'children' => array(), - ); - } - break; - } - } - } - else - { - if(count($files) >= self::SHOW_DOCS_BY_MIME_LIMIT) - { - if(!isset($documents[$file['mime']])) - { - $documents[$file['mime']] = array( - 'icon' => Api\Vfs::mime_icon($file['mime']), - 'caption' => Api\MimeMagic::mime2label($file['mime']), - 'group' => 2, - 'children' => array(), - ); - } - $documents[$file['mime']]['children'][$prefix . $file['name']] = array(); - self::document_editable_action($documents[$file['mime']]['children'][$prefix . $file['name']], $file); - if($file['mime'] == 'message/rfc822') - { - self::document_mail_action($documents[$file['mime']]['children'][$prefix . $file['name']], $file); - } - } - else - { - $documents[$prefix . $file['name']] = array(); - self::document_editable_action($documents[$prefix . $file['name']], $file); - if($file['mime'] == 'message/rfc822') - { - self::document_mail_action($documents[$prefix . $file['name']], $file); - } - } - } - } - - // Add PDF checkbox - $documents['as_pdf'] = array( - 'caption' => 'As PDF', - 'checkbox' => true, - ); return array( 'icon' => 'etemplate/merge', 'caption' => $caption, - 'children' => $documents, + 'onExecute' => 'javaScript:app.' . $GLOBALS['egw_info']['flags']['currentapp'] . '.mergeAction', // disable action if no document or export completly forbidden for non-admins - 'enabled' => (boolean)$documents && (self::hasExportLimit($export_limit, 'ISALLOWED') || self::is_export_limit_excepted()), + 'enabled' => (self::hasExportLimit($export_limit, 'ISALLOWED') || self::is_export_limit_excepted()), 'hideOnDisabled' => true, // do not show 'Insert in document', if no documents defined or no export allowed 'group' => $group, - ); - } - - /** - * Set up a document action for an eml (email) document - * - * Email (.eml) documents get special action handling. They don't send a file - * back to the client like the other documents. Merging for a single selected - * contact opens a compose window, multiple contacts just sends. - * - * @param array &$action Action to be modified for mail - * @param array $file Array of information about the document from Api\Vfs::find - * @return void - */ - private static function document_mail_action(array &$action, $file) - { - unset($action['postSubmit']); - unset($action['onExecute']); - - // Lots takes a while, confirm - $action['confirm_multiple'] = lang('Do you want to send the message to all selected entries, WITHOUT further editing?'); - - // These parameters trigger compose + merge - only if 1 row - $extra = array( - 'from=merge', - 'document=' . $file['path'], - 'merge=' . get_called_class() - ); - - // egw.open() used if only 1 row selected - $action['egw_open'] = 'edit-mail--' . implode('&', $extra); - $action['target'] = 'compose_' . $file['path']; - - // long_task runs menuaction once for each selected row - $action['nm_action'] = 'long_task'; - $action['popup'] = Api\Link::get_registry('mail', 'edit_popup'); - $action['message'] = lang('insert in %1', Api\Vfs::decodePath($file['name'])); - $action['menuaction'] = 'mail.mail_compose.ajax_merge&document=' . $file['path'] . '&merge=' . get_called_class(); - } - - /** - * Set up a document action so the generated file is saved and opened in - * the collabora editor (if collabora is available) - * - * @param array &$action Action to be modified for editor - * @param array $file Array of information about the document from Api\Vfs::find - * @return void - */ - private static function document_editable_action(array &$action, $file) - { - static $action_base = array( - // The same for every file - 'group' => 2, - // Overwritten for every file - 'icon' => '', //Api\Vfs::mime_icon($file['mime']), - 'caption' => '', //Api\Vfs::decodePath($name_arr[$count]), - ); - $edit_attributes = array( - 'menuaction' => $GLOBALS['egw_info']['flags']['currentapp'] . '.' . get_called_class() . '.merge_entries', - 'document' => $file['path'], - 'merge' => get_called_class(), - ); - - $action = array_merge( - $action_base, - array( - 'icon' => Api\Vfs::mime_icon($file['mime']), - 'caption' => Api\Vfs::decodePath(Api\Vfs::basename($file['name'])), - 'onExecute' => 'javaScript:app.' . $GLOBALS['egw_info']['flags']['currentapp'] . '.merge', - 'merge_data' => $edit_attributes - ), - // Merge in provided action last, so we can customize if needed (eg: default document) - $action + 'merge_data' => array( + 'menuaction' => $GLOBALS['egw_info']['flags']['currentapp'] . '.' . get_called_class() . '.merge_entries', + 'merge' => get_called_class() + ) ); } diff --git a/api/templates/default/etemplate2.css b/api/templates/default/etemplate2.css index a254aabc76..0f93757b08 100644 --- a/api/templates/default/etemplate2.css +++ b/api/templates/default/etemplate2.css @@ -4202,4 +4202,10 @@ button.shortcut-buttons-flatpickr-button { /* Fix tinemce toolbar z-index is way higher (safari only) */ .tox .tox-editor-container .tox-editor-header { z-index: auto; +} + +/* Right-align "As PDF" checkbox in merge dialog */ +et2-vfs-select-dialog.egw_app_merge_document et2-checkbox { + order: 10; + margin-left: auto; } \ No newline at end of file