Viewing File: /usr/local/cpanel/whostmgr/docroot/templates/feature/services/featureListService.js

/*
# feature/services/featureListService.js          Copyright(c) 2020 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, PAGE */

define(
    [

        // Libraries
        "angular",
        "lodash",

        // CJT
        "cjt/io/api",
        "cjt/io/whm-v1-request",
        "cjt/io/whm-v1",
        "cjt/services/APIService"
    ],
    function(angular, _, API, APIREQUEST, APIDRIVER) {

        // Constants
        var NO_MODULE = "";

        // Fetch the current application
        var app;

        try {
            app = angular.module("App"); // For runtime
        } catch (e) {
            app = angular.module("App", ["cjt2.services.api"]); // Fall-back for unit testing
        }

        /**
         * Setup the feature list models API service
         */
        app.factory("featureListService", ["$q", "APIService", "PAGE", function($q, APIService, PAGE) {

            /**
             * Converts the response to our application data structure
             *
             * @method convertResponseToList
             * @private
             * @param  {Object} response
             * @return {Object} Sanitized data structure.
             */
            function convertResponseToList(response) {
                var items = [];
                if (response.status) {
                    var data = response.data;
                    for (var i = 0, length = data.length; i < length; i++) {
                        items.push(data[i]);
                    }

                    var meta = response.meta;

                    var totalItems = meta.paginate.total_records || data.length;
                    var totalPages = meta.paginate.total_pages || 1;

                    return {
                        items: items,
                        totalItems: totalItems,
                        totalPages: totalPages,
                        status: response.status
                    };
                } else {
                    return {
                        items: [],
                        totalItems: 0,
                        totalPages: 0,
                        status: response.status
                    };
                }
            }

            /**
             * Helper method to retrieve feature lists in chained actions
             *
             * @method _fetchLists
             * @private
             * @param  {Deferred} deferred
             * @return {Promise}
             */
            function _fetchLists(deferred) {
                var apiCall = new APIREQUEST.Class();
                apiCall.initialize(NO_MODULE, "get_featurelists");
                apiCall.addSorting("", "asc", "lexicographic_caseless");

                this.deferred(apiCall, {
                    transformAPISuccess: convertResponseToList
                }, deferred);

                // pass the promise back to the controller
                return deferred.promise;
            }

            /**
             * Helper method to save addon feature lists in chained actions
             *
             * @method _saveAddons
             * @private
             * @param  {Deferred} deferred
             * @param  {String} name The name of the base featurelist calling this chained action
             * @param  {Array} list The Array of addons features to be saved
             * @return {Promise}
             */
            function _saveAddons(deferred, name, list) {
                var apiCall = new APIREQUEST.Class();
                apiCall.initialize(NO_MODULE, "update_featurelist");
                apiCall.addArgument("featurelist", name + ".cpaddons");

                _.each(list, function(feature) {
                    apiCall.addArgument(feature.name, feature.value);
                });

                this.deferred(apiCall, {}, deferred);

                // pass the promise back to the controller
                return deferred.promise;
            }

            // Set up the service's constructor and parent
            var FeatureListService = function() {};
            FeatureListService.prototype = new APIService();

            // Extend the prototype with any class-specific functionality
            angular.extend(FeatureListService.prototype, {

                /**
                 * Get a list of feature lists
                 *
                 * @method loadFeatureLists
                 * @return {Promise} Promise that will fulfill the request.
                 * @throws Error
                 */
                loadFeatureLists: function() {
                    var deferred = $q.defer();

                    // pass the promise back to the controller
                    return _fetchLists.call(this, deferred);
                },

                /**
                 * Get a single feature list by its name from the backend and merges
                 * it with the list of descriptions
                 *
                 * @method load
                 * @param {String} name The name of a feature list to fetch.
                 * @param {Array} dictionary Array of human readable labels for feature names.
                 * @return {Promise} Promise that will fulfill the request.
                 */
                load: function(name, dictionary) {
                    var apiCall = new APIREQUEST.Class();
                    apiCall.initialize(NO_MODULE, "get_featurelist_data");
                    apiCall.addArgument("featurelist", name);

                    var deferred = this.deferred(apiCall, {
                        apiSuccess: function(response, deferred) {
                            response.items = [];

                            // legacy features only supported by x and x2 interfaces
                            var legacyNames = ["bbs", "chat", "cpanelpro_support", "searchsubmit", "advguest", "guest", "cgi", "scgiwrap", "counter", "entropybanner", "entropysearch", "clock", "countdown", "randhtml", "videotut", "getstart"],
                                featurePluginFlag = false,
                                featureAddonFlag = false,
                                legacyFeature,
                                featureLabel,
                                featureID,
                                featureState;

                            _.each(response.data.features, function(feature) {

                                legacyFeature = false;
                                if ( _.includes(legacyNames, feature.id) ) {
                                    if ( PAGE.legacySupport ) {
                                        legacyFeature = true;
                                    } else {

                                        // exclude legacy feature
                                        return;
                                    }
                                }

                                if ( feature.id === "fantastico" && !PAGE.fantasticoSupport ) {

                                    // exclude fantastico feature
                                    return;
                                }

                                // check the dictionary for additional meta data about the feature
                                featureID = feature.id;
                                featureLabel = feature.id;
                                if ( feature.id in dictionary ) {
                                    featureLabel = dictionary[feature.id].name;
                                    featurePluginFlag = dictionary[feature.id].is_plugin === "1" ? true : false;
                                    featureAddonFlag = dictionary[feature.id].is_cpaddon === "1" ? true : false;
                                }

                                // handle api oddities for disabled list
                                featureState = false;
                                if ( name === "disabled" ) {
                                    if ( feature.value === "0" ) {
                                        featureState = true;
                                    }
                                } else {
                                    featureState = feature.value === "1" ? true : false;
                                }

                                response.items.push({
                                    name: featureID,
                                    label: featureLabel,
                                    value: featureState,
                                    legacy: legacyFeature,
                                    disabled: feature.is_disabled === "1" ? true : false,
                                    plugin: featurePluginFlag,
                                    addon: featureAddonFlag
                                });
                            }, response.data.features);

                            // sort features by the readable labels
                            response.items = _.sortBy(response.items, function(feature) {
                                return feature.label.toLowerCase();
                            });

                            deferred.resolve(response);
                        }
                    });

                    // pass the promise back to the controller
                    return deferred.promise;
                },

                /**
                 * Saves the states of a list of a features
                 *
                 * @method save
                 * @param {String} name The name of a feature list to save.
                 * @param {Array} list The array of list objects to save.

                 * @return {Promise} Promise that will fulfill the request.
                 */
                save: function(name, list) {
                    var apiCall = new APIREQUEST.Class();
                    apiCall.initialize(NO_MODULE, "update_featurelist");
                    apiCall.addArgument("featurelist", name);

                    var addons = [], featureList = angular.copy(list);
                    _.each(featureList, function(feature) {

                        // conditionally flip the logic from the checkboxes
                        if ( name === "disabled" ) {
                            feature.value = feature.value === true ? "0" : "1";
                        } else {
                            feature.value = feature.value === true ? "1" : "0";
                        }

                        if ( feature.addon ) {
                            addons.push(feature);
                        } else {
                            apiCall.addArgument(feature.name, feature.value);
                        }
                    });

                    var deferred = this.deferred(apiCall, {
                        context: this,
                        apiSuccess: function() {
                            _saveAddons.call(this, deferred, name, addons);
                        }
                    }, deferred);

                    // pass the promise back to the controller
                    return deferred.promise;
                },

                /**
                 * Add a feature list
                 *
                 * @method add
                 * @param {String} name The name of the feature list to be created
                 * @return {Promise} Promise that will fulfill the request.
                 */
                add: function(name) {
                    var apiCall = new APIREQUEST.Class();
                    apiCall.initialize(NO_MODULE, "create_featurelist");
                    apiCall.addArgument("featurelist", name);

                    var deferred = this.deferred(apiCall, {
                        context: this,
                        apiSuccess: function(response) {
                            deferred.resolve(response);
                        }
                    }, deferred);

                    // pass the promise back to the controller
                    return deferred.promise;
                },

                /**
                 * Delete a feature list by its name
                 *
                 * @method remove
                 * @param  {String} name The name of the feature list to delete.
                 * @return {Promise} Promise that will fulfill the request.
                 */
                remove: function(name) {
                    var apiCall = new APIREQUEST.Class();
                    apiCall.initialize(NO_MODULE, "delete_featurelist");
                    apiCall.addArgument("featurelist", name);

                    var deferred = this.deferred(apiCall, {
                        context: this,
                        apiSuccess: function() {
                            deferred.notify();
                            _fetchLists.call(this, deferred);
                        }
                    }, deferred);

                    // pass the promise back to the controller
                    return deferred.promise;
                },

                /**
                *  Helper method that calls convertResponseToList to prepare the data structure
                *
                * @method  prepareList
                * @param  {Object} response
                * @return {Object} Sanitized data structure.
                */
                prepareList: function(response) {

                    // Since this is coming from the backend, but not through the api.js layer,
                    // we need to parse it to the frontend format.
                    response = APIDRIVER.parse_response(response).parsedResponse;
                    return convertResponseToList(response);
                }
            });

            return new FeatureListService();
        }]);
    }
);
Back to Directory File Manager