diff --git a/api/etemplate.php b/api/etemplate.php index 5d12ce9ced..ddff2cd431 100644 --- a/api/etemplate.php +++ b/api/etemplate.php @@ -13,7 +13,8 @@ use EGroupware\Api; // add et2- prefix to following widgets/tags -const ADD_ET2_PREFIX_REGEXP = '#<((/?)([vh]?box|textbox|textarea|button|colorpicker|description))(/?|\s[^>]*)>#m'; +const ADD_ET2_PREFIX_REGEXP = '#<((/?)([vh]?box|textbox|textarea|button|colorpicker|description|url(-email|-phone|-fax)?))(/?|\s[^>]*)>#m'; +const ADD_ET2_PREFIX_LAST_GROUP = 5; // switch evtl. set output-compression off, as we cant calculate a Content-Length header with transparent compression ini_set('zlib.output_compression', 0); @@ -152,7 +153,8 @@ function send_template() { return '<' . $matches[2] . 'et2-' . $matches[3] . // web-components must not be self-closing (no "", but "") - (substr($matches[4], -1) === '/' ? substr($matches[4], 0, -1) . '>'; + (substr($matches[ADD_ET2_PREFIX_LAST_GROUP], -1) === '/' ? substr($matches[ADD_ET2_PREFIX_LAST_GROUP], 0, -1) . + '>'; }, $str); // handling of date and partially implemented select widget (no search or tags attribute), incl. removing of type attribute @@ -175,14 +177,6 @@ function send_template() return $matches[0]; }, $str); - // add et2- prefix to url widget, as far as it is currently implemented - $str = preg_replace_callback('##', static function($matches) - { - if (strpos($matches[2], 'readonly="true"')) return $matches[0]; // leave readonly alone for now - return str_replace(''; - }, $str); - $processing = microtime(true); if(isset($cache) && (file_exists($cache_dir = dirname($cache)) || mkdir($cache_dir, 0755, true))) diff --git a/api/js/etemplate/Et2Url/Et2Url.ts b/api/js/etemplate/Et2Url/Et2Url.ts new file mode 100644 index 0000000000..3d3fbf6342 --- /dev/null +++ b/api/js/etemplate/Et2Url/Et2Url.ts @@ -0,0 +1,64 @@ +/** + * EGroupware eTemplate2 - Url input widget + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @link https://www.egroupware.org + * @author Ralf Becker + */ + +/* eslint-disable import/no-extraneous-dependencies */ +import {Et2InvokerMixin} from "./Et2InvokerMixin"; +import {IsEmail} from "../Validators/IsEmail"; +import {Et2Textbox} from "../Et2Textbox/Et2Textbox"; + +/** + * @customElement et2-url + * + * @ToDo: implement allow_path and trailing_slash attributes + */ +export class Et2Url extends Et2InvokerMixin(Et2Textbox) +{ + /** @type {any} */ + static get properties() + { + return { + ...super.properties, + /** + * Allow a path instead of a URL, path must start with /, default false = not allowed + */ + allow_path: { + type: Boolean, + }, + /** + * Require (or forbid) that the path ends with a /, default not checked + */ + trailing_slash: { + type: Boolean, + }, + }; + } + + constructor() + { + super(); + this.defaultValidators.push(new IsEmail()); + this._invokerLabel = '⎆'; + this._invokerTitle = 'Open'; + this._invokerAction = () => { + Et2Url.action(this.value); + } + this.allow_path = false; + this.trailing_slash = undefined; + } + + static action(value) + { + if (!value) return; + // implicit add http:// if no protocol given + if(value.indexOf("://") == -1) value = "http://"+value; + egw.open_link(value, '_blank'); + } +} +// @ts-ignore TypeScript is not recognizing that this is a LitElement +customElements.define("et2-url", Et2Url); \ No newline at end of file diff --git a/api/js/etemplate/Et2Url/Et2UrlEmail.ts b/api/js/etemplate/Et2Url/Et2UrlEmail.ts index fdb87f9b4a..a163a751a0 100644 --- a/api/js/etemplate/Et2Url/Et2UrlEmail.ts +++ b/api/js/etemplate/Et2Url/Et2UrlEmail.ts @@ -23,15 +23,20 @@ export class Et2UrlEmail extends Et2InvokerMixin(Et2Textbox) this.defaultValidators.push(new IsEmail()); this._invokerLabel = '@'; this._invokerTitle = 'Compose mail to'; - this._invokerAction = () => this.__invokerAction(); + this._invokerAction = () => + { + if (!this._isEmpty() && !this.hasFeedbackFor.length) + { + Et2UrlEmail.action(this.value); + } + } } - __invokerAction() + static action(value) { - if (!this._isEmpty() && !this.hasFeedbackFor.length && - this.egw().user('apps').mail && this.egw().preference('force_mailto','addressbook') != '1' ) + if (egw.user('apps').mail && egw.preference('force_mailto','addressbook') != '1' ) { - egw.open_link('mailto:'+this.value); + egw.open_link('mailto:'+value); } } } diff --git a/api/js/etemplate/Et2Url/Et2UrlEmailReadonly.ts b/api/js/etemplate/Et2Url/Et2UrlEmailReadonly.ts new file mode 100644 index 0000000000..3629bc6a28 --- /dev/null +++ b/api/js/etemplate/Et2Url/Et2UrlEmailReadonly.ts @@ -0,0 +1,41 @@ +/** + * EGroupware eTemplate2 - Email url/compose widget + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @link https://www.egroupware.org + * @author Ralf Becker + */ + +/* eslint-disable import/no-extraneous-dependencies */ +import {IsEmail} from "../Validators/IsEmail"; +import {Et2UrlEmail} from "./Et2UrlEmail"; +import {Et2UrlReadonly} from "./Et2UrlReadonly"; + +/** + * @customElement et2-url-email_ro + */ +export class Et2UrlEmailReadonly extends Et2UrlReadonly +{ + constructor() + { + super(); + } + + transformAttributes(attrs) + { + if (typeof attrs.onclick === 'undefined') + { + attrs.onclick = () => + { + if (IsEmail.EMAIL_PREG.exec(this.value)) + { + Et2UrlEmail.action(this.value); + } + } + } + super.transformAttributes(attrs); + } +} +// @ts-ignore TypeScript is not recognizing that this is a LitElement +customElements.define("et2-url-email_ro", Et2UrlEmailReadonly); \ No newline at end of file diff --git a/api/js/etemplate/Et2Url/Et2UrlFax.ts b/api/js/etemplate/Et2Url/Et2UrlFax.ts new file mode 100644 index 0000000000..f6fb675938 --- /dev/null +++ b/api/js/etemplate/Et2Url/Et2UrlFax.ts @@ -0,0 +1,46 @@ +/** + * EGroupware eTemplate2 - Fax input widget + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @link https://www.egroupware.org + * @author Ralf Becker + */ + +/* eslint-disable import/no-extraneous-dependencies */ +import {Et2UrlPhone} from "./Et2UrlPhone"; +import {Et2UrlEmail} from "./Et2UrlEmail"; + +/** + * @customElement et2-url-phone + */ +export class Et2UrlFax extends Et2UrlPhone +{ + constructor() + { + super(); + //this.defaultValidators.push(...); + this._invokerLabel = '📠'; + this._invokerTitle = 'Send'; + this._invokerAction = () => { + Et2UrlFax.action(this.value); + } + } + + static action(value) + { + // convert fax numbers to email, if configured + if (egw.config('fax_email') && (value = value.replace('♥','').replace('(0)','').replace(/[^0-9+]/g, ''))) + { + value = value.replace(new RegExp(egw.config('fax_email_regexp')||'(.*)'), + egw.config('fax_email')); + Et2UrlEmail.action(value); + } + else + { + Et2UrlPhone.action(value); + } + } +} +// @ts-ignore TypeScript is not recognizing that this is a LitElement +customElements.define("et2-url-fax", Et2UrlFax); \ No newline at end of file diff --git a/api/js/etemplate/Et2Url/Et2UrlFaxReadonly.ts b/api/js/etemplate/Et2Url/Et2UrlFaxReadonly.ts new file mode 100644 index 0000000000..b238c2c13e --- /dev/null +++ b/api/js/etemplate/Et2Url/Et2UrlFaxReadonly.ts @@ -0,0 +1,37 @@ +/** + * EGroupware eTemplate2 - Fax url/send widget + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @link https://www.egroupware.org + * @author Ralf Becker + */ + +/* eslint-disable import/no-extraneous-dependencies */ +import {Et2UrlFax} from "./Et2UrlFax"; +import {Et2UrlReadonly} from "./Et2UrlReadonly"; + +/** + * @customElement et2-url-fax_ro + */ +export class Et2UrlFaxReadonly extends Et2UrlReadonly +{ + constructor() + { + super(); + } + + transformAttributes(attrs) + { + if (typeof attrs.onclick === 'undefined') + { + attrs.onclick = () => + { + Et2UrlFax.action(this.value); + } + } + super.transformAttributes(attrs); + } +} +// @ts-ignore TypeScript is not recognizing that this is a LitElement +customElements.define("et2-url-fax_ro", Et2UrlFaxReadonly); \ No newline at end of file diff --git a/api/js/etemplate/Et2Url/Et2UrlPhone.ts b/api/js/etemplate/Et2Url/Et2UrlPhone.ts index b78bff37be..7e3809f1d8 100644 --- a/api/js/etemplate/Et2Url/Et2UrlPhone.ts +++ b/api/js/etemplate/Et2Url/Et2UrlPhone.ts @@ -22,12 +22,13 @@ export class Et2UrlPhone extends Et2InvokerMixin(Et2Textbox) //this.defaultValidators.push(...); this._invokerLabel = '✆'; this._invokerTitle = 'Call'; - this._invokerAction = () => this.__invokerAction(); + this._invokerAction = () => { + Et2UrlPhone.action(this.value); + } } - __invokerAction() + static action(value) { - let value = this.value; // Clean number value = value.replace('♥','').replace('(0)',''); value = value.replace(/[abc]/gi,2).replace(/[def]/gi,3).replace(/[ghi]/gi,4).replace(/[jkl]/gi,5).replace(/[mno]/gi,6); @@ -41,15 +42,15 @@ export class Et2UrlPhone extends Et2InvokerMixin(Et2Textbox) { window.open("tel:"+value); } - else if (this.egw().config("call_link")) + else if (egw.config("call_link")) { - var link = this.egw().config("call_link") + var link = egw.config("call_link") // tel: links use no URL encoding according to rfc3966 section-5.1.4 - .replace("%1", this.egw().config("call_link").substr(0, 4) == 'tel:' ? + .replace("%1", egw.config("call_link").substr(0, 4) == 'tel:' ? value : encodeURIComponent(value)) - .replace("%u",this.egw().user('account_lid')) - .replace("%t",this.egw().user('account_phone')); - var popup = this.egw().config("call_popup"); + .replace("%u",egw.user('account_lid')) + .replace("%t",egw.user('account_phone')); + var popup = egw.config("call_popup"); if (popup && popup !== '_self' || !link.match(/^https?:/)) // execute non-http(s) links eg. tel: like before { egw.open_link(link, '_phonecall', popup); diff --git a/api/js/etemplate/Et2Url/Et2UrlPhoneReadonly.ts b/api/js/etemplate/Et2Url/Et2UrlPhoneReadonly.ts new file mode 100644 index 0000000000..131c511865 --- /dev/null +++ b/api/js/etemplate/Et2Url/Et2UrlPhoneReadonly.ts @@ -0,0 +1,37 @@ +/** + * EGroupware eTemplate2 - Phone url/call widget + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @link https://www.egroupware.org + * @author Ralf Becker + */ + +/* eslint-disable import/no-extraneous-dependencies */ +import {Et2UrlPhone} from "./Et2UrlPhone"; +import {Et2UrlReadonly} from "./Et2UrlReadonly"; + +/** + * @customElement et2-url-phone_ro + */ +export class Et2UrlPhoneReadonly extends Et2UrlReadonly +{ + constructor() + { + super(); + } + + transformAttributes(attrs) + { + if (typeof attrs.onclick === 'undefined') + { + attrs.onclick = () => + { + Et2UrlPhone.action(this.value); + } + } + super.transformAttributes(attrs); + } +} +// @ts-ignore TypeScript is not recognizing that this is a LitElement +customElements.define("et2-url-phone_ro", Et2UrlPhoneReadonly); \ No newline at end of file diff --git a/api/js/etemplate/Et2Url/Et2UrlReadonly.ts b/api/js/etemplate/Et2Url/Et2UrlReadonly.ts new file mode 100644 index 0000000000..e9c19dd60f --- /dev/null +++ b/api/js/etemplate/Et2Url/Et2UrlReadonly.ts @@ -0,0 +1,55 @@ +/** + * EGroupware eTemplate2 - Url r/o widget + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @link https://www.egroupware.org + * @author Ralf Becker + */ + +/* eslint-disable import/no-extraneous-dependencies */ +import {IsEmail} from "../Validators/IsEmail"; +import {Et2Description} from "../Et2Description/Et2Description"; +import {Et2UrlEmail} from "./Et2UrlEmail"; +import {css} from "@lion/core"; +import {Et2Url} from "./Et2Url"; + +/** + * @customElement et2-url_ro + */ +export class Et2UrlReadonly extends Et2Description +{ + constructor() + { + super(); + } + + static get styles() + { + return [ + ...super.styles, + css` + :host { + cursor: pointer; + color: #26537c; + }` + ]; + } + + transformAttributes(attrs) + { + if (typeof attrs.onclick === 'undefined') + { + attrs.onclick = () => + { + if (this.value) + { + Et2Url.action(this.value); + } + } + } + super.transformAttributes(attrs); + } +} +// @ts-ignore TypeScript is not recognizing that this is a LitElement +customElements.define("et2-url_ro", Et2UrlReadonly); \ No newline at end of file diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts index a70d0c852d..4c73a979b1 100644 --- a/api/js/etemplate/etemplate2.ts +++ b/api/js/etemplate/etemplate2.ts @@ -46,8 +46,14 @@ import './Et2Textbox/Et2Number'; import './Et2Textbox/Et2NumberReadonly'; import './Et2Colorpicker/Et2Colorpicker'; import './Et2Taglist/Et2Taglist'; +import './Et2Url/Et2Url'; +import './Et2Url/Et2UrlReadonly'; import './Et2Url/Et2UrlEmail'; +import './Et2Url/Et2UrlEmailReadonly'; import './Et2Url/Et2UrlPhone'; +import './Et2Url/Et2UrlPhoneReadonly'; +import './Et2Url/Et2UrlFax'; +import './Et2Url/Et2UrlFaxReadonly'; /* Include all widget classes here, we only care about them registering, not importing anything*/ import './et2_widget_vfs'; // Vfs must be first (before et2_widget_file) due to import cycle