diff --git a/api/js/etemplate/et2_widget_password.js b/api/js/etemplate/et2_widget_password.js
new file mode 100644
index 0000000000..98ec03088e
--- /dev/null
+++ b/api/js/etemplate/et2_widget_password.js
@@ -0,0 +1,253 @@
+"use strict";
+/**
+ * EGroupware eTemplate2 - JS Textbox object
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package etemplate
+ * @subpackage api
+ * @link http://www.egroupware.org
+ * @author Andreas Stöckel
+ */
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+Object.defineProperty(exports, "__esModule", { value: true });
+/*egw:uses
+ /vendor/bower-asset/jquery/dist/jquery.js;
+ et2_core_inputWidget;
+ et2_core_valueWidget;
+*/
+require("./et2_core_common");
+var et2_core_inheritance_1 = require("./et2_core_inheritance");
+var et2_core_widget_1 = require("./et2_core_widget");
+var et2_widget_textbox_1 = require("./et2_widget_textbox");
+var et2_widget_dialog_1 = require("./et2_widget_dialog");
+/**
+ * Class which implements the "textbox" XET-Tag
+ *
+ * @augments et2_inputWidget
+ */
+var et2_password = /** @class */ (function (_super) {
+ __extends(et2_password, _super);
+ /**
+ * Constructor
+ */
+ function et2_password(_parent, _attrs, _child) {
+ var _this =
+ // Call the inherited constructor
+ _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_password._attributes, _child || {})) || this;
+ // The password is stored encrypted server side, and passed encrypted.
+ // This flag is for if we've decrypted the password to show it already
+ _this.encrypted = true;
+ return _this;
+ }
+ et2_password.prototype.createInputWidget = function () {
+ this.wrapper = jQuery(document.createElement("div"))
+ .addClass("et2_password");
+ this.input = jQuery(document.createElement("input"));
+ this.input.attr("type", "password");
+ // Make autocomplete default value off for password field
+ // seems browsers not respecting 'off' anymore and started to
+ // implement a new key called "new-password" considered as switching
+ // autocomplete off.
+ // https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
+ if (this.options.autocomplete === "" || this.options.autocomplete == "off")
+ this.options.autocomplete = "new-password";
+ if (this.options.size) {
+ this.set_size(this.options.size);
+ }
+ if (this.options.blur) {
+ this.set_blur(this.options.blur);
+ }
+ if (this.options.readonly) {
+ this.set_readonly(true);
+ }
+ this.input.addClass("et2_textbox")
+ .appendTo(this.wrapper);
+ this.setDOMNode(this.wrapper[0]);
+ if (this.options.value) {
+ this.set_value(this.options.value);
+ }
+ if (this.options.onkeypress && typeof this.options.onkeypress == 'function') {
+ var self = this;
+ this.input.on('keypress', function (_ev) {
+ return self.options.onkeypress.call(this, _ev, self);
+ });
+ }
+ this.input.on('change', function () {
+ this.encrypted = false;
+ }.bind(this));
+ // Show button is needed from start as you can't turn viewable on via JS
+ var attrs = {
+ class: "show_hide",
+ image: "visibility",
+ onclick: this.toggle_visibility.bind(this),
+ statustext: this.egw().lang("Show password")
+ };
+ if (this.options.viewable) {
+ this.show_button = et2_core_widget_1.et2_createWidget("button", attrs, this);
+ }
+ };
+ et2_password.prototype.getInputNode = function () {
+ return this.input[0];
+ };
+ /**
+ * Override the parent set_id method to manuipulate the input DOM node
+ *
+ * @param {type} _value
+ * @returns {undefined}
+ */
+ et2_password.prototype.set_id = function (_value) {
+ _super.prototype.set_id.call(this, _value);
+ // Remove the name attribute inorder to affect autocomplete="off"
+ // for no password save. ATM seems all browsers ignore autocomplete for
+ // input field inside the form
+ if (this.options.autocomplete === "off")
+ this.input.removeAttr('name');
+ };
+ /**
+ * Set whether or not the password is allowed to be shown in clear text.
+ *
+ * @param viewable
+ */
+ et2_password.prototype.set_viewable = function (viewable) {
+ this.options.viewable = viewable;
+ if (viewable) {
+ jQuery('.show_hide', this.wrapper).show();
+ }
+ else {
+ jQuery('.show_hide', this.wrapper).hide();
+ }
+ };
+ /**
+ * Turn on or off the suggest password button.
+ *
+ * When clicked, a password of the set length will be generated.
+ *
+ * @param length Length of password to generate. 0 to disable.
+ */
+ et2_password.prototype.set_suggest = function (length) {
+ if (typeof length !== "number") {
+ length = typeof length === "string" ? parseInt(length) : (length ? et2_password.DEFAULT_LENGTH : 0);
+ }
+ this.options.suggest = length;
+ if (length && !this.suggest_button) {
+ var attrs = {
+ class: "generate_password",
+ image: "generate_password",
+ onclick: this.suggest_password.bind(this),
+ statustext: this.egw().lang("Suggest password")
+ };
+ this.suggest_button = et2_core_widget_1.et2_createWidget("button", attrs, this);
+ }
+ if (length) {
+ jQuery('.suggest', this.wrapper).show();
+ }
+ else {
+ jQuery('.suggest', this.wrapper).hide();
+ }
+ };
+ /**
+ * If the password is viewable, toggle the visibility.
+ * If the password is still encrypted, we'll ask for the user's password then have the server decrypt it.
+ *
+ * @param on
+ */
+ et2_password.prototype.toggle_visibility = function (on) {
+ if (typeof on !== "boolean") {
+ on = this.input.attr("type") == "password";
+ }
+ if (!this.options.viewable) {
+ this.input.attr("type", "password");
+ return;
+ }
+ if (this.show_button) {
+ this.show_button.set_image(this.egw().image(on ? 'visibility_off' : 'visibility'));
+ }
+ // If we are not encrypted or not showing it, we're done
+ if (!this.encrypted || !on) {
+ this.input.attr("type", on ? "textbox" : "password");
+ return;
+ }
+ // Need username & password to decrypt
+ var callback = function (button, user_password) {
+ if (button == et2_widget_dialog_1.et2_dialog.CANCEL_BUTTON) {
+ return this.toggle_visibility(false);
+ }
+ var request = egw.json("EGroupware\\Api\\Etemplate\\Widget\\Password::ajax_decrypt", [user_password, this.options.value], function (decrypted) {
+ if (decrypted) {
+ this.encrypted = false;
+ this.input.val(decrypted);
+ this.input.attr("type", "textbox");
+ }
+ else {
+ this.set_validation_error(this.egw().lang("invalid password"));
+ window.setTimeout(function () {
+ this.set_validation_error(false);
+ }.bind(this), 2000);
+ }
+ }, this, true, this).sendRequest();
+ }.bind(this);
+ var prompt = et2_widget_dialog_1.et2_dialog.show_prompt(callback, this.egw().lang("Enter your password"), this.egw().lang("Authenticate"));
+ // Make the password prompt a password field
+ prompt.div.on("load", function () {
+ jQuery(prompt.template.widgetContainer.getWidgetById('value').getInputNode())
+ .attr("type", "password");
+ });
+ };
+ /**
+ * Ask the server for a password suggestion
+ */
+ et2_password.prototype.suggest_password = function () {
+ // They need to see the suggestion
+ this.encrypted = false;
+ this.options.viewable = true;
+ this.toggle_visibility(true);
+ var suggestion = "Suggestion";
+ var request = egw.json("EGroupware\\Api\\Etemplate\\Widget\\Password::ajax_suggest", [this.options.suggest], function (suggestion) {
+ this.encrypted = false;
+ this.input.val(suggestion);
+ }, this, true, this).sendRequest();
+ };
+ et2_password.prototype.destroy = function () {
+ _super.prototype.destroy.call(this);
+ };
+ et2_password.prototype.getValue = function () {
+ return this.input.val();
+ };
+ et2_password._attributes = {
+ "autocomplete": {
+ "name": "Autocomplete",
+ "type": "string",
+ "default": "Off",
+ "description": "Whether or not browser should autocomplete that field: 'on', 'off', 'default' (use attribute from form). Default value is set to off."
+ },
+ "viewable": {
+ "name": "Viewable",
+ "type": "boolean",
+ "default": false,
+ "description": "Allow password to be shown"
+ },
+ "suggest": {
+ name: "Suggest password",
+ type: "integer",
+ default: 16,
+ description: "Suggest password length (0 for off)"
+ }
+ };
+ et2_password.DEFAULT_LENGTH = 16;
+ return et2_password;
+}(et2_widget_textbox_1.et2_textbox));
+exports.et2_password = et2_password;
+et2_core_widget_1.et2_register_widget(et2_password, ["passwd"]);
+//# sourceMappingURL=et2_widget_password.js.map
\ No newline at end of file
diff --git a/pixelegg/images/generate_password.svg b/pixelegg/images/generate_password.svg
new file mode 100644
index 0000000000..c9c792b4c5
--- /dev/null
+++ b/pixelegg/images/generate_password.svg
@@ -0,0 +1,103 @@
+
+
+
+
\ No newline at end of file
diff --git a/pixelegg/images/visibility.svg b/pixelegg/images/visibility.svg
new file mode 100644
index 0000000000..daa32c0c50
--- /dev/null
+++ b/pixelegg/images/visibility.svg
@@ -0,0 +1,59 @@
+
+
diff --git a/pixelegg/images/visibility_off.svg b/pixelegg/images/visibility_off.svg
new file mode 100644
index 0000000000..5b631f9039
--- /dev/null
+++ b/pixelegg/images/visibility_off.svg
@@ -0,0 +1,59 @@
+
+