Switch on new Et2VfsSelect widget(s)

includes mail
This commit is contained in:
nathan 2024-02-12 10:32:28 -07:00
parent 6d8d15fbcc
commit 288c0c39e9
7 changed files with 140 additions and 48 deletions

View File

@ -13,11 +13,11 @@
use EGroupware\Api;
// add et2- prefix to following widgets/tags, if NO <overlay legacy="true"
const ADD_ET2_PREFIX_REGEXP = '#<((/?)([vh]?box))(/?|\s[^>]*)>#m';
const ADD_ET2_PREFIX_REGEXP = '#<((/?)([vh]?box)|vfs-select)(/?|\s[^>]*)>#m';
const ADD_ET2_PREFIX_LAST_GROUP = 4;
// unconditional of legacy add et2- prefix to this widgets
const ADD_ET2_PREFIX_LEGACY_REGEXP = '#<((/?)(tabbox|description|searchbox|textbox|label|avatar|lavatar|image|appicon|colorpicker|checkbox|url(-email|-phone|-fax)?|vfs-mime|vfs-uid|vfs-gid|link|link-[a-z]+|favorites))(/?|\s[^>]*)>#m';
const ADD_ET2_PREFIX_LEGACY_REGEXP = '#<((/?)(tabbox|description|searchbox|textbox|label|avatar|lavatar|image|appicon|colorpicker|checkbox|url(-email|-phone|-fax)?|vfs-mime|vfs-uid|vfs-gid|vfs-select|link|link-[a-z]+|favorites))(/?|\s[^>]*)>#m';
const ADD_ET2_PREFIX_LEGACY_LAST_GROUP = 5;
// switch evtl. set output-compression off, as we can't calculate a Content-Length header with transparent compression

View File

