* Api: Use file selection dialog for selecting target merge document instead of nested menus

This commit is contained in:
nathan 2024-03-08 16:34:19 -07:00
parent 7c9b3dd9e8
commit d24ca39d09
3 changed files with 125 additions and 251 deletions

View File

@ -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 = <string>this.egw.preference('document_dir', this.appname) ?? "";
let dirs = dirPref.split('/[,\s]+\//');
dirs.forEach((d, index) =>
{
if(index)
{
d = "/" + d;
}
});
let fileSelect = <Et2VfsSelectDialog><unknown>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 = <Et2Checkbox><unknown>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)
*

View File

@ -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()
)
);
}

View File

@ -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;
}