egroupware_official/api/js/etemplate/et2_widget_placeholder.ts
nathan aaefb6ce68 Fix some app code using Et2Dialog.template, which previously returned the eTemplate2 object
It Et2Dialog.template is an attribute, and has to return the template name.
Use Et2Dialog.eTemplate to access the loaded etemplate2 object.
2024-12-11 08:33:32 -07:00

576 lines
16 KiB
TypeScript

/**
* EGroupware eTemplate2 - JS Placeholder widgets
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link https://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2021
*/
/*egw:uses
et2_core_inputWidget;
et2_core_valueWidget;
et2_widget_description;
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {ClassWithAttributes} from "./et2_core_inheritance";
import {et2_inputWidget} from "./et2_core_inputWidget";
import {Et2Dialog} from "./Et2Dialog/Et2Dialog";
import {Et2LinkEntry} from "./Et2Link/Et2LinkEntry";
import {Et2Select} from "./Et2Select/Et2Select";
import {Et2Description} from "./Et2Description/Et2Description";
import {Et2Button} from "./Et2Button/Et2Button";
/**
* Display a dialog to choose a placeholder
*/
export class et2_placeholder_select extends et2_inputWidget
{
static readonly _attributes : any = {
insert_callback: {
"name": "Insert callback",
"description": "Method called with the selected placeholder text",
"type": "js"
},
dialog_title: {
"name": "Dialog title",
"type": "string",
"default": "Insert Placeholder"
}
};
static placeholders : Object | null = null;
button : JQuery;
submit_callback : any;
dialog : Et2Dialog;
protected value : any;
protected LIST_URL = 'EGroupware\\Api\\Etemplate\\Widget\\Placeholder::ajax_get_placeholders';
protected TEMPLATE = '/api/templates/default/insert_merge_placeholder.xet?1';
/**
* Constructor
*
* @param _parent
* @param _attrs
* @memberOf et2_vfsSelect
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_placeholder_select._attributes, _child || {}));
// Allow no child widgets
this.supportedWidgetClasses = [];
}
_content(_content, _callback)
{
let self = this;
if(this.dialog)
{
this.dialog.close();
}
var callback = _callback || this._buildDialog;
if(et2_placeholder_select.placeholders === null)
{
this.egw().loading_prompt('placeholder_select', true, '', 'body');
this.egw().json(
this.LIST_URL,
[],
function(_content)
{
if(typeof _content === 'object' && _content.message)
{
// Something went wrong
this.egw().message(_content.message, 'error');
return;
}
this.egw().loading_prompt('placeholder_select', false);
et2_placeholder_select.placeholders = _content;
callback.apply(self, arguments);
}.bind(this)
).sendRequest(true);
}
else
{
this._buildDialog(et2_placeholder_select.placeholders);
}
}
/**
* Builds placeholder selection dialog
*
* @param {object} _data content
*/
protected _buildDialog(_data)
{
let buttons = [
{
label: this.egw().lang("Insert"),
id: "submit",
image: "export"
}
];
let extra_buttons_action = {};
if(this.options.extra_buttons && this.options.method)
{
for(let i = 0; i < this.options.extra_buttons.length; i++)
{
delete (this.options.extra_buttons[i]['click']);
buttons.push(this.options.extra_buttons[i]);
extra_buttons_action[this.options.extra_buttons[i]['id']] = this.options.extra_buttons[i]['id'];
}
}
buttons.push({label: this.egw().lang("Cancel"), id: "cancel", image: "cancel"});
let data = {
content: {app: '', group: '', entry: {}},
sel_options: {app: [], group: []},
modifications: {
entry: {
application_list: []
}
}
};
Object.keys(_data).map((key) =>
{
data.sel_options.app.push(
{
value: key,
label: this.egw().lang(key)
});
});
data.sel_options.group = this._get_group_options(Object.keys(_data)[0]);
data.content.app = data.sel_options.app[0].value;
data.content.group = data.sel_options.group[0]?.value;
data.content.entry = {app: data.content.app};
data.modifications.entry.application_list = Object.keys(_data);
// Remove non-app placeholders (user & general)
let non_apps = ['user', 'general'];
for(let i = 0; i < non_apps.length; i++)
{
let index = data.modifications.entry.application_list.indexOf(non_apps[i]);
data.modifications.entry.application_list.splice(index, 1);
}
// callback for dialog
this.submit_callback = function(submit_button_id, submit_value)
{
if((submit_button_id == 'submit' || (extra_buttons_action && extra_buttons_action[submit_button_id])) && submit_value)
{
this._do_insert_callback(submit_value);
return true;
}
else if(submit_button_id == 'cancel')
{
return true;
}
else
{
// Keep dialog open
return false;
}
}.bind(this);
this.dialog = new Et2Dialog(this.egw());
this.dialog.transformAttributes({
callback: this.submit_callback,
title: this.options.dialog_title || "Insert Placeholder",
buttons: buttons,
value: data,
template: this.egw().webserverUrl + this.TEMPLATE,
resizable: true,
width: ''
});
document.body.appendChild(<HTMLElement><unknown>this.dialog);
this.dialog.addEventListener('open', this._on_template_load.bind(this));
}
doLoadingFinished()
{
this._content.call(this, null);
return true;
}
/**
* Post-load of the dialog
* Bind internal events, set some things that are difficult to do in the template
*/
_on_template_load()
{
let app = <Et2Select><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("app");
let group = <Et2Select><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("group");
let placeholder_list = <Et2Select><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("placeholder_list");
let preview = <Et2Description><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("preview_placeholder");
let entry = <Et2LinkEntry><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("entry");
placeholder_list.set_select_options(this._get_placeholders(app.get_value(), group.get_value()));
// Bind some handlers
app.onchange = (node, widget) =>
{
preview.set_value("");
if(['user', 'filemanager'].indexOf(widget.get_value()) >= 0)
{
// These ones don't let you select an entry for preview (they don't work)
entry.set_disabled(true);
entry.set_value({app: 'user', id: '', query: ''});
}
else if(widget.get_value() == 'general')
{
// Don't change entry app, leave it
entry.set_disabled(false);
}
else
{
// Load app translations
this.egw().langRequireApp(this.egw().window, widget.get_value());
entry.set_disabled(false);
entry.set_value({app: widget.get_value(), id: '', query: ''});
}
let groups = this._get_group_options(widget.get_value());
group.set_select_options(groups);
group.set_value(groups[0].value);
group.onchange();
}
group.onchange = (select_node, select_widget) =>
{
let options = this._get_placeholders(app.get_value(), group.get_value())
placeholder_list.set_select_options(options);
preview.set_value("");
placeholder_list.updateComplete.then(() => placeholder_list.set_value(options[0].value));
}
placeholder_list.onchange = this._on_placeholder_select.bind(this);
entry.onchange = this._on_placeholder_select.bind(this);
(<Et2Button><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("insert_placeholder")).onclick = () =>
{
this.options.insert_callback(this.dialog.eTemplate.widgetContainer.getDOMWidgetById("preview_placeholder").getDOMNode().textContent);
};
(<Et2Button><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("insert_content")).onclick = () =>
{
this.options.insert_callback(this.dialog.eTemplate.widgetContainer.getDOMWidgetById("preview_content").getDOMNode().textContent);
};
app.set_value(app.get_value());
}
/**
* User has selected a placeholder
* Update the UI, and if they have an entry selected do the replacement and show that.
*/
_on_placeholder_select()
{
let app = <Et2LinkEntry><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("app");
let entry = <Et2LinkEntry><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("entry");
let placeholder_list = <Et2Select><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("placeholder_list");
let preview = <Et2Description><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("preview_placeholder");
let preview_content = <Et2Description><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("preview_content");
// Show the selected placeholder
this.set_value(placeholder_list.get_value());
preview.set_value(placeholder_list.get_value());
preview.getDOMNode().parentNode.style.visibility = placeholder_list.get_value()?.trim() ? null : 'hidden';
if(placeholder_list.get_value() && entry.get_value())
{
// Show the selected placeholder replaced with value from the selected entry
this.egw().json(
'EGroupware\\Api\\Etemplate\\Widget\\Placeholder::ajax_fill_placeholders',
[placeholder_list.get_value(), entry.get_value()],
function(_content)
{
if(!_content)
{
_content = '';
}
preview_content.set_value(_content);
preview_content.getDOMNode().parentNode.style.visibility = _content.trim() ? null : 'hidden';
}.bind(this)
).sendRequest(true);
}
else
{
// No value, hide the row
preview_content.getDOMNode().parentNode.style.visibility = 'hidden';
}
}
/**
* Get the list of placeholder groups under the selected application
* @param appname
* @returns {value:string, label:string}[]
*/
_get_group_options(appname : string)
{
let options = [];
Object.keys(et2_placeholder_select.placeholders[appname]).map((key) =>
{
// @ts-ignore
if(Object.keys(et2_placeholder_select.placeholders[appname][key]).filter((key) => isNaN(key)).length > 0)
{
// Handle groups of groups
if(typeof et2_placeholder_select.placeholders[appname][key].label !== "undefined")
{
options.push({label:key, value: et2_placeholder_select.placeholders[appname][key]});
}
else
{
let a = {label: key, value:[]};
for(let sub of Object.keys(et2_placeholder_select.placeholders[appname][key]))
{
if(!et2_placeholder_select.placeholders[appname][key][sub])
{
continue;
}
a.value.push({
value: key + '-' + sub,
label: this.egw().lang(sub)
});
}
options.push(a);
}
}
else
{
options.push({
value: key,
label: this.egw().lang(key)
});
}
});
return options;
}
/**
* Get a list of placeholders under the given application + group
*
* @param appname
* @param group
* @returns {value:string, label:string}[]
*/
_get_placeholders(appname : string, group : string)
{
let _group = group.split('-', 2);
let ph = et2_placeholder_select.placeholders[appname];
for(let i = 0; typeof ph !== "undefined" && i < _group.length; i++)
{
ph = ph[_group[i]];
}
return ph || [];
}
/**
* Get the correct insert text call the insert callback with it
*
* @param dialog_values
*/
_do_insert_callback(dialog_values : Object)
{
this.options.insert_callback(this.get_value());
}
set_value(value)
{
this.value = value;
}
getValue()
{
return this.value;
}
};
et2_register_widget(et2_placeholder_select, ["placeholder-select"]);
/**
* Display a dialog to choose from a set list of placeholder snippets
*/
export class et2_placeholder_snippet_select extends et2_placeholder_select
{
static readonly _attributes : any = {
dialog_title: {
"default": "Insert address"
}
};
static placeholders = {
"addressbook": {
"addresses": {
"{{org_name}}\n{{n_fn}}\n{{adr_one_street}}{{NELF adr_one_street2}}\n{{adr_one_formatted}}": "Business address",
"{{n_fn}}\n{{adr_two_street}}{{NELF adr_two_street2}}\n{{adr_two_formatted}}": "Home address",
"{{n_fn}}\n{{email}}\n{{tel_work}}": "Name, email, phone"
}
}
};
button : JQuery;
submit_callback : any;
dialog : Et2Dialog;
protected value : any;
protected LIST_URL = 'EGroupware\\Api\\Etemplate\\Widget\\Placeholder::ajax_get_placeholders';
protected TEMPLATE = '/api/templates/default/placeholder_snippet.xet?1';
/**
* Constructor
*
* @param _parent
* @param _attrs
* @memberOf et2_vfsSelect
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_placeholder_select._attributes, _child || {}));
// Load app translations
this.egw().langRequireApp(this.egw().window, "addressbook");
}
/**
* Post-load of the dialog
* Bind internal events, set some things that are difficult to do in the template
*/
_on_template_load()
{
let app = <Et2Select><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("app");
let placeholder_list = <Et2Select><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("placeholder_list");
let preview = <Et2Description><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("preview_content");
let entry = <Et2LinkEntry><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("entry");
placeholder_list.set_select_options(this._get_placeholders("addressbook", "addresses"));
// Further setup / styling that can't be done in etemplate
app.setAttribute("readonly", true);
// Bind some handlers
app.onchange = (node, widget) =>
{
entry.set_value({app: widget.get_value()});
placeholder_list.set_select_options(this._get_placeholders(app.value, "addresses"));
}
placeholder_list.onchange = this._on_placeholder_select.bind(this);
entry.onchange = this._on_placeholder_select.bind(this);
app.set_value(app.value);
this._on_placeholder_select();
}
/**
* User has selected a placeholder
* Update the UI, and if they have an entry selected do the replacement and show that.
*/
_on_placeholder_select()
{
let app = <Et2Select><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("app");
let entry = <Et2LinkEntry><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("entry");
let placeholder_list = <Et2Select><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("placeholder_list");
let preview_content = <Et2Description><unknown>this.dialog.eTemplate.widgetContainer.getDOMWidgetById("preview_content");
let placeholder = "";
if(app && app.value)
{
placeholder = Object.keys(et2_placeholder_snippet_select.placeholders[<string>app.value]["addresses"])[<string>placeholder_list.value];
}
if(placeholder && entry.get_value())
{
// Show the selected placeholder replaced with value from the selected entry
this.egw().json(
'EGroupware\\Api\\Etemplate\\Widget\\Placeholder::ajax_fill_placeholders',
[placeholder, {app: "addressbook", id: entry.get_value()}],
function(_content)
{
if(!_content)
{
_content = '';
}
this.set_value(_content);
preview_content.set_value(_content);
preview_content.getDOMNode().parentNode.style.visibility = _content.trim() ? null : 'hidden';
}.bind(this)
).sendRequest(true);
}
else
{
// No value, hide the row
preview_content.getDOMNode().parentNode.style.visibility = 'hidden';
}
if(!entry.get_value())
{
entry._searchNode.focus();
}
}
/**
* Get the list of placeholder groups under the selected application
* @param appname
* @returns {value:string, label:string}[]
*/
_get_group_options(appname : string)
{
let options = [];
Object.keys(et2_placeholder_select.placeholders[appname]).map((key) =>
{
options.push(
{
value: key,
label: this.egw().lang(key)
});
});
return options;
}
/**
* Get a list of placeholders under the given application + group
*
* @param appname
* @param group
* @returns {value:string, label:string}[]
*/
_get_placeholders(appname : string, group : string)
{
let options = [];
Object.keys(et2_placeholder_snippet_select.placeholders[appname][group]).map((key, index) =>
{
options.push(
{
value: index,
label: this.egw().lang(et2_placeholder_snippet_select.placeholders[appname][group][key])
});
});
return options;
}
/**
* Get the correct insert text call the insert callback with it
*
* @param dialog_values
*/
_do_insert_callback(dialog_values : Object)
{
this.options.insert_callback(this.get_value());
}
set_value(value)
{
this.value = value;
}
getValue()
{
return this.value;
}
};
et2_register_widget(et2_placeholder_snippet_select, ["placeholder-snippet"]);