Viewing File: /usr/local/cpanel/base/frontend/jupiter/js2/contact/index.js

/*
# cpanel - base/frontend/jupiter/js2/contact/index.js
#                                                  Copyright 2022 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/

/* global Promise */

(function(window, CPANEL, PAGE, LOCALE) {
    "use strict";

    var USER_IS_WEBMAIL = PAGE.username.includes("@") && CPANEL.application === "webmail";
    var CERT_FAILURE_SETTINGS = ["all", "failWarnDefer", "certFailures"];

    var SIMPLE_BOOLEANS = [
        "notify_account_login",
        "notify_account_login_for_known_netblock",
        "notify_account_login_notification_disabled",
    ];

    if (!USER_IS_WEBMAIL) {
        SIMPLE_BOOLEANS.push(
            "notify_bandwidth_limit",
            "notify_contact_address_change",
            "notify_contact_address_change_notification_disabled",
            "notify_disk_limit",
            "notify_email_quota_limit",
            "notify_password_change",
            "notify_password_change_notification_disabled",
            "notify_account_authn_link",
            "notify_account_authn_link_notification_disabled",
            "notify_twofactorauth_change",
            "notify_twofactorauth_change_notification_disabled",
            "notify_ssl_expiry"
        );
    }

    var ALL_BOOLEANS = USER_IS_WEBMAIL
        ? SIMPLE_BOOLEANS
        : SIMPLE_BOOLEANS.concat( [
            "notify_autossl_renewal",
            "notify_autossl_renewal_coverage",
            "notify_autossl_expiry",
            "notify_autossl_expiry_coverage",
            "notify_autossl_renewal_coverage_reduced",
            "notify_autossl_renewal_uncovered_domains",
        ] );

    var EMAIL_FIELDS = ["email", "second_email"];
    var NON_EMAIL_STRINGS = ["pushbullet_access_token"];

    var contactForm = document.getElementById("mainform");
    if (!contactForm) {
        throw "Document lacks `mainform` element??";
    }

    var savedNotice;

    var isTruthy = Boolean;

    /**
     * Determine whether two arrays contain identical content.
     *
     * @param   {any[]}     a
     * @param   {any[]}     b
     *
     * @result  {boolean}
     */
    function arraysDiffer(a, b) {
        return a.length !== b.length || a.some(
            function(v, i) {
                return v !== b[i];
            }
        );
    }

    /**
     * Read the saved email addresses from the given HTML form.
     *
     * @param   {HTMLFormElement}   theForm
     *
     * @result  {string[]}
     */
    function readSavedEmails(theForm) {
        return EMAIL_FIELDS.map(
            function(name) {
                return theForm[name].defaultValue;
            }
        ).filter(isTruthy);
    }

    /**
     * A class to coordinate dynamic interactions with this form.
     * This implements logic to enable/disable the form’s password
     * field, to set that field’s validation text, etc.
     *
     * @constructor
     *
     * @param   {HTMLFormElement}   theForm
     *
     */
    function EmailState(theForm) {
        this.form = theForm;

        this._setDOMListeners();

        this.refresh();
    }

    Object.assign(
        EmailState.prototype,
        {

            /**
             * (Re-)initialize object state.
             *
             */
            refresh: function() {
                this._savedEmails = readSavedEmails(this.form);
                this._updatePasswordField();
            },

            _initAutofill: function() {
                var autofillBtn = this.form.querySelector("#autofill-btn");
                if (!autofillBtn) {
                    return;
                }
                var autofillEmailAddress = this._autofillEmailAddress.bind(this);
                var updateAutofillVisibility = this._updateAutofillVisibility.bind(this);

                autofillBtn.addEventListener( "click", autofillEmailAddress);
                this.form.email.addEventListener( "input", updateAutofillVisibility );
            },

            _setDOMListeners: function() {
                var theForm = this.form;
                var updatePwField = this._updatePasswordField.bind(this);

                this._initAutofill.bind(this)();

                EMAIL_FIELDS.forEach( function(name) {
                    theForm[name].addEventListener( "input", updatePwField );
                } );

                this.form.password.addEventListener(
                    "input",
                    this._updatePasswordValidityMessage.bind(this)
                );
            },

            _updatePasswordField: function() {
                var theForm = this.form;

                var curEmails = EMAIL_FIELDS.map(
                    function(name) {
                        return theForm[name].value.trim();
                    }
                ).filter(isTruthy);

                var emailChanged = arraysDiffer(curEmails, this._savedEmails);

                var pwEl = this.form.password;

                pwEl.disabled = !emailChanged;
                pwEl.required = emailChanged;

                this._updatePasswordValidityMessage();
            },

            _updatePasswordValidityMessage: function() {
                var el = this.form.password;

                var msg;
                if (el.required && !el.value) {
                    msg = LOCALE.maketext("You must enter your password to update your contact email addresses.");
                }

                el.setCustomValidity(msg || "");
            },

            _autofillEmailAddress: function(event) {
                var el = event.target || event.srcElement;
                var email = this.form.email;

                event.preventDefault();

                email.value = el.getAttribute("data-email");
                this._updatePasswordField.bind(this)();
                this._updateAutofillVisibility.bind(this)();
            },

            _updateAutofillVisibility: function(event) {
                var value = event ? event.target.value : this.form.email.value;
                var autofillBtn = this.form.querySelector("#autofill-btn");
                var defaultAddress = autofillBtn.getAttribute("data-email");

                if (value === defaultAddress) {
                    autofillBtn.style = "display: none;";
                } else {
                    autofillBtn.style = "display: initial;";
                }
            },
        }
    );

    var emailState = !USER_IS_WEBMAIL && new EmailState(contactForm);

    function _readNewEmails(formData) {
        return EMAIL_FIELDS.map( function(name) {
            return formData.get(name).trim();
        }).filter(isTruthy);
    }

    /**
     * If the form’s email addresses have changed **and** the user is a
     * cPanel user (regardless of whether we’re in cPanel or Webmail),
     * this saves that new state. If there’s no change, or if the user is
     * a Webmail user, this does nothing.
     *
     * @param   {HTMLFormElement}   theForm
     * @param   {object}            formData
     *
     * @return  {Promise | null}    The save’s promise, or null if no
     *  save happens.
     */
    function saveEmailsIfNeeded(theForm, formData) {

        var newEmails = _readNewEmails(formData);

        var oldEmails = readSavedEmails(theForm);

        if (arraysDiffer(oldEmails, newEmails)) {
            var hasEmails = !!newEmails.length;
            var uapiFunc = hasEmails ? "set_email_addresses" : "unset_email_addresses";

            return new Promise( function(res, rej) {
                CPANEL.api( {
                    version: 3,
                    module: "ContactInformation",
                    func: uapiFunc,
                    data: {
                        address: newEmails,
                        old_address: oldEmails,
                        password: formData.get("password"),     // no trim
                    },
                    callback: {
                        success: function() {
                            EMAIL_FIELDS.forEach( function(name, idx) {
                                var formEl = theForm[name];
                                formEl.value = newEmails[idx] || "";
                                formEl.defaultValue = newEmails[idx] || "";

                                theForm.password.value = "";

                                if (emailState) {
                                    emailState.refresh();
                                }

                                res();
                            } );
                        },
                        failure: rej,
                    },
                } );
            } );
        }

        return null;
    }

    /**
     * Save the state of contact preferences and Pushbullet.
     * For Webmail users this also includes email addresses.
     *
     * @param   {HTMLFormElement}   theForm
     * @param   {object}            formData
     *
     * @return  {Promise}    The save’s promise.
     */
    function saveContactPreferences(theForm, formData) {
        var contactData = {};

        SIMPLE_BOOLEANS.forEach( function(name) {
            contactData[name] = formData.get(name);
        } );

        var stringFields = USER_IS_WEBMAIL ? EMAIL_FIELDS.concat(NON_EMAIL_STRINGS) : NON_EMAIL_STRINGS;

        stringFields.forEach( function(name) {
            contactData[name] = formData.get(name).trim();
        } );

        var autosslNotifications = formData.get("autosslNotifications");

        if (autosslNotifications !== undefined) {
            var notifyOnCertFailures = CERT_FAILURE_SETTINGS.filter(
                function(n) {
                    return n === autosslNotifications;
                }
            ).length;

            contactData.notify_autossl_renewal = (autosslNotifications === "all");
            contactData.notify_autossl_renewal_coverage = notifyOnCertFailures;
            contactData.notify_autossl_expiry = notifyOnCertFailures;
            contactData.notify_autossl_expiry_coverage = notifyOnCertFailures;
            contactData.notify_autossl_renewal_coverage_reduced = notifyOnCertFailures;
            contactData.notify_autossl_renewal_uncovered_domains = (autosslNotifications === "all") || (autosslNotifications === "failWarnDefer");
        }

        ALL_BOOLEANS.forEach( function(name) {
            contactData[name] = contactData[name] ? 1 : 0;
        } );

        return new Promise( function(res, rej) {
            CPANEL.api( {
                version: 2,
                module: "CustInfo",
                func: "savecontactinfo",
                data: contactData,
                callback: {
                    success: res,
                    failure: rej,
                },
            } );
        } );
    }

    /**
     * Save the passed-in form.
     *
     * @param   {HTMLFormElement}   theForm
     */
    function savemailform(theForm) {
        var fieldset = theForm.querySelector("fieldset");
        if (!fieldset) {
            throw "Form lacks a fieldset??";
        }

        var formData = new FormData(theForm);

        fieldset.disabled = true;

        var noticeProperties;

        var uapiEmailsSaved = false;

        var mainPromise;

        if (USER_IS_WEBMAIL) {
            mainPromise = saveContactPreferences(theForm, formData);
        } else {
            var emailsPromise = saveEmailsIfNeeded(theForm, formData);

            mainPromise = Promise.resolve(emailsPromise).then(
                function() {
                    uapiEmailsSaved = !!emailsPromise;
                    return saveContactPreferences(theForm, formData);
                }
            );
        }

        return mainPromise.then(
            function onSuccess() {
                noticeProperties = {
                    level: "success",
                    content: LOCALE.maketext("Success!"),
                };
            },
            function onFailure(o) {
                var errHtml = o.cpanel_error.html_encode();

                var msgHtml;

                if (uapiEmailsSaved) {
                    var emailsCount = _readNewEmails(formData).length;

                    var partialSuccessPhrase1;

                    if (emailsCount) {
                        partialSuccessPhrase1 = LOCALE.maketext("Your new contact email [quant,_1,address has,addresses have] been saved.", emailsCount);
                    } else {
                        partialSuccessPhrase1 = LOCALE.maketext("You have unset your contact email addresses.");
                    }

                    var partialSuccessPhrases = [
                        partialSuccessPhrase1,
                        LOCALE.maketext("The system failed to save your other contact preferences due to the following error: [_1]", errHtml),
                    ];

                    msgHtml = partialSuccessPhrases.join(" ");
                } else {
                    msgHtml = errHtml;
                }

                noticeProperties = {
                    level: "error",
                    content: msgHtml,
                    fade_delay: 0,
                };
            }
        ).finally(
            function() {
                fieldset.disabled = false;

                noticeProperties.replaces = savedNotice;

                savedNotice = new CPANEL.ajax.Dynamic_Notice(noticeProperties);
            }
        );
    }
    window.savemailform = savemailform;
})(window, CPANEL, PAGE, LOCALE);
Back to Directory File Manager