diff --git a/api/etemplate.php b/api/etemplate.php index f7cf3f05db..5d12ce9ced 100644 --- a/api/etemplate.php +++ b/api/etemplate.php @@ -143,7 +143,6 @@ function send_template() if (!empty($matches[3])) $tag = str_replace($matches[3], '', $tag); if ($type !== 'float') $tag .= ' precision="0"'; return $tag.'>'; - }, $str); // fix --> + `; + } + } + return Et2Invoker; +}) \ No newline at end of file diff --git a/api/js/etemplate/Et2Url/Et2UrlEmail.ts b/api/js/etemplate/Et2Url/Et2UrlEmail.ts new file mode 100644 index 0000000000..fdb87f9b4a --- /dev/null +++ b/api/js/etemplate/Et2Url/Et2UrlEmail.ts @@ -0,0 +1,39 @@ +/** + * EGroupware eTemplate2 - Email 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-email + */ +export class Et2UrlEmail extends Et2InvokerMixin(Et2Textbox) +{ + constructor() + { + super(); + this.defaultValidators.push(new IsEmail()); + this._invokerLabel = '@'; + this._invokerTitle = 'Compose mail to'; + this._invokerAction = () => this.__invokerAction(); + } + + __invokerAction() + { + if (!this._isEmpty() && !this.hasFeedbackFor.length && + this.egw().user('apps').mail && this.egw().preference('force_mailto','addressbook') != '1' ) + { + egw.open_link('mailto:'+this.value); + } + } +} +// @ts-ignore TypeScript is not recognizing that this is a LitElement +customElements.define("et2-url-email", Et2UrlEmail); \ No newline at end of file diff --git a/api/js/etemplate/Et2Url/Et2UrlPhone.ts b/api/js/etemplate/Et2Url/Et2UrlPhone.ts new file mode 100644 index 0000000000..b78bff37be --- /dev/null +++ b/api/js/etemplate/Et2Url/Et2UrlPhone.ts @@ -0,0 +1,69 @@ +/** + * EGroupware eTemplate2 - Phone 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 {Et2Textbox} from "../Et2Textbox/Et2Textbox"; + +/** + * @customElement et2-url-phone + */ +export class Et2UrlPhone extends Et2InvokerMixin(Et2Textbox) +{ + constructor() + { + super(); + //this.defaultValidators.push(...); + this._invokerLabel = '✆'; + this._invokerTitle = 'Call'; + this._invokerAction = () => this.__invokerAction(); + } + + __invokerAction() + { + 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); + value = value.replace(/[pqrs]/gi,7).replace(/[tuv]/gi,8).replace(/[wxyz]/gi,9); + // remove everything but numbers and plus, as telephon software might not like it + value = value.replace(/[^0-9+]/g, ''); + + // mobile Webkit (iPhone, Android) have precedence over server configuration! + if (navigator.userAgent.indexOf('AppleWebKit') !== -1 && + (navigator.userAgent.indexOf("iPhone") !== -1 || navigator.userAgent.indexOf("Android") !== -1)) + { + window.open("tel:"+value); + } + else if (this.egw().config("call_link")) + { + var link = this.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:' ? + value : encodeURIComponent(value)) + .replace("%u",this.egw().user('account_lid')) + .replace("%t",this.egw().user('account_phone')); + var popup = this.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); + } + else + { + // No popup, use AJAX. We don't care about the response. + window.fetch(link, { + headers: { 'Content-Type': 'application/json'}, + method: "GET", + }); + } + } + } +} +// @ts-ignore TypeScript is not recognizing that this is a LitElement +customElements.define("et2-url-phone", Et2UrlPhone); \ No newline at end of file diff --git a/api/js/etemplate/Validators/IsEmail.ts b/api/js/etemplate/Validators/IsEmail.ts new file mode 100644 index 0000000000..949c74dae3 --- /dev/null +++ b/api/js/etemplate/Validators/IsEmail.ts @@ -0,0 +1,44 @@ +import {Pattern} from "@lion/form-core"; + +export class IsEmail extends Pattern +{ + /** + * Regexes for validating email addresses incl. email in angle-brackets eg. + * + "Ralf Becker " + * + "Ralf Becker (EGroupware GmbH) " + * + "" or "rb@egroupware.org" + * + '"Becker, Ralf" ' + * + "'Becker, Ralf' " + * but NOT: + * - "Becker, Ralf " (contains comma outside " or ' enclosed block) + * - "Becker < Ralf " (contains < ----------- " ---------------) + * + * About umlaut or IDN domains: we currently only allow German umlauts in domain part! + * We forbid all non-ascii chars in local part, as Horde does not yet support SMTPUTF8 extension (rfc6531) + * and we get a "SMTP server does not support internationalized header data" error otherwise. + * + * Using \042 instead of " to NOT stall minifyer! + * + * Similar, but not identical, preg is in Etemplate\Widget\Url PHP class! + * We can not use "(?@,;:\042\[\]\x80-\xff]+@([a-z0-9ÄÖÜäöüß](|[a-z0-9ÄÖÜäöüß_-]*[a-z0-9ÄÖÜäöüß])\.)+[a-z]{2,}>?$/i); + + constructor() + { + super(IsEmail.EMAIL_PREG); + } + + /** + * Give a message about this field being required. Could be customised according to MessageData. + * @param {MessageData | undefined} data + * @returns {Promise} + */ + static async getMessage(data) + { + return data.formControl.egw().lang("Invalid email"); + } +} \ No newline at end of file diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts index 89913a1294..5841b19465 100644 --- a/api/js/etemplate/etemplate2.ts +++ b/api/js/etemplate/etemplate2.ts @@ -46,6 +46,8 @@ import './Et2Textbox/Et2Number'; import './Et2Textbox/Et2NumberReadonly'; import './Et2Colorpicker/Et2Colorpicker'; import './Et2Taglist/Et2Taglist'; +import './Et2Url/Et2UrlEmail'; +import './Et2Url/Et2UrlPhone'; /* 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