Viewing File: /usr/local/cpanel/whostmgr/docroot/templates/transfer_tool/getacctlist.cmb.js
/*
# app/services/workerNodes.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 */
define(
'app/services/workerNodes',[
"angular",
"lodash",
"cjt/util/locale",
],
function(angular, _, LOCALE) {
"use strict";
var MODULE_NAMESPACE = "whm.transfers.services.workerNodes";
var SERVICE_NAME = "WorkerNodesService";
var MODULE_REQUIREMENTS = [];
var SERVICE_INJECTABLES = ["LOCAL_WORKER_NODES", "REMOTE_WORKER_NODES", "DEFAULT_WORKER_OPTIONS"];
/**
* Service for Handling Worker / Linked Nodes
*
* @param {object[]} LOCAL_WORKER_NODES local worker nodes possible to be used by accounts
* @param {object[]} REMOTE_WORKER_NODES remote worker nodes that may or may not be used by individual accounts
* @param {object} DEFAULT_WORKER_OPTIONS worker type keyed object representing the keys and defaults for the system
* @returns instance of a workerNodesService
*/
var SERVICE_FACTORY = function(LOCAL_WORKER_NODES, REMOTE_WORKER_NODES, DEFAULT_WORKER_OPTIONS) {
var Service = function() {};
Service.prototype = Object.create({});
_.assign(Service.prototype, {
_localWorkerNodes: null,
_remoteWorkerNodes: null,
_workerOptionTypes: null,
_defaultWorkerOptions: null,
_accountWorkerNodeOptions: {},
_accountWorkerDefaultValues: {},
/**
* Parse a raw worker node
*
* @private
*
* @param {object{alias:string,hostname:string,worker_capabilities:object}} rawWorkerNode raw worker node
* @returns {object} parsed worker node
*/
_parseWorkerNode: function _parseWorkerNode(rawWorkerNode) {
var workerNode = {
alias: rawWorkerNode["alias"],
hostname: rawWorkerNode["hostname"],
workerCapabilities: Object.keys(rawWorkerNode.worker_capabilities),
};
return workerNode;
},
/**
* Parse a set of raw worker nodes
*
* @private
*
* @param {object[]} rawWorkerNodes raw worker nodes to parse
* @returns {object[]} parsed worker nodes
*/
_parseWorkerNodes: function _parseWorkerNodes(rawWorkerNodes) {
return rawWorkerNodes.map(this._parseWorkerNode);
},
/**
* Get the listed of remote worker nodes
*
* @returns {object[]} parsed remote worker nodes
*/
getRemoteWorkerNodes: function getRemoteWorkerNodes() {
if (_.isNull(this._remoteWorkerNodes)) {
this._remoteWorkerNodes = this._parseWorkerNodes(REMOTE_WORKER_NODES);
}
return this._remoteWorkerNodes;
},
/**
* Get the list of local worker nodes
*
* @returns {object[]} parsed local worker nodes
*/
getLocalWorkerNodes: function getLocalWorkerNodes() {
if (_.isNull(this._localWorkerNodes)) {
this._localWorkerNodes = this._parseWorkerNodes(LOCAL_WORKER_NODES);
}
return this._localWorkerNodes;
},
/**
* Get the default worker options.
* These are the settings that determine which workers, what defaults, and what the modelKey is
*
* @returns {object} worker type keyed object
*/
getDefaultWorkerOptions: function getDefaultWorkerOptions() {
if ( _.isNull(this._defaultWorkerOptions) ) {
this._defaultWorkerOptions = DEFAULT_WORKER_OPTIONS;
// Check to ensure the default exists, if not, set it to local
Object.keys(this._defaultWorkerOptions).filter(function(workerType) {
return !this.getWorkerByAlias(this.getLocalWorkerNodes(), this._defaultWorkerOptions[workerType].value);
}, this).forEach(function(workerType) {
// These don't exist locally, so we should restore to controller
this._defaultWorkerOptions[workerType].value = ".local";
}, this);
}
return this._defaultWorkerOptions;
},
/**
* Get a list of worker options that are supported
*
* @returns {string[]} array of strings representing the worker types supported
*
* @example
* this.getWorkerOptionTypes(); // ['Mail', 'Web']
*/
getWorkerOptionTypes: function getWorkerOptionTypes() {
if (_.isNull(this._workerOptionTypes)) {
this._workerOptionTypes = Object.keys(this.getDefaultWorkerOptions());
}
return this._workerOptionTypes;
},
/**
* Look up a worker based on the hostname and type
*
* @param {object[]} workerList list of workers to search
* @param {string} hostname hostname of the worker 'host.name.com'
* @param {string} desiredWorkerType worker type 'Mail' etc
* @returns {object|null} worker type if it matches the hostname and type
*/
getWorkerByHostname: function getWorkerByHostname(workerList, hostname, desiredWorkerType) {
if (!workerList) {
return;
}
return _.find(workerList, function(worker) {
return worker.hostname === hostname && worker.workerCapabilities.indexOf(desiredWorkerType) > -1;
});
},
/**
* Get a worker from a worker list based on the alias
*
* @param {object[]} workerList list of workers to search
* @param {string} alias alias for which to search
* @returns {object|null} returns the worker object if it exists
*/
getWorkerByAlias: function getWorkerByAlias(workerList, alias) {
if (!workerList) {
return;
}
return _.find(workerList, function(worker) {
return worker.alias === alias;
});
},
/**
* Build an item for a worker list of a specific type
*
* @private
*
* @param {string} label descriptive label of the item
* @param {string} value specific key value for comparison
* @param {string} type type of worker (Mail, etc)
* @returns {object} worker item
*/
_createWorkerListTypeItem: function _createWorkerListTypeItem(label, value, type) {
var defaultWorkerOptions = this.getDefaultWorkerOptions();
var workerOption = {
label: label,
value: value,
modelKey: defaultWorkerOptions[type].modelKey,
};
return workerOption;
},
/**
* Build an options list for a specific type of worker for a specific account
*
* @private
*
* @param {object} account account to base to list from
* @param {string} workerType type of worker to build the list for
* @returns {object[]} list of worker options for the account
*/
_createAccountWorkerListType: function _createAccountWorkerListType(account, workerType) {
var defaultWorkerOptions = this.getDefaultWorkerOptions();
var remoteWorkerNodes = this.getRemoteWorkerNodes();
var localWorkerNodes = this.getLocalWorkerNodes();
var existingNodeFound;
var workerTypeList = [];
// Add local, should always be an option
workerTypeList.push( this._createWorkerListTypeItem( LOCALE.maketext("Use only this server. Transfer or restore locally."), ".local", workerType) );
// Check for a local alias that matches the hostname and workertype of the remote user type
if (account.workerNodes && account.workerNodes[workerType]) {
var remoteWorker = this.getWorkerByAlias(remoteWorkerNodes, account.workerNodes[workerType]);
var matchingLocalWorker = remoteWorker && this.getWorkerByHostname(localWorkerNodes, remoteWorker.hostname, workerType);
if (matchingLocalWorker) {
existingNodeFound = matchingLocalWorker.alias;
workerTypeList.push(this._createWorkerListTypeItem( LOCALE.maketext("Use the [asis,cPanel] account’s package configuration.") + " (" + matchingLocalWorker.hostname + ")", existingNodeFound, workerType) );
}
}
// Add the rest
localWorkerNodes.filter(function(workerNode) {
return workerNode.alias !== existingNodeFound;
}).forEach(function(workerNode) {
workerTypeList.push( this._createWorkerListTypeItem(workerNode.alias + " (" + workerNode.hostname + ")", workerNode.alias, workerType) );
}, this);
var defaultAlias = existingNodeFound ? existingNodeFound : defaultWorkerOptions[workerType].value;
workerTypeList.filter(function(workerItem) {
return workerItem.value === defaultAlias;
}).forEach(function(workerItem) {
workerItem.isDefault = true;
});
return workerTypeList;
},
/**
* See <getAccountWorkerNodeOptions>
*
* @private
*/
_createAccountWorkerLists: function _createAccountWorkerLists(account) {
var workerLists = {};
this.getWorkerOptionTypes().forEach(function(workerType) {
workerLists[workerType] = this._createAccountWorkerListType(account, workerType);
}, this);
return workerLists;
},
/**
* Build all the worker options for all supported types for an account
*
* @private
*
* @param {object} account
* @returns {object} object with lists based on specific type
*
* @example
* this.getAccountWorkerNodeOptions(acccount); // {'Mail':[{label:"",value,"",modelKey:"",isDefault:false},{},{},...], 'Web':[], ...}
*/
getAccountWorkerNodeOptions: function getAccountWorkerNodeOptions(account) {
var cacheKey = this._getAccountCacheKey(account);
if (_.isUndefined(this._accountWorkerNodeOptions[cacheKey])) {
this._accountWorkerNodeOptions[cacheKey] = this._createAccountWorkerLists(account);
}
return this._accountWorkerNodeOptions[cacheKey];
},
/**
* Determine if any of the worker options for an account have been altered from the default
*
* @param {object} account account to check
* @returns {boolean} has the account's worker options been altered
*/
checkWorkerOptionsAltered: function checkWorkerOptionsAltered(account) {
return this.getAccountWorkerDefaultValues(account).some(function(workerDefaultObj) {
var modelKey = workerDefaultObj.modelKey;
var defaultValue = workerDefaultObj.value;
if (account[modelKey] !== defaultValue) {
return true;
}
return false;
});
},
/**
* Get the default worker values for an account. Used to establish initial values
*
* @param {object} account account to build the list from
* @returns {object[]} returns a list of modelKey and values for an account
*
* @example
* this.getAccountWorkerDefaultValues(account); // [{modelKey:"mail_location", value:'the_default_value"},...]
*/
getAccountWorkerDefaultValues: function getAccountWorkerDefaultValues(account) {
var cacheKey = this._getAccountCacheKey(account);
if (_.isUndefined(this._accountWorkerDefaultValues[cacheKey])) {
var workerNodeOptions = this.getAccountWorkerNodeOptions(account);
var defaultWorkerOptions = this.getDefaultWorkerOptions();
var accountDefaults = [];
Object.keys(workerNodeOptions).forEach(function(workerType) {
var accountDefault = {
workerType: workerType,
modelKey: defaultWorkerOptions[workerType].modelKey
};
workerNodeOptions[workerType].forEach(function(workerOption) {
if (workerOption.isDefault) {
accountDefault.value = workerOption.value;
}
});
if (!accountDefault.value) {
accountDefault.value = defaultWorkerOptions[workerType].value;
}
accountDefaults.push(accountDefault);
});
this._accountWorkerDefaultValues[cacheKey] = accountDefaults;
}
return this._accountWorkerDefaultValues[cacheKey];
},
/**
* Reset an account's worker settings to the defaults for that account
*
* @param {object} account
*/
resetAccountToDefaults: function resetAccountToDefaults(account) {
// Get Worker Node Default Values (this is specific to the individual account)
var defaultValues = this.getAccountWorkerDefaultValues(account);
defaultValues.forEach(function(defaultValueObj) {
account[defaultValueObj.modelKey] = defaultValueObj.value;
}, this);
},
/**
* Build a key to use for caching account lists
*
* @private
*
* @param {object} account account for which to build the key
* @returns {string} a string combination of the remote_user and domain, combined with a pipe
*/
_getAccountCacheKey: function _getAccountCacheKey(account) {
// This is probably over-safe. remote_user _shouldn't_ change
return account.remote_user + "|" + account.domain;
}
});
return new Service();
};
SERVICE_INJECTABLES.push(SERVICE_FACTORY);
var app = angular.module(MODULE_NAMESPACE, MODULE_REQUIREMENTS);
// This determines which options are revealed
app.value("DEFAULT_WORKER_OPTIONS", {
"Mail": {
value: ".local",
modelKey: "mail_location"
}
});
app.factory(SERVICE_NAME, SERVICE_INJECTABLES);
return {
"class": SERVICE_FACTORY,
"serviceName": SERVICE_NAME,
"namespace": MODULE_NAMESPACE
};
}
);
/*
# templates/transfer_tool/controllers/AccountTableController.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 */
/* jshint -W003 */
/* jshint -W098*/
define(
'app/directives/accountExpandPanel',[
"angular",
"lodash",
"cjt/util/locale",
"app/services/workerNodes",
],
function(angular, _, LOCALE, WorkerNodesService) {
"use strict";
var MODULE_NAMESPACE = "whm.transfers.directives.accountExpandPanel";
var MODULE_INJECTABLES = [WorkerNodesService.namespace];
var DIRECTIVE_NAME = "accountExpandPanel";
/* embedded in getacctlist.tmpl so relative pathing is not necessary */
var DIRECTIVE_TEMPLATE = "directives/accountExpandPanel.ptt";
var CONTROLLER_INJECTABLES = ["$scope", WorkerNodesService.serviceName ];
var CONTROLLER = function AccountExpandPanelController($scope, $workerNodesService) {
function _resetSkipOption(skipOption) {
$scope.account[skipOption.modelKey] = skipOption.default;
$scope.skipOptionChanged(skipOption);
}
function _resetWorkerType(workerDefaultValueObj) {
var modelKey = workerDefaultValueObj.modelKey;
var defaultValue = workerDefaultValueObj.value;
var workerType = workerDefaultValueObj.workerType;
$scope.account[modelKey] = defaultValue;
$scope.workerNodeOption[workerType] = $scope.findWorkerOptionByValue(workerType, defaultValue);
$scope.workerNodeOptionChanged(workerType);
}
/**
* Function Called when reset button is clicked from the view
*
*/
$scope.resetToDefaultClicked = function resetToDefaultClicked() {
if ($scope.account.proxyOption) {
$scope.account.proxyOption.value = $scope.account.proxyOption.default;
}
$scope.skipOptions.forEach(_resetSkipOption);
$workerNodesService.getAccountWorkerDefaultValues($scope.account).forEach(_resetWorkerType);
};
/**
* Function called when 'apply to all' button is clicked from the view
*
*/
$scope.applyToAllClicked = function allToAllClicked() {
$scope.onApplyToAll({ updatedAccount: $scope.account, skipOptions: $scope.skipOptions, workerOptions: $scope.workerNodeOption, proxyOption: $scope.account.proxyOption });
};
/**
* Function called on change of a specific option from the view
*
* @param {object} skipOption
*/
$scope.skipOptionChanged = function skipOptionChanged(skipOption) {
$scope.onChange({ updatedAccount: $scope.account, modelKey: skipOption.modelKey, value: $scope.account[skipOption.modelKey] });
};
/**
* Function called on change of proxying option froom view
* @param {object} proxyOption
*/
$scope.proxyOptionChanged = function proxyOptionChanged(proxyOption) {
$scope.onChange({ updatedAccount: $scope.account, modelKey: proxyOption.modelKey, value: !proxyOption.value });
};
/**
* Check if all options are set to default
*
* @returns {boolean} If all options match the default, this is true, else false
*/
$scope.isSetToDefault = function isSetToDefault() {
return !$scope._skipOptionsAltered() && !$scope._workerOptionsAltered() && !$scope._proxyOptionAltered();
};
/**
* Determine the number of accounts selected based on whether this account is selected or not
*
* @returns {number} Number of accounts selected
*/
$scope.getOtherSelectedAccountsCount = function getOtherSelectedAccountsCount() {
var count = $scope.selectedAccountsCount;
if ($scope.account.selected && count > 0) {
count--;
}
return count;
};
/**
* Dispatches a close call to allow the closing of the expansion panel
*
*/
$scope.applyAndClose = function applyAndClose() {
$scope.onClose({ updatedAccount: $scope.account });
};
/**
* Check to see if any skip options are altered
*
* @returns {boolean} returns true if any are altered
*/
$scope._skipOptionsAltered = function _skipOptionsAltered() {
return $scope.skipOptions.some(function(skipOption) {
if ($scope.account[skipOption.modelKey] !== skipOption.default) {
return true;
}
return false;
});
};
/**
* Check to see if proxying option has been changed
* @return {boolean}
*/
$scope._proxyOptionAltered = function _proxyOptionAltered() {
if ($scope.account.proxyOption) {
return $scope.account.proxyOption.value !== $scope.account.proxyOption.default;
}
return false;
};
/**
* Check to see if any worker options are altered
*
* @returns {boolean} returns true if any are altered
*/
$scope._workerOptionsAltered = function _workerOptionsAltered() {
return $workerNodesService.checkWorkerOptionsAltered($scope.account);
};
/**
* Get the label for a worker option menu
*
* @param {string} workerType string identifier of the node type (Mail)
* @returns {string} localized label
*/
$scope.getWorkerConfigLabel = function getWorkerConfigLabel(workerType) {
switch (workerType.toLowerCase()) {
case "mail":
return LOCALE.maketext("Mail");
}
};
/**
* Called when a worker node option changes
*
* @param {string} workerType the worker type that changed
*/
$scope.workerNodeOptionChanged = function workerNodeOptionChanged(workerType) {
var worker = $workerNodesService.getDefaultWorkerOptions()[workerType];
$scope.onChange({ updatedAccount: $scope.account, modelKey: worker.modelKey, value: $scope.workerNodeOption[workerType].value });
};
/**
* Find a specific worker option (object) by the .value property
*
* @param {string} workerType the worker type to search
* @param {string} value the specific value to look for
* @returns {object|null} returns the object if found
*/
$scope.findWorkerOptionByValue = function findWorkerOptionByValue(workerType, value) {
return _.find($scope.workerNodeOptions[workerType], function(workerOption) {
if (workerOption.value === value) {
return true;
}
return false;
});
};
/**
* Set the current value of a worker type (done on intiation)
*
* @param {string} workerType worker type to set
*/
$scope.setCurrentWorkerTypeValue = function setCurrentWorkerTypeValue(workerType) {
var modelKey = $workerNodesService.getDefaultWorkerOptions()[workerType].modelKey;
$scope.workerNodeOption[workerType] = $scope.findWorkerOptionByValue(workerType, $scope.account[modelKey]);
};
// Build Worker Option Sets
$scope.workerNodeOptions = $workerNodesService.getAccountWorkerNodeOptions($scope.account);
$scope.workerNodeOption = {};
$workerNodesService.getWorkerOptionTypes().forEach(function(workerType) {
$scope.setCurrentWorkerTypeValue(workerType);
var modelKey = $workerNodesService.getDefaultWorkerOptions()[workerType].modelKey;
// Watch changes on the account
$scope.$watch(function() {
return $scope.account[modelKey];
}, function() {
$scope.setCurrentWorkerTypeValue(workerType);
});
});
// We should only show worker choices if there is greater than one thing to choose
// because only one thing means that there is only .local
$scope.showWorkerNodeOptions = $workerNodesService.getWorkerOptionTypes().some(function(typeKey) {
if ($scope.workerNodeOptions[typeKey].length > 1) {
return true;
}
return false;
});
};
var LINK = function AccountExpandPanelLink($scope, element, attrs) {
if ( _.isUndefined($scope.parentID) ) {
throw new Error("“id” must be set for " + DIRECTIVE_NAME);
}
if ( _.isUndefined($scope.account) ) {
throw new Error("“account” must be set for " + DIRECTIVE_NAME);
}
if ( _.isUndefined($scope.selectedAccountsCount) ) {
throw new Error("“selectedAccountsCount” must be set for " + DIRECTIVE_NAME);
}
if (!$scope.skipOptions) {
$scope.skipOptions = [];
}
$scope.selectedAccountsCount = parseInt($scope.selectedAccountsCount, 10);
$scope.skipOptions.forEach(function validateSkipOption(skipOption) {
var requiredParameters = ["label", "id", "modelKey", "default"];
requiredParameters.forEach(function(param) {
if ( _.isUndefined(skipOption[param]) ) {
throw new Error("[" + DIRECTIVE_NAME + "] all skip-options items must have the following parameters:\n" + requiredParameters.join(", ") + "\nInvalid item:\n" + JSON.stringify(skipOption));
}
});
skipOption.id = $scope.parentID + "_" + skipOption.id;
});
};
var DIRECTIVE_FACTORY = function DirectiveFactory() {
return {
restrict: "E",
transclude: true,
scope: {
parentID: "@id",
skipOptions: "=",
proxyOption: "=",
account: "=",
selectedAccountsCount: "@",
onApplyToAll: "&onApplyToAll",
onChange: "&onChange",
onClose: "&onClose",
},
link: LINK,
controller: CONTROLLER_INJECTABLES.concat(CONTROLLER),
templateUrl: DIRECTIVE_TEMPLATE,
};
};
var module = angular.module(MODULE_NAMESPACE, MODULE_INJECTABLES);
module.directive(DIRECTIVE_NAME, DIRECTIVE_FACTORY);
return {
namespace: MODULE_NAMESPACE,
class: CONTROLLER,
factory: LINK,
template: DIRECTIVE_TEMPLATE
};
}
);
define(
'app/overwriteStates',[],
function() {
"use strict";
var OVERWRITE_STATES = {
NO_OVERWRITE: "noOverwrite",
OVERWRITE: "overwrite",
OVERWRITE_WITH_DELETE: "overwriteWithDelete",
};
return OVERWRITE_STATES;
}
);
define(
'app/overwriteOptions',[
"app/overwriteStates",
"cjt/util/locale",
],
function(OVERWRITE_STATES, LOCALE) {
"use strict";
var OVERWRITE_OPTIONS = [
{ label: LOCALE.maketext("Do Not Overwrite"), value: OVERWRITE_STATES.NO_OVERWRITE },
{ label: LOCALE.maketext("Overwrite"), value: OVERWRITE_STATES.OVERWRITE },
{ label: LOCALE.maketext("Overwrite with Delete"), value: OVERWRITE_STATES.OVERWRITE_WITH_DELETE },
];
return OVERWRITE_OPTIONS;
}
);
(function(root) {
define("jquery-chosen", ["jquery"], function() {
return (function() {
/*!
Chosen, a Select Box Enhancer for jQuery and Prototype
by Patrick Filler for Harvest, http://getharvest.com
Version 1.5.1
Full source at https://github.com/harvesthq/chosen
Copyright (c) 2011-2016 Harvest http://getharvest.com
MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
This file is generated by `grunt build`, do not edit it by hand.
*/
(function() {
var $, AbstractChosen, Chosen, SelectParser, _ref,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
SelectParser = (function() {
function SelectParser() {
this.options_index = 0;
this.parsed = [];
}
SelectParser.prototype.add_node = function(child) {
if (child.nodeName.toUpperCase() === "OPTGROUP") {
return this.add_group(child);
} else {
return this.add_option(child);
}
};
SelectParser.prototype.add_group = function(group) {
var group_position, option, _i, _len, _ref, _results;
group_position = this.parsed.length;
this.parsed.push({
array_index: group_position,
group: true,
label: this.escapeExpression(group.label),
title: group.title ? group.title : void 0,
children: 0,
disabled: group.disabled,
classes: group.className
});
_ref = group.childNodes;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
option = _ref[_i];
_results.push(this.add_option(option, group_position, group.disabled));
}
return _results;
};
SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
if (option.nodeName.toUpperCase() === "OPTION") {
if (option.text !== "") {
if (group_position != null) {
this.parsed[group_position].children += 1;
}
this.parsed.push({
array_index: this.parsed.length,
options_index: this.options_index,
value: option.value,
text: option.text,
html: option.innerHTML,
title: option.title ? option.title : void 0,
selected: option.selected,
disabled: group_disabled === true ? group_disabled : option.disabled,
group_array_index: group_position,
group_label: group_position != null ? this.parsed[group_position].label : null,
classes: option.className,
style: option.style.cssText
});
} else {
this.parsed.push({
array_index: this.parsed.length,
options_index: this.options_index,
empty: true
});
}
return this.options_index += 1;
}
};
SelectParser.prototype.escapeExpression = function(text) {
var map, unsafe_chars;
if ((text == null) || text === false) {
return "";
}
if (!/[\&\<\>\"\'\`]/.test(text)) {
return text;
}
map = {
"<": "<",
">": ">",
'"': """,
"'": "'",
"`": "`"
};
unsafe_chars = /&(?!\w+;)|[\<\>\"\'\`]/g;
return text.replace(unsafe_chars, function(chr) {
return map[chr] || "&";
});
};
return SelectParser;
})();
SelectParser.select_to_array = function(select) {
var child, parser, _i, _len, _ref;
parser = new SelectParser();
_ref = select.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
parser.add_node(child);
}
return parser.parsed;
};
AbstractChosen = (function() {
function AbstractChosen(form_field, options) {
this.form_field = form_field;
this.options = options != null ? options : {};
if (!AbstractChosen.browser_is_supported()) {
return;
}
this.is_multiple = this.form_field.multiple;
this.set_default_text();
this.set_default_values();
this.setup();
this.set_up_html();
this.register_observers();
this.on_ready();
}
AbstractChosen.prototype.set_default_values = function() {
var _this = this;
this.click_test_action = function(evt) {
return _this.test_active_click(evt);
};
this.activate_action = function(evt) {
return _this.activate_field(evt);
};
this.active_field = false;
this.mouse_on_container = false;
this.results_showing = false;
this.result_highlighted = null;
this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false;
this.disable_search_threshold = this.options.disable_search_threshold || 0;
this.disable_search = this.options.disable_search || false;
this.enable_split_word_search = this.options.enable_split_word_search != null ? this.options.enable_split_word_search : true;
this.group_search = this.options.group_search != null ? this.options.group_search : true;
this.search_contains = this.options.search_contains || false;
this.single_backstroke_delete = this.options.single_backstroke_delete != null ? this.options.single_backstroke_delete : true;
this.max_selected_options = this.options.max_selected_options || Infinity;
this.inherit_select_classes = this.options.inherit_select_classes || false;
this.display_selected_options = this.options.display_selected_options != null ? this.options.display_selected_options : true;
this.display_disabled_options = this.options.display_disabled_options != null ? this.options.display_disabled_options : true;
this.include_group_label_in_selected = this.options.include_group_label_in_selected || false;
return this.max_shown_results = this.options.max_shown_results || Number.POSITIVE_INFINITY;
};
AbstractChosen.prototype.set_default_text = function() {
if (this.form_field.getAttribute("data-placeholder")) {
this.default_text = this.form_field.getAttribute("data-placeholder");
} else if (this.is_multiple) {
this.default_text = this.options.placeholder_text_multiple || this.options.placeholder_text || AbstractChosen.default_multiple_text;
} else {
this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || AbstractChosen.default_single_text;
}
return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || AbstractChosen.default_no_result_text;
};
AbstractChosen.prototype.choice_label = function(item) {
if (this.include_group_label_in_selected && (item.group_label != null)) {
return "<b class='group-name'>" + item.group_label + "</b>" + item.html;
} else {
return item.html;
}
};
AbstractChosen.prototype.mouse_enter = function() {
return this.mouse_on_container = true;
};
AbstractChosen.prototype.mouse_leave = function() {
return this.mouse_on_container = false;
};
AbstractChosen.prototype.input_focus = function(evt) {
var _this = this;
if (this.is_multiple) {
if (!this.active_field) {
return setTimeout((function() {
return _this.container_mousedown();
}), 50);
}
} else {
if (!this.active_field) {
return this.activate_field();
}
}
};
AbstractChosen.prototype.input_blur = function(evt) {
var _this = this;
if (!this.mouse_on_container) {
this.active_field = false;
return setTimeout((function() {
return _this.blur_test();
}), 100);
}
};
AbstractChosen.prototype.results_option_build = function(options) {
var content, data, data_content, shown_results, _i, _len, _ref;
content = '';
shown_results = 0;
_ref = this.results_data;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
data = _ref[_i];
data_content = '';
if (data.group) {
data_content = this.result_add_group(data);
} else {
data_content = this.result_add_option(data);
}
if (data_content !== '') {
shown_results++;
content += data_content;
}
if (options != null ? options.first : void 0) {
if (data.selected && this.is_multiple) {
this.choice_build(data);
} else if (data.selected && !this.is_multiple) {
this.single_set_selected_text(this.choice_label(data));
}
}
if (shown_results >= this.max_shown_results) {
break;
}
}
return content;
};
AbstractChosen.prototype.result_add_option = function(option) {
var classes, option_el;
if (!option.search_match) {
return '';
}
if (!this.include_option_in_results(option)) {
return '';
}
classes = [];
if (!option.disabled && !(option.selected && this.is_multiple)) {
classes.push("active-result");
}
if (option.disabled && !(option.selected && this.is_multiple)) {
classes.push("disabled-result");
}
if (option.selected) {
classes.push("result-selected");
}
if (option.group_array_index != null) {
classes.push("group-option");
}
if (option.classes !== "") {
classes.push(option.classes);
}
option_el = document.createElement("li");
option_el.className = classes.join(" ");
option_el.style.cssText = option.style;
option_el.setAttribute("data-option-array-index", option.array_index);
option_el.innerHTML = option.search_text;
if (option.title) {
option_el.title = option.title;
}
return this.outerHTML(option_el);
};
AbstractChosen.prototype.result_add_group = function(group) {
var classes, group_el;
if (!(group.search_match || group.group_match)) {
return '';
}
if (!(group.active_options > 0)) {
return '';
}
classes = [];
classes.push("group-result");
if (group.classes) {
classes.push(group.classes);
}
group_el = document.createElement("li");
group_el.className = classes.join(" ");
group_el.innerHTML = group.search_text;
if (group.title) {
group_el.title = group.title;
}
return this.outerHTML(group_el);
};
AbstractChosen.prototype.results_update_field = function() {
this.set_default_text();
if (!this.is_multiple) {
this.results_reset_cleanup();
}
this.result_clear_highlight();
this.results_build();
if (this.results_showing) {
return this.winnow_results();
}
};
AbstractChosen.prototype.reset_single_select_options = function() {
var result, _i, _len, _ref, _results;
_ref = this.results_data;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
result = _ref[_i];
if (result.selected) {
_results.push(result.selected = false);
} else {
_results.push(void 0);
}
}
return _results;
};
AbstractChosen.prototype.results_toggle = function() {
if (this.results_showing) {
return this.results_hide();
} else {
return this.results_show();
}
};
AbstractChosen.prototype.results_search = function(evt) {
if (this.results_showing) {
return this.winnow_results();
} else {
return this.results_show();
}
};
AbstractChosen.prototype.winnow_results = function() {
var escapedSearchText, option, regex, results, results_group, searchText, startpos, text, zregex, _i, _len, _ref;
this.no_results_clear();
results = 0;
searchText = this.get_search_text();
escapedSearchText = searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
zregex = new RegExp(escapedSearchText, 'i');
regex = this.get_search_regex(escapedSearchText);
_ref = this.results_data;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
option = _ref[_i];
option.search_match = false;
results_group = null;
if (this.include_option_in_results(option)) {
if (option.group) {
option.group_match = false;
option.active_options = 0;
}
if ((option.group_array_index != null) && this.results_data[option.group_array_index]) {
results_group = this.results_data[option.group_array_index];
if (results_group.active_options === 0 && results_group.search_match) {
results += 1;
}
results_group.active_options += 1;
}
option.search_text = option.group ? option.label : option.html;
if (!(option.group && !this.group_search)) {
option.search_match = this.search_string_match(option.search_text, regex);
if (option.search_match && !option.group) {
results += 1;
}
if (option.search_match) {
if (searchText.length) {
startpos = option.search_text.search(zregex);
text = option.search_text.substr(0, startpos + searchText.length) + '</em>' + option.search_text.substr(startpos + searchText.length);
option.search_text = text.substr(0, startpos) + '<em>' + text.substr(startpos);
}
if (results_group != null) {
results_group.group_match = true;
}
} else if ((option.group_array_index != null) && this.results_data[option.group_array_index].search_match) {
option.search_match = true;
}
}
}
}
this.result_clear_highlight();
if (results < 1 && searchText.length) {
this.update_results_content("");
return this.no_results(searchText);
} else {
this.update_results_content(this.results_option_build());
return this.winnow_results_set_highlight();
}
};
AbstractChosen.prototype.get_search_regex = function(escaped_search_string) {
var regex_anchor;
regex_anchor = this.search_contains ? "" : "^";
return new RegExp(regex_anchor + escaped_search_string, 'i');
};
AbstractChosen.prototype.search_string_match = function(search_string, regex) {
var part, parts, _i, _len;
if (regex.test(search_string)) {
return true;
} else if (this.enable_split_word_search && (search_string.indexOf(" ") >= 0 || search_string.indexOf("[") === 0)) {
parts = search_string.replace(/\[|\]/g, "").split(" ");
if (parts.length) {
for (_i = 0, _len = parts.length; _i < _len; _i++) {
part = parts[_i];
if (regex.test(part)) {
return true;
}
}
}
}
};
AbstractChosen.prototype.choices_count = function() {
var option, _i, _len, _ref;
if (this.selected_option_count != null) {
return this.selected_option_count;
}
this.selected_option_count = 0;
_ref = this.form_field.options;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
option = _ref[_i];
if (option.selected) {
this.selected_option_count += 1;
}
}
return this.selected_option_count;
};
AbstractChosen.prototype.choices_click = function(evt) {
evt.preventDefault();
if (!(this.results_showing || this.is_disabled)) {
return this.results_show();
}
};
AbstractChosen.prototype.keyup_checker = function(evt) {
var stroke, _ref;
stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
this.search_field_scale();
switch (stroke) {
case 8:
if (this.is_multiple && this.backstroke_length < 1 && this.choices_count() > 0) {
return this.keydown_backstroke();
} else if (!this.pending_backstroke) {
this.result_clear_highlight();
return this.results_search();
}
break;
case 13:
evt.preventDefault();
if (this.results_showing) {
return this.result_select(evt);
}
break;
case 27:
if (this.results_showing) {
this.results_hide();
}
return true;
case 9:
case 38:
case 40:
case 16:
case 91:
case 17:
case 18:
break;
default:
return this.results_search();
}
};
AbstractChosen.prototype.clipboard_event_checker = function(evt) {
var _this = this;
return setTimeout((function() {
return _this.results_search();
}), 50);
};
AbstractChosen.prototype.container_width = function() {
if (this.options.width != null) {
return this.options.width;
} else {
return "" + this.form_field.offsetWidth + "px";
}
};
AbstractChosen.prototype.include_option_in_results = function(option) {
if (this.is_multiple && (!this.display_selected_options && option.selected)) {
return false;
}
if (!this.display_disabled_options && option.disabled) {
return false;
}
if (option.empty) {
return false;
}
return true;
};
AbstractChosen.prototype.search_results_touchstart = function(evt) {
this.touch_started = true;
return this.search_results_mouseover(evt);
};
AbstractChosen.prototype.search_results_touchmove = function(evt) {
this.touch_started = false;
return this.search_results_mouseout(evt);
};
AbstractChosen.prototype.search_results_touchend = function(evt) {
if (this.touch_started) {
return this.search_results_mouseup(evt);
}
};
AbstractChosen.prototype.outerHTML = function(element) {
var tmp;
if (element.outerHTML) {
return element.outerHTML;
}
tmp = document.createElement("div");
tmp.appendChild(element);
return tmp.innerHTML;
};
AbstractChosen.browser_is_supported = function() {
if (/iP(od|hone)/i.test(window.navigator.userAgent)) {
return false;
}
if (/Android/i.test(window.navigator.userAgent)) {
if (/Mobile/i.test(window.navigator.userAgent)) {
return false;
}
}
if (/IEMobile/i.test(window.navigator.userAgent)) {
return false;
}
if (/Windows Phone/i.test(window.navigator.userAgent)) {
return false;
}
if (/BlackBerry/i.test(window.navigator.userAgent)) {
return false;
}
if (/BB10/i.test(window.navigator.userAgent)) {
return false;
}
if (window.navigator.appName === "Microsoft Internet Explorer") {
return document.documentMode >= 8;
}
return true;
};
AbstractChosen.default_multiple_text = "Select Some Options";
AbstractChosen.default_single_text = "Select an Option";
AbstractChosen.default_no_result_text = "No results match";
return AbstractChosen;
})();
$ = jQuery;
$.fn.extend({
chosen: function(options) {
if (!AbstractChosen.browser_is_supported()) {
return this;
}
return this.each(function(input_field) {
var $this, chosen;
$this = $(this);
chosen = $this.data('chosen');
if (options === 'destroy') {
if (chosen instanceof Chosen) {
chosen.destroy();
}
return;
}
if (!(chosen instanceof Chosen)) {
$this.data('chosen', new Chosen(this, options));
}
});
}
});
Chosen = (function(_super) {
__extends(Chosen, _super);
function Chosen() {
_ref = Chosen.__super__.constructor.apply(this, arguments);
return _ref;
}
Chosen.prototype.setup = function() {
this.form_field_jq = $(this.form_field);
this.current_selectedIndex = this.form_field.selectedIndex;
return this.is_rtl = this.form_field_jq.hasClass("chosen-rtl");
};
Chosen.prototype.set_up_html = function() {
var container_classes, container_props;
container_classes = ["chosen-container"];
container_classes.push("chosen-container-" + (this.is_multiple ? "multi" : "single"));
if (this.inherit_select_classes && this.form_field.className) {
container_classes.push(this.form_field.className);
}
if (this.is_rtl) {
container_classes.push("chosen-rtl");
}
container_props = {
'class': container_classes.join(' '),
'style': "width: " + (this.container_width()) + ";",
'title': this.form_field.title
};
if (this.form_field.id.length) {
container_props.id = this.form_field.id.replace(/[^\w]/g, '_') + "_chosen";
}
this.container = $("<div />", container_props);
if (this.is_multiple) {
this.container.html('<ul class="chosen-choices"><li class="search-field"><input type="text" value="' + this.default_text + '" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chosen-drop"><ul class="chosen-results"></ul></div>');
} else {
this.container.html('<a class="chosen-single chosen-default"><span>' + this.default_text + '</span><div><b></b></div></a><div class="chosen-drop"><div class="chosen-search"><input type="text" autocomplete="off" /></div><ul class="chosen-results"></ul></div>');
}
this.form_field_jq.hide().after(this.container);
this.dropdown = this.container.find('div.chosen-drop').first();
this.search_field = this.container.find('input').first();
this.search_results = this.container.find('ul.chosen-results').first();
this.search_field_scale();
this.search_no_results = this.container.find('li.no-results').first();
if (this.is_multiple) {
this.search_choices = this.container.find('ul.chosen-choices').first();
this.search_container = this.container.find('li.search-field').first();
} else {
this.search_container = this.container.find('div.chosen-search').first();
this.selected_item = this.container.find('.chosen-single').first();
}
this.results_build();
this.set_tab_index();
return this.set_label_behavior();
};
Chosen.prototype.on_ready = function() {
return this.form_field_jq.trigger("chosen:ready", {
chosen: this
});
};
Chosen.prototype.register_observers = function() {
var _this = this;
this.container.bind('touchstart.chosen', function(evt) {
_this.container_mousedown(evt);
return evt.preventDefault();
});
this.container.bind('touchend.chosen', function(evt) {
_this.container_mouseup(evt);
return evt.preventDefault();
});
this.container.bind('mousedown.chosen', function(evt) {
_this.container_mousedown(evt);
});
this.container.bind('mouseup.chosen', function(evt) {
_this.container_mouseup(evt);
});
this.container.bind('mouseenter.chosen', function(evt) {
_this.mouse_enter(evt);
});
this.container.bind('mouseleave.chosen', function(evt) {
_this.mouse_leave(evt);
});
this.search_results.bind('mouseup.chosen', function(evt) {
_this.search_results_mouseup(evt);
});
this.search_results.bind('mouseover.chosen', function(evt) {
_this.search_results_mouseover(evt);
});
this.search_results.bind('mouseout.chosen', function(evt) {
_this.search_results_mouseout(evt);
});
this.search_results.bind('mousewheel.chosen DOMMouseScroll.chosen', function(evt) {
_this.search_results_mousewheel(evt);
});
this.search_results.bind('touchstart.chosen', function(evt) {
_this.search_results_touchstart(evt);
});
this.search_results.bind('touchmove.chosen', function(evt) {
_this.search_results_touchmove(evt);
});
this.search_results.bind('touchend.chosen', function(evt) {
_this.search_results_touchend(evt);
});
this.form_field_jq.bind("chosen:updated.chosen", function(evt) {
_this.results_update_field(evt);
});
this.form_field_jq.bind("chosen:activate.chosen", function(evt) {
_this.activate_field(evt);
});
this.form_field_jq.bind("chosen:open.chosen", function(evt) {
_this.container_mousedown(evt);
});
this.form_field_jq.bind("chosen:close.chosen", function(evt) {
_this.input_blur(evt);
});
this.search_field.bind('blur.chosen', function(evt) {
_this.input_blur(evt);
});
this.search_field.bind('keyup.chosen', function(evt) {
_this.keyup_checker(evt);
});
this.search_field.bind('keydown.chosen', function(evt) {
_this.keydown_checker(evt);
});
this.search_field.bind('focus.chosen', function(evt) {
_this.input_focus(evt);
});
this.search_field.bind('cut.chosen', function(evt) {
_this.clipboard_event_checker(evt);
});
this.search_field.bind('paste.chosen', function(evt) {
_this.clipboard_event_checker(evt);
});
if (this.is_multiple) {
return this.search_choices.bind('click.chosen', function(evt) {
_this.choices_click(evt);
});
} else {
return this.container.bind('click.chosen', function(evt) {
evt.preventDefault();
});
}
};
Chosen.prototype.destroy = function() {
$(this.container[0].ownerDocument).unbind("click.chosen", this.click_test_action);
if (this.search_field[0].tabIndex) {
this.form_field_jq[0].tabIndex = this.search_field[0].tabIndex;
}
this.container.remove();
this.form_field_jq.removeData('chosen');
return this.form_field_jq.show();
};
Chosen.prototype.search_field_disabled = function() {
this.is_disabled = this.form_field_jq[0].disabled;
if (this.is_disabled) {
this.container.addClass('chosen-disabled');
this.search_field[0].disabled = true;
if (!this.is_multiple) {
this.selected_item.unbind("focus.chosen", this.activate_action);
}
return this.close_field();
} else {
this.container.removeClass('chosen-disabled');
this.search_field[0].disabled = false;
if (!this.is_multiple) {
return this.selected_item.bind("focus.chosen", this.activate_action);
}
}
};
Chosen.prototype.container_mousedown = function(evt) {
if (!this.is_disabled) {
if (evt && evt.type === "mousedown" && !this.results_showing) {
evt.preventDefault();
}
if (!((evt != null) && ($(evt.target)).hasClass("search-choice-close"))) {
if (!this.active_field) {
if (this.is_multiple) {
this.search_field.val("");
}
$(this.container[0].ownerDocument).bind('click.chosen', this.click_test_action);
this.results_show();
} else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chosen-single").length)) {
evt.preventDefault();
this.results_toggle();
}
return this.activate_field();
}
}
};
Chosen.prototype.container_mouseup = function(evt) {
if (evt.target.nodeName === "ABBR" && !this.is_disabled) {
return this.results_reset(evt);
}
};
Chosen.prototype.search_results_mousewheel = function(evt) {
var delta;
if (evt.originalEvent) {
delta = evt.originalEvent.deltaY || -evt.originalEvent.wheelDelta || evt.originalEvent.detail;
}
if (delta != null) {
evt.preventDefault();
if (evt.type === 'DOMMouseScroll') {
delta = delta * 40;
}
return this.search_results.scrollTop(delta + this.search_results.scrollTop());
}
};
Chosen.prototype.blur_test = function(evt) {
if (!this.active_field && this.container.hasClass("chosen-container-active")) {
return this.close_field();
}
};
Chosen.prototype.close_field = function() {
$(this.container[0].ownerDocument).unbind("click.chosen", this.click_test_action);
this.active_field = false;
this.results_hide();
this.container.removeClass("chosen-container-active");
this.clear_backstroke();
this.show_search_field_default();
return this.search_field_scale();
};
Chosen.prototype.activate_field = function() {
this.container.addClass("chosen-container-active");
this.active_field = true;
this.search_field.val(this.search_field.val());
return this.search_field.focus();
};
Chosen.prototype.test_active_click = function(evt) {
var active_container;
active_container = $(evt.target).closest('.chosen-container');
if (active_container.length && this.container[0] === active_container[0]) {
return this.active_field = true;
} else {
return this.close_field();
}
};
Chosen.prototype.results_build = function() {
this.parsing = true;
this.selected_option_count = null;
this.results_data = SelectParser.select_to_array(this.form_field);
if (this.is_multiple) {
this.search_choices.find("li.search-choice").remove();
} else if (!this.is_multiple) {
this.single_set_selected_text();
if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) {
this.search_field[0].readOnly = true;
this.container.addClass("chosen-container-single-nosearch");
} else {
this.search_field[0].readOnly = false;
this.container.removeClass("chosen-container-single-nosearch");
}
}
this.update_results_content(this.results_option_build({
first: true
}));
this.search_field_disabled();
this.show_search_field_default();
this.search_field_scale();
return this.parsing = false;
};
Chosen.prototype.result_do_highlight = function(el) {
var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
if (el.length) {
this.result_clear_highlight();
this.result_highlight = el;
this.result_highlight.addClass("highlighted");
maxHeight = parseInt(this.search_results.css("maxHeight"), 10);
visible_top = this.search_results.scrollTop();
visible_bottom = maxHeight + visible_top;
high_top = this.result_highlight.position().top + this.search_results.scrollTop();
high_bottom = high_top + this.result_highlight.outerHeight();
if (high_bottom >= visible_bottom) {
return this.search_results.scrollTop((high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0);
} else if (high_top < visible_top) {
return this.search_results.scrollTop(high_top);
}
}
};
Chosen.prototype.result_clear_highlight = function() {
if (this.result_highlight) {
this.result_highlight.removeClass("highlighted");
}
return this.result_highlight = null;
};
Chosen.prototype.results_show = function() {
if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
this.form_field_jq.trigger("chosen:maxselected", {
chosen: this
});
return false;
}
this.container.addClass("chosen-with-drop");
this.results_showing = true;
this.search_field.focus();
this.search_field.val(this.search_field.val());
this.winnow_results();
return this.form_field_jq.trigger("chosen:showing_dropdown", {
chosen: this
});
};
Chosen.prototype.update_results_content = function(content) {
return this.search_results.html(content);
};
Chosen.prototype.results_hide = function() {
if (this.results_showing) {
this.result_clear_highlight();
this.container.removeClass("chosen-with-drop");
this.form_field_jq.trigger("chosen:hiding_dropdown", {
chosen: this
});
}
return this.results_showing = false;
};
Chosen.prototype.set_tab_index = function(el) {
var ti;
if (this.form_field.tabIndex) {
ti = this.form_field.tabIndex;
this.form_field.tabIndex = -1;
return this.search_field[0].tabIndex = ti;
}
};
Chosen.prototype.set_label_behavior = function() {
var _this = this;
this.form_field_label = this.form_field_jq.parents("label");
if (!this.form_field_label.length && this.form_field.id.length) {
this.form_field_label = $("label[for='" + this.form_field.id + "']");
}
if (this.form_field_label.length > 0) {
return this.form_field_label.bind('click.chosen', function(evt) {
if (_this.is_multiple) {
return _this.container_mousedown(evt);
} else {
return _this.activate_field();
}
});
}
};
Chosen.prototype.show_search_field_default = function() {
if (this.is_multiple && this.choices_count() < 1 && !this.active_field) {
this.search_field.val(this.default_text);
return this.search_field.addClass("default");
} else {
this.search_field.val("");
return this.search_field.removeClass("default");
}
};
Chosen.prototype.search_results_mouseup = function(evt) {
var target;
target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
if (target.length) {
this.result_highlight = target;
this.result_select(evt);
return this.search_field.focus();
}
};
Chosen.prototype.search_results_mouseover = function(evt) {
var target;
target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
if (target) {
return this.result_do_highlight(target);
}
};
Chosen.prototype.search_results_mouseout = function(evt) {
if ($(evt.target).hasClass("active-result" || $(evt.target).parents('.active-result').first())) {
return this.result_clear_highlight();
}
};
Chosen.prototype.choice_build = function(item) {
var choice, close_link,
_this = this;
choice = $('<li />', {
"class": "search-choice"
}).html("<span>" + (this.choice_label(item)) + "</span>");
if (item.disabled) {
choice.addClass('search-choice-disabled');
} else {
close_link = $('<a />', {
"class": 'search-choice-close',
'data-option-array-index': item.array_index
});
close_link.bind('click.chosen', function(evt) {
return _this.choice_destroy_link_click(evt);
});
choice.append(close_link);
}
return this.search_container.before(choice);
};
Chosen.prototype.choice_destroy_link_click = function(evt) {
evt.preventDefault();
evt.stopPropagation();
if (!this.is_disabled) {
return this.choice_destroy($(evt.target));
}
};
Chosen.prototype.choice_destroy = function(link) {
if (this.result_deselect(link[0].getAttribute("data-option-array-index"))) {
this.show_search_field_default();
if (this.is_multiple && this.choices_count() > 0 && this.search_field.val().length < 1) {
this.results_hide();
}
link.parents('li').first().remove();
return this.search_field_scale();
}
};
Chosen.prototype.results_reset = function() {
this.reset_single_select_options();
this.form_field.options[0].selected = true;
this.single_set_selected_text();
this.show_search_field_default();
this.results_reset_cleanup();
this.form_field_jq.trigger("change");
if (this.active_field) {
return this.results_hide();
}
};
Chosen.prototype.results_reset_cleanup = function() {
this.current_selectedIndex = this.form_field.selectedIndex;
return this.selected_item.find("abbr").remove();
};
Chosen.prototype.result_select = function(evt) {
var high, item;
if (this.result_highlight) {
high = this.result_highlight;
this.result_clear_highlight();
if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
this.form_field_jq.trigger("chosen:maxselected", {
chosen: this
});
return false;
}
if (this.is_multiple) {
high.removeClass("active-result");
} else {
this.reset_single_select_options();
}
high.addClass("result-selected");
item = this.results_data[high[0].getAttribute("data-option-array-index")];
item.selected = true;
this.form_field.options[item.options_index].selected = true;
this.selected_option_count = null;
if (this.is_multiple) {
this.choice_build(item);
} else {
this.single_set_selected_text(this.choice_label(item));
}
if (!((evt.metaKey || evt.ctrlKey) && this.is_multiple)) {
this.results_hide();
}
this.show_search_field_default();
if (this.is_multiple || this.form_field.selectedIndex !== this.current_selectedIndex) {
this.form_field_jq.trigger("change", {
'selected': this.form_field.options[item.options_index].value
});
}
this.current_selectedIndex = this.form_field.selectedIndex;
evt.preventDefault();
return this.search_field_scale();
}
};
Chosen.prototype.single_set_selected_text = function(text) {
if (text == null) {
text = this.default_text;
}
if (text === this.default_text) {
this.selected_item.addClass("chosen-default");
} else {
this.single_deselect_control_build();
this.selected_item.removeClass("chosen-default");
}
return this.selected_item.find("span").html(text);
};
Chosen.prototype.result_deselect = function(pos) {
var result_data;
result_data = this.results_data[pos];
if (!this.form_field.options[result_data.options_index].disabled) {
result_data.selected = false;
this.form_field.options[result_data.options_index].selected = false;
this.selected_option_count = null;
this.result_clear_highlight();
if (this.results_showing) {
this.winnow_results();
}
this.form_field_jq.trigger("change", {
deselected: this.form_field.options[result_data.options_index].value
});
this.search_field_scale();
return true;
} else {
return false;
}
};
Chosen.prototype.single_deselect_control_build = function() {
if (!this.allow_single_deselect) {
return;
}
if (!this.selected_item.find("abbr").length) {
this.selected_item.find("span").first().after("<abbr class=\"search-choice-close\"></abbr>");
}
return this.selected_item.addClass("chosen-single-with-deselect");
};
Chosen.prototype.get_search_text = function() {
return $('<div/>').text($.trim(this.search_field.val())).html();
};
Chosen.prototype.winnow_results_set_highlight = function() {
var do_high, selected_results;
selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : [];
do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first();
if (do_high != null) {
return this.result_do_highlight(do_high);
}
};
Chosen.prototype.no_results = function(terms) {
var no_results_html;
no_results_html = $('<li class="no-results">' + this.results_none_found + ' "<span></span>"</li>');
no_results_html.find("span").first().html(terms);
this.search_results.append(no_results_html);
return this.form_field_jq.trigger("chosen:no_results", {
chosen: this
});
};
Chosen.prototype.no_results_clear = function() {
return this.search_results.find(".no-results").remove();
};
Chosen.prototype.keydown_arrow = function() {
var next_sib;
if (this.results_showing && this.result_highlight) {
next_sib = this.result_highlight.nextAll("li.active-result").first();
if (next_sib) {
return this.result_do_highlight(next_sib);
}
} else {
return this.results_show();
}
};
Chosen.prototype.keyup_arrow = function() {
var prev_sibs;
if (!this.results_showing && !this.is_multiple) {
return this.results_show();
} else if (this.result_highlight) {
prev_sibs = this.result_highlight.prevAll("li.active-result");
if (prev_sibs.length) {
return this.result_do_highlight(prev_sibs.first());
} else {
if (this.choices_count() > 0) {
this.results_hide();
}
return this.result_clear_highlight();
}
}
};
Chosen.prototype.keydown_backstroke = function() {
var next_available_destroy;
if (this.pending_backstroke) {
this.choice_destroy(this.pending_backstroke.find("a").first());
return this.clear_backstroke();
} else {
next_available_destroy = this.search_container.siblings("li.search-choice").last();
if (next_available_destroy.length && !next_available_destroy.hasClass("search-choice-disabled")) {
this.pending_backstroke = next_available_destroy;
if (this.single_backstroke_delete) {
return this.keydown_backstroke();
} else {
return this.pending_backstroke.addClass("search-choice-focus");
}
}
}
};
Chosen.prototype.clear_backstroke = function() {
if (this.pending_backstroke) {
this.pending_backstroke.removeClass("search-choice-focus");
}
return this.pending_backstroke = null;
};
Chosen.prototype.keydown_checker = function(evt) {
var stroke, _ref1;
stroke = (_ref1 = evt.which) != null ? _ref1 : evt.keyCode;
this.search_field_scale();
if (stroke !== 8 && this.pending_backstroke) {
this.clear_backstroke();
}
switch (stroke) {
case 8:
this.backstroke_length = this.search_field.val().length;
break;
case 9:
if (this.results_showing && !this.is_multiple) {
this.result_select(evt);
}
this.mouse_on_container = false;
break;
case 13:
if (this.results_showing) {
evt.preventDefault();
}
break;
case 32:
if (this.disable_search) {
evt.preventDefault();
}
break;
case 38:
evt.preventDefault();
this.keyup_arrow();
break;
case 40:
evt.preventDefault();
this.keydown_arrow();
break;
}
};
Chosen.prototype.search_field_scale = function() {
var div, f_width, h, style, style_block, styles, w, _i, _len;
if (this.is_multiple) {
h = 0;
w = 0;
style_block = "position:absolute; left: -1000px; top: -1000px; display:none;";
styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing'];
for (_i = 0, _len = styles.length; _i < _len; _i++) {
style = styles[_i];
style_block += style + ":" + this.search_field.css(style) + ";";
}
div = $('<div />', {
'style': style_block
});
div.text(this.search_field.val());
$('body').append(div);
w = div.width() + 25;
div.remove();
f_width = this.container.outerWidth();
if (w > f_width - 10) {
w = f_width - 10;
}
return this.search_field.css({
'width': w + 'px'
});
}
};
return Chosen;
})(AbstractChosen);
}).call(this);
}).apply(root, arguments);
});
}(this));
(function(root) {
define("angular-chosen", ["angular","jquery-chosen"], function() {
return (function() {
/**
* angular-chosen-localytics - Angular Chosen directive is an AngularJS Directive that brings the Chosen jQuery in a Angular way
* @version v1.3.0
* @link http://github.com/leocaseiro/angular-chosen
* @license MIT
*/
(function() {
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
angular.module('localytics.directives', []);
angular.module('localytics.directives').directive('chosen', [
'$timeout', function($timeout) {
var CHOSEN_OPTION_WHITELIST, NG_OPTIONS_REGEXP, isEmpty, snakeCase;
NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
CHOSEN_OPTION_WHITELIST = ['persistentCreateOption', 'createOptionText', 'createOption', 'skipNoResults', 'noResultsText', 'allowSingleDeselect', 'disableSearchThreshold', 'disableSearch', 'enableSplitWordSearch', 'inheritSelectClasses', 'maxSelectedOptions', 'placeholderTextMultiple', 'placeholderTextSingle', 'searchContains', 'singleBackstrokeDelete', 'displayDisabledOptions', 'displaySelectedOptions', 'width', 'includeGroupLabelInSelected', 'maxShownResults'];
snakeCase = function(input) {
return input.replace(/[A-Z]/g, function($1) {
return "_" + ($1.toLowerCase());
});
};
isEmpty = function(value) {
var key;
if (angular.isArray(value)) {
return value.length === 0;
} else if (angular.isObject(value)) {
for (key in value) {
if (value.hasOwnProperty(key)) {
return false;
}
}
}
return true;
};
return {
restrict: 'A',
require: '?ngModel',
priority: 1,
link: function(scope, element, attr, ngModel) {
var chosen, empty, initOrUpdate, match, options, origRender, startLoading, stopLoading, updateMessage, valuesExpr, viewWatch;
scope.disabledValuesHistory = scope.disabledValuesHistory ? scope.disabledValuesHistory : [];
element = $(element);
element.addClass('localytics-chosen');
options = scope.$eval(attr.chosen) || {};
angular.forEach(attr, function(value, key) {
if (indexOf.call(CHOSEN_OPTION_WHITELIST, key) >= 0) {
return attr.$observe(key, function(value) {
options[snakeCase(key)] = String(element.attr(attr.$attr[key])).slice(0, 2) === '{{' ? value : scope.$eval(value);
return updateMessage();
});
}
});
startLoading = function() {
return element.addClass('loading').attr('disabled', true).trigger('chosen:updated');
};
stopLoading = function() {
element.removeClass('loading');
if (angular.isDefined(attr.disabled)) {
element.attr('disabled', attr.disabled);
} else {
element.attr('disabled', false);
}
return element.trigger('chosen:updated');
};
chosen = null;
empty = false;
initOrUpdate = function() {
var defaultText;
if (chosen) {
return element.trigger('chosen:updated');
} else {
$timeout(function() {
chosen = element.chosen(options).data('chosen');
});
if (angular.isObject(chosen)) {
return defaultText = chosen.default_text;
}
}
};
updateMessage = function() {
if (empty) {
element.attr('data-placeholder', chosen.results_none_found).attr('disabled', true);
} else {
element.removeAttr('data-placeholder');
}
return element.trigger('chosen:updated');
};
if (ngModel) {
origRender = ngModel.$render;
ngModel.$render = function() {
origRender();
return initOrUpdate();
};
element.on('chosen:hiding_dropdown', function() {
return scope.$apply(function() {
return ngModel.$setTouched();
});
});
if (attr.multiple) {
viewWatch = function() {
return ngModel.$viewValue;
};
scope.$watch(viewWatch, ngModel.$render, true);
}
} else {
initOrUpdate();
}
attr.$observe('disabled', function() {
return element.trigger('chosen:updated');
});
if (attr.ngOptions && ngModel) {
match = attr.ngOptions.match(NG_OPTIONS_REGEXP);
valuesExpr = match[7];
scope.$watchCollection(valuesExpr, function(newVal, oldVal) {
var timer;
return timer = $timeout(function() {
if (angular.isUndefined(newVal)) {
return startLoading();
} else {
empty = isEmpty(newVal);
stopLoading();
return updateMessage();
}
});
});
return scope.$on('$destroy', function(event) {
if (typeof timer !== "undefined" && timer !== null) {
return $timeout.cancel(timer);
}
});
}
}
};
}
]);
}).call(this);
}).apply(root, arguments);
});
}(this));
/*
# templates/transfer_tool/filters/startFromFilter.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 */
define(
'app/filters/startFromFilter',[
"angular"
],
function(angular) {
"use strict";
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
/**
* Angular filter which returns array started at position defined by start
* @return {array}
*/
app.filter("startFrom", function() {
return function(input, start) {
if (input && angular.isArray(input)) {
start = Number(start); // parse to int
return input.slice(start);
}
};
});
}
);
/*
# templates/transfer_tool/filters/cpLimitToFilter.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 */
define(
'app/filters/cpLimitToFilter',[
"angular"
],
function(angular) {
"use strict";
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
// Main - reusable
/**
* Angular filter that limits arrays to a defined limit
* @return {array}
*/
app.filter("cpLimitTo", function() {
return function(input, limit) {
return limit ? input.slice(0, limit) : input;
};
});
}
);
/*
# cpanel - templates/transfer_tool/controllers/MainController.js
# Copyright(c) 2021 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 */
/* jshint -W003 */
/**
* @class Class constructor for the main controller
* @param {object} $scope
* @param {object} $filter
*/
define(
'app/controllers/MainController',[
"angular",
"cjt/util/locale",
"cjt/directives/pageSizeDirective",
"../filters/startFromFilter",
"../filters/cpLimitToFilter",
],
function(angular, LOCALE) {
"use strict";
var CONTROLLER_INJECTABLES = ["$scope", "$log", "$filter", "PAGE", "OVERWRITE_OPTIONS", "OVERWRITE_STATES"];
var MainController = function($scope, $log, $filter, PAGE, OVERWRITE_OPTIONS, OVERWRITE_STATES) {
var _this = this;
_this._log = $log;
_this.PAGE = PAGE;
_this.REMAINING_ACCOUNT_SLOTS = parseInt(PAGE.REMAINING_ACCOUNT_SLOTS, 10);
_this.SERVER_MAX_USERS = parseInt(PAGE.SERVER_MAX_USERS, 10);
_this.pkgTable = {
currentPage: 1,
pageSize: 10,
reverse: false,
order: "name",
};
_this.overwriteStates = OVERWRITE_STATES;
// Determine if cPanel version is less than 11.55.
_this.olderRemote = parseInt(_this.PAGE.remote.version.split(".")[1]) < 55;
var serverType = LOCALE.maketext("Unknown");
if (_this.PAGE.remote.server_type.match(/WHM/)) {
serverType = "WHM " + _this.PAGE.remote.version;
} else {
serverType = _this.PAGE.remote.server_type + " " + _this.PAGE.remote.version;
}
var isCpanel = true;
if ( !serverType.match(/WHM/) ) {
isCpanel = false;
}
_this.meta = {
server_type: serverType,
is_cpanel: isCpanel,
panel_name: _this.PAGE.remote.server_type,
};
_this.LOCALE = LOCALE;
// Set default values - this checks them
_this.control_bwdata = true;
_this.control_homedir = true;
_this.control_databases = true;
_this.control_reseller_privs = true;
// Packages / Main
angular.forEach(_this.PAGE.items.packages, function(val, key) {
_this.PAGE.items.packages[key].selected = 0;
});
_this.PAGE.domainsWithDedicatedIp = {};
angular.forEach(_this.PAGE.local.dedicated_ips, function(value, key) {
this[value] = true;
}, _this.PAGE.domainsWithDedicatedIp);
var startFrom = $filter("startFrom"),
orderBy = $filter("orderBy"),
limitTo = $filter("cpLimitTo"),
filter = $filter("filter");
_this.existingUsersFilter = $filter("existingUsers");
_this.selectedAccounts = filter(_this.PAGE.items.accounts, {
selected: 1,
});
_this.selectedPackages = filter(_this.PAGE.items.packages, {
selected: 1,
});
// To better understand what these assignments are look at updateFilteredPackages and updateViewablePackages
_this.filteredPackages = filter(_this.PAGE.items.packages, _this.pkgsFilter);
_this.viewablePackages = limitTo(startFrom(orderBy(_this.filteredPackages, _this.pkgTable.order, _this.pkgTable.reverse), (_this.pkgTable.currentPage - 1) * _this.pkgTable.pageSize),
_this.pkgTable.pageSize);
/**
* Updates the selectedPackages array based on what packages are selected
*/
_this.updateSelectedPackages = function() {
_this.selectedPackages = filter(this.PAGE.items.packages, {
selected: 1,
});
};
_this.updateSelectedConfigurations = function() {
_this.selectedConfigurations = [];
angular.forEach(_this.PAGE.configuration_modules, function(value, key) {
if (value.selected) {
var config = {};
config[key + "_enabled"] = 1;
_this.selectedConfigurations.push( config );
}
});
};
_this.updateSelectedConfigurations();
/**
* Dispatches Event to be caught by AccountTableController
*/
_this.recheckUsers = function(dataset) {
$scope.$broadcast("recheckUsersCalled", {
dataset: dataset,
});
};
/**
* Updates the selectedAccounts array based on what packages are selected
*/
_this.updateSelectedAccounts = function() {
_this.selectedAccounts = filter(this.PAGE.items.accounts, {
selected: 1,
});
};
/**
* Reapplies filters for the filteredPackages array
* Array -> Filtered by pkgsFilter
* packages | filter:pkgsFilter
*/
_this.updateFilteredPackages = function() {
_this.filteredPackages = filter(this.PAGE.items.packages, this.pkgsFilter);
};
/**
* Reapplies filteres for the viewablePackages array.
* FilteredPackages -> Ordered by(reverse?) -> start at page# * pageSize -> limit to page size
* filteredPackages | orderBy: acctTable.order:acctTable.reverse | startFrom:(acctTable.currentPage - 1 ) * acctTable.pageSize | limitTo: acctTable.pageSize
*/
_this.updateViewablePackages = function() {
_this.viewablePackages = limitTo(startFrom(orderBy(_this.filteredPackages, _this.pkgTable.order, _this.pkgTable.reverse), (_this.pkgTable.currentPage - 1) * _this.pkgTable.pageSize),
_this.pkgTable.pageSize);
};
_this.getPossiblePageSizes = function() {
return _this.PAGE.all_possible_page_sizes;
};
/**
* Returns true if the source account is being assigned a new dedicated ip.
* @param {object} item Account object
* @return {Boolean}
*/
_this.newDedicatedIpFilter = function(item) {
if (!item.dedicated_ip) {
return false;
}
return !_this.PAGE.domainsWithDedicatedIp[item.domain];
};
_this.toArray = function() {
_this.configuration_modules = [];
angular.forEach(_this.PAGE.configuration_modules, function(val, key) {
var analysis = [];
angular.forEach(val.analysis, function(analysisValue, analysisKey) {
analysis.push({ analysis: analysisValue, analysisKey: analysisKey });
});
val.analysis = analysis;
_this.configuration_modules.push({ key: key, value: val });
});
};
_this.overwriteIsDefault = function overwriteIsDefault(item) {
return item.overwrite_type.value === _this.overwriteStates.NO_OVERWRITE;
};
_this.overwriteOptions = OVERWRITE_OPTIONS;
// If no viewable accounts are selected, make sure the checkbox is unchecked.
$scope.$watch(function() {
return _this.selectedAccounts.length;
}, function(newVal) {
if (!newVal) {
$scope.$broadcast("selectedAccountsEmptied", {});
}
});
$scope.$watch(function() {
return _this.pkgTable;
}, function() {
_this.updateViewablePackages();
}, true);
_this.toArray();
return _this;
};
/**
* Function that updates both filtered and viewable packages array results
*/
MainController.prototype.updatePackages = function() {
this.updateFilteredPackages();
this.updateViewablePackages();
};
MainController.prototype.updateConfigurations = function() {
this.updateSelectedConfigurations();
};
/**
* Returns 'icon-arrow-up' or 'icon-arrow-down' if column is selected for ordering. The result is based on what order. Used to indicate sort order.
* @param {objec} tableOptionsObj
* @param {string} column
* @return {string|Boolean}
*/
MainController.prototype.selectedHeaderClass = function(tableOptionsObj, column) {
var className = "icon-arrow-" + (tableOptionsObj.reverse ? "down" : "up");
return column === tableOptionsObj.order && className;
};
/**
* Returns 'active' if column is selected for ordering. Used to highlight selected column.
* @param {object} tableOptionsObj
* @param {string} column
* @return {string|Boolean}
*/
MainController.prototype.selectedColClass = function(tableOptionsObj, column) {
return column === tableOptionsObj.order && "active";
};
/**
* Takes in an array of accounts and sets the attr of each account to a value of 1 or 0 corresponding to isChecked. 1 or 0 for Perl parsing after form submit.
* @param {array} dataset
* @param {string} attr
* @param {Boolean} isChecked
*/
MainController.prototype.toggleCheckAll = function(dataset, attr, isChecked) {
var newValue;
if (attr === "overwrite_type") {
newValue = isChecked;
} else {
newValue = isChecked ? 1 : 0;
}
angular.forEach(dataset, function(o) {
o[attr] = newValue;
});
};
MainController.prototype.toggleCheckAllAndReValidate = function(dataset, attr, isChecked) {
var _this = this;
_this.toggleCheckAll.apply(_this, arguments);
/* selected accounts list must be updated before validation can occur */
_this.updateSelectedAccounts();
_this.recheckUsers(dataset);
};
/**
* Sets the order and reverse attributes for tableOptions. Used for column sorting.
* @param {object} tableOptionsObj
* @param {string} fieldName
*/
MainController.prototype.toggleSort = function(tableOptionsObj, fieldName) {
tableOptionsObj.reverse = (tableOptionsObj.order === fieldName ? !tableOptionsObj.reverse : 0);
tableOptionsObj.order = fieldName;
};
/**
* Returns true if the account is selected and has an invalid user
* @param {object} item
* @return {Boolean}
*/
MainController.prototype.invalidUserFilter = function(item) {
return item.selected && item.invalidUser;
};
/**
* Returns true if the account's local name has not changed from the remote name.
* @param {object} item
* @return {Boolean}
*/
MainController.prototype.noUsernameChange = function(item) {
return item.localuser === item.remote_user;
};
/**
* Returns true if the account matches a reserved account name on the file system
* @param {object} item
* @return {Boolean}
*/
MainController.prototype.reservedUserFilter = function(item) {
return item.selected && item.invalidUser && item.userCheckStates.reserved;
};
/**
* Returns true if the account username matches another name selected for importation
* @param {object} item
* @return {Boolean}
*/
MainController.prototype.duplicateUserFilter = function(item) {
return item.selected && !!item.localuser && item.invalidUser && item.userCheckStates.duplicate;
};
/**
* Returns true if the account does not match the system regular expression requirements
* @param {object} item
* @return {Boolean}
*/
MainController.prototype.invalidUsernameFilter = function(item) {
return item.selected && item.invalidUser && item.userCheckStates.invalid;
};
/**
* Returns true if the account is selected and has an invalid domain.
* @param {object} item
* @return {Boolean}
*/
MainController.prototype.invalidDomainFilter = function(item) {
return item.selected && item.invalidDomain;
};
/**
* Returns true if the account is selected, the user is a reseller on the source server,
* and the reseller privileges are not being transferred.
* @param {object} item
* @return {Boolean}
*/
MainController.prototype.resellerNoCopyFilter = function(item) {
return item.selected && item.is_reseller && !item.copy_reseller_privs;
};
MainController.prototype.canNotTransferTempDomainFilter = function(item) {
return item.selected && item.isTempDomain && !item.serverSupportsTempDomains;
};
/**
* Revalidates all account usernames in provided array
* @param {array} array
*/
MainController.prototype.reValidateDomainAndUsernameFor = function(array) {
if (!array) {
return;
}
for (var x = 0; x < array.length; x++) {
var username = array[x].localuser || "";
if (!username.length) {
username = array[x].remote_user;
}
array[x].invalidUser = this.PAGE.local.users[username] && !array[x].invalidDomain && array[x].overwrite_type === this.overwriteOptions[0] ? 1 : 0;
}
};
/**
* Returns message based on the way in which an account matches a local account.
* @param {object} acct
* @return {String}
*/
MainController.prototype.getExistingUserMessage = function(acct) {
var incomingLocal = acct.localuser;
var matchedLocal = acct.similarLocalUser;
if (incomingLocal === matchedLocal) {
return this.LOCALE.maketext("The remote account “[_1]” cannot transfer because an account with the same username exists on the local server.", incomingLocal);
} else {
return this.LOCALE.maketext("The remote account “[_1]” cannot transfer because the first [quant,_3,non-special character matches,non-special characters match] the local username “[_2]”.", incomingLocal, matchedLocal, this.PAGE.USERNAME_UNIQUE_LENGTH);
}
};
MainController.prototype.existingAccountWarning = function(acctCount) {
return LOCALE.maketext("[output,strong,Error]: You have selected [quant,_1,account transfer,account transfers] that will not complete properly because the [numerate,_1,username,usernames] already [numerate,_1,exists,exist] on the local server.", acctCount) + " " +
LOCALE.maketext("To resolve this, you can overwrite the local [numerate,_1,account,accounts], rename the incoming [numerate,_1,account,accounts], or deselect the [numerate,_1,account,accounts].", acctCount);
};
MainController.prototype.existingDomainWarning = function(domainCount) {
return LOCALE.maketext("[output,strong,Error]: You have selected [quant,_1,account transfer,account transfers] that will not complete properly because of [numerate,_1,a domain conflict,domain conflicts].", domainCount) + " " +
LOCALE.maketext("This is due to [numerate,_1,an existing domain,existing domains] that [numerate,_1,matches an incoming domain,match incoming domains] but [numerate,_1,does,do] not match the local [numerate,_1,username,usernames].", domainCount) + " " +
LOCALE.maketext("To resolve this, remove the matching [numerate,_1,domain,domains] from the local machine for any [numerate,_1,account,accounts] that you wish to transfer.", domainCount);
};
MainController.prototype.dedicatedIpAddrWarning = function(ipsToAdd, availableIps) {
return LOCALE.maketext("[output,strong,Error]: You have selected [numf,_1] of [quant,_2,available IP address,available IP addresses]. Either deselect an account to transfer, or deselect its corresponding “[_3]” field.", ipsToAdd, availableIps, LOCALE.maketext("Dedicated IP Address"));
};
MainController.prototype.frontPageWarning = function(fpAccts) {
return LOCALE.maketext("[output,strong,Warning]: You selected [quant,_1,account,accounts] that use [asis,Microsoft® FrontPage Extensions] on the source server. The local server does not support [asis,FrontPage]. To resolve this issue, disable [asis,FrontPage] for each account before you attempt the transfer.", fpAccts);
};
MainController.prototype.unusedDedicatedIpWarning = function(unusedDedIpAcctCount) {
return LOCALE.maketext("[output,strong,Warning]: You have selected [quant,_1,account,accounts] which previously had a dedicated [asis,IP] address but you have chosen not to assign one after transfer.", unusedDedIpAcctCount);
};
MainController.prototype.packageFilterMessage = function() {
return (this.pkgsFilter) ?
LOCALE.maketext("Your search matched [numf,_1] of [quant,_2,record,records].", this.filteredPackages.length, this.PAGE.items.packages.length) :
LOCALE.maketext("There [numerate,_1,is,are] [quant,_1,record,records].", this.PAGE.items.packages.length);
};
MainController.prototype.packageSelectedMessage = function() {
return LOCALE.maketext("[_1] selected", this.selectedPackages.length || 0);// this.PAGE.items.packages | filter:{selected: 1}).length);
};
MainController.prototype.accountFilterMessage = function(acctCount) {
return (acctCount !== this.PAGE.items.accounts.length) ?
LOCALE.maketext("Your search matched [numf,_1] of [quant,_2,record,records].", acctCount || 0, this.PAGE.items.accounts.length) :
LOCALE.maketext("There [numerate,_1,is,are] [quant,_1,record,records].", this.PAGE.items.accounts.length);
};
MainController.prototype.accountSelectedMessage = function(acctCount) {
return LOCALE.maketext("[quant,_1,account,accounts] selected.", acctCount || 0);
};
MainController.prototype.accountConflictSelectedMessage = function(count) {
return LOCALE.maketext("You have selected [quant,_1,account,accounts] that cannot transfer properly because [numerate,_1,its username conflicts,their usernames conflict] with [numerate,_1,a username,usernames] on the local server.", count);
};
MainController.prototype.licenseOverloadMessage = function() {
if (this.REMAINING_ACCOUNT_SLOTS === 0) {
return LOCALE.maketext("Because the destination server’s license is limited to [quant,_1,account,accounts], you can only overwrite existing accounts.", this.SERVER_MAX_USERS);
} else {
return LOCALE.maketext("Because the destination server’s license is limited to [quant,_1,account,accounts], you can only transfer [quant,_2,account,accounts].", this.SERVER_MAX_USERS, this.REMAINING_ACCOUNT_SLOTS);
}
};
MainController.prototype.licenseOverloadCancelMessage = function(count) {
return LOCALE.maketext("Deselect [quant,_1,account,accounts] to continue.", count);
};
MainController.prototype.accountOverwriteResolveMessage = function(count) {
return LOCALE.maketext("To resolve this issue, you can overwrite the local [numerate,_1,account,accounts], rename the incoming [numerate,_1,account,accounts] so that the first [quant,_2,alphanumeric character,alphanumeric characters] do not match those of any local accounts, or deselect the [numerate,_1,account,accounts]. These options are mutually exclusive.",
count, this.PAGE.USERNAME_UNIQUE_LENGTH);
};
MainController.prototype.accountOverwriteConfirmMessage = function(count) {
return LOCALE.maketext("Overwrite conflicted [numerate,_1,account,accounts]", count);
};
MainController.prototype.accountConflictCancelMessage = function(count) {
return LOCALE.maketext("Deselect conflicted [numerate,_1,account,accounts]", count);
};
MainController.prototype.accountConflictResolveMessage = function(count) {
return LOCALE.maketext("To resolve this issue, you can rename the incoming [numerate,_1,account,accounts] so that the first [quant,_2,alphanumeric character,alphanumeric characters] do not match those of any local accounts, or deselect the [numerate,_1,account,accounts].",
count, this.PAGE.USERNAME_UNIQUE_LENGTH);
};
MainController.prototype.accountConflictSelectedResolveMessage = function(count) {
return LOCALE.maketext("You have selected [quant,_1,account transfer,account transfers] that will not complete properly because the [numerate,_1,username is,usernames are] reserved on the local server. To resolve this, you can either rename the incoming [numerate,_1,account,accounts] or deselect the [numerate,_1,account,accounts].", count);
};
MainController.prototype.accountConflictNameResolveMessage = function(count) {
return LOCALE.maketext("The following [quant,_1,remote user is set,remote users are set] to migrate with [numerate,_1,a new name,new names] whose first [quant,_2,character conflicts,characters conflict] with one or more other proposed new usernames. To resolve this, you can either rename the incoming [numerate,_1,account,accounts] or deselect [numerate,_1,it,them].",
count, this.PAGE.USERNAME_UNIQUE_LENGTH);
};
MainController.prototype.accountRenameMessage = function(to, from) {
return LOCALE.maketext("“[_1]” is set to be renamed “[_2]”.", to, from);
};
MainController.prototype.usernameValidationTitle = function(count) {
return LOCALE.maketext("You have entered [quant,_1,username that does,usernames that do] not meet this server’s username requirements:", count);
};
MainController.prototype.usernameValidationMessage = function(count) {
return LOCALE.maketext("Usernames must be no longer than [quant,_1,character,characters] and must contain only lowercase letters and numerals. They may not begin with a numeral. To resolve this, you can either fix the [numerate,_2,username,usernames] or deselect the [numerate,_1,account,accounts].",
this.PAGE.MAX_USERNAME_LENGTH, count);
};
MainController.prototype.usernameValidationAction = function(count) {
return LOCALE.maketext("Deselect the [numerate,_1,account,accounts] with [numerate,_1,the invalid username,invalid usernames].", count);
};
MainController.prototype.dedicatedIPDeselectAction = function(count) {
return LOCALE.maketext("Deselect “Dedicated IP Address” for conflicted [numerate,_1,account,accounts]", count);
};
MainController.prototype.dedicatedIPReselectAction = function(count) {
return LOCALE.maketext("Reselect “[_1]” for [numerate,_2,account,accounts]", LOCALE.maketext("Dedicated IP Address"), count);
};
MainController.prototype.domainsWithFrontpageMessage = function(count) {
return LOCALE.maketext("The following domains use [asis,FrontPage] on the source server:");
};
MainController.prototype.resellerNoCopyTitle = function(count) {
return LOCALE.maketext("You have selected [quant,_1,user,users] that will be transferred with no reseller privileges.", count);
};
MainController.prototype.resellerNoCopyMessage = function(count) {
if (this.PAGE.restricted_restore) {
return LOCALE.maketext("[numerate,_1,This user is a reseller,These users are resellers] on the source server. Restricted Restore does not allow the transfer of reseller privileges. After the system restores the [numerate,_1,user,users], you can assign reseller privileges in WHM’s Reseller Center interface ([output,em,WHM » Home » Resellers » Reseller Center]).", count);
} else {
return LOCALE.maketext("[numerate,_1,This user is a reseller,These users are resellers] on the source server. To transfer an account’s reseller privileges from the source server, edit the [output,strong,Transfer Configuration] for the desired [numerate,_1,account,accounts] above. Be aware that reseller privileges can give users special permissions, including full root access, to your server.", count);
}
};
MainController.prototype.resellerNoCopyFixMessage = function(count) {
return LOCALE.maketext("Transfer reseller privileges for the selected [numerate,_1,account,accounts].", count);
};
MainController.prototype.canNotTransferTempDomainTitle = function(count) {
return LOCALE.maketext("You have selected [numerate,_1,a temporary domain,temporary domains] for transfer. This server does not support temporary domains.", count);
};
MainController.prototype.canNotTransferTempDomainMessage = function(count) {
return LOCALE.maketext("The following [numerate,_1,domain is,domains are] temporary:", count);
};
MainController.prototype.canSubmitForm = function(warningLists, dedicatedIPAccounts, newSelectedUserList, canNotTransferTempDomainList) {
// Do warnings exists for any of these view lists
for (var i = 0; i < warningLists.length; i++) {
if (warningLists[i].length) {
return false;
}
}
// Block temp domain transfer
if ( canNotTransferTempDomainList > 0 ) {
return false;
}
// Check for selected items
if (!this.selectedPackages.length && !this.selectedAccounts.length && !this.selectedConfigurations.length) {
return false;
}
// Check for dedicated IP overages
if (dedicatedIPAccounts.length > this.PAGE.local.available_ips.length) {
return false;
}
// Check for Empty Slots
if (this.SERVER_MAX_USERS && newSelectedUserList.length > this.REMAINING_ACCOUNT_SLOTS) {
return false;
}
return true;
};
MainController.prototype.getCopyButtonLabel = function getCopyButtonLabel() {
return LOCALE.maketext("Copy");
};
MainController.prototype.submitSummaryMessage = function() {
var summaryItems = [];
if (this.selectedAccounts.length) {
summaryItems.push(LOCALE.maketext("[quant,_1,Account,Accounts]", this.selectedAccounts.length));
}
if (this.selectedPackages.length) {
summaryItems.push(LOCALE.maketext("[quant,_1,Package,Packages]", this.selectedPackages.length));
}
if (this.selectedConfigurations.length) {
summaryItems.push(LOCALE.maketext("[quant,_1,Server Configuration,Server Configurations]", this.selectedConfigurations.length));
}
return LOCALE.maketext("When you click [output,em,_1], you will start the transfer process for the following: [list_and,_2]", this.getCopyButtonLabel(), summaryItems);
};
var app;
try {
app = angular.module("App"); // For runtime
app.value("PAGE", PAGE);
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
/**
* Returns true if the account does not exist locally on the server
* @param {object} item
* @return {Boolean}
*/
app.filter("existingUsers", function() {
return function(items, exactMatchesOnly) {
var matched = [];
angular.forEach(items, function(item, index) {
if (exactMatchesOnly && item.localuser !== item.similarLocalUser) {
/* if exactMatchOnly is set, it will not return n character matches */
/* this is to allow filtering items to show only overwritable ones (exact matches) */
return false;
} else if (!exactMatchesOnly && item.localuser === item.similarLocalUser) {
return false;
}
/* will not return if user is also reserved because a reserved name overrides an existing name in transfers */
if (item.selected && item.invalidUser && item.userCheckStates.existing && !item.userCheckStates.reserved) {
matched.push(item);
}
});
return matched;
};
});
MainController.$inject = CONTROLLER_INJECTABLES;
var controller = app.controller("MainController", MainController);
return controller;
}
);
/*
# templates/transfer_tool/filters/accountFilter.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 */
define(
'app/filters/accountFilter',[
"angular"
],
function(angular) {
"use strict";
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
/**
* Filter that filters only on specific field based on $scope.acctsFilter. Will return true if the account passes the filter. Necessary for performance optimization.
* @param {onject} item
* @return {array}
*/
app.filter("accountFilter", function() {
return function(accounts, filterText) {
if (!filterText) {
return accounts;
}
var filteredAccounts = [];
angular.forEach(accounts, function(account) {
/* isUser */
if (account.user.indexOf(filterText) !== -1) {
filteredAccounts.push(account);
} else if (account.domain.indexOf(filterText) !== -1) {
/* isDomain */
filteredAccounts.push(account);
} else if (account.owner.indexOf(filterText) !== -1) {
/* isOwner */
filteredAccounts.push(account);
}
});
return filteredAccounts;
};
});
}
);
/*
# templates/transfer_tool/filters/advanceAccountFilter.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 */
define(
'app/filters/advanceAccountFilter',[
"angular"
],
function(angular) {
"use strict";
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
/**
* Filter that uses the options set in the advance filter form to further filter accounts. Will return true if the account passes the filter.
* @param {object} item
* @return {?array}
*/
app.filter("advanceAccountFilter", function() {
return function(accounts, advanceFilter) {
if (!advanceFilter) {
return accounts;
}
var filteredAccounts = [];
angular.forEach(accounts, function(account) {
if (account.remote_user.indexOf(advanceFilter.user) === -1) {
return;
}
if (account.domain.indexOf(advanceFilter.domain) === -1) {
return;
}
if (advanceFilter.owner && advanceFilter.owner.length && advanceFilter.owner.indexOf(account.owner) === -1) {
return;
}
if (advanceFilter.dedicated_ip >= 0 && account.dedicated_ip !== advanceFilter.dedicated_ip) {
return;
}
filteredAccounts.push(account);
});
return filteredAccounts;
};
});
}
);
/*
# templates/transfer_tool/controllers/AccountTableController.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 */
/* jshint -W003 */
/* jshint -W098*/
define(
'app/controllers/AccountTableController',[
"angular",
"cjt/util/locale",
"cjt/util/parse",
"app/services/workerNodes",
"app/filters/startFromFilter",
"app/filters/cpLimitToFilter",
"app/filters/accountFilter",
"app/filters/advanceAccountFilter",
"cjt/services/whm/nvDataService",
],
function(angular, LOCALE, PARSE, WorkerNodesService) {
"use strict";
var DEFAULT_CUSTOMIZED_LABEL = LOCALE.maketext("Default");
var CUSTOM_CUSTOMIZED_LABEL = LOCALE.maketext("Custom");
var SKIP_FLAG_DEFAULTS = {
"copy_homedir": 1,
"copy_databases": 1,
"copy_bwdata": 1,
"copy_reseller_privs": 0,
"copy_integration_links": 0,
};
var CONTROLLER_INJECTABLES = ["$scope", "$rootScope", WorkerNodesService.serviceName, "$filter", "$uibModal", "PAGE", "nvDataService", "OVERWRITE_OPTIONS", "OVERWRITE_STATES", "OVERWRITE_DESCRIPTION_TEMPLATE"];
/**
* @class Controller class for the account table. Used in the angular app with 'controller as'. This contains most logic pertaining to accounts.
* @param {object} $scope
* @param {object} $filter
*/
var AccountTableController = function($scope, $rootScope, $workerNodesService, $filter, $uibModal, PAGE, nvDataService, OVERWRITE_OPTIONS, OVERWRITE_STATES, OVERWRITE_DESCRIPTION_TEMPLATE) {
var _this = this;
_this.PAGE = PAGE;
_this.disableLiveTransfers = _this.PAGE.disable_live_transfers;
_this.isRestrictedRestore = _this.PAGE.restricted_restore;
_this.isRTL = PAGE.locale_is_rtl.toString() === "1";
_this.overwriteOptions = OVERWRITE_OPTIONS;
_this.reservedUsernamePatterns = _this.PAGE.local.reserved_username_patterns;
_this.localUserHash = Object.create(null);
_this.reservedUsernames = {};
_this.localPartialUsernames = {};
_this.defaultWorkerOptions = $workerNodesService.getDefaultWorkerOptions();
_this.workerOptionTypes = $workerNodesService.getWorkerOptionTypes();
_this.control_overwrite = _this.overwriteOptions[0];
_this.overwriteStates = OVERWRITE_STATES;
_this.accountFilterText = "";
_this.acctTable = {
currentPage: 1,
pageSize: 10,
reverse: false,
order: "remote_user",
advSearchOptions: {
domain: "",
user: "",
owner: [],
dedicated_ip: -1,
},
activeAdvFilter: null,
};
_this.getDedicatedIPDomains = function() {
return _this.PAGE.domainsWithDedicatedIp;
};
// This will require more logic as the banner messages that can reset dedicated
// IP addresses so for now we are returning the status
// quo of always displaying the column.
_this.showDedicatedIPColumn = true;
var supportsExpressTransfers = PAGE.remote ? PARSE.parsePerlBoolean(PAGE.remote.supports_express_transfers) : false;
var supportsLiveTransfers = PAGE.remote ? PARSE.parsePerlBoolean(PAGE.remote.supports_live_transfers) : false;
$scope.supportsTempDomains = PAGE.local ? PARSE.parsePerlBoolean(PAGE.local.supports_temp_domains) : false;
// If we support live transfers, it defaults to on
// Unless it has been disabled
var PROXY_FLAG_DEFAULT = supportsLiveTransfers && !_this.disableLiveTransfers;
$scope.supportsLiveTransfers = supportsLiveTransfers;
$scope.liveTransfersNoticeIsDismissed = PAGE.liveTransfersNoticeIsDismissed;
$scope.liveTransfersPre90NoticeIsDismissed = PAGE.liveTransfersPre90NoticeIsDismissed;
$scope.isAcctTabActive = true;
// Overload the live transfer support. This gets set when the hostname is invalid.
// We also need to overload and hide the notifications for live transfers
if (_this.disableLiveTransfers) {
$scope.liveTransfersNoticeIsDismissed = true;
}
$scope.dismissLiveTransfersNotice = function dismissLiveTransfersNotice() {
$scope.liveTransfersNoticeIsDismissed = true;
nvDataService.set("live_transfers_notice_is_dismissed", 1);
};
$scope.dismissLiveTransfersPre90Notice = function dismissLiveTransfersPre90Notice() {
$scope.liveTransfersPre90NoticeIsDismissed = true;
$scope.liveTransfersNoticeIsDismissed = true;
nvDataService.set("live_transfers_pre_90_notice_is_dismissed", 1);
};
$rootScope.showTable = function showTable() {
return !$scope.isAcctTabActive || (!$scope.supportsLiveTransfers || ($scope.supportsLiveTransfers && $scope.liveTransfersNoticeIsDismissed));
};
$rootScope.setAcctTabActive = function(isAcctTabActive) {
$scope.isAcctTabActive = isAcctTabActive;
};
_this.createUser = function(obj, index) {
obj.initialindex = index;
obj.copy_homedir = SKIP_FLAG_DEFAULTS["copy_homedir"];
obj.copy_databases = SKIP_FLAG_DEFAULTS["copy_databases"];
obj.copy_bwdata = SKIP_FLAG_DEFAULTS["copy_bwdata"];
obj.copy_reseller_privs = SKIP_FLAG_DEFAULTS["copy_reseller_privs"];
obj.copy_integration_links = SKIP_FLAG_DEFAULTS["copy_integration_links"];
obj.copy_proxy_option = PROXY_FLAG_DEFAULT;
obj.proxyOption = {
id: "proxy_option",
modelKey: "copy_proxy_option",
value: PROXY_FLAG_DEFAULT,
default: PROXY_FLAG_DEFAULT,
supports_live_transfers: PROXY_FLAG_DEFAULT,
supports_express_transfers: supportsExpressTransfers,
};
obj.selected = 0;
obj.overwrite_type = _this.overwriteOptions[0];
obj.overwrite_account = 0;
obj.overwrite_with_delete = 0;
obj.userCheckStates = {
duplicate: false,
reserved: false,
existing: false,
};
obj.dedicated_ip = (obj.dedicated_ip || _this.getDedicatedIPDomains()[obj.domain]) ? 1 : 0;
obj.existing_dedicated_ip = obj.dedicated_ip;
obj.oldlocaluser = obj.user;
obj.localuser = obj.user;
obj.invalidDomain = _this.PAGE.local.domains[obj.domain] && _this.PAGE.local.domains[obj.domain] !== obj.user ? 1 : 0;
obj.is_reseller = _this.PAGE.remote.resellers && _this.PAGE.remote.resellers[obj.remote_user] ? 1 : 0;
obj.invalidUser = _this.PAGE.local.users[obj.remote_user] ? 1 : 0;
obj.isCustomized = false;
obj.workerNodes = obj.worker_nodes || {};
obj.isTempDomain = obj.is_temp_domain || 0;
obj.serverSupportsTempDomains = $scope.supportsTempDomains;
// This is done in the service because it's done in multiple controllers
$workerNodesService.resetAccountToDefaults(obj);
};
_this.expandedAccount = false;
_this.PAGE.items.accounts.forEach(_this.createUser);
var accountFilter = $filter("accountFilter"),
advanceAccountFilter = $filter("advanceAccountFilter"),
startFrom = $filter("startFrom"),
orderBy = $filter("orderBy"),
limitTo = $filter("cpLimitTo");
// To better understand what these assignments are look at updateFilteredAccounts and updateViewableAccounts
_this.filteredAccounts = accountFilter(advanceAccountFilter(this.PAGE.items.accounts, _this.acctTable.activeAdvFilter), _this.accountFilterText);
_this.viewableAccounts = limitTo(startFrom(orderBy(_this.filteredAccounts, _this.acctTable.order, _this.acctTable.reverse), (_this.acctTable.currentPage - 1) * _this.acctTable.pageSize),
_this.acctTable.pageSize);
/**
* Reapplies filters for the filteredAccounts array
* Array -> Filtered by advanced -> filtered by basic
* accounts | advanceAccountFilter:activeAdvFilter | accountFilter:accountFilterText
*/
_this.updateFilteredAccounts = function() {
_this.filteredAccounts = accountFilter(
advanceAccountFilter(this.PAGE.items.accounts, _this.acctTable.activeAdvFilter), _this.accountFilterText);
};
/**
* Reapplies filteres for the viewableAccounts array.
* FilteredArray -> Ordered by(reverse?) -> start based on page # -> limit to page size
* filteredAccounts | orderBy: acctTable.order:acctTable.reverse | startFrom:(acctTable.currentPage - 1 ) * acctTable.pageSize | limitTo: acctTable.pageSize
*/
_this.updateViewableAccounts = function() {
var pageSize = _this.acctTable.pageSize;
var accounts = orderBy(_this.filteredAccounts, _this.acctTable.order, _this.acctTable.reverse);
accounts = limitTo(startFrom(accounts, (_this.acctTable.currentPage - 1) * pageSize), pageSize);
_this.viewableAccounts = accounts;
};
_this.getUsernamePattern = function(key) {
if (key === "TRIM_PATTERN") {
return new RegExp(_this.PAGE.UN_TRIM_REG_EXP);
} else if (key === "TRANSFER_USER_RESTRICT") {
return new RegExp(_this.PAGE.USERNAME_TRANSFER_REGEXP);
} else if (key === "NONTRANSFER_USER_RESTRICT") {
return new RegExp(_this.PAGE.USERNAME_REGEXP);
} else {
return null;
}
};
$scope.$on("recheckUsersCalled", function(eo, args) {
angular.forEach(args.dataset, function(acct) {
// Always skip forcing selection, since "Select None" will cause this to just get re-selected
_this.updateOverwriteFlags(acct, false, true);
_this.updateUserCheckStates(acct);
_this.checkAccountForDuplicateLocals(acct);
_this.updateAccountsControlRow();
});
});
$scope.$on("selectedAccountsEmptied", function() {
_this.acct_checkbox_control = false;
_this.updateAccountsControlRow();
});
$scope.$watch(function() {
return _this.acctTable;
}, function() {
_this.updateViewableAccounts();
}, true);
$scope.$watch(function() {
return _this.viewableAccounts;
}, function() {
_this.updateAccountsControlRow();
}, true);
for (var rUi = 0; rUi < _this.PAGE.local.reserved_usernames.length; rUi++) {
var reservedUsername = _this.PAGE.local.reserved_usernames[rUi].toLowerCase();
_this.reservedUsernames[reservedUsername] = 1;
}
/* used to check the eight v 16 restrictions of (no same first USERNAME_UNIQUE_LENGTH characters) */
/* pre processed for big lists */
angular.forEach(_this.PAGE.local.users, function(value, lUsername) {
var plUsername = _this.makeUsernamePartial(lUsername, true);
this[plUsername] = lUsername;
}, _this.localPartialUsernames);
angular.forEach(_this.PAGE.items.accounts, function(acct, key) {
/* store local user hash */
_this.storeLocalUserHash(acct.initialindex, acct.oldlocaluser, acct.localuser);
});
// Set the columnWidth
_this.numberOfColumns = 9;
["bytesused", "owner", "dedicated_ip"].forEach(function(key) {
if (!_this.showColumn(key)) {
_this.numberOfColumns--;
}
});
_this.skipOptions = [
{ id: "copy_homedir_for", label: LOCALE.maketext("Home Directory"), modelKey: "copy_homedir", default: SKIP_FLAG_DEFAULTS["copy_homedir"] },
// Disable reseller privileges skipOption for restricted restores
{ id: "reseller_privs_for", label: LOCALE.maketext("Reseller Privileges"), modelKey: "copy_reseller_privs", default: SKIP_FLAG_DEFAULTS["copy_reseller_privs"], disabled: _this.isRestrictedRestore },
{ id: "copy_acctdb_for", label: LOCALE.maketext("Databases"), modelKey: "copy_databases", default: SKIP_FLAG_DEFAULTS["copy_databases"] },
{ id: "copy_bwdata_for", label: LOCALE.maketext("Bandwidth Data"), modelKey: "copy_bwdata", default: SKIP_FLAG_DEFAULTS["copy_bwdata"] },
{ id: "integration_links_for", label: LOCALE.maketext("Integration Links"), modelKey: "copy_integration_links", default: SKIP_FLAG_DEFAULTS["copy_integration_links"] },
];
_this.openModal = $uibModal.open.bind($uibModal);
_this.overwriteDescriptionTemplate = OVERWRITE_DESCRIPTION_TEMPLATE;
this._workerOptionAltered = $workerNodesService.checkWorkerOptionsAltered.bind($workerNodesService);
return _this;
};
angular.extend(AccountTableController.prototype, {
/**
* Stores hashed usernames for duplication checks on large amounts of usernames
*/
storeLocalUserHash: function storeLocalUserHash(index, oldValue, newValue) {
var _this = this;
var newValuePartial = this.makeUsernamePartial(newValue, true);
var oldValuePartial = this.makeUsernamePartial(oldValue, true);
_this.localUserHash[oldValuePartial] = _this.localUserHash[oldValuePartial] || [];
angular.forEach(_this.localUserHash[oldValuePartial], function(value, key) {
if (value === index) {
_this.localUserHash[oldValuePartial].splice(key, 1);
}
});
_this.localUserHash[newValuePartial] = _this.localUserHash[newValuePartial] || [];
_this.localUserHash[newValuePartial].push(index);
},
/**
* Calls functions that update filtered and viewable accounts
*/
updateAccounts: function updateAccounts() {
this.updateFilteredAccounts();
this.updateViewableAccounts();
},
/**
* Returns class corresponding to glyph for account. Account must be selected and not have a domain conflict.
* @param {object} acct
* @return {?string}
*/
getGlyphClass: function getGlyphClass(acct) {
if (!acct.selected || acct.domainConflict) {
return;
}
if (acct.invalidUser) {
return "glyphicon-exclamation-sign";
} else {
return "glyphicon-ok";
}
},
/**
* Returns class for button that corresponds to what choice for dedicated ip was selected. Used in advance search form to indicate selection similar to radio button.
* @param {int} thisVal
* @return {string|Boolean}
*/
dedicatedIpClass: function dedicatedIpClass(thisVal) {
return thisVal === this.acctTable.advSearchOptions.dedicated_ip ? "btn-primary" : "btn-default";
},
/**
* Check the state of the various control columns
*
* @param {object[]} accounts list of accounts to build the value based on
* @param {string} param parameter on account to check
* @returns {*} current value of control column. Various values possible.
*/
_getColumnControlState: function _getColumnControlState(accounts, param) {
// If there are no accounts, return false
if (!accounts.length) {
return false;
}
// If any are not checked, return false
for (var i = 0; i < accounts.length; i++) {
var account = accounts[i];
if (!account[param]) {
return false;
}
}
// If all are checked, return true
return true;
},
/**
* Check the state of the overwrite control column
*
* @param {object[]} accounts list of accounts to build the value based on
* @param {string} param parameter on account to check
* @returns {object|null} value of the parameter for accounts if the same. Undefined otherwise.
*/
_getOverwriteColumnControlState: function _getOverwriteColumnControlState(accounts, param) {
// Update only based on selected accounts in list
// If there are no accounts, return false
if (!accounts.length) {
return this.overwriteOptions[0];
}
var currentValue;
for (var i = 0; i < accounts.length; i++) {
var account = accounts[i];
if (!this.canNeedOverwrite(account)) {
// Only ones that might need overwrite should be considered
continue;
}
// Store the first value as a baseline
if (!currentValue) {
currentValue = account[param];
}
// If any are different from one another, default to undef (to allow setting it)
if (account[param].value !== currentValue.value) {
return;
}
}
// If all are one of the two overwrite values [1,2], use that value
// If no value was determined, then no overwrite-capable items were found, use the zero option
return currentValue ? currentValue : this.overwriteOptions[0];
},
/**
* Called to check and adjust control row check boxes based on viewable account params
*/
updateAccountsControlRow: function updateAccountsControlRow() {
var checks = [{
param: "selected",
controlModel: "acct_checkbox_control",
checkFunction: this._getColumnControlState.bind(this),
}, {
param: "dedicated_ip",
controlModel: "control_dedicated_ip",
onlySelected: true,
checkFunction: this._getColumnControlState.bind(this),
}, {
param: "overwrite_type",
controlModel: "control_overwrite",
onlySelected: true,
checkFunction: this._getOverwriteColumnControlState.bind(this),
}];
var selectedAccounts = this.viewableAccounts.filter(function(account) {
return !!account.selected;
});
angular.forEach(checks, function(check) {
var accounts = check.onlySelected ? selectedAccounts : this.viewableAccounts;
this[check.controlModel] = check.checkFunction(accounts, check.param);
}, this);
},
/**
* Sets the advance search($scope.acctTable.advSearchOptions) to either be a copy of the object passed in or if no object, to the default value. We set it this way
* because otherwise Angular will bind the object directily causing any updates to be instantaneous, breaking the experience.
* @param {object} obj
*/
setAdvanceSearch: function setAdvanceSearch(obj) {
this.acctTable.activeAdvFilter = angular.copy(obj);
this.updateAccounts();
if (!obj) {
this.acctTable.advSearchOptions = {
domain: "",
user: "",
owner: [],
dedicated_ip: -1,
};
}
},
/**
* Goes over array of accounts and sets the dedicated_ip flag if the destination domain already has a dedicated ip.
* @param {array} dataset
*/
handleDedicatedIpForArray: function handleDedicatedIpForArray(dataset) {
var _self = this;
angular.forEach(dataset, function(acct) {
if (_self.getDedicatedIPDomains()[acct.domain]) {
acct.dedicated_ip = 1;
}
});
},
/**
* Returns true if the account has a destination domain with a dedicated ip already or if there are no available IP addresses.
* @param {object} acct
* @return {Boolean}
*/
isDedicatedIpDisabled: function isDedicatedIpDisabled(acct) {
return this.getDedicatedIPDomains()[acct.domain] ? true : !this.PAGE.local.available_ips.length;
},
/**
* Returns true for account that can be overwritten
* @param {object} item
* @return {Boolean}
*/
overwriteFilter: function overwriteFilter(item) {
return this.PAGE.local.users[item.user] || this.PAGE.local.users[item.localuser] || (this.PAGE.local.domains[item.domain] === item.localuser);
},
/**
* Updates account username checks and stores then on acct.userCheckStates
* then updates calls updateLocalUserStatus() to reset acct.invalidUser
*
* @method validateAccount
* @param acct {Object} angular user account object
*/
validateAccount: function validateAccount(acct) {
// update account localuser hash for duplicate validation
/* which large accounts this was the only feasible option. */
/* deep watch expressions were far too cumbersome. */
this.storeLocalUserHash(acct.initialindex, acct.oldlocaluser, acct.localuser);
/* must be called before oldusername is reset to reset duplication on old set */
this.checkAccountForDuplicateLocals(acct);
acct.oldlocaluser = acct.localuser;
this.updateUserCheckStates(acct);
},
/**
* Seperate function allows independent updates to userCheckStates
* used in ToggleCheckAll
*
* @method updateuserCheckStatus
* @param acct {Object} angular user account object
*/
updateUserCheckStates: function updateUserCheckStates(acct) {
/* response is now in the form of false, or failure object */
acct.userCheckStates.reserved = this.isReservedUsername(acct);
acct.userCheckStates.existing = this.isExistingLocalUser(acct);
/* refers to the specific makeup of the new username (does it match the regexp) */
acct.userCheckStates.invalid = !this.isLocalUsernameValid(acct);
acct.overwrite_type = acct.selected ? acct.overwrite_type : this.overwriteOptions[0];
this.updateLocalUserStatus(acct);
},
/**
* updates 'invalidUser' based on state check flags
*
* @method updateLocalUserStatus
* @param acct {Object} angular user account object
*/
updateLocalUserStatus: function updateLocalUserStatus(acct) {
if ((acct.userCheckStates.existing !== false && acct.overwrite_type.value === this.overwriteStates.NO_OVERWRITE ) ||
acct.userCheckStates.duplicate !== false ||
acct.userCheckStates.invalid !== false ||
acct.userCheckStates.reserved !== false) {
acct.invalidUser = true;
} else {
acct.invalidUser = false;
}
},
/**
* Returns the class to be used for input field highlighting.
* @param {object} acct
* @return {?string}
*/
getInputClass: function getInputClass(acct) {
if (!acct.selected || acct.domainConflict) {
return;
}
if (acct.invalidUser) {
return "has-error";
} else {
return "has-success";
}
},
/**
* Returns username converted from account as as string
*
* @method getUserNameFromAcct
* @param acct {Object} angular user account object
* @return {String} value of username
*/
getUserNameFromAcct: function getUserNameFromAcct(acct) {
var username = acct.localuser || "";
if (!username.length) {
username = acct.remote_user;
}
return username;
},
/**
* Returns Array of accounts that the username matches (can be partial match).
*
* @method findAccountsByUsername
* @param username {String} angular user account object
* @param matchFirstNChars {int} number of matching characters defaults to all
* passing -1 will be treated as a 'match all characters'
* @param startingIndex {int} which account to begin the interation from (faster)
* @return {Array} of acct indexes that match
*/
findAccountsByUsername: function findAccountsByUsername(username, matchFirstNChars, startingIndex) {
/* if matchFirstNChars > 0 use matchFirstNChars | else use -1 (interpretted as all) */
matchFirstNChars = Number(matchFirstNChars) || -1;
startingIndex = Number(startingIndex) || 0;
if (matchFirstNChars !== -1) {
username = username.substr(0, matchFirstNChars);
}
var matchedAccounts = [];
var otherAccount;
for (var i = startingIndex; i < this.PAGE.items.accounts.length; i++) {
otherAccount = this.PAGE.items.accounts[i];
if (otherAccount.localuser.toLowerCase().indexOf(username) === 0) {
if (username.length < matchFirstNChars && otherAccount.localuser.length > username.length) {
/* this means that the otherAccount has additional characters in the NChars range
so it is no longer enough matching characters */
continue;
}
/* username was found in it */
if (matchFirstNChars === -1 && username.length !== otherAccount.localuser.length) {
/* matchFirstNChars is set to default (match all), but lengths of the usernames */
continue;
}
/* good length, good first n chars */
matchedAccounts.push(i);
}
}
return matchedAccounts;
},
/**
* finds accounts by username, then returns selected accounts
*
* @method findSelectedAccountsByUsername
* see: findAccountsByUsername for @params
*/
findSelectedAccountsByUsername: function findSelectedAccountsByUsername( /* inherit from findAccountsByUsername */ ) {
var usernames = this.findAccountsByUsername.apply(this, arguments);
var selected = [];
if (usernames === false) {
return selected;
}
var uIndex;
for (var i = 0; i < usernames.length; i++) {
uIndex = usernames[i];
if (this.PAGE.items.accounts[uIndex].selected) {
selected.push(uIndex);
}
}
return selected;
},
/**
* Checks one local account for duplicates and marks userCheckStates.duplicate accordingly
*
* @method checkAccountForDuplicateLocals
*/
checkAccountForDuplicateLocals: function checkAccountForDuplicateLocals(acct) {
/* clear status flag */
acct.userCheckStates.duplicate = false;
/* get oldlocaluser and reset old duplicates */
var oldUsernamePartial = this.makeUsernamePartial(acct.oldlocaluser, true);
/* recheck old username set to update their duplicate flag */
if (this.localUserHash[oldUsernamePartial] && this.localUserHash[oldUsernamePartial].length === 1) {
var previousDuplicateUser = this.localUserHash[oldUsernamePartial][0];
this.PAGE.items.accounts[previousDuplicateUser].userCheckStates.duplicate = false;
this.updateLocalUserStatus(this.PAGE.items.accounts[previousDuplicateUser]);
}
/* no local username entered? Ignore it! */
if (!acct.localuser) {
return;
}
var username = acct.localuser;
var trimmedUsername = this.makeUsernamePartial(username, true);
var accountIndexOrObject;
var accountIndex;
var selectedOthers = [];
/* reset all, store still selected items for next loop */
for (var i = 0; i < this.localUserHash[trimmedUsername].length; i++) {
accountIndex = this.localUserHash[trimmedUsername][i];
accountIndexOrObject = this.PAGE.items.accounts[accountIndex];
accountIndexOrObject.userCheckStates.duplicate = false;
this.updateLocalUserStatus(accountIndexOrObject);
/* items that aren't selected are ignored */
if (!accountIndexOrObject.selected) {
continue;
}
selectedOthers.push(accountIndexOrObject);
}
/* if multiple items remain selected in this hash, set them as duplicates */
if (selectedOthers.length > 1) {
for (i = 0; i < selectedOthers.length; i++) {
accountIndexOrObject = selectedOthers[i];
accountIndexOrObject.userCheckStates.duplicate = true;
this.updateLocalUserStatus(accountIndexOrObject);
}
}
},
/**
* Checks all local accounts for duplicates and marks userCheckStates.duplicate accordingly
*
* @method checkForDuplicateLocals
*/
checkForDuplicateLocals: function checkForDuplicateLocals() {
/* check this username against other usernames that have been checked for transfer */
var i, otherAccount;
/* reset all initially to allow faster parsing later in function*/
for (i = 0; i < this.PAGE.items.accounts.length; i++) {
otherAccount = this.PAGE.items.accounts[i];
otherAccount.userCheckStates.duplicate = false;
this.updateLocalUserStatus(otherAccount);
}
/* iterate through all, skip already set duplicates, */
for (i = 0; i < this.PAGE.items.accounts.length; i++) {
otherAccount = this.PAGE.items.accounts[i];
if (otherAccount.userCheckStates.duplicate) {
/* because of forward iteration nature of this, this
has been cleared and marked as a set for another item */
continue;
}
this.checkAccountForDuplicateLocals(otherAccount);
}
},
/**
* Returns Boolean defining whether account username is considered reserved.
*
* @method isReservedUsername
* @param acct {Object} angular user account object
* @return {Boolean} true if username is reserved
*/
isReservedUsername: function isReservedUsername(acct) {
var username = this.getUserNameFromAcct(acct);
/* trim spaces from ends of username */
var trimmedUsername = username.replace(/^\s*(.+)\s*$/i, "$1");
/* check against reserved system names to see if this on exists */
if (trimmedUsername in this.reservedUsernames) {
return true;
}
/* interim list of restricted partial names (reg ex patterns) */
/* list cannot contain these specific parts */
/*
matches names, or parts of names dependent on need
<[name]>$ matches a <[name]> at the end of a username
^<[name]> matches a <[name]> at the beginning of the username
^<[name]>$ matches exactly that <[name]> with no trailing or begining characters
*/
var currentRegExp, reservedNamePattern;
for (var i = 0; i < this.reservedUsernamePatterns.length; i++) {
reservedNamePattern = this.reservedUsernamePatterns[i];
/* build dynamic regular expression */
currentRegExp = new RegExp(reservedNamePattern, "gi");
if (trimmedUsername.match(currentRegExp) !== null) {
return true;
}
}
return false;
},
/**
* Returns Boolean representing whether acct.username exists on local machine
* in part or fully (only first USERNAME_UNIQUE_LENGTH characters need to match)
*
* @method isExistingLocalUser
* @param acct {Object} angular user account object
* @return {Boolean} dependent on user existing on local server
*/
isExistingLocalUser: function isExistingLocalUser(acct) {
var username = this.getUserNameFromAcct(acct);
var trimmedUsername = this.makeUsernamePartial(username, true);
if (trimmedUsername in this.localPartialUsernames) {
acct.similarLocalUser = this.localPartialUsernames[trimmedUsername];
return true;
}
return false;
},
/**
* Tests username against globally established regexp
* uses the transfer specific regexp if remote_user === localuser
* uses the non-transfer regexp otherwise
*
* @method isLocalUsernameValid
* @param acct {Object} angular user account object
* @return {Boolean} true or false based on regexp.test
*/
isLocalUsernameValid: function isLocalUsernameValid(acct) {
var re = this.getUsernamePattern("TRANSFER_USER_RESTRICT");
var partial = this.makeUsernamePartial(acct.localuser, true);
if (!partial.replace(/\-/gi, "").length) {
return false;
}
return re.test(acct.localuser);
},
makeUsernamePartial: function makeUsernamePartial(username, removeSpecialCharacters) {
if (removeSpecialCharacters) {
username = username.replace(/[\_\.]/gi, "");
}
return username.replace(this.getUsernamePattern("TRIM_PATTERN"), "$1").toLowerCase();
},
/**
* Get the classes for each account row
*
* @param {object} account user account to check against
* @returns {string} if the item is selected, and either the domain or user is invalid, else nothing
*/
getAccountRowClasses: function getAccountRowClasses(account) {
if (!account.selected) {
return "";
}
if (!account.invalidUser && !account.invalidDomain) {
return "success";
}
return "danger";
},
/**
* Determine whether a column should show based on the key
*
* @param {string} columnName account key to check
* @returns {boolean} qwhether to show the column
*
*/
showColumn: function showColumn(columnName) {
switch (columnName) {
case "bytesused":
return !!this.PAGE.remote.has_disk_used;
case "owner":
return !!this.PAGE.remote.has_owners;
case "dedicated_ip":
return !!this.showDedicatedIPColumn;
default:
return true;
}
},
/**
* For Each account, determine the current chevron icon state
*
* @param {*} account account to check
* @returns {string} if the account is expanded, 'fa-chevron-down', else a left or right based on the RTL settings of LOCALE
*/
getChevronClasses: function getChevronClasses(account) {
var collapsedClass = this.isRTL ? "fa-chevron-left" : "fa-chevron-right";
return this.isAccountExpanded(account) ? "fa-chevron-down" : collapsedClass;
},
/**
* Update the isCustomized value for an account based on skipOptions states
*
* @param {object} account account to check
*/
updateCustomizedFlag: function updateCustomizedFlag(account) {
account.isCustomized = this._skipOptionsAltered(account) || this._workerOptionAltered(account) || this._proxyOptionAltered(account);
},
_skipOptionsAltered: function _skipOptionsAltered(account) {
var skipKeys = Object.keys(SKIP_FLAG_DEFAULTS);
for (var i = 0; i < skipKeys.length; i++) {
var skipKey = skipKeys[i];
if (account[skipKey] !== SKIP_FLAG_DEFAULTS[skipKey]) {
return true;
}
}
return false;
},
/**
* Check if Live Transfer option has be altered
*
* @param {object} account account to check
*/
_proxyOptionAltered: function _proxyOptionAltered(account) {
if (account.proxyOption.value !== account.proxyOption.default) {
return true;
}
return false;
},
/**
* Check if the account is expanded
*
* @param {object} account account to check
* @returns {boolean} true|false state of account expansion
*/
isAccountExpanded: function isAccountExpanded(account) {
return this.expandedAccount === account;
},
/**
* Expand the account
*
* @param {object} account account to expand
*/
expandAccount: function expandAccount(account) {
if (this.expandedAccount) {
this.collapseAccount(this.expandedAccount);
}
this.expandedAccount = account;
this.accountSettingUpdated(account, true);
},
/**
* Collapse the account
*
* @param {object} account account to collapse
*/
collapseAccount: function collapseAccount(account) {
this.expandedAccount = false;
},
/**
* Toggle the expanded state of an account
*
* @param {object} account account to toggle
*/
toggleAccountExpansion: function toggleAccountExpansion(account) {
if ( this.isAccountExpanded(account) ) {
this.collapseAccount(account);
} else {
this.expandAccount(account);
}
},
/**
* Get the label based on the isCustomized value of the account
*
* @param {object} account account to check
*/
getCustomizeLabel: function getCustomizeLabel(account) {
return account.isCustomized ? CUSTOM_CUSTOMIZED_LABEL : DEFAULT_CUSTOMIZED_LABEL;
},
/**
* Called from the view when the expanded panel of an account change
*
* @param {object} account that changed
* @param {string} key what option changed
* @param {*} value what is the new value of that option
*/
onExpandPanelOptionChanged: function onExpandPanelOptionChanged(account, key, value) {
this.updateAccountSetting(account, key, value);
},
/**
* Called from the view when "apply to all" is emmitted
*
* @param {object} fromAccount source account to copy from
* @param {object[]} toAccounts destination accounts to copy to
* @param {object[]} skipOptions which skip options to update
* @param {object} workerOptions which worker options to update
* @param {object} proxyOption proxy option to update
*/
onExpandPanelApplyToAll: function onExpandPanelApplyToAll(fromAccount, toAccounts, skipOptions, workerOptions, proxyOption) {
// Get Skip Model Keys Once
var skipModelKeys = skipOptions.map(function(skipOption) {
return skipOption.modelKey;
}, this);
// Get Worker Model Keys Once
var workerModelKeys = workerOptions && Object.keys(workerOptions).map(function(workerOptionType) {
return workerOptions[workerOptionType].modelKey;
}, this) || [];
// Include other options
var modelKeys = ["dedicated_ip", "copy_proxy_option"].concat(skipModelKeys, workerModelKeys);
var copyOverwrite = this.canNeedOverwrite(fromAccount);
// Update extra items and validate
toAccounts.filter(function(toAccount) {
if (toAccount === fromAccount) {
return false;
}
return true;
}).forEach(function(toAccount) {
var myCopyOverwrite = copyOverwrite && this.canNeedOverwrite(toAccount);
var myModelKeys = modelKeys.slice();
if (myCopyOverwrite) {
myModelKeys.push("overwrite_type");
}
myModelKeys.forEach(function(modelKey) {
this.updateAccountSetting(toAccount, modelKey, fromAccount[modelKey], false);
}, this);
// If, but only if, both accounts are need-overwrite
// we should copy the overwrite setting.
if (myCopyOverwrite) {
this.updateOverwriteFlags(toAccount, false);
}
this.validateAccount(toAccount);
}, this);
},
/**
* Update an account setting and update settings based on it.
*
* @param {object} account account on which to update the setting
* @param {string} key setting to update
* @param {*} value new value
* @param {boolean} validate validate after update
*/
updateAccountSetting: function updateAccountSetting(account, key, value, validate) {
if (!validate && key === "copy_proxy_option") {
account.proxyOption.value = value;
}
account[key] = value;
this.accountSettingUpdated(account, validate);
},
/**
* Called when any account setting is updated. Optionally validates the account based on state.
*
* @param {object} account account that had the update occur
* @param {boolean} validate when true, validateAccount() will be called
*/
accountSettingUpdated: function accountSettingUpdated(account, validate, skipForceSelect) {
var callUpdate;
if (!skipForceSelect && !account.selected) {
account.selected = 1;
callUpdate = true;
}
this.updateCustomizedFlag(account);
if (validate) {
this.validateAccount(account);
}
if (callUpdate) {
this.updateSelectedAccounts();
}
},
/**
* Set a function to call when the selected accounts change
*
* @param {function} func function to call when the selected accounts change
*/
setUpdateSelectedAccountsFunction: function setUpdateSelectedAccountsFunction(func) {
this._updateSelectedAccounts = func;
},
/**
* Called when selected accounts change, if set by setUpdateSelectedAccountsFunction() a callback function will be called too
*
*/
updateSelectedAccounts: function updateSelectedAccounts() {
if (this._updateSelectedAccounts) {
this._updateSelectedAccounts();
}
},
/**
* Called by the view when an accounts selection changes
*
* @param {object} account the account that changed. Unused by the function.
*/
accountSelectionUpdated: function accountSelectionUpdated(account) {
this.updateSelectedAccounts();
},
/**
* Check if an account is eligible for the overwrite dropdown
*
* @param {object} account account to be checked
* @returns {boolean} whether or not the account could ever need overwriting
*/
canNeedOverwrite: function canNeedOverwrite(account) {
var localUsers = this.PAGE.local.users;
var localDomains = this.PAGE.local.domains;
return localUsers[account.user] || localUsers[account.localuser] || (localDomains[account.domain] === account.localuser);
},
/**
* Update the specific overwrite flags based on the overwrite_type of the account
* accountSettingUpdate is then called, passing along the validate value
*
* @param {object} account account to be updated
* @param {boolean} validate whether to validate during the accountSettingUpdated phase
*/
updateOverwriteFlags: function updateOverwriteFlags(account, validate, skipForceSelect) {
account.overwrite_account = 0;
account.overwrite_with_delete = 0;
switch (account.overwrite_type.value) {
case this.overwriteStates.OVERWRITE_WITH_DELETE:
account.overwrite_with_delete = 1;
// fall through here is intentional as we want it to set overwrite_account as well when we set overwrite_with_delete
case this.overwriteStates.OVERWRITE:
account.overwrite_account = 1;
break;
}
this.accountSettingUpdated(account, validate, skipForceSelect);
},
openOverwriteDescriptionModal: function openOverwriteDescriptionModal() {
var self = this;
self.overwriteModal = this.openModal({
templateUrl: this.overwriteDescriptionTemplate,
controller: ["$scope", function($scope) {
$scope.close = function() {
self.closeOverwriteDescriptionModal();
};
}],
});
},
closeOverwriteDescriptionModal: function closeOverwriteDescriptionModal() {
if (this.overwriteModal) {
this.overwriteModal.close();
this.overwriteModal = null;
}
},
});
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
app.value("PAGE", PAGE);
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
AccountTableController.$inject = CONTROLLER_INJECTABLES;
var controller = app.controller("AccountTableController", AccountTableController);
return controller;
}
);
/*
# templates/transfer_tool/directives/boolToIntDirective.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 */
define(
'app/directives/boolToIntDirective',[
"angular"
],
function(angular) {
"use strict";
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
// Main - reusable
/**
* Angular directive that when attached to an element with an ng-model will render that model as true or false
* but ensure that any changing will result in 1 or 0 values. Necessary because Perl cannot evaluate JavaScript
* true/false when submitted in JSON.
*/
app.directive("boolToInt", [
function() {
return {
restrict: "A",
require: "ngModel",
priority: 99,
link: function(scope, elem, attrs, controller) {
controller.$formatters.push(function(modelValue) {
return !!modelValue;
});
controller.$parsers.push(function(viewValue) {
return viewValue ? 1 : 0;
});
}
};
}
]);
}
);
/*
# templates/transfer_tool/directives/preventBubblingDirective.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 */
define(
'app/directives/ngDebounceDirective',[
"angular"
],
function(angular) {
"use strict";
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
// Main - reusable
// https://gist.github.com/tommaitland/7579618
/**
* Angular directive that prevents input from being processed. Useful when paired with an input filter or ajax request
* to prevent rapid calling of underlining functionality.
*/
app.directive("ngDebounce", ["$timeout",
function($timeout) {
return {
restrict: "A",
require: "ngModel",
priority: 99,
link: function(scope, elm, attr, ngModelCtrl) {
if (attr.type === "radio" || attr.type === "checkbox") {
return;
}
elm.unbind("input");
var debounce;
elm.bind("input", function() {
$timeout.cancel(debounce);
debounce = $timeout(function() {
scope.$apply(function() {
ngModelCtrl.$setViewValue(elm.val());
});
}, 250);
});
elm.bind("blur", function() {
// http://stackoverflow.com/questions/12729122/prevent-error-digest-already-in-progress-when-calling-scope-apply
$timeout(function() {
scope.$apply(function() {
ngModelCtrl.$setViewValue(elm.val());
});
});
});
}
};
}
]);
}
);
/*
# templates/transfer_tool/directives/preventBubblingDirective.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 */
define(
'app/directives/preventBubblingDirective',[
"angular"
],
function(angular) {
"use strict";
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
// Main - reusable
/**
* Angular directive which prevents event propogation. Used in a Bootstrap dropdown menu with a form to prevent
* accidental closure when interacting with the fields.
*/
app.directive("preventBubbling", [
function() {
return {
restrict: "A",
link: function(scope, element) {
element.bind("click", function(event) {
event.preventDefault();
event.stopPropagation();
});
}
};
}
]);
}
);
/*
# templates/transfer_tool/directives/clickOnceDirective.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 */
define(
'app/directives/clickOnceDirective',[
"angular"
],
function(angular) {
"use strict";
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
// Main - reusable
/**
* Angular directive which disables a button on form submit.
* Original work found here: http://stackoverflow.com/a/19825570
*/
app.directive("clickOnce", ["$timeout",
function($timeout) {
return {
restrict: "A",
link: function(scope, element, attrs) {
var replacementText = attrs.clickOnce;
element.bind("click", function() {
$timeout(function() {
if (replacementText) {
element.html(replacementText);
}
element.attr("disabled", true);
}, 0);
});
}
};
}
]);
}
);
/*
# templates/transfer_tool/filters/overwriteFilter.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(
'app/filters/overwriteFilter',[
"angular"
],
function(angular) {
"use strict";
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
/**
* Returns true for account that can be overwritten
* @param {object} item
* @return {array}
*/
app.filter("overwriteFilter", function() {
var localUsers = PAGE.local.users;
var localDomains = PAGE.local.domains;
return function(accounts) {
var filteredAccounts = [];
angular.forEach(accounts, function(account) {
if (localUsers[account.user] ||
localUsers[account.localuser] ||
localDomains[account.domain] === account.localuser) {
filteredAccounts.push(account);
}
});
return filteredAccounts;
};
});
}
);
/*
# templates/transfer_tool/filters/bytesFilter.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 */
define(
'app/filters/bytesFilter',[
"angular"
],
function(angular) {
"use strict";
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
// Main - reusable
/**
* Angular filter which returns a string localized with LOCALE.format_bytes
* @return {string}
*/
app.filter("bytes", function() {
return function(bytes) {
return LOCALE.format_bytes(bytes);
};
});
}
);
/*
# cpanel - templates/transfer_tool/getacctlist.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 require:false, define:false, confirm:false, alert:false, PAGE, EVENT:true */
(function(window) {
"use strict";
var enterSessionIfNotPending = function() {
CPANEL.api({
"func": "get_transfer_session_state",
"data": {
"transfer_session_id": PAGE.transfer_session_id
},
"callback": {
success: function(o) {
var response = o.cpanel_data;
var statename = response.state_name;
if (o.cpanel_error) {
alert(LOCALE.maketext("Failed to retrieve the session state: [_1]", o.cpanel_error));
} else if (statename) {
if (statename !== "PENDING") {
if (confirm(LOCALE.maketext("The session has already started and cannot accept additional inputs. Would you like to view the transfer session?"))) {
window.location.href = "transfer_session?transfer_session_id=" + encodeURIComponent(PAGE.transfer_session_id);
} else {
window.history.go(-1);
/* Don't let them enter data on the screen as it will screen as it will just fail on the next screen since it the transfer sessions is already in progress */
}
}
}
},
failure: function() {
alert(LOCALE.maketext("Failed to retrieve the session state."));
}
}
});
};
var reAnalyzeRemote = function() {
var reAnalyzeRemoteButton = CPANEL.Y.one("#reAnalyzeRemoteButton"),
preChangeText = reAnalyzeRemoteButton.innerHTML;
reAnalyzeRemoteButton.disabled = true;
reAnalyzeRemoteButton.innerHTML = "<i class='glyphicon glyphicon-refresh animate-spin'></i> " + LOCALE.maketext("Performing Analysis …");
CPANEL.api({
func: "analyze_transfer_session_remote",
data: {
"transfer_session_id": PAGE.transfer_session_id
},
callback: CPANEL.ajax.build_page_callback(function() {
window.location.href = "transfer_selection?transfer_session_id=" + encodeURIComponent(PAGE.transfer_session_id);
}, {
pagenotice_container: "callback_block",
on_error: function() {
reAnalyzeRemoteButton.disabled = false;
reAnalyzeRemoteButton.innerHTML = preChangeText;
}
})
});
};
var init = function() {
EVENT.on(CPANEL.Y.one("#reAnalyzeRemoteButton"), "click", reAnalyzeRemote);
enterSessionIfNotPending();
// Parse Blocker Data for Easy Apache
if (PAGE.configuration_modules.Apache.analysis) {
PAGE.EABlockers = PAGE.configuration_modules.Apache.analysis["Blocker Data"];
// Loop through each item, look for Blocker level item
for (var i = PAGE.EABlockers.length - 1; i >= 0; i--) {
if (PAGE.EABlockers[i].vendor_id === "Cpanel" && PAGE.EABlockers[i].items) {
for (var j = PAGE.EABlockers[i].items.length - 1; j >= 0; j--) {
if (PAGE.EABlockers[i].items[j].status === 2) {
PAGE.blockerExists = true;
}
}
}
}
}
};
EVENT.onDOMReady(init);
})(window);
/* angular portion */
define(
'app/getacctlist',[
"angular",
"app/directives/accountExpandPanel",
"cjt/util/locale",
"app/overwriteStates",
"app/overwriteOptions",
"jquery",
"ngRoute",
"uiBootstrap",
"angular-chosen",
"ngSanitize",
"cjt/modules",
"cjt/directives/toggleLabelInfoDirective",
],
function(angular, AccountExpandPanel, LOCALE, OVERWRITE_STATES, OVERWRITE_OPTIONS) {
"use strict";
return function() {
var app = angular.module("App", [
"cjt2.config.whm.configProvider", // This needs to load first
"cjt2.whm",
"ngSanitize",
"ui.bootstrap",
AccountExpandPanel.namespace
]);
app.value("OVERWRITE_DESCRIPTION_TEMPLATE", "overwriteWithDeleteDescription.ptt");
app.value("OVERWRITE_STATES", OVERWRITE_STATES);
app.value("OVERWRITE_OPTIONS", OVERWRITE_OPTIONS);
app.value("LOCAL_WORKER_NODES", PAGE.local.linked_nodes);
app.value("REMOTE_WORKER_NODES", PAGE.remote.linked_nodes);
return require(
[
"cjt/bootstrap",
// Application Modules
"app/controllers/MainController",
"app/controllers/AccountTableController",
"app/directives/boolToIntDirective",
"app/directives/ngDebounceDirective",
"app/directives/preventBubblingDirective",
"cjt/directives/pageSizeDirective",
"app/directives/clickOnceDirective",
"app/filters/overwriteFilter",
"app/filters/bytesFilter"
], function(BOOTSTRAP) {
BOOTSTRAP(document);
});
};
}
);
Back to Directory
File Manager