@ -153,9 +153,18 @@ export class Et2VfsSelectButton extends Et2InputWidget(LitElement)
protected sendFiles(button? : string | number)
{
// Some destinations expect only a single value when multiple=false
let value : string[] | FileInfo[] | string = this.value;
if(!this.multiple && this.value.length > 0)
{
// @ts-ignore This is the typecheck, no need to warn about it
value = (typeof this.value[0].path != "undefined") ? this.value[0].path : this.value[0];
}
// Send to server
this.processingPromise = this.egw().request(
this.method,
[this.methodId, this.value, button/*, savemode*/]
[this.methodId, value, button/*, savemode*/]
).then((data) =>
{
this.processingPromise = null;

View File

@ -20,7 +20,7 @@ import {SearchMixinInterface} from "../Et2Select/SearchMixin";
import {SelectOption} from "../Et2Select/FindSelectOptions";
import {DialogButton, Et2Dialog} from "../Et2Dialog/Et2Dialog";
import {HasSlotController} from "../Et2Widget/slot";
import {IegwAppLocal} from "../../jsapi/egw_global";
import {egw, IegwAppLocal} from "../../jsapi/egw_global";
import {Et2Select} from "../Et2Select/Et2Select";
import {Et2VfsSelectRow} from "./Et2VfsSelectRow";
import {Et2VfsPath} from "./Et2VfsPath";
@ -131,7 +131,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
// Still need some server-side info
protected _serverContent : Promise<any> = Promise.resolve({});
private static SERVER_URL = "EGroupware\\Api\\Etemplate\\Widget\\Vfs::ajax_vfsSelectContent";
private static SERVER_URL = "EGroupware\\Api\\Etemplate\\Widget\\Vfs::ajax_vfsSelect_content";
protected readonly hasSlotController = new HasSlotController(this, 'help-text', 'toolbar', 'footer');
@ -179,6 +179,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
// Use filemanager translations
this.egw().langRequireApp(this.egw().window, "filemanager", () => {this.requestUpdate()});
this.handleClose = this.handleClose.bind(this);
this.handleCreateDirectory = this.handleCreateDirectory.bind(this);
this.handleSearchKeyDown = this.handleSearchKeyDown.bind(this);
}
@ -312,14 +313,12 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
return this._dialog.hide();
}
getComplete() : Promise<[number, Object]>
async getComplete() : Promise<[number, Object]>
{
return this._dialog.getComplete().then((value) =>
{
// Overwrite dialog's value with what we say
value[1] = this.value;
return value
});
const value = await this._dialog.getComplete();
await this.handleClose();
value[1] = this.value;
return value;
}
startSearch() : Promise<void>
@ -409,6 +408,85 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
}
}
private async handleClose()
{
// Should already be complete, we want the button
let dialogValue = await this._dialog.getComplete();
switch(this.mode)
{
case "select-dir":
// If they didn't pick a specific directory and didn't cancel, use the current directory
this.value = this.value.length ? this.value : [this.path];
break;
case "saveas":
// Saveas wants a full path, including filename
this.value = [this.path + "/" + this.filename];
// Check for existing file, ask what to do
if(this.fileInfo(this.value[0]))
{
let result = await this.overwritePrompt(this.filename);
if(result == null)
{
return;
}
this.value = [this.path + "/" + result];
}
break;
}
this.dispatchEvent(new Event("change", {bubbles: true}));
}
/**
* User tried to saveas when we can see that file already exists. Prompt to overwrite or rename.
*
* We offer a suggested new name by appending "(#)", and give back either the original filename, their
* modified filename, or null if they cancel.
*
* @param filename
* @returns {Promise<[number|string, Object]|null>} [Button,filename] or null if they cancel
* @private
*/
private overwritePrompt(filename) : Promise<[number | string, object] | null>
{
// Make a filename suggestion
const parts = filename.split(".");
const extension = parts.pop();
const newName = parts.join(".");
let counter = 0;
let suggestion;
do
{
counter++;
suggestion = `${newName} (${counter}).${extension}`;
}
while(this.fileInfo(suggestion))
// Ask about it
const saveModeDialogButtons = [
{
label: self.egw().lang("Yes"),
id: "overwrite",
class: "ui-priority-primary",
"default": true,
image: 'check'
},
{label: self.egw().lang("Rename"), id: "rename", image: 'edit'},
{label: self.egw().lang("Cancel"), id: "cancel"}
];
return Et2Dialog.show_prompt(null,
self.egw().lang('Do you want to overwrite existing file %1 in directory %2?', filename, this.path),
self.egw().lang('File %1 already exists', filename),
suggestion, saveModeDialogButtons, null).getComplete().then(([button, value]) =>
{
if(button == "cancel")
{
return null;
}
return button == "rename" ? value.value : filename;
});
}
/**
* Sets the selected files
* @param {Et2VfsSelectRow | Et2VfsSelectRow[]} file
@ -551,8 +629,8 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
{
this.currentFile = file;
// Can't select a directory normally
if(file.value.isDir && this.mode != "select-dir")
// Can't select a directory normally, can't select anything in "saveas"
if(file.value.isDir && this.mode != "select-dir" || this.mode == "saveas")
{
return;
}
@ -567,15 +645,6 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
// Set focus after updating so the value is announced by screen readers
//this.updateComplete.then(() => this.displayInput.focus({ preventScroll: true }));
if(this.value !== oldValue)
{
// Emit after updating
this.updateComplete.then(() =>
{
this.dispatchEvent(new Event('change', {bubbles: true}));
});
}
}
}
@ -838,8 +907,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
const hasToolbar = !!hasToolbarSlot;
const hasFilename = this.mode == "saveas";
const mime = this.mimeList.length == 1 ? this.mimeList[0].value :
(typeof this.mime == "string" ? this.mime : "");
const mime = typeof this.mime == "string" ? this.mime : (this.mimeList.length == 1 ? this.mimeList[0].value : "");
return html`
<et2-dialog
@ -848,8 +916,14 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
.title=${this.title}
.open=${this.open}
@keydown=${this.handleKeyDown}
@close=${this.handleClose}
>
${hasFilename ? html`<input id="filename"/>` : nothing}
${hasFilename ? html`
<et2-textbox id="filename"
.value=${this.filename}
@change=${(e) => {this.filename = e.target.value;}}
>
</et2-textbox>` : nothing}
<div
part="toolbar"
id="toolbar"

View File

@ -1396,4 +1396,4 @@ export class et2_vfsSelect extends et2_inputWidget
return this.value;
}
};
et2_register_widget(et2_vfsSelect, ["vfs-select"]);
et2_register_widget(et2_vfsSelect, ["vfs-select", "old-vfs-select"]);

View File

@ -23,6 +23,8 @@ import {
egw_keycode_makeValid,
egw_keyHandler
} from "../../api/js/egw_action/egw_keymanager";
import {loadWebComponent} from "../../api/js/etemplate/Et2Widget/Et2Widget";
import {Et2VfsSelectButton} from "../../api/js/etemplate/Et2Vfs/Et2VfsSelectButton";
/* required dependency, commented out because no module, but egw:uses is no longer parsed
*/
@ -3266,15 +3268,19 @@ app.classes.mail = AppJS.extend(
ids.push(mail_id+'::'+attachments[i].partID+'::'+attachments[i].winmailFlag+'::'+attachments[i].filename);
}
}
var vfs_select = et2_createWidget('vfs-select', {
let vfs_select = loadWebComponent('et2-vfs-select', {
mode: action === 'saveOneToVfs' ? 'saveas' : 'select-dir',
method: 'mail.mail_ui.ajax_vfsSave',
button_label: this.egw.lang(action === 'saveOneToVfs' ? 'Save' : 'Save all'),
dialog_title: this.egw.lang(action === 'saveOneToVfs' ? 'Save attachment' : 'Save attachments'),
method_id: ids.length > 1 ? {ids: ids, action: 'attachment'} : {ids: ids[0], action: 'attachment'},
name: action === 'saveOneToVfs' ? attachments[0]['filename'] : null
});
vfs_select.click();
buttonLabel: this.egw.lang(action === 'saveOneToVfs' ? 'Save' : 'Save all'),
title: this.egw.lang(action === 'saveOneToVfs' ? 'Save attachment' : 'Save attachments'),
filename: action === 'saveOneToVfs' ? attachments[0]['filename'] : null
}, this.et2);
// Serious violation of type - methodId is a string
// Set it to an array here bypassing normal checking
vfs_select.methodId = ids.length > 1 ? {ids: ids, action: 'attachment'} : {ids: ids[0], action: 'attachment'},
vfs_select.updateComplete.then(() => vfs_select.click());
// Single use only, remove when done
vfs_select.addEventListener("change", () => vfs_select.remove());
break;
case 'collabora':
attachment = attachments[row_id];
@ -3388,16 +3394,20 @@ app.classes.mail = AppJS.extend(
ids.push(_id);
names.push(filename+'.eml');
}
var vfs_select = et2_createWidget('vfs-select', {
let vfs_select = loadWebComponent('et2-vfs-select', {
mode: _elems.length > 1 ? 'select-dir' : 'saveas',
mime: 'message/rfc822',
method: 'mail.mail_ui.ajax_vfsSave',
button_label: _elems.length>1 ? egw.lang('Save all') : egw.lang('save'),
dialog_title: this.egw.lang("Save email"),
method_id: _elems.length > 1 ? {ids:ids, action:'message'}: {ids: ids[0], action: 'message'},
name: _elems.length > 1 ? names : names[0],
});
vfs_select.click();
buttonLabel: _elems.length > 1 ? egw.lang('Save all') : egw.lang('save'),
title: this.egw.lang("Save email"),
filename: _elems.length > 1 ? names : names[0],
}, this.et2);
// Serious violation of type - methodId is a string
// Set it to an array here bypassing normal checking
vfs_select.methodId = _elems.length > 1 ? {ids: ids, action: 'message'} : {ids: ids[0], action: 'message'};
vfs_select.updateComplete.then(() => vfs_select.click());
// Single use only, remove when done
vfs_select.addEventListener("change", () => vfs_select.remove());
},
/**
@ -5513,6 +5523,9 @@ app.classes.mail = AppJS.extend(
case 'uploadForCompose':
document.getElementById('mail-compose_uploadForCompose').click();
break;
case 'selectFromVFSForCompose':
widget.show();
break;
default:
widget.click();
}

View File

@ -954,11 +954,7 @@ and (orientation : landscape) {
}
#mail-compose_mimeType{margin-left:1em;}
/*Make file uploads in compose dialog invisible*/
.mail-compose_toolbar_assist div.mail-compose_fileselector, #mail-compose_selectFromVFSForCompose, .mail-compose_toolbar_assist {
display:none;
}
/*Make file uploads in compose dialog invisible*/
.mail-compose_toolbar_assist div.mail-compose_fileselector, #mail-compose_selectFromVFSForCompose, .mail-compose_toolbar_assist {
.mail-compose_toolbar_assist div.mail-compose_fileselector, .mail-compose_toolbar_assist {
display:none;
}

