Viewing File: /usr/local/cpanel/whostmgr/docroot/templates/multiphp_manager/views/poolOptions.js

/*
 * templates/multiphp_manager/views/poolOptions.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 define: false */
/* eslint no-use-before-define: 0*/

define(
    [
        "angular",
        "lodash",
        "cjt/util/parse",
        "cjt/util/locale",
        "uiBootstrap",
        "cjt/directives/alertList",
        "cjt/services/alertService",
        "app/services/configService",
        "cjt/directives/validationContainerDirective",
        "cjt/directives/validationItemDirective",
        "cjt/directives/toggleLabelInfoDirective",
        "cjt/directives/loadingPanel",
        "cjt/directives/actionButtonDirective",
        "cjt/validator/datatype-validators",
        "cjt/validator/compare-validators",
        "cjt/validator/path-validators",
    ],
    function(angular, _, PARSE, LOCALE) {
        "use strict";

        var app = angular.module("App");

        var controller = app.controller(
            "poolOptionsController",
            ["$q", "$scope", "$anchorScroll", "$rootScope", "alertService", "configService",
                function($q, $scope, $anchorScroll, $rootScope, alertService, configService) {

                    $scope.displayValue = {
                        selectedDomain: "",
                        docRootDisplayValue: "",
                        logDirDisplayValue: "",
                        reportedErrs: "",
                        disabledFuncs: "",
                        displayMode: null,
                        disabledFuncsPanelOpen: false,
                        errsReportedPanelOpen: false,
                        saveReminderDisplayed: false,
                        saveReminderMessage: LOCALE.maketext("Click [output,em,Save Configuration] to save your changes."),
                    };

                    $scope.poolOptions = {};
                    $scope.poolOptionsCache = {};

                    $scope.additionalResources = [
                        {
                            text: "cPanel Documentation",
                            link: "https://docs.cpanel.net/",
                        },
                        {
                            text: LOCALE.maketext("Official [asis,PHP] Configuration Documentation"),
                            link: "https://secure.php.net/manual/en/install.fpm.configuration.php",
                        },
                        {
                            text: "Bottleneck with Child Processes",
                            link: "https://go.cpanel.net/ApachevsPHP-FPMBottleneckwithChildProcesses",
                        },
                    ];

                    /**
                 * Return default button classes to work with cp-action directive
                 *
                 * @scope
                 * @method getDefaultButtonClasses
                 */
                    $scope.getDefaultButtonClasses = function() {
                        return "btn btn-default";
                    };

                    /**
                 * Return small default button classes to work with cp-action directive
                 * @method getSmallDefaultButtonClasses
                 */
                    $scope.getSmallDefaultButtonClasses = function() {
                        return "btn btn-sm btn-default";
                    };

                    /**
                 * Return default button classes to work with cp-action directive
                 *
                 * @scope
                 * @method getButtonClasses
                 * @return {String}         Default button classes
                 */
                    $scope.getButtonClasses = function() {
                        return "btn btn-default";
                    };

                    /**
                 * Return primary button classes to work with cp-action directive
                 *
                 * @scope
                 * @method getPrimaryButtonsClasses
                 * @return {String}           Promary button classes
                 */
                    $scope.getPrimaryButtonsClasses = function() {
                        return "btn btn-sm btn-primary";
                    };

                    /**
                 * Add functions to the disable_functions value list
                 *
                 * @scope
                 * @method addFunctionsToDisable
                 * @param  {Array.<String>}      funcs   array of functions to validate for disabling
                 * @param  {Object}              formVal object representing php-fpm form
                 * @return {Promise.<Array.<String>> | } if the promise exists it returns an array of strings of validated funcs, if the promise does not exist, the function returns nothing
                 */
                    $scope.addFunctionsToDisable = function(funcs, formVal) {
                        var funcsPromises = formatAndValidateFunctions(funcs, formVal);
                        if (!funcsPromises) {
                            return;
                        }
                        $scope.actions.validatingFuncs = true;
                        return funcsPromises.then(function(validatedFuncs) {
                            alertService.add({
                                type: "success",
                                autoClose: 5000,
                                message: LOCALE.maketext("You successfully added the “[_1]” function to the list. Click [output,em,Save Configuration] to save your changes.", _.escape(validatedFuncs)),
                            });
                            $scope.actions.validatingFuncs = false;
                        });
                    };

                    /**
                 * Remove functions from disable_functions value list
                 *
                 * @scope
                 * @method removeDisabledFunction
                 * @param  {String}               func    function to remove from disable_functions value list
                 * @param  {Object}               formVal object representing php-fpm form
                 */
                    $scope.removeDisabledFunction = function(func, formVal) {
                        var commands = $scope.poolOptions.disable_functions.value;
                        for (var i = 0, len = $scope.poolOptions.disable_functions.value.length; i < len; i++) {
                            if (func === commands[i]) {
                                $scope.poolOptions.disable_functions.value.splice(i, 1);
                                formVal.$setDirty();
                                return;
                            }
                        }
                    };

                    /**
                 * Add errors to error_reporting value list
                 *
                 * @scope
                 * @method addErrsToReport
                 * @param  {Array.<String>}           errs    array of errors to validate before adding to error_reporting value list
                 * @param  {Object}                   formVal object representing php-fpm form
                 * @return {Promise.<Array.<String>>}         if the promise exists it returns and array of strings of validated errors, if it does not exist the function return nothing
                 */
                    $scope.addErrsToReport = function(errs, formVal) {
                        var errsPromises = formatAndValidateErrs(errs, formVal);
                        if (!errsPromises) {
                            return;
                        }
                        $scope.actions.validatingErrs = true;
                        return errsPromises.then(function(validatedErrs) {
                            alertService.add({
                                type: "success",
                                autoClose: 5000,
                                message: LOCALE.maketext("You successfully added the “[_1]” error to the list. Click [output,em,Save Configuration] to save your changes.", _.escape(validatedErrs)),
                            });
                            $scope.actions.validatingErrs = false;
                        });
                    };

                    /**
                 * Remove errors from error_reporting value list
                 *
                 * @scope
                 * @method removeReportedErrs
                 * @param  {String}           err     error to remove from error_reporting value list
                 * @param  {Object}           formVal object representing php-fpm form
                 */
                    $scope.removeReportedErrs = function(err, formVal) {
                        var errs = $scope.poolOptions.error_reporting.value;
                        for (var i = 0, len = $scope.poolOptions.error_reporting.value.length; i < len; i++) {
                            if (err === errs[i]) {
                                $scope.poolOptions.error_reporting.value.splice(i, 1);
                                formVal.$setDirty();
                                return;
                            }
                        }
                    };

                    /**
                 * Emit event to return to PHP Version domain list view
                 *
                 * @scope
                 * @method returnToDomainsList
                 */
                    $scope.returnToDomainsList = function() {
                        $rootScope.$emit("returnToDomainList");
                    };

                    /**
                 * Toggle betwee php_value and php_admin_value for given options
                 *
                 * @scope
                 * @method toggleOverrideVal
                 * @param  {String}          overrideVal which pool option is being toggled
                 * @param  {Object}          formVal     object representing php-fpm form
                 * @throws {String}                      error informing developer of invalid value
                 */
                    $scope.toggleOverrideVal = function(overrideVal, formVal) {
                        formVal.$setDirty();

                        if (!$scope.displayValue.saveReminderDisplayed) {
                            alertService.add({
                                type: "info",
                                closeable: true,
                                autoClose: 5000,
                                message: $scope.displayValue.saveReminderMessage,
                            });
                            $scope.displayValue.saveReminderDisplayed = true;
                        }

                        switch (overrideVal) {
                            case "allow_url_fopen":
                                $scope.poolOptions.allow_url_fopen.admin = !$scope.poolOptions.allow_url_fopen.admin;
                                break;
                            case "log_errors":
                                $scope.poolOptions.log_errors.admin = !$scope.poolOptions.log_errors.admin;
                                break;
                            case "short_open_tag":
                                $scope.poolOptions.short_open_tag.admin = !$scope.poolOptions.short_open_tag.admin;
                                break;
                            case "doc_root":
                                $scope.poolOptions.doc_root.admin = !$scope.poolOptions.doc_root.admin;
                                break;
                            case "error_log":
                                $scope.poolOptions.error_log.admin = !$scope.poolOptions.error_log.admin;
                                break;
                            case "disable_functions":
                                $scope.poolOptions.disable_functions.admin = !$scope.poolOptions.disable_functions.admin;
                                break;
                            case "error_reporting":
                                $scope.poolOptions.error_reporting.admin = !$scope.poolOptions.error_reporting.admin;
                                break;
                            default:
                                throw new Error("DEVELOPER ERROR: invalid override value given");
                        }
                    };

                    /**
                 * Save new pool options
                 *
                 * @scope
                 * @method savePoolOptions
                 * @param  {Object}        formVal object representing php-fpm form
                 */
                    $scope.savePoolOptions = function(formVal) {
                        return submitPoolOptions($scope.poolOptions, false, $scope.displayValue.selectedDomain, formVal);
                    };

                    /**
                 * Validate new pool options
                 *
                 * @scope
                 * @method validatePoolOptions
                 */
                    $scope.validatePoolOptions = function() {
                        return submitPoolOptions($scope.poolOptions, true, $scope.displayValue.selectedDomain);
                    };

                    /**
                 * Set the form to pristine
                 *
                 * @scope
                 * @method deactivateSaveActions
                 * @param  {Object}              formVal object representing php-fpm form
                 */
                    $scope.deactivateSaveActions = function(formVal) {
                        formVal.$setPristine();
                    };

                    /**
                 * Reset form to initial state
                 *
                 * @scope
                 * @method resetPoolOptionsForm
                 * @param  {Object}             formVal object representing php-fpm form
                 */
                    $scope.resetPoolOptionsForm = function(formVal) {
                        $scope.poolOptions = $scope.poolOptionsCache;
                        $scope.poolOptionsCache = angular.copy($scope.poolOptions);
                        formVal.$setPristine();
                    };

                    /**
                 * Check for duplicate entries in entered list, and existing list. Format each function for validation submission
                 *
                 * @method formatAndValidateFunctions
                 * @param  {Array.<String>}           funcs   functions to format and validate
                 * @param  {Object}                   formVal object representing php-fpm form
                 * @return {Array.<Promise>}                  if functions is a duplicate, return nothing, if it isn't return array of promises from validated functions
                 */
                    function formatAndValidateFunctions(funcs, formVal) {
                        funcs = funcs.split(",");
                        funcs = formatListVals(funcs);
                        var optionForValidation;
                        var validationPromises = [];
                        var validationPromise;

                        for (var i = 0, len = funcs.length; i < len; i++) {

                            // checks for duplicates within the array entered and returns out of if()
                            // unless it is the last index of that function
                            if (funcs.indexOf(funcs[i]) !== -1 && funcs.indexOf(funcs[i], i + 1) !== -1) {
                                return;
                            }

                            // checks for duplicates in already existing function list
                            if ($scope.poolOptions.disable_functions.value.indexOf(funcs[i]) !== -1) {
                                var duplicateMessage = LOCALE.maketext("The “[_1]” function already appears on the disabled functions list.", _.escape(funcs[i]));
                                alertService.add({
                                    type: "warning",
                                    autoClose: 5000,
                                    closeable: true,
                                    message: duplicateMessage,
                                });
                                if (len === 1) {
                                    return;
                                } else {
                                    continue;
                                }
                            }

                            optionForValidation = {
                                disable_functions: {
                                    value: [],
                                    admin: $scope.poolOptions.disable_functions.admin,
                                },
                            };
                            optionForValidation.disable_functions.value.push(funcs[i]);
                            validationPromise = validateInlineOption(optionForValidation, true, $scope.displayValue.selectedDomain, formVal);
                            validationPromises.push(validationPromise);
                        }
                        return $q.all(validationPromises);
                    }

                    /**
                 * Check for duplicate entries in entered error list, and existing error list. Format each error for validation submission
                 *
                 * @method formatAndValidateErrs
                 * @param  {Array.<String>}        errs    errors to format and validate
                 * @param  {Object}                formVal object representing php-fpm form
                 * @return {Array.<Promise>}                      if error is a duplicate, return nothing, if it isn't return array of promises from validated errors
                 */
                    function formatAndValidateErrs(errs, formVal) {
                        errs = errs.split(",");
                        errs = formatListVals(errs);
                        var optionForValidation;
                        var validationPromises = [];
                        var validationPromise;

                        for (var i = 0, len = errs.length; i < len; i++) {

                            // checks for duplicates within the array entered and returns out of if()
                            // unless it is the last index of that function
                            if (errs.indexOf(errs[i]) !== -1 && errs.indexOf(errs[i], i + 1) !== -1) {
                                return;
                            }

                            // checks for duplicates in already existing function list
                            if ($scope.poolOptions.error_reporting.value.indexOf(errs[i]) !== -1) {
                                var duplicateMessage = LOCALE.maketext("The “[_1]” error already appears on the errors list.", _.escape(errs[i]));
                                alertService.add({
                                    type: "warning",
                                    autoClose: 5000,
                                    closeable: true,
                                    message: duplicateMessage,
                                });
                                if (len === 1) {
                                    return;
                                } else {
                                    continue;
                                }
                            }

                            optionForValidation = {
                                error_reporting: {
                                    value: [],
                                    admin: $scope.poolOptions.error_reporting.admin,
                                },
                            };
                            optionForValidation.error_reporting.value.push(errs[i]);
                            validationPromise = validateInlineOption(optionForValidation, true, $scope.displayValue.selectedDomain, formVal);
                            validationPromises.push(validationPromise);
                        }
                        return $q.all(validationPromises);
                    }

                    /**
                 * Remove leading and trailing spaces from each value
                 *
                 * @method formatListVals
                 * @param  {Array.<String>}       vals array of function or error values to format
                 * @return {Array.<String>}            array of parsed error or function values
                 */
                    function formatListVals(vals) {
                        var parsedVals = [];

                        function removeSpaceChars(val) {
                            var endIndex = val.length - 1;
                            if (val.indexOf(" ") !== 0 && val.indexOf(" ") !== endIndex) {
                                return val;

                            }
                            if (val.indexOf(" ") === 0) {
                                val = val.slice(1);
                            }
                            if (val.indexOf(" ") === endIndex) {
                                val = val.slice(0, endIndex);
                            }

                            return removeSpaceChars(val);
                        }

                        vals.forEach(function(val) {
                            val = removeSpaceChars(val);
                            parsedVals.push(val);
                        });
                        return parsedVals;
                    }

                    /**
                 * Validate options entered into disable_functions or error_reporting
                 *
                 * @method validateInlineOption
                 * @param  {Object}             poolOption name and value of option to validate
                 * @param  {Boolean}            validate   boolean always set to true so that option is validated, not saved
                 * @param  {String}             [domain]   if it exists then it is validating options for that domain, if it does not the options are validated for system config
                 * @param  {[type]}             formVal    object representing php-fpm form
                 * @return {String | Promise}              on success return value of option validated, on failure return error
                 */
                    function validateInlineOption(poolOption, validate, domain, formVal) {
                        return configService.submitPoolOptions(poolOption, validate, domain)
                            .then(function(data) {

                                if (poolOption.disable_functions) {
                                    $scope.poolOptions.disable_functions.value.push(poolOption.disable_functions.value[0]);
                                    $scope.displayValue.disabledFuncsPanelOpen = true;
                                    $scope.displayValue.disabledFuncs = "";
                                    formVal.$setDirty();
                                    return poolOption.disable_functions.value[0];
                                } else if (poolOption.error_reporting) {
                                    $scope.displayValue.reportedErrs = "";
                                    $scope.poolOptions.error_reporting.value.push(poolOption.error_reporting.value[0]);
                                    $scope.displayValue.errsReportedPanelOpen = true;
                                    formVal.$setDirty();
                                    return poolOption.error_reporting.value[0];
                                }
                            })
                            .catch(function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                });
                            });
                    }

                    /**
                 * Save or validate PHP-FPM form
                 *
                 * @method submitPoolOptions
                 * @param  {Object}          poolOptions names and value of pool options to submit
                 * @param  {Boolean}         validate    if true options are validated, if false options are saved
                 * @param  {String}          [domain]    if it exists options are saved/validated for individual domain, if it does not options are saved/validated for the system config
                 * @param  {Object}          formVal     object representing php-fpm form
                 * @return {Promise}
                 */
                    function submitPoolOptions(poolOptions, validate, domain, formVal) {
                        return configService.submitPoolOptions(poolOptions, validate, domain)
                            .then(function(data) {
                                var successMessage;
                                if (validate) {
                                    successMessage = LOCALE.maketext("The system successfully validated the [asis,PHP-FPM] configuration.");
                                } else {
                                    formVal.$setPristine();
                                    $scope.poolOptionsCache = angular.copy(poolOptions);
                                    successMessage = LOCALE.maketext("The system successfully saved the [asis,PHP-FPM] configuration.");
                                }
                                alertService.add({
                                    type: "success",
                                    message: successMessage,
                                    autoClose: 5000,
                                });
                            })
                            .catch(function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                });
                            });
                    }

                    /**
                 * Get document root from global value
                 *
                 * @method getDocRootValue
                 * @return {String}        document root
                 */
                    function getDocRootValue() {
                        return PAGE.selectedDomainDocRoot;
                    }

                    /**
                 * Get domain home directory from global value
                 *
                 * @method getHomeDirectory
                 * @return {String}         domain home directory
                 */
                    function getHomeDirectory() {
                        return PAGE.selectedDomainHomeDir;
                    }

                    /**
                 * Get selected domain name from global values
                 *
                 * @method getSelectedDomainName
                 * @return {String}              selected domain value
                 */
                    function getSelectedDomainName() {
                        return PAGE.selectedDomainName;
                    }

                    /**
                 * Get display mode from global values
                 *
                 * @method getDisplayMode
                 * @return {String}       display mode
                 */
                    function getDisplayMode() {
                        return PAGE.poolOptionsDisplayMode;
                    }

                    /**
                 * Parse location of error log for use by front end
                 *
                 * @method parseErrorLog
                 * @param  {String}      data raw error log location
                 * @return {String}           parsed error log location
                 */
                    function parseErrorLog(data) {

                        function replacer() {
                            return "_";
                        }

                        function removeLeadingChars(log) {
                            if (log.indexOf(".") === 0) {
                                return log;

                            }
                            log = log.slice(1);
                            return removeLeadingChars(log);
                        }

                        var scrubbedDomainSplitter = "[% scrubbed_domain %]";
                        var scrubbedDomain = $scope.displayValue.selectedDomain.replace(/\./, replacer);
                        var parsedErrorLog;
                        if (data.error_log.value.indexOf(scrubbedDomainSplitter) !== -1) {
                            parsedErrorLog = removeLeadingChars(data.error_log.value.split("scrubbed_domain")[1]);
                            parsedErrorLog = scrubbedDomain + parsedErrorLog;
                            data.error_log.value = parsedErrorLog;
                        }
                        return data;
                    }

                    /**
                 * Get existing pool options
                 *
                 * @method getPoolOptions
                 * @param  {String}       [domain] if it exists fetch pool options for individual domain, if it doesn't fetch system pool options
                 */
                    function getPoolOptions(domain) {
                        return configService.getPHPFPMSettings(domain)
                            .then(function(data) {
                                if (domain) {
                                    data = parseErrorLog(data);
                                }
                                $scope.poolOptions = data;
                                $scope.poolOptionsCache = angular.copy($scope.poolOptions);
                            })
                            .catch(function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                });
                            })
                            .finally(function() {
                                scrollToFormTop();
                                $scope.actions.initialLoading = false;
                            });

                    }

                    /**
                 * Scroll to top of form
                 *
                 * @method scrollToFormTop
                 */
                    function scrollToFormTop() {
                        $anchorScroll.yOffset = -100;
                        $anchorScroll("content");
                    }

                    /**
                 * Initialize app
                 *
                 * @method init
                 */
                    function init() {

                        $scope.actions = {
                            initialLoading: true,
                            validatingFuncs: false,
                            validatingErrs: false,
                        };

                        $scope.displayValue.displayMode = getDisplayMode() || "default";
                        if ($scope.displayValue.displayMode === "domain") {
                            $scope.displayValue.docRootDisplayValue = getDocRootValue() + "/";
                            $scope.displayValue.logDirDisplayValue = getHomeDirectory() + "/logs/";
                            $scope.displayValue.selectedDomain = getSelectedDomainName();
                            getPoolOptions($scope.displayValue.selectedDomain);
                        } else {
                            getPoolOptions();
                        }

                    }
                    init();
                }]
        );
        return controller;
    }
);
Back to Directory File Manager