Viewing File: /usr/local/cpanel/base/frontend/jupiter/domains/directives/itemLister.js

/*
# domains/directives/itemLister.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: false */

/** @namespace cpanel.domains.directive.itemLister */

define(
    [
        "angular",
        "cjt/util/locale",
        "cjt/core",
        "ngRoute",
        "ngSanitize",
        "cjt/modules",
        "app/directives/tableShowingDirective",
        "cjt/services/cpanel/componentSettingSaverService",
        "app/services/domains",
        "cjt/directives/toggleSortDirective",
        "cjt/directives/searchDirective",
        "cjt/directives/pageSizeDirective",
        "cjt/filters/startFromFilter",
        "cjt/decorators/paginationDecorator"
    ],
    function(angular, LOCALE, CJT) {

        "use strict";

        var module = angular.module("cpanel.domains.itemLister.directive", [
            "cpanel.domains.tableShowing.directive",
            "ngRoute",
            "ngSanitize",
            "cjt2.filters.startFrom"
        ]);

        module.value("PAGE", PAGE);

        module.directive("itemLister", ["$window", "$log", "componentSettingSaverService", "PAGE", function itemLister($window, $log, $CSSS, PAGE) {

            /**
             * Item Lister combines the typical table functions, pageSize,
             * showing, paginator, search, and allows you to plug in multiple
             * views.
             *
             * @module item-lister
             * @restrict EA
             *
             * @param  {String} id disseminated to other objects
             * @param  {Array} items Items that will be paginated, array of objs
             * @param  {Array} configuration-options Items that will be in the configuration cog
             * @param  {Array} header-items represents the columns of the table
             *
             * @example
             * <item-lister
             *      id="MyItemLister"
             *      items="[a,b,c,d,e]"
             *      configuration-options="configurationOptions"
             *      header-items="[{field:"blah",label:"Blah",sortable:false}]">
             *   <my-item-lister-view></my-item-lister-view>
             * </item-lister>
             *
             */

            var COMPONENT_NAME = "domainsItemLister";
            var TEMPLATE_PATH = "directives/itemLister.ptt";
            var RELATIVE_PATH = "domains/" + TEMPLATE_PATH;

            return {
                templateUrl: CJT.config.debug ? CJT.buildFullPath(RELATIVE_PATH) : TEMPLATE_PATH,
                restrict: "EA",
                scope: {
                    parentID: "@id",
                    items: "=",
                    headerItems: "=",
                    configurationOptions: "="
                },
                transclude: true,
                replace: true,
                link: function(scope, element, attrs, transclude) {

                    var controlsBlock;
                    var contentBlock;

                    function _attachControls(elem) {
                        controlsBlock.append(elem);
                    }

                    function _attachOthers(elem) {
                        elem.setAttribute("id", scope.parentID + "_transcludePoint");
                        elem.setAttribute("ng-if", "filteredItems.length");
                        contentBlock.replaceWith(elem);
                    }

                    function _attachTransclude(elem) {
                        if (angular.element(elem).hasClass("lister-controls")) {
                            _attachControls(elem);
                        } else {
                            _attachOthers(elem);
                        }
                    }

                    function _findTranscludes() {

                        // *cackles maniacally*
                        // *does a multi-transclude anyways*
                        controlsBlock = element.find("#" + scope.parentID + "_transcludedControls");
                        contentBlock = element.find("#" + scope.parentID + "_transcludePoint");
                        var transcludedBlock = element.find("div.transcluded");
                        var transcludedItems = transcludedBlock.children();
                        angular.forEach(transcludedItems, _attachTransclude);
                        transcludedBlock.remove();
                    }

                    /* There is a dumb race condition here */
                    /* So we have to delay to get the content transcluded */
                    setTimeout(_findTranscludes, 2);
                },
                controller: ["$routeParams", "$scope", "$filter", "ITEM_LISTER_CONSTANTS", "domains", "alertService", function itemListerController($routeParams, $scope, $filter, ITEM_LISTER_CONSTANTS, $domainServices, $alertService) {

                    $scope.redirectSelection = 1;
                    $scope.viewCallbacks = [];
                    $scope.webserverRoleAvailable = PAGE.hasWebServerRole;

                    var filters = {
                        filter: $filter("filter"),
                        orderBy: $filter("orderBy"),
                        startFrom: $filter("startFrom"),
                        limitTo: $filter("limitTo")
                    };

                    function _filter(filteredItems) {

                        // filter list based on search text
                        if ($scope.filterValue !== "") {
                            return filters.filter(filteredItems, $scope.filterValue, false);
                        }

                        return filteredItems;
                    }

                    function _sort(filteredItems) {

                        // sort the filtered list
                        if ($scope.sort.sortDirection !== "" && $scope.sort.sortBy !== "") {

                            // Normally we would sort by the 'domains' field by virtue it was first.  It's not anymore, so correct that.
                            if ($scope.sort.sortBy === "batchSelect") {
                                $scope.sort.sortBy = "domain";
                            }
                            return filters.orderBy(filteredItems, $scope.sort.sortBy, $scope.sort.sortDirection !== "asc");
                        }

                        return filteredItems;
                    }

                    function _paginate(filteredItems) {

                        // filter list based on page size and pagination
                        if ($scope.totalItems > _.min($scope.pageSizes)) {
                            var start = ($scope.currentPage - 1) * $scope.pageSize;
                            var limit = $scope.pageSize;

                            filteredItems = filters.startFrom(filteredItems, start);
                            filteredItems = filters.limitTo(filteredItems, limit);
                            $scope.showPager = true;

                            // table statistics
                            $scope.start = start + 1;
                            $scope.limit = start + filteredItems.length;

                        } else {

                            // hide pager and pagination
                            $scope.showPager = false;

                            if (filteredItems.length === 0) {
                                $scope.start = 0;
                            } else {

                                // table statistics
                                $scope.start = 1;
                            }

                            $scope.limit = filteredItems.length;
                        }

                        return filteredItems;
                    }

                    function _updatedListerState(lastInteractedItem) {

                        if ($scope.loadingInitialState) {
                            return;
                        }

                        var storedSettings = {
                            totalItems: $scope.totalItems,
                            currentPage: $scope.currentPage,
                            pageSize: $scope.pageSize,
                            start: $scope.start,
                            limit: $scope.limit,
                            lastInteractedItem: lastInteractedItem,
                            filterValue: $scope.filterValue,
                            sort: {
                                sortDirection: $scope.sort.sortDirection,
                                sortBy: $scope.sort.sortBy
                            }
                        };

                        $CSSS.set(COMPONENT_NAME, storedSettings);
                    }

                    function _itemInteracted(event, parameters) {
                        if (parameters.interactionID) {
                            _updatedListerState(parameters.interactionID);
                        }
                    }

                    function _itemSelected(event, parameters) {

                        // Go over all domains to see if all are selected
                        var allSelected = true,
                            currentDomainList = $domainServices.getCurrentDomains();
                        for (var i = 0; i < currentDomainList.length; i++) {
                            if (!currentDomainList[i].selected) {
                                allSelected = false;
                                break;
                            }
                        }

                        // Update Select All checkbox
                        $scope.selectAll = allSelected;
                    }

                    /**
                     * Register a callback to call on the update of the lister
                     *
                     * @method registerViewCallback
                     *
                     * @param  {Function} callback function to callback to
                     *
                     */

                    this.registerViewCallback = function registerViewCallback(callback) {
                        $scope.viewCallbacks.push(callback);
                        callback($scope.filteredItems);
                    };

                    /**
                     * Get the header items
                     *
                     * @method getHeaderItems
                     *
                     * @return {Array} returns array of objects containing labels
                     *
                     */
                    this.getHeaderItems = function getHeaderItems() {
                        return $scope.headerItems;
                    };

                    /**
                     * Deregister a callback (useful for view changes)
                     *
                     * @method deregisterViewCallback
                     *
                     * @param  {Function} callback callback to deregister
                     *
                     */

                    this.deregisterViewCallback = function deregisterViewCallback(callback) {
                        for (var i = $scope.viewCallbacks.length - 1; i >= 0; i--) {
                            if ($scope.viewCallbacks[i] === callback) {
                                $scope.viewCallbacks.splice(i, 1);
                            }
                        }
                    };

                    /**
                     * Function called to rebuild the view from internal components
                     *
                     * @method fetch
                     *
                     */

                    $scope.fetch = function fetch() {

                        var filteredItems = [];

                        filteredItems = _filter($scope.items);

                        // update the total items after search
                        $scope.totalItems = filteredItems.length;

                        filteredItems = _sort(filteredItems);
                        filteredItems = _paginate(filteredItems);

                        $scope.filteredItems = filteredItems;

                        _updatedListerState();

                        angular.forEach($scope.viewCallbacks, function updateCallback(viewCallback) {
                            viewCallback($scope.filteredItems);
                        });

                        $scope.$emit(ITEM_LISTER_CONSTANTS.ITEM_LISTER_UPDATED_EVENT, { meta: { filterValue: $scope.filterValue } });

                        return filteredItems;

                    };

                    /**
                     * Return the focus of the page to the search at the top and scroll to it
                     *
                     * @method focusSearch
                     *
                     */

                    $scope.focusSearch = function focusSearch() {
                        angular.element(document).find("#" + $scope.parentID + "_search_input").focus();
                        $window.scrollTop = 0;
                    };

                    $scope.configurationClicked = function(configEvent) {
                        $scope.$emit(configEvent);
                    };

                    $scope.$on(ITEM_LISTER_CONSTANTS.TABLE_ITEM_BUTTON_EVENT, _itemInteracted);
                    $scope.$on(ITEM_LISTER_CONSTANTS.ITEM_SELECT_EVENT, _itemSelected);

                    angular.extend($scope, {
                        maxPages: 5,
                        totalItems: $scope.items.length,
                        filteredItems: [],
                        currentPage: 1,
                        pageSize: 20,
                        pageSizes: [20, 50, 100, 500],

                        start: 0,
                        limit: 20,

                        filterValue: "",
                        sort: {
                            sortDirection: "asc",
                            sortBy: $scope.headerItems.length ? $scope.headerItems[0].field : ""
                        }
                    }, {
                        filterValue: $routeParams["q"]
                    });

                    function _savedStateLoaded(initialState) {
                        angular.extend($scope, initialState, {
                            filterValue: $routeParams["q"]
                        });
                    }

                    var registerSuccess = $CSSS.register(COMPONENT_NAME);
                    if ( registerSuccess ) {
                        $scope.loadingInitialState = true;
                        registerSuccess.then(_savedStateLoaded, $log.error).finally(function() {
                            $scope.loadingInitialState = false;
                            $scope.fetch();
                        });
                    }

                    this.showConfigColumn = $scope.showConfigColumn = $scope.configurationOptions && $scope.configurationOptions.length;

                    $scope.$on("$destroy", function() {
                        $CSSS.unregister(COMPONENT_NAME);
                    });

                    $scope.fetch();
                    $scope.canRedirectHTTPS = $domainServices.canRedirectHTTPS();
                    $scope.$watch("items", $scope.fetch);

                    /**
                     * Toggles the selection state of all domains in the form.
                     *
                     * @method toggleSelectAll
                     *
                     */

                    $scope.toggleSelectAll = function() {
                        var domains = $domainServices.getCurrentDomains();
                        for (var i = 0; i < domains.length; i++) {
                            if (!$scope.canRedirectHTTPS ) {
                                if (domains[i].isHttpsRedirecting) {
                                    domains[i].selected = $scope.selectAll;
                                }
                            } else {
                                if (!domains[i].nonHTTPS || domains[i].isHttpsRedirecting) {
                                    domains[i].selected = $scope.selectAll;
                                }
                            }
                        }
                    };

                    /**
                     * Checks to see if any domains are currently selected
                     *
                     * @method  hasSelected
                     *
                     * @returns {boolean} TRUE if one or more options are selected
                     *
                     */

                    $scope.hasSelected = function() {
                        var domains = $domainServices.getCurrentDomains();
                        for (var i = 0; i < domains.length; i++) {
                            if (domains[i].selected) {
                                return true;
                            }
                        }
                        return false;
                    };

                    $scope.applyMultiSecureRedirects = function(state) {
                        var domains = $domainServices.getCurrentDomains();
                        var successMessage, failureMessage;
                        if (state) { // true apparently means enabling
                            successMessage = LOCALE.maketext("Force [asis,HTTPS] Redirect is enabled for the selected domains.");
                            failureMessage = LOCALE.maketext("The system failed to enable Force [asis,HTTPS] Redirect.");
                        } else {
                            successMessage = LOCALE.maketext("Force [asis,HTTPS] Redirect is disabled for the selected domains.");
                            failureMessage = LOCALE.maketext("The system failed to disable Force [asis,HTTPS] Redirect.");
                        }
                        if (!$scope.hasSelected()) {
                            return;
                        }

                        $domainServices.toggleHTTPSRedirect(state)
                            .then(function(data) {
                                if (data.status) {

                                    // Deselect each selected domains and properly toggle the HTTPS Redirect option
                                    for (var i = 0; i < domains.length; i++) {
                                        if (domains[i].selected) {
                                            domains[i].selected = false;
                                            domains[i].isHttpsRedirecting = state;

                                            // Change state of the link to the domain to properly reflect https status
                                            var theLink = document.getElementById(domains[i].domain + "_domain_link");
                                            if (typeof (theLink) !== "undefined") {
                                                var protocol = domains[i].isHttpsRedirecting ? "https" : "http";
                                                theLink.href = protocol + "://" + domains[i].domain;
                                            }
                                        }
                                    }

                                    // Empty the domain list from Domain service
                                    $scope.selectAll = false;
                                    $scope.toggleSelectAll();
                                    $alertService.add({
                                        message: successMessage,
                                        type: "success",
                                        autoClose: 10000
                                    });
                                } else {
                                    $alertService.add({
                                        message: failureMessage,
                                        type: "danger"
                                    });
                                }
                            });
                    };
                }]
            };

        }]);
    }
);
Back to Directory