Viewing File: /usr/local/cpanel/base/frontend/jupiter/api_tokens/index.cmb.js
/*
# api_tokens/services/apiTokens.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/apiTokens',[
"angular",
"lodash",
"cjt/util/locale",
"cjt/io/uapi-request",
"cjt/io/batch-request",
"cjt/modules",
"cjt/io/api",
"cjt/io/uapi",
"cjt/services/APICatcher"
],
function(angular, _, LOCALE, APIRequest, BatchAPIRequest) {
"use strict";
var MODULE_NAMESPACE = "cpanel.apiTokens.services.apiTokens";
var SERVICE_NAME = "APITokensService";
var MODULE_REQUIREMENTS = [ "cjt2.services.apicatcher" ];
var SERVICE_INJECTABLES = ["APICatcher", "$q", "$log"];
var _currentDateTime = new Date();
_currentDateTime = _currentDateTime.getTime() / 1000;
var _twentyFourHours = 24 * 60 * 60;
var APIToken = function(id, unrestricted, features, createdOn, expiresAt) {
this.id = id;
this.new = false;
this.label = id;
this.unrestricted = unrestricted;
if (!_.isBoolean(unrestricted)) {
this.unrestricted = unrestricted && unrestricted.toString() === "1";
}
this.features = features;
this.createdOn = parseInt(createdOn, 10);
this.expired = false;
this.expiresSoon = false;
this.hasExpiry = false;
if (expiresAt) {
this.hasExpiry = true;
this.expiresAt = parseInt(expiresAt, 10);
if (this.expiresAt <= _currentDateTime) {
this.expired = true;
} else if (this.expiresAt - _currentDateTime < _twentyFourHours) {
this.expiresSoon = true;
}
}
return this;
};
/**
*
* Service Factory to generate the Account Preferences service
*
* @module ApiTokensService
* @memberof cpanel.apiTokens
*
* @param {Object} APICatcher base service
* @returns {Service} instance of the Domains service
*/
var SERVICE_FACTORY = function(APICatcher, $q, $log) {
var Service = function() {};
Service.prototype = Object.create(APICatcher);
_.assign(Service.prototype, {
_featuresMetadata: null,
_tokens: [],
/**
* Wrapper for .promise method from APICatcher
*
* @param {Object} apiCall api call to pass to .promise
* @returns {Promise}
*
* @example $service._promise( $service._apiCall( "Tokens", "rename", { name:"OLDNAME", new_name:"NEWNAME" } ) );
*/
_promise: function _promise() {
// Because nested inheritence is annoying
return APICatcher.promise.apply(this, arguments);
},
/**
* Wrapper for building an apiCall
*
* @private
*
* @param {String} module module name to call
* @param {String} func api function name to call
* @param {Object} args key value pairs to pass to the api
* @returns {UAPIRequest} returns the api call
*
* @example _apiCall( "Tokens", "rename", { name:"OLDNAME", new_name:"NEWNAME" } )
*/
_apiCall: function _createApiCall(module, func, args) {
var apiCall = new APIRequest.Class();
apiCall.initialize(module, func, args);
return apiCall;
},
/**
* Wrapper for building an batch apiCall
*
* @private
*
* @param {Array<APIRequest>} apiCalls Api calls to process as a batch
* @returns {BatchAPIRequest} returns the api call
*
* @example _batchAPICall( [_apiCall("Tokens", "rename", { name:"OLDNAME", new_name:"NEWNAME" }), _apiCall("Tokens", "rename", { name:"OLDNAME", new_name:"NEWNAME" })] )
*/
_batchAPICall: function _createBatchAPICall(apiCalls) {
var batchAPICall = new BatchAPIRequest.Class(apiCalls);
return batchAPICall;
},
/**
* Process a single result of a fetchTokens call
*
* @private
*
* @param {Object} result single result item from a fetchTokens() call
* @returns {Object} sanitized result object
*
* @example fetchTokens()
*/
_processAPITokenResult: function _processAPITokenResult(apiTokenResult) {
var id = apiTokenResult.name;
var unrestricted = apiTokenResult.has_full_access;
var features = apiTokenResult.features;
var createdOn = apiTokenResult.create_time;
var expiresAt = apiTokenResult.expires_at;
return new APIToken(id, unrestricted, features, createdOn, expiresAt);
},
/**
* Process the results of a fetchTokens call
*
* @private
*
* @param {Object} results fetchTokens() api results object with a .data param
* @returns {Array<Object>} returns an array of processed items
*
* @example _processAPITokensResults({data:[]})
*/
_processAPITokensResults: function _processAPITokensResults(results) {
var apiTokens = results.data;
this._tokens = apiTokens.map(this._processAPITokenResult.bind(this));
return this._tokens;
},
/**
* Create a fetchTokens api call, but don't process it
*
* @private
*
* @returns {APIRequest} returns the APIRequest for fetchTokens();
*
* @example _getAPITokens()
*/
_fetchTokens: function _getAPITokens() {
var apiCall = this._apiCall("Tokens", "list");
return apiCall;
},
/**
* Create a deleteToken api call, but don't process it
*
* @private
*
* @param {String} id token name to delete
* @returns {APIRequest} returns the APIRequest for fetchTokens();
*
* @example _deleteToken("mytokenhash")
*/
_deleteToken: function _deleteAPIToken(id) {
var apiCall = this._apiCall("Tokens", "revoke", {
name: id
});
return apiCall;
},
/**
* Process A Feature Result
*
* @param {string} installed perl boolean 1|0
* @param {string} featureKey id of the feature
* @param {string} featureLabel descriptive label of the feature
* @param {Array} badges array of badge strings associated with the feature
* @returns {object} feature object
*/
_processFeatureResult: function _processFeatureResult(installed, featureKey, featureLabel, badges) {
return {
label: featureLabel,
id: featureKey,
installed: installed && installed.toString() === "1",
badges: badges
};
},
/**
* For each feature item in the results, create a feature object
*
* @param {Object} results
* @returns {Array<Object>} array of feature objects
*/
_processFeaturesResults: function _processFeaturesResults(results) {
var self = this;
var data = results.data;
if (data) {
var features = [];
angular.forEach(data, function(installed, featureKey) {
var featureLabel = featureKey;
var badges = [];
if (self._featuresMetadata && self._featuresMetadata[featureKey]) {
var featureMeta = self._featuresMetadata[featureKey];
featureLabel = featureMeta.name;
if (featureMeta.is_cpaddon.toString() === "1") {
badges.push( LOCALE.maketext("[asis,cPAddon]") );
}
if (featureMeta.is_plugin.toString() === "1") {
badges.push( LOCALE.maketext("Plugin") );
}
}
features.push( self._processFeatureResult(installed, featureKey, featureLabel, badges ) );
});
// Only return the installed ones
var installedFeatures = features.filter(function(feature) {
return feature.installed;
});
// Always sort them by label
return _.sortBy(installedFeatures, function(feature) {
return feature.label;
});
}
},
/**
* Build the getFeatures APICall
*
* @returns {UAPIRequest} uapi request for Features list_features
*/
_getFeatures: function _getFeatures() {
var apiCall = this._apiCall("Features", "list_features");
return apiCall;
},
/**
* Process the result of a Features get_features_metadata call
*
* @param {Object} result object with a data parameter
*/
_processFeatureMetadataResult: function _processFeatureMetadataResult(result) {
var data = result.data;
this._featuresMetadata = {};
if (data) {
data.forEach(function(featureMetaItem) {
this._featuresMetadata[featureMetaItem.id] = featureMetaItem;
}, this);
}
},
/**
* Wrapper for building an fetchTokens apiCall
*
* @public
*
* @returns {Promise<Object>} returns the promise and then the processed api tokens
*
* @example fetchTokens()
*/
fetchTokens: function getAPITokens() {
var apiCall = this._fetchTokens();
return this._promise(apiCall).then(this._processAPITokensResults.bind(this));
},
/**
* Get the current list of tokens
*
* @returns {Array<APIToken>} array of APIToken objects
*/
getTokens: function getTokens() {
return this._tokens;
},
/**
* Wrapper for building an deleteTokens apiCall
*
* @public
*
* @param {Array<String>} tokens array of token hashes to delete
* @returns {Promise<Object>} returns the promise and then the updated api tokens
*
* @example deleteTokens(["tokenHASH","otherTokenHash"])
*/
deleteTokens: function deleteAPITokens(tokens) {
var self = this;
var tokenCalls = tokens.map( self._deleteToken.bind(self) ).map( self._promise.bind(self) );
return $q.all(tokenCalls).then( self.fetchTokens.bind(self) );
},
/**
* API Wrapper call for obtaining the features list
*
* @returns {Array<Object>} array of feature objects
*/
getFeatures: function getFeatures() {
var self = this;
var featuresAPICall = this._getFeatures();
var apiCalls = [featuresAPICall];
if (!self._featuresMetadata) {
apiCalls.unshift( this._apiCall("Features", "get_feature_metadata") );
}
var batchCall = self._batchAPICall(apiCalls);
return self._promise(batchCall).then(function _separateBatchResults(result) {
if (apiCalls.length > 1) {
// Extract Feature Metadata for Processing
var featureMetadataResult = result.data.shift();
self._processFeatureMetadataResult.call(self, featureMetadataResult);
}
return result.data.pop();
}).then(self._processFeaturesResults.bind(self));
},
/**
* Process the results of a token creation API call
*
* @param {String} id name of the token created
* @param {Boolean} unrestricted whether the token is unrestricted
* @param {Array<String>} features added features
* @param {Object} results api results object
* @returns {String} newly created token
*/
_processTokenCreationResults: function _processTokenCreationResults(id, unrestricted, features, expiresAt, results) {
var data = results.data;
var newToken = new APIToken(id, unrestricted, features, new Date().getTime() / 1000, expiresAt);
newToken.new = true;
this._tokens.push(newToken);
return data.token;
},
/**
* API Wrapper call for createing tokens
*
* @param {String} id name of the token created
* @param {Boolean} unrestricted whether the token is unrestricted
* @param {Array<String>} features added features
* @param {Array<String>} expiresAt (optional) epoch time in seconds that token expires
* @returns {Promise<String>} promise and then the newly created token
*/
createToken: function createToken(id, unrestricted, features, expiresAt) {
var apiCall;
if (unrestricted) {
apiCall = this._apiCall("Tokens", "create_full_access", {
name: id,
expires_at: expiresAt
});
} else {
apiCall = this._apiCall("Tokens", "create_limited", {
name: id,
feature: features,
expires_at: expiresAt
});
}
return this._promise(apiCall).then(this._processTokenCreationResults.bind(this, id, unrestricted, features, expiresAt));
},
/**
* Get an API Token by id
*
* @param {String} id
* @returns {APIToken} api token with the matching id
*/
getTokenById: function getTokenById(id) {
var tokens = this.getTokens();
for (var i = 0; i < tokens.length; i++) {
if (tokens[i].id === id) {
return tokens[i];
}
}
return;
},
/**
* Update the Token restrictions for a token
*
* @param {String} tokenName id of the token to update
* @param {Boolean} unrestricted whether it should be treated as restricted
* @param {Array} features what features to restrict by
* @returns {Promise} restriction updating promise
*/
updateTokenRestrictions: function updateTokenRestrictions(tokenName, unrestricted, features) {
var apiCall;
var self = this;
if (unrestricted) {
apiCall = this._apiCall("Tokens", "set_full_access", {
name: tokenName
});
} else {
apiCall = this._apiCall("Tokens", "set_features", {
name: tokenName,
feature: features
});
}
return self._promise(apiCall).then(function() {
var token = self.getTokenById(tokenName);
token.unrestricted = unrestricted;
token.features = features;
});
},
/**
* Wrapper to call Token rename
*
* @param {String} tokenName old token name
* @param {Strng} newTokenName new token name
* @returns {Promise} rename token promise
*/
renameToken: function renameToken(tokenName, newTokenName) {
var apiCall = this._apiCall("Tokens", "rename", {
name: tokenName,
new_name: newTokenName
});
return this._promise(apiCall);
}
});
return new Service();
};
SERVICE_INJECTABLES.push(SERVICE_FACTORY);
var app = angular.module(MODULE_NAMESPACE, MODULE_REQUIREMENTS);
app.factory(SERVICE_NAME, SERVICE_INJECTABLES);
return {
"class": SERVICE_FACTORY,
"serviceName": SERVICE_NAME,
"namespace": MODULE_NAMESPACE
};
}
);
/*
# api_tokens/directives/tableShowingDirecitve.js Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define: false */
define(
'app/directives/tableShowing',[
"angular",
"cjt/util/locale",
"cjt/core"
],
function(angular, LOCALE, CJT) {
"use strict";
/**
* Directive to render the "Showing 1 - 4 of 10"
*
* @module table-showing
* @memberof cpanel.apiTokens
*
* @param {Number} start first number in range ([1]-4)
* @param {Number} limit second number in range (1-[4])
* @param {Number} total total number of items (10)
*
* @example
* <table-showing start="1" limit="4" total="10"></table-showing>
*
*/
var TEMPLATE = "directives/tableShowing.phtml";
var RELATIVE_PATH = "api_tokens/" + TEMPLATE;
var TEMPLATE_PATH = CJT.config.debug ? CJT.buildFullPath(RELATIVE_PATH) : TEMPLATE;
var MODULE_NAMESPACE = "cpanel.apiTokens.tableShowing.directive";
var module = angular.module(MODULE_NAMESPACE, []);
var CONTROLLER = function($scope) {
/**
* Get the rendered string from LOCALE
*
* @method getShowingText
* @public
*
* @return {String} localized string
*
*/
$scope.getShowingText = function getShowingText() {
return LOCALE.maketext("[_1] - [_2] of [_3]", $scope.start, $scope.limit, $scope.total);
};
};
module.directive("tableShowing", function tableShowing() {
return {
templateUrl: TEMPLATE_PATH,
restrict: "EA",
scope: {
start: "=",
limit: "=",
total: "="
},
transclude: true,
controller: ["$scope", CONTROLLER]
};
});
return {
"class": CONTROLLER,
"namespace": MODULE_NAMESPACE,
"template": TEMPLATE
};
}
);
/*
# cjt/decorators/paginationDecorator.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/decorators/paginationDecorator',[
"angular",
"cjt/core",
"cjt/util/locale",
"uiBootstrap"
],
function(angular, CJT, LOCALE) {
"use strict";
var module;
var MODULE_NAMESPACE = "cpanel.emailAccounts";
var TEMPLATE_PATH = "decorators/pagination.phtml";
var RELATIVE_PATH = "email_accounts/" + TEMPLATE_PATH;
try {
module = angular.module(MODULE_NAMESPACE);
} catch (e) {
module = angular.module(MODULE_NAMESPACE, ["ui.bootstrap.pagination"]);
}
module.config(["$provide", function($provide) {
// Extend the ngModelDirective to interpolate its name attribute
$provide.decorator("uibPaginationDirective", ["$delegate", function($delegate) {
var uiPaginationDirective = $delegate[0];
/**
* Update the ids in the page collection
*
* @method updateIds
* @param {Array} pages
* @param {string} id Id of the directive, used as a prefix
*/
var updateIds = function(pages, id) {
if (!pages) {
return;
}
pages.forEach(function(page) {
page.id = id + "_" + page.text;
});
};
/**
* Update aria labels page collection
*
* @method updateIds
* @param {Array} pages
*/
var updateAriaLabel = function(pages) {
if (!pages) {
return;
}
pages.forEach(function(page) {
page.ariaLabel = LOCALE.maketext("Go to page “[_1]”.", page.text);
});
};
/**
* Update current selected text
*
* @method updateCurrentSelectedText
* @param {string} page - Current page number
* @param {string} totalPages - Total pages
* @returns {string} Text to display
*/
var updateCurrentSelectedText = function(page, totalPages) {
return LOCALE.maketext("Page [numf,_1] of [numf,_2]", page, totalPages);
};
// Use a local template
uiPaginationDirective.templateUrl = CJT.config.debug ? CJT.buildFullPath(RELATIVE_PATH) : TEMPLATE_PATH;
// Extend the page model with the id field.
var linkFn = uiPaginationDirective.link;
/**
* Compile function for uiPagination Directive
*
* @method uiPaginationDirective.compile
*/
uiPaginationDirective.compile = function() {
return function(scope, element, attrs, ctrls) {
var paginationCtrl = ctrls[0];
linkFn.apply(this, arguments);
scope.parentId = attrs.id;
scope.ariaLabels = {
title: LOCALE.maketext("Pagination"),
firstPage: LOCALE.maketext("Go to first page."),
previousPage: LOCALE.maketext("Go to previous page."),
nextPage: LOCALE.maketext("Go to next page."),
lastPage: LOCALE.maketext("Go to last page."),
};
scope.updateCurrentSelectedText = updateCurrentSelectedText;
var render = paginationCtrl.render;
paginationCtrl.render = function() {
render.apply(paginationCtrl);
updateIds(scope.pages, scope.parentId);
updateAriaLabel(scope.pages);
};
};
};
return $delegate;
}]);
}]);
return {
namespace: MODULE_NAMESPACE,
template: TEMPLATE_PATH
};
}
);
/*
# api_tokens/directives/itemLister.js Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define: false */
define(
'app/directives/itemLister',[
"angular",
"lodash",
"cjt/core",
"app/directives/tableShowing",
"app/decorators/paginationDecorator",
"ngSanitize",
"ngRoute",
"cjt/modules",
"cjt/directives/pageSizeButtonDirective",
"cjt/services/cpanel/componentSettingSaverService",
"cjt/directives/toggleSortDirective",
"cjt/directives/searchDirective",
"cjt/directives/pageSizeDirective",
"cjt/directives/indeterminateState",
"cjt/filters/startFromFilter",
"cjt/decorators/paginationDecorator"
],
function(angular, _, CJT, TableShowingDirective, PaginationDecorator) {
"use strict";
/**
* Item Lister combines the typical table functions, pageSize,
* showing, paginator, search, and allows you to plug in multiple
* views.
*
* @module item-lister
* @memberof cpanel.apiTokens
* @restrict EA
*
* @param {String} id disseminated to other objects
* @param {Array} items Items that will be paginated, array of objs
* @param {Array} header-items represents the columns of the table
*
* @example
* <item-lister
* id="MyItemLister"
* items="[a,b,c,d,e]"
* create-route="/create"
* can-select-all
* header-items="[{field:"blah",label:"Blah",sortable:false}]">
* <my-item-lister-view></my-item-lister-view>
* </item-lister>
*
*/
var MODULE_REQUIREMENTS = [
TableShowingDirective.namespace,
PaginationDecorator.namespace,
"ngRoute",
"ngSanitize",
"cjt2.filters.startFrom"
];
var MODULE_NAMESPACE = "cpanel.apiTokens.itemLister.directive";
var CSSS_COMPONENT_NAME = "apiTokensItemLister";
var CONTROLLER_INJECTABLES = ["$routeParams", "$scope", "$filter", "$log", "$window", "componentSettingSaverService", "ITEM_LISTER_CONSTANTS"];
var CONTROLLER = function itemListerController($routeParams, $scope, $filter, $log, $window, $CSSS, ITEM_LISTER_CONSTANTS) {
$scope.viewCallbacks = [];
$scope.checkAll = {
indeterminate: false,
all: false
};
var filters = {
filter: $filter("filter"),
orderBy: $filter("orderBy"),
startFrom: $filter("startFrom"),
limitTo: $filter("limitTo")
};
/**
*
* Filter items based on filterValue
*
* @private
*
* @param {Array} filteredItems items to filter
* @returns {Array} filtered items
*/
$scope._filter = function _filter(filteredItems) {
// filter list based on search text
if ($scope.filterValue !== "") {
return filters.filter(filteredItems, $scope.filterValue, false);
}
return filteredItems;
};
/**
*
* Sort items based on sort.sortDirection and sort.sortBy
*
* @private
*
* @param {Array} filteredItems items to sort
* @returns {Array} sorted items
*/
$scope._sort = function _sort(filteredItems) {
// sort the filtered list
if ($scope.sort.sortDirection !== "" && $scope.sort.sortBy !== "") {
return filters.orderBy(filteredItems, $scope.sort.sortBy, $scope.sort.sortDirection !== "asc");
}
return filteredItems;
};
/**
*
* Paginate the items based on pageSize and currentPage
*
* @private
*
* @param {Array} filteredItems items to paginate
* @returns {Array} paginated items
*/
$scope._paginate = function _paginate(filteredItems) {
// filter list based on page size and pagination
if ($scope.totalItems > _.min($scope.pageSizes)) {
var start = ($scope.currentPage - 1) * $scope.pageSize;
var limit = $scope.pageSize;
filteredItems = filters.startFrom(filteredItems, start);
filteredItems = filters.limitTo(filteredItems, limit);
$scope.showPager = true;
// table statistics
$scope.start = start + 1;
$scope.limit = start + filteredItems.length;
} else {
// hide pager and pagination
$scope.showPager = false;
if (filteredItems.length === 0) {
$scope.start = 0;
} else {
// table statistics
$scope.start = 1;
}
$scope.limit = filteredItems.length;
}
return filteredItems;
};
/**
*
* Update the NVData stored settings for the directive
*
* @private
*
* @param {String} lastInteractedItem last item interacted with
*/
$scope._updatedListerState = function _updatedListerState(lastInteractedItem) {
if ($scope.loadingInitialState) {
return;
}
var storedSettings = {
pageSize: $scope.pageSize,
sort: {
sortDirection: $scope.sort.sortDirection,
sortBy: $scope.sort.sortBy
}
};
$CSSS.set(CSSS_COMPONENT_NAME, storedSettings);
};
/**
*
* Event function called on interaction with an item
*
* @private
*
* @param {Object} event event object
* @param {Object} parameters event parameters {interactionID:...}
*/
$scope._itemInteracted = function _itemInteracted(event, parameters) {
if (parameters.interactionID) {
$scope._updatedListerState(parameters.interactionID);
}
};
/**
* Register a callback to call on the update of the lister
*
* @method registerViewCallback
*
* @param {Function} callback function to callback to
*
*/
this.registerViewCallback = function registerViewCallback(callback) {
$scope.viewCallbacks.push(callback);
callback($scope.filteredItems);
};
/**
* Get the header items
*
* @method getHeaderItems
*
* @return {Array} returns array of objects containing labels
*
*/
this.getHeaderItems = function getHeaderItems() {
return $scope.headerItems;
};
/**
* Deregister a callback (useful for view changes)
*
* @method deregisterViewCallback
*
* @param {Function} callback callback to deregister
*
*/
this.deregisterViewCallback = function deregisterViewCallback(callback) {
for (var i = $scope.viewCallbacks.length - 1; i >= 0; i--) {
if ($scope.viewCallbacks[i] === callback) {
$scope.viewCallbacks.splice(i, 1);
}
}
};
/**
* Function called to rebuild the view from internal components
*
* @return {Array} filtered items
*/
$scope.fetch = function fetch() {
var filteredItems = [];
if ($scope.items) {
filteredItems = $scope._filter($scope.items);
}
// update the total items after search
$scope.totalItems = filteredItems.length;
filteredItems = $scope._sort(filteredItems);
filteredItems = $scope._paginate(filteredItems);
$scope.filteredItems = filteredItems;
$scope._updatedListerState();
angular.forEach($scope.viewCallbacks, function updateCallback(viewCallback) {
viewCallback($scope.filteredItems);
});
$scope.$emit(ITEM_LISTER_CONSTANTS.ITEM_LISTER_UPDATED_EVENT, { meta: { filterValue: $scope.filterValue }, items: filteredItems });
$scope._updateSelectAllState();
return filteredItems;
};
/**
* Return the focus of the page to the search at the top and scroll to it
*
*/
$scope.focusSearch = function focusSearch() {
angular.element(document).find("#" + $scope.parentID + "_search_input").focus();
$window.scrollTop = 0;
};
/**
*
* Event function for a table configuration being clicked
*
* @param {Object} config which config was clicked
*/
$scope.tableConfigurationClicked = function tableConfigurationClicked(config) {
$scope.$emit(ITEM_LISTER_CONSTANTS.TABLE_ITEM_BUTTON_EVENT, { actionType: "tableConfigurationClicked", config: config });
};
$scope._updateSelectAllState = function _updateSelectAllState() {
$scope.selectedItems = $scope.filteredItems.filter(function filterSelectedItems(item) {
return item.selected;
});
if ($scope.selectedItems.length && $scope.filteredItems.length && $scope.selectedItems.length === $scope.filteredItems.length) {
$scope.checkAll.all = true;
} else {
$scope.checkAll.all = false;
}
};
$scope.getIndeterminateState = function getIndeterminateState() {
return $scope.selectedItems.length && $scope.filteredItems.length && $scope.filteredItems.length !== $scope.selectedItems.length;
};
$scope.toggleSelectAll = function toggleSelectAll() {
if ($scope.selectedItems.length < $scope.filteredItems.length) {
$scope.filteredItems.forEach(function selectAll(item) {
item.selected = true;
});
$scope.$emit(ITEM_LISTER_CONSTANTS.ITEM_LISTER_SELECT_ALL, { items: $scope.filteredItems });
} else {
$scope.filteredItems.forEach(function selectAll(item) {
item.selected = false;
});
$scope.$emit(ITEM_LISTER_CONSTANTS.ITEM_LISTER_DESELECT_ALL, { items: $scope.filteredItems });
}
$scope._updateSelectAllState();
};
$scope.$on(ITEM_LISTER_CONSTANTS.TABLE_ITEM_BUTTON_EVENT, $scope._itemInteracted);
$scope.$on(ITEM_LISTER_CONSTANTS.TABLE_ITEM_SELECTED, $scope._updateSelectAllState);
$scope.$on(ITEM_LISTER_CONSTANTS.TABLE_ITEM_DESELECTED, $scope._updateSelectAllState);
angular.extend($scope, {
maxPages: 5,
totalItems: $scope.items.length,
filteredItems: [],
currentPage: 1,
pageSize: 20,
pageSizes: [20, 100, 500],
start: 0,
limit: 20,
filterValue: "",
sort: {
sortDirection: "asc",
sortBy: $scope.headerItems.length ? $scope.headerItems[0].field : ""
}
}, {
filterValue: $routeParams["q"]
});
/**
*
* Initiate CSSS saved state is loaded
*
* @private
*
* @param {Object} initialState saved state for directive
*/
$scope._savedStateLoaded = function _savedStateLoaded(initialState) {
angular.extend($scope, initialState, {
filterValue: $routeParams["q"]
});
};
var registerSuccess = $CSSS.register(CSSS_COMPONENT_NAME);
if ( registerSuccess ) {
$scope.loadingInitialState = true;
registerSuccess.then($scope._savedStateLoaded, $log.error).finally(function() {
$scope.loadingInitialState = false;
$scope.fetch();
});
}
$scope.$on("$destroy", function() {
$CSSS.unregister(CSSS_COMPONENT_NAME);
});
$scope.fetch();
$scope.$watch("items", $scope.fetch);
};
var TEMPLATE_PATH = "directives/itemLister.ptt";
var RELATIVE_PATH = "api_tokens/" + TEMPLATE_PATH;
var TEMPLATE_URL = CJT.config.debug ? CJT.buildFullPath(RELATIVE_PATH) : TEMPLATE_PATH;
var DIRECTIVE_INJECTABLES = ["$window", "$log", "componentSettingSaverService"];
var DIRECTIVE_LINK = function(scope, element, attrs) {
if (!_.isUndefined(attrs.canSelectAll) && attrs.canSelectAll !== "0") {
scope.canSelectAll = true;
}
scope.controlsBlock = null;
scope.contentBlock = null;
/**
*
* Attach controls to the view
*
* @private
*
* @param {HTMLElement} elem html element to transclude as controls
*/
scope._attachControls = function _attachControls(elem) {
scope.controlsBlock.append(elem);
};
/**
*
* Attach Other items to view
*
* @private
*
* @param {HTMLElement} elem element to treat as the table body
*/
scope._attachOthers = function _attachOthers(elem) {
elem.setAttribute("id", scope.parentID + "_transcludePoint");
elem.setAttribute("ng-if", "filteredItems.length");
scope.contentBlock.replaceWith(elem);
};
/**
*
* Attach a transclude item
*
* @private
*
* @param {HTMLElement} elem html element to determine attachment point for
*/
scope._attachTransclude = function _attachTransclude(elem) {
if (angular.element(elem).hasClass("lister-controls")) {
scope._attachControls(elem);
} else {
scope._attachOthers(elem);
}
};
/**
*
* Find transclude items to attach to the view
*
*/
scope._findTranscludes = function _findTranscludes() {
// *cackles maniacally*
// *does a multi-transclude anyways*
scope.controlsBlock = element.find("#" + scope.parentID + "_transcludedControls");
scope.contentBlock = element.find("#" + scope.parentID + "_transcludePoint");
var transcludedBlock = element.find("div.transcluded");
var transcludedItems = transcludedBlock.children();
angular.forEach(transcludedItems, scope._attachTransclude, scope);
transcludedBlock.remove();
};
/* There is a dumb race condition here */
/* So we have to delay to get the content transcluded */
setTimeout(scope._findTranscludes, 2);
};
var DIRECTIVE = function itemLister($window, $log, $CSSS) {
return {
templateUrl: TEMPLATE_URL,
restrict: "EA",
scope: {
parentID: "@id",
items: "=",
headerItems: "=",
tableConfigurations: "=",
createRoute: "@"
},
transclude: true,
replace: true,
link: DIRECTIVE_LINK,
controller: CONTROLLER_INJECTABLES.concat(CONTROLLER)
};
};
var module = angular.module(MODULE_NAMESPACE, MODULE_REQUIREMENTS);
module.directive("itemLister", DIRECTIVE_INJECTABLES.concat(DIRECTIVE));
return {
"class": CONTROLLER,
"namespace": MODULE_NAMESPACE,
"link": DIRECTIVE_LINK,
"template": TEMPLATE_PATH
};
}
);
/*
# api_tokens/filters/htmlSafeString.js Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define: false */
define(
'app/filters/htmlSafeString',[
"angular",
"lodash"
],
function(angular, _) {
"use strict";
/**
* Wrapper for lodash escape
*
* @module htmlSafeString
* @memberof cpanel.apiTokens
*
* @example
* {{ domain.domain | htmlSafeString }}
*
*/
var MODULE_NAMESPACE = "cpanel.apiTokens.htmlSafeString.filter";
var MODULE_REQUIREMENTS = [ ];
var CONTROLLER_INJECTABLES = [];
var CONTROLLER = function CopyFieldController() {
return _.escape;
};
var module = angular.module(MODULE_NAMESPACE, MODULE_REQUIREMENTS);
module.filter("htmlSafeString", CONTROLLER_INJECTABLES.concat(CONTROLLER));
return {
"class": CONTROLLER,
"namespace": MODULE_NAMESPACE
};
}
);
/*
# api_tokens/directives/itemListerView.js Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define: false */
define(
'app/directives/itemListerView',[
"angular",
"lodash",
"cjt/core",
"cjt/util/locale",
"app/filters/htmlSafeString",
],
function(angular, _, CJT, LOCALE, HTMLSafeString) {
"use strict";
/**
* Item Lister View is a view that pairs with the item lister to
* display items manage link. It must
* be nested within an item lister
*
* @module item-lister-view
* @restrict EA
* @memberof cpanel.apiTokens
*
* @example
* <item-lister>
* <item-lister-view></item-lister-view>
* </item-lister>
*
*/
var MODULE_NAMESPACE = "cpanel.apiTokens.itemListerView.directive";
var MODULE_REQUIREMENTS = [ HTMLSafeString.namespace ];
var TEMPLATE = "directives/itemListerView.ptt";
var RELATIVE_PATH = "api_tokens/" + TEMPLATE;
var TEMPLATE_PATH = CJT.config.debug ? CJT.buildFullPath(RELATIVE_PATH) : TEMPLATE;
var CONTROLLER_INJECTABLES = ["$scope", "$location", "ITEM_LISTER_CONSTANTS"];
var CONTROLLER = function ItemListViewController($scope, $location, ITEM_LISTER_CONSTANTS) {
$scope.toggleSelection = function toggleSelection(item) {
if (item.selected) {
$scope.$emit(ITEM_LISTER_CONSTANTS.TABLE_ITEM_SELECTED, { item: item } );
} else {
$scope.$emit(ITEM_LISTER_CONSTANTS.TABLE_ITEM_DESELECTED, { item: item } );
}
};
$scope.getCreationLabel = function getCreationLabel(createdOn) {
return LOCALE.local_datetime(createdOn, "datetime_format_medium");
};
$scope.getExpirationLabel = function getExpirationLabel(expiresAt) {
return expiresAt ? LOCALE.local_datetime(expiresAt, "datetime_format_medium") : "";
};
$scope.getRestrictionLabel = function getRestrictionLabel(isUnrestricted) {
return isUnrestricted ? LOCALE.maketext("Unrestricted") : LOCALE.maketext("Limited");
};
$scope.getItems = function getItems() {
return $scope.items;
};
$scope.manageToken = function manageToken(token) {
$location.path("/manage").search("token", token.id);
};
};
var module = angular.module(MODULE_NAMESPACE, MODULE_REQUIREMENTS);
module.value("PAGE", PAGE);
var DIRECTIVE_LINK = function($scope, $element, $attrs, $ctrl) {
$scope.items = [];
$scope.headerItems = $ctrl.getHeaderItems();
$scope.updateView = function updateView(viewData) {
$scope.items = viewData;
};
$ctrl.registerViewCallback($scope.updateView.bind($scope));
$scope.$on("$destroy", function() {
$ctrl.deregisterViewCallback($scope.updateView);
});
};
module.directive("itemListerView", function itemListerItem() {
return {
templateUrl: TEMPLATE_PATH,
restrict: "EA",
replace: true,
require: "^itemLister",
link: DIRECTIVE_LINK,
controller: CONTROLLER_INJECTABLES.concat(CONTROLLER)
};
});
return {
"class": CONTROLLER,
"namespace": MODULE_NAMESPACE,
"link": DIRECTIVE_LINK,
"template": TEMPLATE
};
}
);
/*
# cpanel - api_tokens/validators/uniqueTokenName.js Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define: false */
/** @namespace cpanel.domains.validators.tokenNameIsUnique */
define(
'app/validators/uniqueTokenName',[
"angular",
"cjt/util/locale",
"cjt/validator/validator-utils",
"app/services/apiTokens",
"cjt/validator/validateDirectiveFactory"
],
function(angular, LOCALE, validationUtils, APITokensService) {
"use strict";
var _apiTokens;
var factoryMethods = {
/** For mocking */
_lastFetch: 0,
_processLoadedTokens: function(tokens) {
_apiTokens = null;
if (tokens) {
_apiTokens = {};
tokens.forEach(function(token) {
_apiTokens[token.id] = token;
});
}
return _apiTokens;
},
_fetchTokens: function() {
return _apiTokens;
},
};
/**
* Validator to check that the token name is unique compared to the apiTokens.fetchTokens() tokens
*
* @module tokenIsUniqueValidator
*
* @example
* <input token-name-is-unique ng-model="myModel" />
*
*/
var tokenIsUniqueValidator = {
/**
* Check if the domain is unique
*
* @method tokenNameIsUnique
*
* @param {String} tokenName value to check against the validator
*
* @return {Boolean} returns a boolean value determined by the validity of the view
*
*/
tokenNameIsUnique: function(tokenName, validatorArgument) {
var result = validationUtils.initializeValidationResult();
var _apiTokens = factoryMethods._fetchTokens();
// Allows passing in of base value for instances where it would be valid if it stayed the same
if (validatorArgument && tokenName === validatorArgument) {
return result;
}
if (!_apiTokens || (tokenName && _apiTokens[tokenName])) {
result.isValid = false;
result.add("tokenNameIsUnique", LOCALE.maketext("This [asis,API] token name already exists on this account. Enter a different name."), "tokenNameIsUnique");
}
return result;
},
validate: function($service, $q, value, argument, scope) {
// debounce the reload
// If tokens exist, and it's not been more than 1000ms, don't re-fetch
var curDate = new Date().getTime();
if (factoryMethods._fetchTokens() && curDate - factoryMethods._lastFetch < 1000) {
var result = tokenIsUniqueValidator.tokenNameIsUnique(value, argument, scope);
return $q.resolve(result);
}
// It's been more thatn 1000ms or no tokens exist
factoryMethods._lastFetch = curDate;
return $service.fetchTokens().then(function(tokens) {
factoryMethods._processLoadedTokens(tokens);
var result = tokenIsUniqueValidator.tokenNameIsUnique(value, argument, scope);
return $q.resolve(result);
});
}
};
var validatorModule = angular.module("cjt2.validate");
validatorModule.run(["validatorFactory", "$q", APITokensService.serviceName,
function(validatorFactory, $q, $service) {
var validators = {
tokenNameIsUnique: tokenIsUniqueValidator.validate.bind(tokenIsUniqueValidator, $service, $q)
};
validators.tokenNameIsUnique.async = true;
validatorFactory.generate(validators, $q);
}
]);
return {
methods: tokenIsUniqueValidator,
factoryMethods: factoryMethods,
name: "token-name-is-unique",
description: "Validation to ensure the api Token is unique for this account.",
version: 1.0
};
}
);
/*
# api_tokens/views/create.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/views/create',[
"angular",
"lodash",
"cjt/util/locale",
"app/services/apiTokens",
"cjt/directives/copyField",
"cjt/directives/timePicker",
"cjt/directives/datePicker",
"app/validators/uniqueTokenName",
"cjt/modules",
"cjt/directives/actionButtonDirective",
"cjt/services/cpanel/componentSettingSaverService",
"cjt/directives/validationContainerDirective",
"cjt/directives/validationItemDirective",
"cjt/directives/toggleSwitchDirective",
"cjt/directives/searchDirective",
"cjt/directives/indeterminateState",
"cjt/services/alertService",
],
function(angular, _, LOCALE, APITokensService, CopyFieldDirective, TimePickerDirective, DatePickerDirective) {
"use strict";
var CSSS_COMPONENT_NAME = "createAPITokenView";
var VIEW_TITLE = LOCALE.maketext("Create API Token");
var MODULE_NAMESPACE = "cpanel.apiTokens.views.create";
var TEMPLATE_URL = "views/create.ptt";
var MODULE_DEPENDANCIES = [
"cjt2.directives.validationContainer",
"cjt2.directives.validationItem",
"cjt2.directives.toggleSwitch",
"cjt2.directives.search",
"cjt2.directives.indeterminateState",
TimePickerDirective.namespace,
DatePickerDirective.namespace,
CopyFieldDirective.namespace,
];
var CONTROLLER_INJECTABLES = ["$scope", "$location", "alertService", APITokensService.serviceName, "componentSettingSaverService", "CAN_CREATE_LIMITED", "apiTokens", "features"];
var CONTROLLER_NAME = "CreateTokenController";
var CONTROLLER = function APITokensListController($scope, $location, $alertService, $service, $CSSS, CAN_CREATE_LIMITED, apiTokens, features) {
// For contingency of shipping without limited
$scope.canCreateLimited = CAN_CREATE_LIMITED;
$scope.pageTitle = VIEW_TITLE;
$scope.RTL = LOCALE.is_rtl();
$scope.showAllHelp = false;
$scope.selectedFeatures = [];
$scope.ui = {
stayAfterCopy: false
};
$scope.checkAll = {
all: false
};
$scope.features = features;
$scope.apiTokens = apiTokens;
$scope.working = {};
$scope.datePickerOptions = {};
$scope.timePickerOptions = {};
var minDate = new Date();
minDate.setHours(0);
minDate.setMinutes(0);
minDate.setSeconds(0, 0);
var defaultExpiresDate = new Date(minDate.getTime());
defaultExpiresDate.setHours(23);
defaultExpiresDate.setMinutes(59);
defaultExpiresDate.setSeconds(59, 999);
defaultExpiresDate.setFullYear(defaultExpiresDate.getFullYear() + 1);
/**
* Iniitate the view
*
*/
$scope.init = function init() {
$CSSS.register(CSSS_COMPONENT_NAME).then(function CSSSLoaded(data) {
if (data) {
$scope.showAllHelp = data.showAllHelp;
$scope.ui.stayAfterCopy = data.stayAfterCopy;
}
});
$scope.reset();
};
/**
* Reset the form values
*
* @param {HTMLDomElement} form form to set pristine
*/
$scope.reset = function reset(form) {
$scope.datePickerOptions.minDate = minDate;
$scope.timePickerOptions.min = minDate;
$scope.working = {
name: "",
unrestricted: CAN_CREATE_LIMITED ? false : true,
features: {},
tokenExpires: false,
expiresAt: defaultExpiresDate
};
$scope.generatedToken = null;
$scope.pageTitle = VIEW_TITLE;
$scope.selectedFeatures = [];
if (form) {
form.$setPristine();
}
};
/**
* Update the nvdata saved
*
* @private
*
*/
$scope._updateCSSS = function _updateCSSS() {
$CSSS.set(CSSS_COMPONENT_NAME, {
showAllHelp: $scope.showAllHelp,
stayAfterCopy: $scope.ui.stayAfterCopy
});
};
/**
* Toggle Showing or Hiding All help
*
*/
$scope.toggleHelp = function toggleHelp() {
$scope.showAllHelp = !$scope.showAllHelp;
$scope._updateCSSS();
};
/**
* Set the generatedToken and prepare the display for showing it.
*
* @param {String} newToken
*/
$scope._tokenCreated = function _tokenCreated(newToken) {
$scope.generatedToken = newToken;
$scope.pageTitle = LOCALE.maketext("Token Created Successfully");
$scope.apiTokens = $service.getTokens();
var message;
if ($scope.working.unrestricted) {
message = LOCALE.maketext("You successfully created an [output,strong,unrestricted] [asis,API] token “[_1]”.", $scope.working.name);
} else {
message = LOCALE.maketext("You successfully created a [output,strong,limited-access] [asis,API] token “[_1]”.", $scope.working.name);
}
$alertService.success(message);
return newToken;
};
/**
* Create a new token
*
* @param {Object} workingToken
* @returns {Promise<String>} returns the promise and then the newly created token
*/
$scope.create = function create(workingToken) {
if ( workingToken.tokenExpires ) {
workingToken.expiresAt.setHours(23);
workingToken.expiresAt.setMinutes(59);
workingToken.expiresAt.setSeconds(59, 999);
}
var expiresAt = workingToken.tokenExpires ? Math.floor(workingToken.expiresAt / 1000) : null;
return $service.createToken(workingToken.name, workingToken.unrestricted, $scope.selectedFeatures, expiresAt).then($scope._tokenCreated);
};
$scope.newTokenExpiresMessage = function newTokenExpiresMessage(token) {
var expirationDate = LOCALE.datetime(token.expiresAt, "datetime_format_medium");
return LOCALE.maketext("This [asis,API] token will expire on [_1][comment,Bareword is a date].", expirationDate);
};
/**
* Toggle (de)selecting all features in the feature chooser
*
*/
$scope.toggleSelectAllFeatures = function toggleSelectAllFeatures() {
if ($scope.selectedFeatures.length < $scope.features.length) {
$scope.features.forEach(function selectAll(feature) {
$scope.working.features[feature.id] = true;
});
} else {
$scope.features.forEach(function selectAll(feature) {
$scope.working.features[feature.id] = false;
});
}
$scope.updateSelectedFeatures();
};
/**
* Determine if a partial number of items is selected
*
* @returns {Booolean} indeterminate state
*/
$scope.getFeaturesIndeterminateState = function getFeaturesIndeterminateState() {
return $scope.selectedFeatures.length && $scope.features.length && $scope.features.length !== $scope.selectedFeatures.length;
};
/**
* Update the selected features list
*
*/
$scope.updateSelectedFeatures = function updateSelectedFeatures() {
$scope.selectedFeatures = [];
angular.forEach($scope.working.features, function(featureSelected, featureKey) {
if (featureSelected) {
$scope.selectedFeatures.push(featureKey);
}
});
};
/**
* Return to the lister view
*
*/
$scope.backToListView = function backToListView() {
$location.path("/");
};
/**
* Upon completion of a token copy, determine whether to return to the list
*
* @param {HTMLFormElement} form passed to reset for resetting to pristine
*/
$scope.tokenCopied = function tokenCopied(form) {
if ($scope.ui.stayAfterCopy) {
$scope.reset(form);
} else {
$scope.backToListView();
}
};
/**
* Called when the stayAfterCopy is altered
*
*/
$scope.stayAfterCopyChanged = function stayAfterCopyChanged() {
$scope._updateCSSS();
};
$scope.dateValidator = function dateValidator(input) {
if ($scope.working.tokenExpires && $scope.working.expiresAt) {
$scope.working.expiresAt.setHours(23);
$scope.working.expiresAt.setMinutes(59);
$scope.working.expiresAt.setSeconds(59, 999);
}
if ($scope.working.tokenExpires && $scope.datePickerOptions.minDate > $scope.working.expiresAt) {
input.$invalid = true;
input.$valid = false;
}
};
$scope.resetDate = function resetDate() {
if ($scope.working.tokenExpires) {
$scope.working.expiresAt = defaultExpiresDate;
}
};
$scope.$on("$destroy", $CSSS.unregister.bind($CSSS, CSSS_COMPONENT_NAME));
$scope.init();
};
var app = angular.module(MODULE_NAMESPACE, MODULE_DEPENDANCIES);
app.controller(CONTROLLER_NAME, CONTROLLER_INJECTABLES.concat(CONTROLLER));
var resolver = {
"apiTokens": [ APITokensService.serviceName, function($service) {
return $service.fetchTokens();
}],
"features": [ APITokensService.serviceName, function($service) {
return $service.getFeatures();
}]
};
return {
"id": "createAPIToken",
"route": "/create",
"controller": CONTROLLER_NAME,
"class": CONTROLLER,
"templateUrl": TEMPLATE_URL,
"title": VIEW_TITLE,
"namespace": MODULE_NAMESPACE,
"showResourcePanel": true,
"resolve": resolver
};
}
);
/*
# api_tokens/views/list.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/views/list',[
"angular",
"lodash",
"cjt/util/locale",
"app/directives/itemLister",
"app/directives/itemListerView",
"app/services/apiTokens",
"app/views/create",
"cjt/modules",
"cjt/services/alertService",
"cjt/directives/actionButtonDirective",
],
function(angular, _, LOCALE, ItemLister, ItemListerView, APITokensService, CreateView) {
"use strict";
var VIEW_TITLE = LOCALE.maketext("List API Tokens");
var MODULE_NAMESPACE = "cpanel.apiTokens.views.list";
var TEMPLATE_URL = "views/list.ptt";
var MODULE_DEPENDANCIES = [ ItemLister.namespace, ItemListerView.namespace, APITokensService.namespace ];
var CONTROLLER_INJECTABLES = ["$scope", "$location", APITokensService.serviceName, "alertService", "apiTokens", "ITEM_LISTER_CONSTANTS"];
var CONTROLLER_NAME = "ListController";
var CONTROLLER = function APITokensListController($scope, $location, $service, $alertService, apiTokens, ITEM_LISTER_CONSTANTS) {
/**
*
* Initialize the controller, called internally.
*
* @private
*
*/
$scope.init = function init() {
$scope._apiTokens = apiTokens;
$scope._filteredItems = [];
$scope.selectedItems = [];
$scope.confirmingDelete = false;
$scope.deletingTokens = false;
$scope.tableHeaderItems = [
{
field: "label",
label: LOCALE.maketext("Token Name"),
sortable: true
},
{
field: "createdOn",
label: LOCALE.maketext("Created"),
hiddenOnMobile: true,
sortable: true
},
{
field: "expiresAt",
label: LOCALE.maketext("Expires"),
hiddenOnMobile: true,
sortable: true
},
{
field: "actions",
label: "",
sortable: false
}
];
["TABLE_ITEM_SELECTED", "TABLE_ITEM_DESELECTED", "ITEM_LISTER_SELECT_ALL", "ITEM_LISTER_DESELECT_ALL"].forEach(function(updateEventKey) {
$scope.$on(ITEM_LISTER_CONSTANTS[updateEventKey], $scope._updatedSelected);
});
$scope.$on(ITEM_LISTER_CONSTANTS.ITEM_LISTER_UPDATED_EVENT, function(event, parameters) {
$scope._filteredItems = parameters.items;
$scope._updatedSelected();
});
};
/**
*
* On success of deleted tokens, this function notifies the user.
*
* @private
*
* @param {Array<String>} deletedTokenNames tokens that were deleted
* @param {} updatedApiTokens
*/
$scope._deleteTokenSuccess = function _deleteTokenSuccess(deletedTokenNames, updatedApiTokens) {
$scope._apiTokens = updatedApiTokens;
$alertService.success(LOCALE.maketext("The system successfully revoked the following [asis,API] [numerate,_1,token,tokens]: [list_and_quoted,_1]", deletedTokenNames));
if ($scope._apiTokens.length === 0) {
$location.path(CreateView.route);
}
$scope.confirmingDelete = false;
$scope.deletingTokens = false;
};
/**
*
* On update of the selected items, as emitted from the lister, this updates the local array
*
*/
$scope._updatedSelected = function _updatedSelected() {
$scope.selectedItems = $scope._filteredItems.filter(function(item) {
return item.selected;
});
};
/**
*
* Delete tokens
*
* @param {*} items token items to be deleted
* @returns {Promise<Array>} returns the deletion promise and updated api tokens
*/
$scope.deleteTokens = function deleteAPITokens(items) {
var tokenNames = [];
var tokenHashes = [];
items.forEach(function(item) {
tokenHashes.push(item.id);
tokenNames.push(_.escape(item.label));
});
$scope.deletingTokens = true;
return $service.deleteTokens(tokenHashes).then($scope._deleteTokenSuccess.bind($scope, tokenNames));
};
/**
* Show the deletion confirmation message
*
*/
$scope.showDeletionConfirmationMessage = function showDeletionConfirmationMessage() {
$scope.confirmingDelete = true;
};
/**
* Hide the deletion confirmation message
*
*/
$scope.hideDeletionConfirmationMessage = function hideDeletionConfirmationMessage() {
$scope.confirmingDelete = false;
};
/**
* Generate a Label for the Delete Confirmation Button
*
* @returns {String} Delete Confirmation Button Label
*/
$scope.confirmDeleteButtonLabel = function confirmDeleteButtonLabel() {
return LOCALE.maketext("Revoke Selected [asis,API] [numerate,_1,Token,Tokens]", $scope.selectedItems.length);
};
/**
* Get the current api tokens
*
* @returns {Array} current api tokens
*/
$scope.getItems = function getItems() {
return $scope._apiTokens;
};
/**
* Return just the ids of the selected features
*
* @return {Array<String>} array of feature names
*
*/
$scope.getSelectedFeatureNames = function getSelectedFeatureNames() {
return $scope.selectedItems.map(function(selectedFeature) {
return selectedFeature.id;
});
};
/**
* Get the delete confirmation message based on selected features.
*
* @returns {String} delete confirmation message
*/
$scope.confirmDeleteMessage = function confirmDeleteMessage() {
var selectedFeatureNames = $scope.getSelectedFeatureNames().map(_.escape);
return LOCALE.maketext("Are you sure that you want to revoke the following [asis,API] [numerate,_1,token,tokens]: [list_and_quoted,_2]", selectedFeatureNames.length, selectedFeatureNames);
};
$scope.init();
};
var app = angular.module(MODULE_NAMESPACE, MODULE_DEPENDANCIES);
app.controller(CONTROLLER_NAME, CONTROLLER_INJECTABLES.concat(CONTROLLER));
var resolver = {
"apiTokens": [ APITokensService.serviceName, "$location", function($service, $location) {
return $service.fetchTokens().then(function(apiTokens) {
if (apiTokens.length) {
return apiTokens;
} else {
$location.path(CreateView.route);
return false;
}
});
}]
};
return {
"id": "listAPITokens",
"route": "/",
"controller": CONTROLLER_NAME,
"class": CONTROLLER,
"templateUrl": TEMPLATE_URL,
"title": VIEW_TITLE,
"namespace": MODULE_NAMESPACE,
"showResourcePanel": false,
"resolve": resolver
};
}
);
/*
# api_tokens/views/manage.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/views/manage',[
"angular",
"lodash",
"cjt/util/locale",
"app/services/apiTokens",
"app/filters/htmlSafeString",
"app/validators/uniqueTokenName",
"cjt/modules",
"cjt/services/alertService",
"cjt/directives/actionButtonDirective",
"cjt/services/cpanel/componentSettingSaverService",
"cjt/directives/validationContainerDirective",
"cjt/directives/validationItemDirective",
"cjt/directives/toggleSwitchDirective",
"cjt/directives/searchDirective",
"cjt/directives/indeterminateState"
],
function(angular, _, LOCALE, APITokensService, HTMLSafeStringFilter) {
"use strict";
var CSSS_COMPONENT_NAME = "manageAPITokenView";
var VIEW_TITLE = LOCALE.maketext("Manage [asis,API] Token");
var MODULE_NAMESPACE = "cpanel.apiTokens.views.manage";
var TEMPLATE_URL = "views/manage.ptt";
var MODULE_DEPENDANCIES = [
"cjt2.directives.validationContainer",
"cjt2.directives.validationItem",
"cjt2.directives.toggleSwitch",
"cjt2.directives.search",
"cjt2.directives.indeterminateState",
HTMLSafeStringFilter.namespace
];
var CONTROLLER_INJECTABLES = ["$scope", "$location", "$routeParams", "alertService", APITokensService.serviceName, "componentSettingSaverService", "CAN_CREATE_LIMITED", "apiTokens"];
var CONTROLLER_NAME = "ManageTokenController";
var CONTROLLER = function APITokensListController($scope, $location, $routeParams, $alertService, $service, $CSSS, CAN_CREATE_LIMITED, apiTokens) {
$scope.pageTitle = VIEW_TITLE;
$scope.RTL = LOCALE.is_rtl();
$scope.showAllHelp = false;
$scope.ui = {
confirmingRevocation: false
};
$scope.selectedFeatures = [];
$scope.checkAll = {
all: false
};
$scope.apiTokens = apiTokens;
$scope.working = {};
$scope.expiringMessage = function expiringMessage(expiresAt) {
if ($scope.current.expired) {
return LOCALE.maketext("[output,strong,Danger]: This [asis,API] token expired on [datetime,_1,datetime_format_medium].", $scope.current.expiresAt);
} else if ($scope.current.expiresSoon) {
return LOCALE.maketext("[output,strong,Warning]: This [asis,API] token will expire on [datetime,_1,datetime_format_medium].", $scope.current.expiresAt);
}
};
$scope.getExpirationLabel = function getExpirationLabel(expiresAt) {
return expiresAt ? LOCALE.local_datetime(expiresAt, "datetime_format_medium") : "";
};
/**
* Initate the view
*
*/
$scope.init = function init() {
$CSSS.register(CSSS_COMPONENT_NAME).then(function CSSSLoaded(data) {
if (data) {
$scope.showAllHelp = data.showAllHelp;
$scope.stayOnView = data.stayOnView;
}
});
var currentTokenID = $routeParams["token"];
var currentToken = $service.getTokenById(currentTokenID);
if (!currentToken) {
$location.path("/");
return;
}
// For contingency of shipping without limited
$scope.canEditFeatureRestrictions = !currentToken.unrestricted || CAN_CREATE_LIMITED;
$scope.current = currentToken;
$scope.working = {
name: currentToken.id,
unrestricted: currentToken.unrestricted,
features: {}
};
currentToken.features.forEach(function(feature) {
$scope.working.features[feature] = true;
$scope.selectedFeatures.push(feature);
});
if (!$scope.current.unrestricted) {
$service.getFeatures().then($scope._featuresLoaded);
}
};
/**
* Update the nvdata saved
*
* @private
*
*/
$scope._updateCSSS = function _updateCSSS() {
$CSSS.set(CSSS_COMPONENT_NAME, {
showAllHelp: $scope.showAllHelp
});
};
/**
* Set the scope features to the passed features
*
* @param {Array<Object>} features
*/
$scope._featuresLoaded = function _featuresLoaded(features) {
$scope.features = features;
};
/**
* Toggle Showing or Hiding All help
*
*/
$scope.toggleHelp = function toggleHelp() {
$scope.showAllHelp = !$scope.showAllHelp;
$scope._updateCSSS();
};
$scope._tokenRenamed = function _tokenRenamed(originalName, newName) {
$alertService.success(LOCALE.maketext("You have successfully renamed the API Token “[_1]” to “[_2]”.", _.escape(originalName), _.escape(newName)));
};
$scope._renameToken = function _renameToken(originalName, newName) {
return $service.renameToken(originalName, newName).then($scope._tokenRenamed.bind($scope, originalName, newName));
};
$scope._getFeatureById = function _getFeatureById(id) {
var features = $scope.features;
for (var i = 0; i < features.length; i++) {
if (features[i].id === id) {
return features[i];
}
}
return;
};
$scope._tokenRestrictionUpdated = function _tokenRestrictionUpdated(tokenName, unrestricted, features) {
if (unrestricted) {
$alertService.success({
message: LOCALE.maketext("You have successfully set the [asis,API] token “[_1]” to unrestricted.", _.escape(tokenName)),
replace: false
});
} else {
var featureNames = features.map(function(featureId) {
return $scope._getFeatureById(featureId).label;
}).map(_.escape);
$alertService.success({
message: LOCALE.maketext("The [asis,API] Token “[_1]” is now limited to the following features: [list_and_quoted,_2].", tokenName, featureNames),
replace: false
});
}
};
/**
* Update the token
*
* @param {Object} workingToken
* @returns {Promise<String>} returns the promise chain
*/
$scope.update = function update(workingToken) {
var originalToken = $scope.current;
var updatePromise;
var updatingRestrictions = workingToken.unrestricted !== $scope.current.unrestricted || $scope.selectedFeatures.sort().join() !== $scope.current.features.sort().join();
if (originalToken.id !== workingToken.name) {
// Rename it
updatePromise = $scope._renameToken(originalToken.id, workingToken.name);
// If we're not also updating restrictions, add the finally.
if (!updatingRestrictions) {
updatePromise.finally($scope.backToListView);
}
}
if (updatingRestrictions) {
var updateRestriction = $service.updateTokenRestrictions.bind($service, workingToken.name, workingToken.unrestricted, $scope.selectedFeatures);
var _restrictionUpdated = $scope._tokenRestrictionUpdated.bind($scope, workingToken.name, workingToken.unrestricted, $scope.selectedFeatures);
if (updatePromise) {
updatePromise.then(updateRestriction).then(_restrictionUpdated);
} else {
updatePromise = updateRestriction().then(_restrictionUpdated);
}
updatePromise.finally($scope.backToListView);
}
return updatePromise;
};
/**
* Hide or Reveal the Feature Chooser
*
*/
$scope.unrestrictedToggled = function unrestrictedToggled() {
if (!$scope.working.unrestricted && !$scope.features) {
// load the features
$service.getFeatures().then($scope._featuresLoaded);
}
};
/**
* Toggle (de)selecting all features in the feature chooser
*
*/
$scope.toggleSelectAllFeatures = function toggleSelectAllFeatures() {
$scope.working.features = $scope.working.features || {};
if ($scope.selectedFeatures.length < $scope.features.length) {
$scope.features.forEach(function selectAll(feature) {
$scope.working.features[feature.id] = true;
});
} else {
$scope.features.forEach(function selectAll(feature) {
$scope.working.features[feature.id] = false;
});
}
$scope.updateSelectedFeatures();
};
/**
* Determine if a partial number of items is selected
*
* @returns {Booolean} indeterminate state
*/
$scope.getFeaturesIndeterminateState = function getFeaturesIndeterminateState() {
return $scope.selectedFeatures.length && $scope.features.length && $scope.features.length !== $scope.selectedFeatures.length;
};
/**
* Update the selected features list
*
*/
$scope.updateSelectedFeatures = function updateSelectedFeatures() {
$scope.selectedFeatures = [];
angular.forEach($scope.working.features, function(featureSelected, featureKey) {
if (featureSelected) {
$scope.selectedFeatures.push(featureKey);
}
});
};
/**
* Return to the lister view
*
*/
$scope.backToListView = function backToListView() {
$location.path("/");
};
/**
* Show the revocation confirmation dialog
*
*/
$scope.showRevokeConfirm = function showRevokeConfirm() {
$scope.ui.confirmingRevocation = true;
};
/**
* Hide the revocation confirmation dialog
*
*/
$scope.hideRevokeConfirm = function hideRevokeConfirm() {
$scope.ui.confirmingRevocation = false;
};
/**
* Notify user of the success of a token revocation
*
* @param {String} token
*/
$scope._tokenRevoked = function _tokenRevoked(token) {
$alertService.success( LOCALE.maketext("You have successfully revoked the following [asis,API] [numerate,_1,token,tokens]: “[_1]”", _.escape(token)) );
};
/**
* Revoke a token
*
* @param {APIToken} token
* @returns {Promise} revocation promise
*/
$scope.revokeToken = function revokeToken(token) {
return $service.deleteTokens([token.id]).then( $scope._tokenRevoked.bind($scope, token.id) ).then( $scope.backToListView );
};
$scope.$on("$destroy", $CSSS.unregister.bind($CSSS, CSSS_COMPONENT_NAME));
$scope.init();
};
var app = angular.module(MODULE_NAMESPACE, MODULE_DEPENDANCIES);
app.controller(CONTROLLER_NAME, CONTROLLER_INJECTABLES.concat(CONTROLLER));
var resolver = {
"apiTokens": [ APITokensService.serviceName, function($service) {
return $service.fetchTokens();
}]
};
return {
"id": "manageAPIToken",
"route": "/manage",
"controller": CONTROLLER_NAME,
"class": CONTROLLER,
"templateUrl": TEMPLATE_URL,
"title": VIEW_TITLE,
"namespace": MODULE_NAMESPACE,
"showResourcePanel": true,
"resolve": resolver
};
}
);
/*
# account_preferences/index.js Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global require, define */
define(
'app/index',[
"angular",
"cjt/core",
"cjt/util/locale",
"app/services/apiTokens",
"app/views/list",
"app/views/create",
"app/views/manage",
"app/filters/htmlSafeString",
"cjt/modules",
"cjt/directives/alertList",
"cjt/services/alertService",
"cjt/services/APICatcher",
"cjt/directives/loadingPanel",
"cjt/directives/breadcrumbs",
"ngRoute",
],
function(angular, CJT, LOCALE, ApiTokensService, ListView, CreateView, ManageView, HTMLSafeStringFilter) {
"use strict";
var MODULE_NAME = "cpanel.apiTokens";
return function() {
var ROUTES = [];
var MODULE_INJECTABLES = [
"ngRoute",
"cjt2." + CJT.applicationName,
ApiTokensService.namespace,
HTMLSafeStringFilter.namespace
];
[ListView, CreateView, ManageView].forEach(function(routeView) {
routeView.breadcrumb = routeView.breadcrumb ? routeView.breadcrumb : {
id: routeView.id,
name: routeView.title,
path: routeView.route,
parentID: routeView.parentID
};
ROUTES.push(routeView);
MODULE_INJECTABLES.push(routeView.namespace);
});
// First create the application
var appModule = angular.module(MODULE_NAME, MODULE_INJECTABLES);
appModule.value("ITEM_LISTER_CONSTANTS", {
TABLE_ITEM_BUTTON_EVENT: "TableItemActionButtonEmitted",
TABLE_ITEM_SELECTED: "TableItemSelectedEmitted",
TABLE_ITEM_DESELECTED: "TableItemDeselectedEmitted",
ITEM_LISTER_UPDATED_EVENT: "ItemListerUpdatedEvent",
ITEM_LISTER_SELECT_ALL: "ItemListerSelectAllEvent",
ITEM_LISTER_DESELECT_ALL: "ItemListerDeselectAllEvent"
});
appModule.value("CAN_CREATE_LIMITED", PAGE.canCreateLimited);
appModule.controller("MainController", ["$scope", "$rootScope", "alertService", function MainController($scope, $rootScope, $alertService) {
$scope.showResourcePanel = true;
$scope.mainPanelClasses = "";
$scope.sidePanelClasses = "";
$scope.updatePanelClasses = function updatePanelClasses() {
$scope.sidePanelClasses = "col-sm-4 col-md-4 hidden-xs";
$scope.sidePanelClasses += " ";
$scope.sidePanelClasses += LOCALE.is_rtl() ? "pull-left" : "pull-right";
$scope.mainPanelClasses = $scope.showResourcePanel ? "col-xs-12 col-sm-8 col-md-8" : "col-xs-12";
};
/**
* Find a Route by the Path
*
* @private
*
* @method _getRouteByPath
* @param {String} path route to match against the .route property of the existing routes
*
* @returns {Object} route that matches the provided path
*
*/
function _getRouteByPath(path) {
var foundRoute;
ROUTES.forEach(function(route, key) {
if (route.route === path) {
foundRoute = key;
}
});
return foundRoute;
}
$rootScope.$on("$routeChangeStart", function() {
$scope.loading = true;
$alertService.clear("danger");
});
$rootScope.$on("$routeChangeSuccess", function(event, current) {
$scope.loading = false;
if (current) {
var currentRouteKey = _getRouteByPath(current.$$route.originalPath);
if (ROUTES[currentRouteKey]) {
$scope.currentTab = ROUTES[currentRouteKey];
$scope.showResourcePanel = $scope.currentTab.showResourcePanel;
$scope.activeTab = currentRouteKey;
$scope.updatePanelClasses();
}
}
});
$rootScope.$on("$routeChangeError", function() {
$scope.loading = false;
});
}]);
// Then load the application dependencies
require(["cjt/bootstrap"], function(BOOTSTRAP) {
appModule.config([
"$routeProvider",
"$animateProvider",
function($routeProvider, $animateProvider) {
$animateProvider.classNameFilter(/^((?!no-animate).)*$/);
ROUTES.forEach(function(route, key) {
$routeProvider.when(route.route, route);
});
$routeProvider.otherwise({
"redirectTo": "/"
});
}
]);
BOOTSTRAP("#content", MODULE_NAME);
});
return appModule;
};
}
);
Back to Directory
File Manager