View File

@ -4,9 +4,9 @@
<template id="mail.compose" template="" lang="" group="0" version="23.1">
<et2-vbox class="mailCompose mailComposeHeaderSection" width="100%">
<toolbar id="composeToolbar" width="et2_fullWidth" view_range="7" flat_list="false" list_header="short"/>
<et2-vfs-select-dialog class="$cont[vfsNotAvailable] compose_egw_icons" title="Attach files" buttonLabel="attach" id="selectFromVFSForCompose" onchange="app.mail.vfsUploadForCompose"/>
<et2-hbox class="mail-compose_toolbar_assist" width="100%">
<vfs-select class="$cont[vfsNotAvailable] compose_egw_icons" dialog_title="Attach files" button_label="attach" id="selectFromVFSForCompose" onchange="app.mail.vfsUploadForCompose" button_caption=""/>
<file class="mail-compose_fileselector" statustext="Select file to attach to message" multiple="true" progress="attachments" onFinish="app.mail.uploadForCompose" onStart="app.mail.composeUploadStart" id="uploadForCompose" drop_target ="mail-compose"/>
<file class="mail-compose_fileselector" statustext="Select file to attach to message" multiple="true" progress="attachments" onFinish="app.mail.uploadForCompose" onStart="app.mail.composeUploadStart" id="uploadForCompose" drop_target="mail-compose"/>
<et2-checkbox statustext="check to save as infolog on send" id="to_infolog" selectedValue="on" unselectedValue="off" ></et2-checkbox>
<et2-checkbox statustext="check to save as tracker entry on send" id="to_tracker" selectedValue="on" unselectedValue="off" ></et2-checkbox>
<et2-checkbox statustext="check to save as calendar event on send" id="to_calendar" selectedValue="on" unselectedValue="off" ></et2-checkbox>