From d512238dc01480336ba25a821f494213b07af198 Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 21 Sep 2021 14:06:24 -0600 Subject: [PATCH] * Collabora: Add address insert dialog --- api/js/etemplate/et2_widget_placeholder.ts | 177 +++++++++++++++++- api/src/Contacts/Merge.php | 4 + api/src/Storage/Merge.php | 31 ++- api/templates/default/placeholder_snippet.xet | 72 +++++++ 4 files changed, 277 insertions(+), 7 deletions(-) create mode 100644 api/templates/default/placeholder_snippet.xet diff --git a/api/js/etemplate/et2_widget_placeholder.ts b/api/js/etemplate/et2_widget_placeholder.ts index 6cbfaa31ab..06d42a3245 100644 --- a/api/js/etemplate/et2_widget_placeholder.ts +++ b/api/js/etemplate/et2_widget_placeholder.ts @@ -36,6 +36,11 @@ export class et2_placeholder_select extends et2_inputWidget "name": "Insert callback", "description": "Method called with the selected placeholder text", "type": "js" + }, + dialog_title: { + "name": "Dialog title", + "type": "string", + "default": "Insert Placeholder" } }; @@ -99,7 +104,7 @@ export class et2_placeholder_select extends et2_inputWidget * * @param {object} _data content */ - private _buildDialog(_data) + protected _buildDialog(_data) { let self = this; @@ -149,7 +154,7 @@ export class et2_placeholder_select extends et2_inputWidget { if((submit_button_id == 'submit' || (extra_buttons_action && extra_buttons_action[submit_button_id])) && submit_value) { - this.options.insert_callback(submit_value.placeholder_list); + this._do_insert_callback(submit_value); return true; } }.bind(this); @@ -238,6 +243,7 @@ export class et2_placeholder_select extends et2_inputWidget let preview_content = this.dialog.template.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'; @@ -301,6 +307,16 @@ export class et2_placeholder_select extends et2_inputWidget 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; @@ -313,3 +329,160 @@ export class et2_placeholder_select extends et2_inputWidget }; 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": { + "{{n_fn}}\n{{adr_one_street}}{{NELF adr_one_street2}}\n{{adr_one_formatted}}": "Work address", + "{{n_fn}}\n{{adr_two_street}}{{NELF adr_two_street2}}\n{{adr_two_formatted}}": "Home address", + } + } + }; + + button : JQuery; + submit_callback : any; + dialog : et2_dialog; + protected value : any; + + protected LIST_URL = 'EGroupware\\Api\\Etemplate\\Widget\\Placeholder::ajax_get_placeholders'; + protected TEMPLATE = '/api/templates/default/placeholder_snippet.xet?1'; + + /** + * Post-load of the dialog + * Bind internal events, set some things that are difficult to do in the template + */ + _on_template_load() + { + let app = this.dialog.template.widgetContainer.getDOMWidgetById("app"); + let placeholder_list = this.dialog.template.widgetContainer.getDOMWidgetById("placeholder_list"); + let preview = this.dialog.template.widgetContainer.getDOMWidgetById("preview_content"); + let entry = this.dialog.template.widgetContainer.getDOMWidgetById("entry"); + + + placeholder_list.set_select_options(this._get_placeholders("addressbook", "addresses")); + + // Further setup / styling that can't be done in etemplate + app.getInputNode().setAttribute("readonly", true); + this.dialog.template.DOMContainer.style.display = "flex"; + this.dialog.template.DOMContainer.firstChild.style.display = "flex"; + placeholder_list.getDOMNode().size = 5; + + // Bind some handlers + app.onchange = (node, widget) => + { + entry.set_value({app: widget.get_value()}); + placeholder_list.set_select_options(this._get_placeholders(app.get_value(), "addresses")); + } + placeholder_list.onchange = this._on_placeholder_select.bind(this); + entry.onchange = this._on_placeholder_select.bind(this); + + 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 = this.dialog.template.widgetContainer.getDOMWidgetById("app"); + let entry = this.dialog.template.widgetContainer.getDOMWidgetById("entry"); + let placeholder_list = this.dialog.template.widgetContainer.getDOMWidgetById("placeholder_list"); + let preview = this.dialog.template.widgetContainer.getDOMWidgetById("preview_placeholder"); + let preview_content = this.dialog.template.widgetContainer.getDOMWidgetById("preview_content"); + + 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', + [app.get_value(), placeholder_list.get_value(), entry.get_value()], + function(_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.search.get(0).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) => + { + options.push( + { + value: key, + label: 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"]); diff --git a/api/src/Contacts/Merge.php b/api/src/Contacts/Merge.php index 4681067995..b13bd3f0b4 100644 --- a/api/src/Contacts/Merge.php +++ b/api/src/Contacts/Merge.php @@ -309,6 +309,10 @@ class Merge extends Api\Storage\Merge } } + // Correctly formatted address by country / preference + $placeholders['business']["{{adr_one_formatted}}"] = "Formatted business address"; + $placeholders['private']["{{adr_two_formatted}}"] = "Formatted private address"; + $group = 'customfields'; foreach($this->contacts->customfields as $name => $field) { diff --git a/api/src/Storage/Merge.php b/api/src/Storage/Merge.php index 97e51e18d1..6bbd0580fa 100644 --- a/api/src/Storage/Merge.php +++ b/api/src/Storage/Merge.php @@ -283,22 +283,43 @@ abstract class Merge } break; case 'account_id': - if ($value) + if($value) { - $replacements['$$'.($prefix ? $prefix.'/':'').'account_lid$$'] = $GLOBALS['egw']->accounts->id2name($value); + $replacements['$$' . ($prefix ? $prefix . '/' : '') . 'account_lid$$'] = $GLOBALS['egw']->accounts->id2name($value); } break; } - if ($name != 'photo') $replacements['$$'.($prefix ? $prefix.'/':'').$name.'$$'] = $value; + if($name != 'photo') + { + $replacements['$$' . ($prefix ? $prefix . '/' : '') . $name . '$$'] = $value; + } } + + // Formatted address, according to preference or country + foreach(['one', 'two'] as $adr) + { + switch($this->contacts->addr_format_by_country($contact["adr_{$adr}_countryname"])) + { + case 'city_state_postcode': + $formatted_placeholder = $contact["adr_{$adr}_locality"] . " " . + $contact["adr_{$adr}_region"] . " " . $contact["adr_{$adr}_postalcode"]; + break; + case 'postcode_city': + default: + $formatted_placeholder = $contact["adr_{$adr}_postalcode"] . ' ' . $contact["adr_{$adr}_locality"]; + break; + } + $replacements['$$adr_' . $adr . '_formatted$$'] = $formatted_placeholder; + } + // set custom fields, should probably go to a general method all apps can use // need to load all cfs for $ignore_acl=true foreach($ignore_acl ? Customfields::get('addressbook', true) : $this->contacts->customfields as $name => $field) { - $name = '#'.$name; + $name = '#' . $name; if(!array_key_exists($name, $contact) || !$contact[$name]) { - $replacements['$$'.($prefix ? $prefix.'/':'').$name.'$$'] = ''; + $replacements['$$' . ($prefix ? $prefix . '/' : '') . $name . '$$'] = ''; continue; } // Format date cfs per user Api\Preferences diff --git a/api/templates/default/placeholder_snippet.xet b/api/templates/default/placeholder_snippet.xet new file mode 100644 index 0000000000..504c51c7d7 --- /dev/null +++ b/api/templates/default/placeholder_snippet.xet @@ -0,0 +1,72 @@ + + + + + +