Adding the store user interface. (#1084)

This commit is contained in:
Kamidu Sachith Punchihewa 2017-10-25 16:27:55 +05:30 committed by Madhawa Perera
parent 9c364e6631
commit 17bce6fed1
1109 changed files with 233687 additions and 0 deletions

View File

@ -0,0 +1,68 @@
<%
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var log = new Log("api/data-tables-invoker-api.jag");
var uri = request.getRequestURI();
var uriMatcher = new URIMatcher(String(uri));
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var serviceInvokers = require("/app/modules/oauth/token-protected-service-invokers.js")["invokers"];
var utility = require("/app/modules/utility.js")["utility"];
function appendQueryParam (url, queryParam , value) {
if (url.indexOf("?") > 0) {
return url + "&" + queryParam + "=" + value;
}
return url + "?" + queryParam + "=" + value;
}
if (uriMatcher.match("/{context}/api/data-tables/invoker")) {
var url = request.getParameter("url");
var targetURL = devicemgtProps["httpsURL"] + request.getParameter("url");
//noinspection JSUnresolvedFunction getAllParameters
var allParams = request.getAllParameters();
for (var allParamsKey in allParams) {
if (allParams.hasOwnProperty(allParamsKey)) {
if (allParamsKey == "limit" || allParamsKey == "offset") {
targetURL = appendQueryParam(targetURL, allParamsKey, allParams[allParamsKey]);
} else if (allParamsKey == "filter") {
if (allParams[allParamsKey]) {
var searchPayload = JSON.parse(allParams[allParamsKey]);
for (var searchPayloadKey in searchPayload) {
if (searchPayload.hasOwnProperty(searchPayloadKey)) {
targetURL = appendQueryParam(targetURL, searchPayloadKey, searchPayload[searchPayloadKey]);
}
}
}
}
}
}
serviceInvokers.XMLHttp.get(
targetURL,
// response callback
function (backendResponse) {
response["status"] = backendResponse["status"];
response["content"] = utility.encodeJson(backendResponse["responseText"]);
response["contentType"] = "application/json";
}
);
}

View File

@ -0,0 +1,201 @@
<%
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var uri = request.getRequestURI();
var uriMatcher = new URIMatcher(String(uri));
var log = new Log("api/device-api.jag");
var constants = require("/app/modules/constants.js");
var deviceModule = require("/app/modules/business-controllers/device.js")["deviceModule"];
var utility = require("/app/modules/utility.js").utility;
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
var serviceInvokers = require("/app/modules/oauth/token-protected-service-invokers.js")["invokers"];
var user = session.get(constants.USER_SESSION_KEY);
var result;
response.contentType = 'application/json';
if (!user) {
response.sendRedirect("/devicemgt/login?#login-required");
exit();
} else {
if (uriMatcher.match("/{context}/api/devices/sketch/download")) {
// works as a proxy to pass the relavant query string to back end api.
var queryString = request.getQueryString();
if (!queryString) {
queryString = "";
} else {
queryString = "?" + queryString;
}
var deviceType = request.getParameter("deviceType"); // need a better solution here
deviceTypeConfig = utility.getDeviceTypeConfig(deviceType);
if (deviceTypeConfig && deviceTypeConfig.deviceType.downloadAgentUri) {
hearders = [{"name": constants["ACCEPT_IDENTIFIER"], "value": constants["APPLICATION_ZIP"]}];
sketchDownloadEndPoint = devicemgtProps["httpsURL"] + "/" + deviceTypeConfig.deviceType.downloadAgentUri;
serviceInvokers.HttpClient.get(sketchDownloadEndPoint + queryString, function (responsePayload, responseHeaders) {
if (responseHeaders) {
for (var i = 0; i < responseHeaders.length; i++) {
var header = responseHeaders[i];
var headerName = String(header.getName());
var headerValue = String(header.getValue());
response.addHeader(headerName, headerValue);
}
var streamObject = new Stream(responsePayload);
print(streamObject);
} else {
return responsePayload;
}
}, function (responsePayload) {
log.error(responsePayload);
var response = {};
response["status"] = "error";
return response;
}
, hearders);
} else {
result = 400;
}
} else if (uriMatcher.match("/{context}/api/devices/all")) {
result = deviceModule.getOwnDevices();
} else if (uriMatcher.match("/{context}/api/devices/count")) {
var count = deviceModule.getOwnDevicesCount().data;
result = count.toString();
} else if (uriMatcher.match("/{context}/api/devices/types")) {
result = deviceModule.listDeviceTypes();
} else if (uriMatcher.match("/{context}/api/devices/{deviceType}/{deviceId}/remove")) {
var elements = uriMatcher.elements();
var deviceId = elements.deviceId;
var deviceType = elements.deviceType;
result = deviceModule.removeDevice(deviceType, deviceId);
} else if (uriMatcher.match("/{context}/api/devices/{deviceType}/{deviceId}/update")) {
var elements = uriMatcher.elements();
var deviceId = elements.deviceId;
var deviceType = elements.deviceType;
var deviceName = request.getParameter("name");
result = deviceModule.updateDevice(deviceType, deviceId, deviceName);
} else if (uriMatcher.match("/{context}/api/devices")) {
var url = request.getParameter("url");
var draw = request.getParameter("draw");
var length = request.getParameter("length");
var start = request.getParameter("start");
var search = request.getParameter("search[value]");
var deviceName = request.getParameter("columns[1][search][value]");
var owner = request.getParameter("columns[2][search][value]");
var status = request.getParameter("columns[3][search][value]");
var platform = request.getParameter("columns[4][search][value]");
var ownership = request.getParameter("columns[5][search][value]");
var targetURL;
function appendQueryParam (url, queryParam , value) {
if (url.indexOf("?") > 0) {
return url + "&" + queryParam + "=" + value;
}
return url + "?" + queryParam + "=" + value;
}
targetURL = devicemgtProps.httpsURL + request.getParameter("url");
targetURL = appendQueryParam(targetURL, "draw", draw);
targetURL = appendQueryParam(targetURL, "start", start);
targetURL = appendQueryParam(targetURL, "length", length);
if (search && search !== "") {
targetURL = appendQueryParam(targetURL, "search", search);
}
if (deviceName && deviceName !== "") {
targetURL = appendQueryParam(targetURL, "device-name", deviceName);
}
if (owner && owner !== "") {
targetURL = appendQueryParam(targetURL, "user", owner);
}
if (status && status !== "") {
targetURL = appendQueryParam(targetURL, "status", status);
}
if (platform && platform !== "") {
targetURL = appendQueryParam(targetURL, "type", platform);
}
if (ownership && ownership !== "") {
targetURL = appendQueryParam(targetURL, "ownership", ownership);
}
serviceInvokers.XMLHttp.get(
targetURL, function (responsePayload) {
response.status = 200;
result = responsePayload;
},
function (responsePayload) {
response.status = responsePayload.status;
result = responsePayload.responseText;
});
} else if (uriMatcher.match("/{context}/api/devices/")) {
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/list")) {
result = deviceModule.listDevices();
} else {
response.sendError(403);
}
} else if (uriMatcher.match("/{context}/api/devices/{type}/{deviceId}")) {
elements = uriMatcher.elements();
deviceId = elements.deviceId;
type = elements.type;
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/list")) {
result = deviceModule.viewDevice(type, deviceId);
}else {
response.sendError(403);
}
} else if (uriMatcher.match("/{context}/api/devices/agent/{type}/{deviceId}/config")) {
elements = uriMatcher.elements();
deviceId = elements.deviceId;
type = elements.type;
operation = elements.operation;
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device")) {
result = deviceModule.getDeviceAgentConfig(type, deviceId);
if (!result) {
response.sendError(500);
}
} else {
response.sendError(403);
}
} else if (uriMatcher.match("{context}/api/devices/{type}/{deviceId}/{operation}")) {
elements = uriMatcher.elements();
deviceId = elements.deviceId;
type = elements.type;
operation = elements.operation;
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/operation")) {
result = deviceModule.performOperation(deviceId, operation, [], type);
} else {
response.sendError(403);
}
}
}
// Returning the result.
if (result) {
print(result);
}
%>

View File

@ -0,0 +1,87 @@
<%
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var uri = request.getRequestURI();
var uriMatcher = new URIMatcher(String(uri));
var log = new Log("api/device-api.jag");
var constants = require("/app/modules/constants.js");
var utility = require("/app/modules/utility.js").utility;
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var serviceInvokers = require("/app/modules/oauth/token-protected-service-invokers.js")["invokers"];
var user = session.get(constants.USER_SESSION_KEY);
var result;
response.contentType = 'application/json';
if (!user) {
response.sendRedirect("/devicemgt/login?#login-required");
exit();
} else {
if (uriMatcher.match("/{context}/api/groups")) {
var url = request.getParameter("url");
var draw = request.getParameter("draw");
var length = request.getParameter("length");
var start = request.getParameter("start");
var search = request.getParameter("search[value]");
var groupName = request.getParameter("columns[1][search][value]");
var owner = request.getParameter("columns[2][search][value]");
var targetURL;
function appendQueryParam(url, queryParam, value) {
if (url.indexOf("?") > 0) {
return url + "&" + queryParam + "=" + value;
}
return url + "?" + queryParam + "=" + value;
}
targetURL = devicemgtProps.httpsURL + request.getParameter("url");
targetURL = appendQueryParam(targetURL, "start", start);
targetURL = appendQueryParam(targetURL, "length", length);
if (search && search !== "") {
targetURL = appendQueryParam(targetURL, "search", search);
}
if (groupName && groupName !== "") {
targetURL = appendQueryParam(targetURL, "group-name", groupName);
}
if (owner && owner !== "") {
targetURL = appendQueryParam(targetURL, "user", owner);
}
serviceInvokers.XMLHttp.get(
targetURL, function (responsePayload) {
response.status = 200;
result = responsePayload;
},
function (responsePayload) {
response.status = responsePayload.status;
result = responsePayload.responseText;
});
}
}
if (result) {
print(result);
}
%>

View File

@ -0,0 +1,119 @@
<%
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var log = new Log("api/invoker-api.jag");
var uri = request.getRequestURI();
var uriMatcher = new URIMatcher(String(uri));
var constants = require("/app/modules/constants.js");
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var serviceInvokers = require("/app/modules/oauth/token-protected-service-invokers.js")["invokers"];
if (uriMatcher.match("/{context}/api/invoker/execute/")) {
//NOTE: We are only interested in Content-Type headers. Appending all request headers to the back-end call
// will cause unforeseen security issues.
var contentType = request.getHeader(constants.CONTENT_TYPE_IDENTIFIER);
var acceptType = request.getHeader(constants.ACCEPT_IDENTIFIER);
var requestHeaders = [];
requestHeaders.push({"name": constants.CONTENT_TYPE_IDENTIFIER, "value" : contentType});
requestHeaders.push({"name": constants.ACCEPT_IDENTIFIER, "value" : acceptType});
var restAPIRequestDetails = request.getContent();
var requestMethod = restAPIRequestDetails["requestMethod"];
var requestURL = restAPIRequestDetails["requestURL"];
var requestPayload = restAPIRequestDetails["requestPayload"];
if (!requestMethod) {
requestMethod = parse(restAPIRequestDetails)["requestMethod"];
}
if (!requestURL) {
requestURL = parse(restAPIRequestDetails)["requestURL"];
}
if (!requestPayload) {
requestPayload = parse(restAPIRequestDetails)["requestPayload"];
}
var restAPIEndpoint = devicemgtProps["httpsURL"] + requestURL;
try {
switch (requestMethod) {
case constants["HTTP_GET"]:
serviceInvokers.XMLHttp.get(
restAPIEndpoint,
function (restAPIResponse) {
response["status"] = restAPIResponse["status"];
if (restAPIResponse["responseText"]) {
response["content"] = restAPIResponse["responseText"];
}
},
requestHeaders
);
break;
case constants["HTTP_POST"]:
serviceInvokers.XMLHttp.post(
restAPIEndpoint,
requestPayload,
function (restAPIResponse) {
response["status"] = restAPIResponse["status"];
if (restAPIResponse["responseText"]) {
response["content"] = restAPIResponse["responseText"];
}
},
requestHeaders
);
break;
case constants["HTTP_PUT"]:
serviceInvokers.XMLHttp.put(
restAPIEndpoint,
requestPayload,
function (restAPIResponse) {
response["status"] = restAPIResponse["status"];
if (restAPIResponse["responseText"]) {
response["content"] = restAPIResponse["responseText"];
}
},
requestHeaders
);
break;
case constants["HTTP_DELETE"]:
serviceInvokers.XMLHttp.delete(
restAPIEndpoint,
function (restAPIResponse) {
response["status"] = restAPIResponse["status"];
if (restAPIResponse["responseText"]) {
response["content"] = restAPIResponse["responseText"];
}
},
requestHeaders
);
break;
}
} catch (e) {
//Since this is an API we'll log the error message.
log.error(e.message); // JavaScript error message
log.error(e.stack); // Executed JavaScript file stack
throw new Error("Exception occurred while trying to access " +
"backend REST API services from Jaggery API invoker layer", e);
}
}
%>

View File

@ -0,0 +1,66 @@
<%
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var uri = request.getRequestURI();
var uriMatcher = new URIMatcher(String(uri));
var log = new Log("api/operation-api.jag");
var serviceInvokers = require("/app/modules/oauth/token-protected-service-invokers.js")["invokers"];
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
if (uriMatcher.match("/{context}/api/operation/paginate")) {
var deviceType = request.getParameter("deviceType");
var deviceId = request.getParameter("deviceId");
var owner = request.getParameter("owner");
var index = request.getParameter("start");
var length = request.getParameter("length");
var search = request.getParameter("search[value]");
var restAPIEndpoint = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] +
"/devices/" + deviceType + "/" + deviceId + "/operations?owner=" + owner + "&offset=" + index + "&limit=" + length;
serviceInvokers.XMLHttp.get(
restAPIEndpoint,
function (restAPIResponse) {
if (restAPIResponse["status"] == 200 && restAPIResponse["responseText"]) {
var responsePayload = parse(restAPIResponse["responseText"]);
var paginatedResult = {};
paginatedResult["recordsTotal"] = responsePayload["count"];
paginatedResult["recordsFiltered"] = responsePayload["count"];
paginatedResult["data"] = responsePayload["operations"];
response["status"] = restAPIResponse["status"];
response["content"] = paginatedResult;
} else {
response["status"] = restAPIResponse["status"];
if (restAPIResponse["responseText"]) {
var responseText = "";
try {
response["content"] = parse(restAPIResponse["responseText"]);
} catch (e) {
responseText = restAPIResponse["responseText"];
}
}
}
}
);
}
%>

View File

@ -0,0 +1,52 @@
<%
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
@Deprecated
*/
var uri = request.getRequestURI();
var uriMatcher = new URIMatcher(String(uri));
var log = new Log("api/policy-api.jag");
var constants = require("/modules/constants.js");
var policyModule = require("/app/modules/business-controllers/group.js")["groupModule"];
var result;
if (uriMatcher.match("/{context}/api/policies/update")) {
payload = request.getContent();
policyModule.updatePolicyPriorities(payload);
} else if (uriMatcher.match("/{context}/api/policies/{id}/delete")) {
elements = uriMatcher.elements();
policyId = elements.id;
try {
result = policyModule.deletePolicy(policyId);
} catch (e) {
log.error("Exception occurred while trying to delete policy for id:" + policyId, e);
// http status code 500 refers to - Internal Server Error.
result = 500;
}
}
// returning the result.
if (result) {
response.content = result;
}
%>

View File

@ -0,0 +1,77 @@
<%
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var uri = request.getRequestURI();
var uriMatcher = new URIMatcher(String(uri));
var log = new Log("api/stats-api.jag");
var serviceInvokers = require("/app/modules/oauth/token-protected-service-invokers.js")["invokers"];
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
if (uriMatcher.match("/{context}/api/stats/paginate")) {
var deviceType = request.getParameter("deviceType");
var deviceId = request.getParameter("deviceId");
var from = request.getParameter("from");
var to = request.getParameter("to");
var index = request.getParameter("start");
var length = request.getParameter("length");
var keys = request.getParameter("attributes");
keys = JSON.parse(keys);
var restAPIEndpoint = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] + "/events/"
+ deviceType + "/" + deviceId + "?offset=" + index +"&limit=" + length + "&from="+ from + "&to=" + to;
serviceInvokers.XMLHttp.get(
restAPIEndpoint,
function (restAPIResponse) {
if (restAPIResponse["status"] == 200 && restAPIResponse["responseText"]) {
var responsePayload = parse(restAPIResponse["responseText"]);
var paginatedResult = {};
paginatedResult["recordsTotal"] = responsePayload["count"];
paginatedResult["recordsFiltered"] = responsePayload["count"];
var records = responsePayload["records"];
var dataSet = [];
for (var i = 0; i < records.length; i++){
var record = records[i];
var timestamp = record["timestamp"];
var dataRow = [];
dataRow.push(timestamp);
for (var j = 0; j < keys.length; j++) {
var key = keys[j];
dataRow.push(record.values[key]);
}
//dataSet.push(dataRow);
dataSet.push(dataRow);
}
paginatedResult["data"] = dataSet;
response["status"] = restAPIResponse["status"];
response["content"] = paginatedResult;
} else {
response["status"] = 204;
var paginatedResult = {};
var dataSet = [];
paginatedResult["recordsTotal"] = 0;
paginatedResult["recordsFiltered"] = 0;
paginatedResult["data"] = dataSet;
response["content"] = paginatedResult;
}
}
);
}
%>

View File

@ -0,0 +1,206 @@
<%
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var uri = request.getRequestURI();
var uriMatcher = new URIMatcher(String(uri));
var log = new Log("api/user-api.jag");
var constants = require("/app/modules/constants.js");
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
var deviceModule = require("/app/modules/business-controllers/device.js")["deviceModule"];
var utility = require("/app/modules/utility.js")["utility"];
var apiWrapperUtil = require("/app/modules/oauth/token-handlers.js")["handlers"];
var util = require("/app/modules/oauth/token-handler-utils.js")["utils"];
var responseProcessor = require("utils").response;
var result;
if (uriMatcher.match("/{context}/api/user/authenticate")) {
var username = request.getParameter("username");
var password = request.getParameter("password");
//Check if a username and password is provided
if ((!username) || (!password)) {
response = responseProcessor.buildErrorResponse(response, 400, 'Username and Password must be provided');
} else {
try {
userModule.login(username, password, function (user) {
if (log.isDebugEnabled()) {
log.debug("User Logged In : " + user);
}
apiWrapperUtil.setupTokenPairByPasswordGrantType(username, password);
}, function () {
response = responseProcessor.buildSuccessResponse(response, 200, {'sessionId': session.getId()});
});
} catch (e) {
log.error("Exception occurred while a user tried to login to MDM", e);
response = responseProcessor.buildErrorResponse(response, 401, 'username/password is incorrect');
}
}
} else if (uriMatcher.match("/{context}/api/user/login/")) {
username = request.getParameter("username");
password = request.getParameter("password");
username = util.decode(username);
password = util.decode(password);
try {
userModule.login(username, password, function (user) {
if (log.isDebugEnabled()) {
log.debug("User Logged In : " + user);
}
apiWrapperUtil.setupTokenPairByPasswordGrantType(username, password);
var permissions = userModule.getUIPermissions();
if (permissions.VIEW_DASHBOARD) {
response.sendRedirect(devicemgtProps["appContext"]);
} else {
response.sendRedirect(devicemgtProps["appContext"] + "devices");
}
}, function () {
response.sendRedirect(devicemgtProps.appContext + "login?#auth-failed");
});
} catch (e) {
log.error("Exception occurred while a user tried to login to MDM", e);
response.sendRedirect(devicemgtProps.appContext + "login?#error");
}
} else if (uriMatcher.match("/{context}/api/user/logout/")) {
userModule.logout(function () {
response.sendRedirect(devicemgtProps.appContext + "login");
});
} else if (uriMatcher.match("/{context}/api/user/devices/")) {
/*
@Deprecated
*/
if (userModule.isAuthorized("/permission/admin/device-mgt/user/devices/list")) {
carbonUser = session.get(constants.USER_SESSION_KEY);
result = deviceModule.listDevicesForUser(carbonUser.username);
} else {
response.sendError(403);
}
} else if (uriMatcher.match("/{context}/api/user/{username}/invite")) {
/*
@Deprecated
*/
if (userModule.isAuthorized("/permission/admin/device-mgt/user/invite")) {
elements = uriMatcher.elements();
username = elements.username;
userModule.inviteUser(username);
} else {
response.sendError(403);
}
} else if (uriMatcher.match("/{context}/api/user/add")) {
/*
@Deprecated
*/
if (userModule.isAuthorized("/permission/admin/device-mgt/user/add")) {
addUserFormData = request.getContent();
username = addUserFormData.username;
firstname = addUserFormData.firstname;
lastname = addUserFormData.lastname;
emailAddress = addUserFormData.emailAddress;
if (!addUserFormData.userRoles) {
userRoles = null;
} else {
userRoles = String(addUserFormData.userRoles).split(",");
}
if (username.length < devicemgtProps.userValidationConfig.usernameLength) {
log.error("Username Must be between 1 and " + devicemgtProps.userValidationConfig.usernameLength + " characters long");
result = "Username Must be between 1 and " + devicemgtProps.userValidationConfig.usernameLength + " characters long";
} else {
try {
result = userModule.addUser(username, firstname, lastname, emailAddress, userRoles);
} catch (e) {
log.error("Exception occurred while trying to add a user to MDM User Store", e);
// http status code 400 refers to - Bad request.
result = 400;
}
}
} else {
// http status code 403 refers to - forbidden.
result = 403;
}
} else if (uriMatcher.match("/{context}/api/user/register")) {
addUserFormData = request.getContent();
username = addUserFormData.username;
firstname = addUserFormData.firstname;
lastname = addUserFormData.lastname;
emailAddress = addUserFormData.emailAddress;
password = addUserFormData.password;
userRoles = ["internal/devicemgt-user"];
try {
result = userModule.registerUser(username, firstname, lastname, emailAddress, password,
userRoles);
} catch (e) {
log.error("Exception occurred while trying to registering a new user to DC User Store", e);
// http status code 400 refers to - Bad request.
result = 400;
}
} else if (uriMatcher.match("/{context}/api/user/{username}/remove")) {
/*
@Deprecated
*/
if (userModule.isAuthorized("/permission/admin/device-mgt/user/remove")) {
elements = uriMatcher.elements();
username = elements.username;
try {
result = userModule.removeUser(username);
} catch (e) {
log.error("Exception occurred while trying to remove a user from MDM User Store", e);
// http status code 400 refers to - Bad request.
result = 400;
}
} else {
// http status code 403 refers to - forbidden.
result = 403;
}
} else if (uriMatcher.match("/{context}/api/user/all")) {
result = userModule.getUsers();
} else if (uriMatcher.match("/{context}/api/user/environment-loaded")) {
try {
var carbonUser = session.get(constants.USER_SESSION_KEY);
if (!carbonUser) {
response.sendRedirect("/devicemgt/login?#login-required");
exit();
}
utility.startTenantFlow(carbonUser);
var APIManagementProviderService = utility.getAPIManagementProviderService();
var isLoaded = APIManagementProviderService.isTierLoaded();
result = {"isLoaded": isLoaded};
if (isLoaded) {
var samlToken = session.get(constants.SAML_TOKEN_KEY);
if (samlToken) {
apiWrapperUtil.setupTokenPairByJWTGrantType(carbonUser.username + '@' + carbonUser.domain, samlToken);
}
}
response.contentType = 'application/json';
} finally {
utility.endTenantFlow();
}
}
// returning the result.
if (result) {
print(result);
}
%>

View File

@ -0,0 +1,45 @@
{
"appName": "WSO2 IoT Server",
"cachingEnabled": false,
"debuggingEnabled": false,
"permissionRoot": "/",
"portalURL": "https://${server.ip}:9445",
"loginPage": "cdmf.page.sign-in",
"adminServicesUrl": "https://${server.ip}:${server.https_port}/admin/services/",
"authModule": {
"enabled": true,
"login": {
"onSuccess": {
"script": "/app/modules/login.js",
"page": "cdmf.page.processing"
},
"onFail": {
"script": "/app/modules/login.js",
"page": "cdmf.page.sign-in"
}
},
"logout": {
"onSuccess": {
"page": "cdmf.page.sign-in"
},
"onFail": {
"page": "cdmf.page.dashboard"
}
},
"sso": {
"enabled": true,
"issuer" : "devicemgt",
"appName" : "devicemgt",
"identityProviderUrl" : "https://%iot.keymanager.host%:%iot.keymanager.https.port%/samlsso",
"acs": "https://%iot.manager.host%:%iot.manager.https.port%/devicemgt/uuf/sso/acs",
"identityAlias": "wso2carbon",
"responseSigningEnabled" : true,
"validateAssertionValidityPeriod": true,
"validateAudienceRestriction": true,
"assertionSigningEnabled": true
}
},
"errorPages": {
"default": "uuf.page.error"
}
}

View File

@ -0,0 +1,159 @@
{
"appContext": "/devicemgt/",
"isCloud": false,
"isDeviceOwnerEnabled": false,
"httpsURL": "https://%iot.gateway.host%:%iot.gateway.https.port%",
"httpURL": "http://%iot.gateway.host%:%iot.gateway.http.port%",
"wssURL": "https://%iot.analytics.host%:%iot.analytics.https.port%",
"portalURL": "https://%iot.analytics.host%:%iot.analytics.https.port%",
"dashboardServerURL": "%https.ip%",
"androidEnrollmentDir": "/android-web-agent/enrollment",
"windowsEnrollmentDir": "/windows-web-agent/enrollment",
"iOSEnrollmentDir": "/ios-web-agent/enrollment",
"iOSConfigRoot": "%https.ip%/ios-enrollment/",
"iOSAPIRoot": "%https.ip%/ios/",
"adminService": "%https.ip%",
"gatewayEnabled": true,
"oauthProvider": {
"appRegistration": {
"appType": "webapp",
"clientName": "iot_ui",
"owner": "admin@carbon.super",
"dynamicClientAppRegistrationServiceURL": "%https.ip%/dynamic-client-web/register",
"apiManagerClientAppRegistrationServiceURL": "https://%iot.gateway.host%:%iot.gateway.https.port%/api-application-registration/register/tenants",
"grantType": "password refresh_token urn:ietf:params:oauth:grant-type:saml2-bearer urn:ietf:params:oauth:grant-type:jwt-bearer",
"tokenScope": "admin",
"callbackUrl": "%https.ip%/api/device-mgt/v1.0",
"samlGrantTypeName": "urn:ietf:params:oauth:grant-type:saml2-bearer"
},
"tokenServiceURL": "https://%iot.gateway.host%:%iot.gateway.https.port%/token"
},
"adminUser": "admin@carbon.super",
"adminUserTenantId": "-1234",
"adminRole": "admin",
"userValidationConfig": {
"usernameLength": 30,
"usernameJSRegEx": "^[\\S]{3,30}$",
"usernameRegExViolationErrorMsg": "Provided username is invalid.",
"usernameHelpMsg": "Should be in minimum 3 characters long and do not include any whitespaces.",
"firstnameJSRegEx": "^.{3,30}$",
"firstnameRegExViolationErrorMsg": "Provided first name is invalid.",
"lastnameJSRegEx": "^.{3,30}$",
"lastnameRegExViolationErrorMsg": "Provided last name is invalid.",
"emailJSRegEx": "/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/",
"emailRegExViolationErrorMsg": "Provided email is invalid."
},
"groupValidationConfig": {
"groupNameJSRegEx": "^[\\S]{3,30}$",
"groupNameRegExViolationErrorMsg": "Provided group name is invalid.",
"groupNameHelpMsg": "Should be in minimum 3 characters long and should not include any whitespaces."
},
"roleValidationConfig": {
"roleNameJSRegEx": "^[\\S]{3,30}$",
"roleNameRegExViolationErrorMsg": "Provided role name is invalid.",
"roleNameHelpMsg": "should be in minimum 3 characters long and do not include any whitespaces."
},
"generalConfig": {
"host": "%http.ip%",
"companyName": "WSO2 Carbon Device Manager",
"browserTitle": "WSO2 Device Manager",
"copyrightPrefix": "\u00A9 %date-year%, ",
"copyrightOwner": "WSO2 Inc.",
"copyrightOwnersSite": "http://www.wso2.org",
"copyrightSuffix": " All Rights Reserved."
},
"scopes": [
"perm:sign-csr",
"perm:admin:devices:view",
"perm:roles:add",
"perm:roles:add-users",
"perm:roles:update",
"perm:roles:permissions",
"perm:roles:details",
"perm:roles:view",
"perm:roles:create-combined-role",
"perm:roles:delete",
"perm:dashboard:vulnerabilities",
"perm:dashboard:non-compliant-count",
"perm:dashboard:non-compliant",
"perm:dashboard:by-groups",
"perm:dashboard:device-counts",
"perm:dashboard:feature-non-compliant",
"perm:dashboard:count-overview",
"perm:dashboard:filtered-count",
"perm:dashboard:details",
"perm:get-activity",
"perm:devices:delete",
"perm:devices:applications",
"perm:devices:effective-policy",
"perm:devices:compliance-data",
"perm:devices:features",
"perm:devices:operations",
"perm:devices:search",
"perm:devices:details",
"perm:devices:update",
"perm:devices:view",
"perm:view-configuration",
"perm:manage-configuration",
"perm:policies:remove",
"perm:policies:priorities",
"perm:policies:deactivate",
"perm:policies:get-policy-details",
"perm:policies:manage",
"perm:policies:activate",
"perm:policies:update",
"perm:policies:changes",
"perm:policies:get-details",
"perm:users:add",
"perm:users:details",
"perm:users:count",
"perm:users:delete",
"perm:users:roles",
"perm:users:user-details",
"perm:users:credentials",
"perm:users:search",
"perm:users:is-exist",
"perm:users:update",
"perm:users:send-invitation",
"perm:admin-users:view",
"perm:groups:devices",
"perm:groups:update",
"perm:groups:add",
"perm:groups:device",
"perm:groups:devices-count",
"perm:groups:remove",
"perm:groups:groups",
"perm:groups:groups-view",
"perm:groups:share",
"perm:groups:count",
"perm:groups:roles",
"perm:groups:devices-remove",
"perm:groups:devices-add",
"perm:groups:assign",
"perm:device-types:features",
"perm:device-types:types",
"perm:applications:install",
"perm:applications:uninstall",
"perm:admin-groups:count",
"perm:admin-groups:view",
"perm:notifications:mark-checked",
"perm:notifications:view",
"perm:admin:certificates:delete",
"perm:admin:certificates:details",
"perm:admin:certificates:view",
"perm:admin:certificates:add",
"perm:admin:certificates:verify",
"perm:admin",
"perm:devicetype:deployment",
"perm:device-types:events",
"perm:device-types:events:view",
"perm:admin:device-type",
"perm:device:enroll",
"perm:geo-service:analytics-view",
"perm:geo-service:alerts-manage"
],
"isOAuthEnabled": true,
"backendRestEndpoints": {
"deviceMgt": "/api/device-mgt/v1.0"
}
}

View File

@ -0,0 +1,138 @@
{
"Logo": {
"name": "Cloud",
"url": "https://cloudmgt.cloud.wso2.com/cloudmgt",
"target": "_parent"
},
"Main": {
"Domain": {
"url": "#",
"icon": "fw fw-organization",
"isAdminOnly": false,
"target": "_parent",
"dropDown": {
"Organization": {
"url": "https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/organization.jag",
"icon": "fw fw-organization",
"dropDown": "false",
"target": "_self"
},
"Members": {
"url": "https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/user.jag",
"icon": "fa fa-users",
"dropDown": "false",
"target": "_self"
}
}
},
"Account": {
"url": "#",
"icon": "fw fw-resource",
"isAdminOnly": false,
"billingEnabled": true,
"billingApi": {
"username": "admin",
"password": "admin"
},
"cloudMgtHost" : "https://cloudmgt.cloudstaging.wso2.com",
"cloudMgtIndexPage": "https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/index.jag",
"dropDown": {
"Upgrade Now": {
"url": "https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/payment-plans.jag?cloud-type=device_cloud",
"icon": "fw fw-export",
"dropDown": "true",
"target": "_self"
},
"Request Extension": {
"url": "https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/contact-us.jag?cloud-type=device_cloud&request-extension=true",
"icon": "fa fa-mail",
"dropDown": "true",
"target": "_self"
}
}
},
"Support": {
"url": "https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/contact-us.jag?cloud-type=device_cloud",
"icon": "fw fw-mail",
"isAdminOnly": false,
"target": "_self",
"dropDown": "false"
},
"Documentation": {
"url": "#",
"icon": "fw fw-document",
"isAdminOnly": false,
"dropDown": {
"Device Cloud": {
"id": "device_cloud",
"url": "https://docs.wso2.com/display/DeviceCloud/WSO2+Device+Cloud+Documentation",
"icon": "fw fw-mobile",
"target": "_blank"
}
}
}
},
"User": {
"url": "#",
"icon": "fw fw-user",
"dropDown": {
"Profile": {
"url": "https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/user-profile.jag",
"icon": "fw fw-user",
"dropDown": "true",
"target": "_self"
},
"Change Password": {
"url": "https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/change-password.jag",
"icon": "fw fw-lock",
"dropDown": "true",
"target": "_self"
},
"Logout": {
"url": "https://device.cloud.wso2.com/devicemgt/logout",
"icon": "fw fw-sign-out",
"dropDown": "true",
"target": "_self"
}
}
},
"Expand": {
"Clouds": {
"API Cloud": {
"id": "api_cloud",
"url": "https://api.cloud.wso2.com/publisher",
"icon": "fw fw-api fw-3x",
"dropDown": "true",
"target": "_self"
},
"Integration Cloud": {
"id": "integration_cloud",
"url": "https://integration.cloud.wso2.com/appmgt",
"icon": "fw fw-service fw-3x",
"dropDown": "true",
"target": "_self"
},
"Identity Cloud": {
"id": "integration_cloud",
"url": "https://identity.cloud.wso2.com/admin",
"icon": "fw fw-security fw-3x",
"dropDown": "true",
"target": "_self"
}
},
"Actions": {
"Organization": {
"url": "https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/organization.jag",
"icon": "fw fw-organization fw-3x",
"dropDown": "true",
"target": "_self"
},
"Members": {
"url": "https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/user.jag",
"icon": "fa fa-users fa-3x",
"dropDown": "true",
"target": "_self"
}
}
}
}

View File

@ -0,0 +1,4 @@
{
"displayName": "UUF Template App",
"logLevel": "debug"
}

View File

@ -0,0 +1,82 @@
{{!-- Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the
specific language governing permissions and limitations
under the License. --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{defineZone "favicon"}}
<title>
{{defineZone "title"}}
</title>
{{defineZone "topLibCss"}}
{{defineZone "topCss"}}
{{defineZone "topJs"}}
</head>
<body>
<!--modal-->
<div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="modalDemo">
<div class="modal-dialog" role="document">
<div class="modal-content clearfix">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="fw fw-cancel"></i></button>
</div>
<div class="modal-body add-margin-top-2x add-margin-bottom-2x">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!--modal-->
{{defineZone "header"}}
{{defineZone "sidePanes"}}
<!-- page-content-wrapper -->
<div class="page-content-wrapper">
<div class="navbar-wrapper">
{{defineZone "navbars"}}
</div>
{{!defineZone "contentTitle"}}
<div class="container-fluid ">
<div class="body-wrapper">
{{defineZone "content"}}
</div>
</div>
</div>
<!-- /page-content-wrapper -->
<footer class="footer">
<div class="container-fluid">
{{defineZone "footer"}}
</div>
</footer>
{{defineZone "bottomModalContent"}}
{{defineZone "bottomLibJs"}}
{{defineZone "bottomJs"}}
</body>
</html>

View File

@ -0,0 +1,48 @@
{{!-- Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the
specific language governing permissions and limitations
under the License. --}}
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<link rel="stylesheet" href="https://error.cloud.wso2.com/style/style.css">
<link rel="stylesheet" href="https://error.cloud.wso2.com/style/font-mf.css">
<title>
{{defineZone "title"}}
</title>
</head>
<body>
<div class="sky">
<section>
<div class="error-400">
<img src="https://error.cloud.wso2.com/images/400-error.svg">
</div>
<div class="text-label">
<h1>{{#defineZone "messageTitle"}}Oops something went wrong{{/defineZone}}</h1>
<h2>{{defineZone "messageDescription"}}</h2>
<div style="clear: both"></div>
<div class="button-label">
<a href="https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/index.jag"><label class="label-back">Back to Cloud </label></a>
<a href="https://cloudmgt.cloud.wso2.com/cloudmgt/site/pages/contact-us.jag"><label class="label-report"> Report Issue </label></a>
</div>
</div>
</section>
<div class="clouds_one"></div>
<div class="clouds_two"></div>
<div class="clouds_three"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,57 @@
{{!-- Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the
specific language governing permissions and limitations
under the License. --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{defineZone "favicon"}}
<title>
{{defineZone "title"}}
</title>
{{defineZone "topLibCss"}}
{{defineZone "topCss"}}
{{defineZone "topJs"}}
</head>
<body>
<!--modal-->
<div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="modalDemo">
<div class="modal-dialog" role="document">
<div class="modal-content clearfix">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="fw fw-cancel"></i></button>
</div>
<div class="modal-body add-margin-top-2x add-margin-bottom-2x">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!--modal-->
{{defineZone "content"}}
{{defineZone "bottomModalContent"}}
{{defineZone "bottomLibJs"}}
{{defineZone "bottomJs"}}
</body>
</html>

View File

@ -0,0 +1,67 @@
{{!--
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
--}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{#defineZone "title"}}WSO2 Template{{/defineZone}}</title>
{{defineZone "favicon"}}
{{defineZone "topCss"}}
{{defineZone "topJs"}}
</head>
<body>
<!-- header -->
{{defineZone "header"}}
<!-- /header -->
<!-- navbars -->
<div class="navbar-wrapper">
{{defineZone "navbars"}}
</div>
<!-- /navbars -->
<!-- sidepanes -->
{{defineZone "sidePanes"}}
<!-- /sidepanes -->
<!-- page-content-wrapper -->
<div class="page-content-wrapper">
{{defineZone "contentTitle"}}
<div class="container-fluid ">
<div class="body-wrapper">
{{defineZone "content"}}
</div>
</div>
</div>
<!-- /page-content-wrapper -->
<!-- footer -->
<footer class="footer">
<div class="container-fluid">
{{defineZone "footer"}}
</div>
</footer>
<!-- /footer -->
{{defineZone "bottomJs"}}
</body>
</html>

View File

@ -0,0 +1,56 @@
{{!--
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
--}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{#defineZone "title"}}WSO2 Template{{/defineZone}}</title>
{{defineZone "favicon"}}
{{defineZone "topCss"}}
{{defineZone "topJs"}}
</head>
<body>
<!-- header -->
{{defineZone "header"}}
<!-- /header -->
<!-- page-content-wrapper -->
<div class="page-content-wrapper">
<div class="container-fluid ">
<div class="body-wrapper">
{{defineZone "content"}}
</div>
</div>
</div>
<!-- /page-content-wrapper -->
<!-- footer -->
<footer class="footer">
<div class="container-fluid">
{{defineZone "footer"}}
</div>
</footer>
<!-- /footer -->
{{defineZone "bottomJs"}}
</body>
</html>

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var batchProviders;
batchProviders = function () {
var operations = {};
var CONTENT_TYPE_JSON = "application/json";
var JS_MAX_VALUE = "9007199254740992";
var JS_MIN_VALUE = "-9007199254740992";
var TABLENAME_ANDROID = "ORG_WSO2_GEO_FUSEDSPATIALEVENT";
var TABLENAME_ANDROID_SENSE = "ORG_WSO2_IOT_ANDROID_LOCATION";
var tableName = function (deviceType) {
switch (deviceType) {
case "android" :
return TABLENAME_ANDROID;
break;
case "android_sense" :
return TABLENAME_ANDROID_SENSE;
break;
default:
return null;
}
};
var typeMap = {
"bool": "string",
"boolean": "string",
"string": "string",
"int": "number",
"integer": "number",
"long": "number",
"double": "number",
"float": "number",
"time": "time"
};
var log = new Log();
var carbon = require('carbon');
var JSUtils = Packages.org.wso2.carbon.analytics.jsservice.Utils;
var AnalyticsCachedJSServiceConnector = Packages.org.wso2.carbon.analytics.jsservice.AnalyticsCachedJSServiceConnector;
var AnalyticsCache = Packages.org.wso2.carbon.analytics.jsservice.AnalyticsCachedJSServiceConnector.AnalyticsCache;
var cacheTimeoutSeconds = 5;
var cacheSizeBytes = 1024 * 1024 * 1024; // 1GB
response.contentType = CONTENT_TYPE_JSON;
var cache = application.get("AnalyticsWebServiceCache");
if (cache == null) {
cache = new AnalyticsCache(cacheTimeoutSeconds, cacheSizeBytes);
application.put("AnalyticsWebServiceCache", cache);
}
var connector = new AnalyticsCachedJSServiceConnector(cache);
/**
* returns an array of column names & types
* @param providerConfig
*/
operations.getSchema = function (loggedInUser) {
var tablename = tableName(deviceType);
if (tablename == null) {
return [];
}
var schema = [];
var result = connector.getTableSchema(loggedInUser, tablename).getMessage();
result = JSON.parse(result);
var columns = result.columns;
Object.getOwnPropertyNames(columns).forEach(function (name, idx, array) {
var type = "ordinal";
if (columns[name]['type']) {
type = columns[name]['type'];
}
schema.push({
fieldName: name,
fieldType: typeMap[type.toLowerCase()]
});
});
// log.info(schema);
return schema;
};
/**
* returns the actual data
* @param providerConfig
* @param limit
*/
operations.getData = function (loggedInUser, deviceId, deviceType) {
var luceneQuery = "";
var limit = 100;
var result;
var tablename = tableName(deviceType);
if (tablename == null) {
return [];
}
//if there's a filter present, we should perform a Lucene search instead of reading the table
if (luceneQuery) {
luceneQuery = 'id:"' + deviceId + '" AND type:"' + deviceType + '"';
var filter = {
"query": luceneQuery,
"start": 0,
"count": limit
};
result = connector.search(loggedInUser, tablename, stringify(filter)).getMessage();
} else {
var from = JS_MIN_VALUE;
var to = JS_MAX_VALUE;
result = connector.getRecordsByRange(loggedInUser, tablename, from, to, 0, limit, null).getMessage();
}
// error handling ----
var resultString = result.toString();
if (resultString.contains("Failed to get records from table")) {
return null;
}
result = JSON.parse(result);
var data = [];
for (var i = 0; i < result.length; i++) {
var values = result[i].values;
data.push(values);
}
return data;
};
return operations;
}();

View File

@ -0,0 +1,748 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* This module contains user and roles related functionality.
*/
var userModule = function () {
var log = new Log("/app/modules/business-controllers/user.js");
var constants = require("/app/modules/constants.js");
var utility = require("/app/modules/utility.js")["utility"];
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var serviceInvokers = require("/app/modules/oauth/token-protected-service-invokers.js")["invokers"];
/* Initializing user manager */
var carbon = require("carbon");
var url = carbon.server.address("https") + "/admin/services";
var server = new carbon.server.Server(url);
var publicMethods = {};
var privateMethods = {};
/**
* Get the carbon user object from the session. If not found - it will throw a user not found error.
* @returns {object} carbon user object
*/
publicMethods.getCarbonUser = function () {
var carbon = require("carbon");
var carbonUser = session.get(constants["USER_SESSION_KEY"]);
var utility = require("/app/modules/utility.js")["utility"];
if (!carbonUser) {
log.error("User object was not found in the session");
throw constants["ERRORS"]["USER_NOT_FOUND"];
}
return carbonUser;
};
/**
* Only GET method is implemented for now since there are no other type of methods used this method.
* @param url - URL to call the backend without the host
* @param method - HTTP Method (GET, POST)
* @returns An object with 'status': 'success'|'error', 'content': {}
*/
privateMethods.callBackend = function (url, method) {
if (constants["HTTP_GET"] == method) {
return serviceInvokers.XMLHttp.get(url,
function (backendResponse) {
var response = {};
response.content = backendResponse.responseText;
if (backendResponse.status == 200) {
response.status = "success";
} else if (backendResponse.status == 400 || backendResponse.status == 401 ||
backendResponse.status == 404 || backendResponse.status == 500) {
response.status = "error";
}
return response;
}
);
} else {
log.error("Runtime error : This method only support HTTP GET requests.");
}
};
/**
* Build default user claims.
*
* @param firstname First name of the user
* @param lastname Last name of the user
* @param emailAddress Email address of the user
*
* @returns {Object} Default user claims to be provided
*/
privateMethods.buildDefaultUserClaims = function (firstname, lastname, emailAddress) {
var defaultUserClaims = {
"http://wso2.org/claims/givenname": firstname,
"http://wso2.org/claims/lastname": lastname,
"http://wso2.org/claims/emailaddress": emailAddress
};
if (log.isDebugEnabled()) {
log.debug("ClaimMap created for new user : " + stringify(defaultUserClaims));
}
return defaultUserClaims;
};
/**
* Register user to dc-user-store.
*
* @param username Username of the user
* @param firstname First name of the user
* @param lastname Last name of the user
* @param emailAddress Email address of the user
* @param password Password of the user
* @param userRoles Roles assigned to the user
*
* @returns {number} HTTP Status code 201 if succeeded, 409 if user already exists
*/
publicMethods.registerUser = function (username, firstname, lastname, emailAddress, password, userRoles) {
var carbon = require('carbon');
var tenantId = carbon.server.tenantId();
var url = carbon.server.address('https') + "/admin/services";
var server = new carbon.server.Server(url);
var userManager = new carbon.user.UserManager(server, tenantId);
try {
if (userManager.userExists(username)) {
if (log.isDebugEnabled()) {
log.debug("A user with name '" + username + "' already exists.");
}
// http status code 409 refers to - conflict.
return constants.HTTP_CONFLICT;
} else {
var defaultUserClaims = privateMethods.buildDefaultUserClaims(firstname, lastname, emailAddress);
userManager.addUser(username, password, userRoles, defaultUserClaims, "default");
if (log.isDebugEnabled()) {
log.debug("A new user with name '" + username + "' was created.");
}
// http status code 201 refers to - created.
return constants.HTTP_CREATED;
}
} catch (e) {
throw e;
}
};
/*
@Updated
*/
publicMethods.getUsers = function () {
var carbonUser = session.get(constants["USER_SESSION_KEY"]);
var utility = require("/app/modules/utility.js")["utility"];
if (!carbonUser) {
log.error("User object was not found in the session");
throw constants["ERRORS"]["USER_NOT_FOUND"];
}
try {
utility.startTenantFlow(carbonUser);
var url = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] + "/users?offset=0&limit=100";
var response = privateMethods.callBackend(url, constants["HTTP_GET"]);
if (response.status == "success") {
response.content = parse(response.content).users;
}
return response;
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
/*
Get users count from backend services.
*/
publicMethods.getUsersCount = function () {
var carbonUser = session.get(constants["USER_SESSION_KEY"]);
var utility = require("/app/modules/utility.js")["utility"];
if (!carbonUser) {
log.error("User object was not found in the session");
throw constants["ERRORS"]["USER_NOT_FOUND"];
}
try {
utility.startTenantFlow(carbonUser);
var url = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] + "/users/count";
return serviceInvokers.XMLHttp.get(
url, function (responsePayload) {
return parse(responsePayload["responseText"])["count"];
},
function (responsePayload) {
log.error(responsePayload["responseText"]);
return -1;
}
);
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
/**
* Return a User object from the backend by calling the JAX-RS
* @param username
* @returns {object} a response object with status and content on success.
*/
publicMethods.getUser = function (username) {
var carbonUser = publicMethods.getCarbonUser();
var domain;
if (username.indexOf('/') > 0) {
domain = username.substr(0, username.indexOf('/'));
username = username.substr(username.indexOf('/') + 1);
}
try {
utility.startTenantFlow(carbonUser);
var url = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] + "/users/" +
encodeURIComponent(username);
if (domain) {
url += '?domain=' + encodeURIComponent(domain);
}
var response = privateMethods.callBackend(url, constants["HTTP_GET"]);
response["content"] = parse(response.content);
response["userDomain"] = carbonUser.domain;
return response;
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
/**
* Returns a set of roles assigned to a particular user
* @param username
* @returns {object} a response object with status and content on success.
*/
publicMethods.getRolesByUsername = function (username) {
var carbonUser = publicMethods.getCarbonUser();
var domain;
if (username.indexOf('/') > 0) {
domain = username.substr(0, username.indexOf('/'));
username = username.substr(username.indexOf('/') + 1);
}
try {
utility.startTenantFlow(carbonUser);
var url = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] + "/users/" +
encodeURIComponent(username) + "/roles";
if (domain) {
url += '?domain=' + encodeURIComponent(domain);
}
var response = privateMethods.callBackend(url, constants["HTTP_GET"]);
if (response.status == "success") {
response.content = parse(response.content).roles;
}
return response;
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
/*
@NewlyAdded
*/
publicMethods.getUsersByUsername = function () {
var carbonUser = session.get(constants["USER_SESSION_KEY"]);
var utility = require("/app/modules/utility.js")["utility"];
if (!carbonUser) {
log.error("User object was not found in the session");
throw constants["ERRORS"]["USER_NOT_FOUND"];
}
try {
utility.startTenantFlow(carbonUser);
var url = devicemgtProps["httpsURL"] + "/mdm-admin/users/users-by-username";
return privateMethods.callBackend(url, constants["HTTP_GET"]);
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
/*
@Updated
*/
/**
* Get User Roles from user store (Internal roles not included).
*/
publicMethods.getRoles = function () {
var carbonUser = session.get(constants["USER_SESSION_KEY"]);
var utility = require("/app/modules/utility.js")["utility"];
if (!carbonUser) {
log.error("User object was not found in the session");
throw constants["ERRORS"]["USER_NOT_FOUND"];
}
try {
utility.startTenantFlow(carbonUser);
var url = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] +
"/roles?offset=0&limit=100&user-store=all";
var response = privateMethods.callBackend(url, constants["HTTP_GET"]);
if (response.status == "success") {
response.content = parse(response.content).roles;
}
return response;
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
/**
* Get User Roles from user store (Internal roles not included).
*/
publicMethods.getFilteredRoles = function (prefix) {
var carbonUser = session.get(constants["USER_SESSION_KEY"]);
var utility = require("/app/modules/utility.js")["utility"];
if (!carbonUser) {
log.error("User object was not found in the session");
throw constants["ERRORS"]["USER_NOT_FOUND"];
}
try {
utility.startTenantFlow(carbonUser);
var url = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] +
"/roles/filter/" + prefix + "?offset=0&limit=100&user-store=all";
var response = privateMethods.callBackend(url, constants["HTTP_GET"]);
if (response.status == "success") {
response.content = parse(response.content);
}
return response;
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
/**
* Get User Roles count from user store (Internal roles not included).
*/
publicMethods.getRolesCount = function () {
var carbonUser = session.get(constants["USER_SESSION_KEY"]);
var utility = require("/app/modules/utility.js")["utility"];
if (!carbonUser) {
log.error("User object was not found in the session");
throw constants["ERRORS"]["USER_NOT_FOUND"];
}
try {
utility.startTenantFlow(carbonUser);
var url = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] +
"/roles?offset=0&limit=1&user-store=all";
return serviceInvokers.XMLHttp.get(
url, function (responsePayload) {
return parse(responsePayload["responseText"])["count"];
},
function (responsePayload) {
log.error(responsePayload["responseText"]);
return -1;
}
);
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
/*
@Updated
*/
/**
* Get User Roles from user store (Internal roles not included).
* @returns {object} a response object with status and content on success.
*/
publicMethods.getRolesByUserStore = function (userStore) {
userStore = userStore ? userStore : "all";
var carbonUser = session.get(constants["USER_SESSION_KEY"]);
var utility = require("/app/modules/utility.js")["utility"];
if (!carbonUser) {
log.error("User object was not found in the session");
throw constants["ERRORS"]["USER_NOT_FOUND"];
}
try {
utility.startTenantFlow(carbonUser);
var url = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] +
"/roles?user-store=" + encodeURIComponent(userStore) + "&limit=100";
var response = privateMethods.callBackend(url, constants["HTTP_GET"]);
if (response.status == "success") {
response.content = parse(response.content).roles;
}
return response;
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
/**
* Get Platforms.
* @deprecated moved this device module under getDeviceTypes.
*/
//TODO Move this piece of logic out of user.js to somewhere else appropriate.
publicMethods.getPlatforms = function () {
var carbonUser = session.get(constants["USER_SESSION_KEY"]);
var utility = require("/app/modules/utility.js")["utility"];
if (!carbonUser) {
log.error("User object was not found in the session");
throw constants["ERRORS"]["USER_NOT_FOUND"];
}
try {
utility.startTenantFlow(carbonUser);
var url = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] + "/device-types";
var response = privateMethods.callBackend(url, constants["HTTP_GET"]);
if (response.status == "success") {
response.content = parse(response.content);
}
return response;
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
/**
* Get role
*/
publicMethods.getRole = function (roleName) {
var carbonUser = session.get(constants["USER_SESSION_KEY"]);
var utility = require("/app/modules/utility.js")["utility"];
var userStore;
if (!carbonUser) {
log.error("User object was not found in the session");
throw constants["ERRORS"]["USER_NOT_FOUND"];
}
try {
utility.startTenantFlow(carbonUser);
if (roleName.indexOf('/') > 0) {
userStore = roleName.substr(0, roleName.indexOf('/'));
roleName = roleName.substr(roleName.indexOf('/') + 1);
}
var url = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] +
"/roles/" + encodeURIComponent(roleName);
if (userStore) {
url += "?user-store=" + encodeURIComponent(userStore);
}
var response = privateMethods.callBackend(url, constants["HTTP_GET"]);
if (response.status == "success") {
response.content = parse(response.content);
}
return response;
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
/**
* Authenticate a user when he or she attempts to login to MDM.
*
* @param username Username of the user
* @param password Password of the user
* @param successCallback Function to be called at the event of successful authentication
* @param failureCallback Function to be called at the event of failed authentication
*/
publicMethods.login = function (username, password, successCallback, failureCallback) {
var carbonModule = require("carbon");
var carbonServer = application.get("carbonServer");
try {
// check if the user is an authenticated user.
var isAuthenticated = carbonServer.authenticate(username, password);
if (!isAuthenticated) {
failureCallback("authentication");
return;
}
var tenantUser = carbonModule.server.tenantUser(username);
var isAuthorizedToLogin = privateMethods.isAuthorizedToLogin(tenantUser);
if (!isAuthorizedToLogin) {
failureCallback("authorization");
return;
}
session.put(constants.USER_SESSION_KEY, tenantUser);
successCallback(tenantUser);
} catch (e) {
throw e;
}
};
publicMethods.logout = function (successCallback) {
session.invalidate();
successCallback();
};
publicMethods.isAuthorized = function (permission) {
var carbon = require("carbon");
var carbonServer = application.get("carbonServer");
var carbonUser;
try {
carbonUser = session.get(constants.USER_SESSION_KEY);
} catch (e) {
log.error("User object was not found in the session");
carbonUser = null;
}
var utility = require('/app/modules/utility.js').utility;
if (!carbonUser) {
log.error("User object was not found in the session");
response.sendError(401, constants.ERRORS.USER_NOT_FOUND);
exit();
}
try {
utility.startTenantFlow(carbonUser);
var tenantId = carbon.server.tenantId();
var userManager = new carbon.user.UserManager(server, tenantId);
var user = new carbon.user.User(userManager, carbonUser.username);
return user.isAuthorized(permission, "ui.execute");
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
privateMethods.isAuthorizedToLogin = function(carbonUser) {
var utility = require('/app/modules/utility.js').utility;
try {
utility.startTenantFlow(carbonUser);
var tenantId = carbon.server.tenantId();
var userManager = new carbon.user.UserManager(server, tenantId);
var user = new carbon.user.User(userManager, carbonUser.username);
return user.isAuthorized("/permission/admin/login", "ui.execute");
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
};
publicMethods.getUIPermissions = function () {
var permissions = {};
if (publicMethods.isAuthorized("/permission/admin/device-mgt/devices/any-device")) {
permissions["LIST_DEVICES"] = true;
permissions["LIST_OWN_DEVICES"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/devices/owning-device/view")) {
permissions["LIST_OWN_DEVICES"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/admin/groups/view")) {
permissions["LIST_ALL_GROUPS"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/groups/view")) {
permissions["LIST_GROUPS"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/users/list")) {
permissions["LIST_USERS"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/roles/list")) {
permissions["LIST_ROLES"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/policies/list")) {
permissions["LIST_ALL_POLICIES"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/user/policies/list")) {
permissions["LIST_POLICIES"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/devices/enroll")) {
permissions["ADD_DEVICE"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/groups/add")) {
permissions["ADD_GROUP"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/users/add")) {
permissions["ADD_USER"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/groups/devices/add")) {
permissions["ADD_GROUP_DEVICES"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/groups/devices/remove")) {
permissions["REMOVE_GROUP_DEVICES"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/groups/devices/view")) {
permissions["VIEW_GROUP_DEVICES"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/groups/roles/view")) {
permissions["VIEW_GROUP_ROLES"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/groups/update")) {
permissions["UPDATE_GROUP"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/groups/share")) {
permissions["SHARE_GROUP"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/users/remove")) {
permissions["REMOVE_USER"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/groups/remove")) {
permissions["REMOVE_GROUP"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/roles/add")) {
permissions["ADD_ROLE"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/policies/add")) {
permissions["ADD_ADMIN_POLICY"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/user/policies/add")) {
permissions["ADD_POLICY"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/policies/priority")) {
permissions["CHANGE_POLICY_PRIORITY"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/dashboard/view")) {
permissions["VIEW_DASHBOARD"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/platform-configs/view")) {
permissions["TENANT_CONFIGURATION"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt/devices/change-status")) {
permissions["CHANGE_DEVICE_STATUS"] = true;
}
if (publicMethods.isAuthorized("/permission/admin/device-mgt")) {
permissions["IS_ADMIN"] = true;
}
return permissions;
};
/**
* Add new role with permissions.
*
* @param roleName Name of the role
* @param users List of users to assign the role
* @param permissions List of permissions
*/
publicMethods.addRole = function (roleName, users, permissions) {
var carbon = require('carbon');
var tenantId = carbon.server.tenantId();
var url = carbon.server.address('https') + "/admin/services";
var server = new carbon.server.Server(url);
var userManager = new carbon.user.UserManager(server, tenantId);
try {
if (!userManager.roleExists(roleName)) {
userManager.addRole(roleName, users, permissions);
} else {
var array = Object.keys(permissions);
var i, permission;
for (i = 0; i < array.length; i++) {
permission = array[i];
userManager.authorizeRole(roleName, permission, "ui.execute");
}
}
} catch (e) {
throw e;
}
};
publicMethods.addPermissions = function (permissionList, path, init) {
var registry, carbon = require("carbon");
var carbonServer = application.get("carbonServer");
var utility = require('/app/modules/utility.js').utility;
var options = {system: true};
if (init == "login") {
try {
var carbonUser = session.get(constants.USER_SESSION_KEY);
if (!carbonUser) {
log.error("User object was not found in the session");
throw constants.ERRORS.USER_NOT_FOUND;
}
utility.startTenantFlow(carbonUser);
var tenantId = carbon.server.tenantId();
if (carbonUser) {
options.tenantId = tenantId;
}
registry = new carbon.registry.Registry(carbonServer, options);
var i, permission, resource;
for (i = 0; i < permissionList.length; i++) {
permission = permissionList[i];
resource = {
collection: true,
name: permission.name,
properties: {
name: permission.name
}
};
if (path != "") {
registry.put("/_system/governance/permission/admin/" + path + "/" + permission.key, resource);
} else {
registry.put("/_system/governance/permission/admin/" + permission.key, resource);
}
}
} catch (e) {
throw e;
} finally {
utility.endTenantFlow();
}
} else {
registry = new carbon.registry.Registry(carbonServer, options);
var i, permission, resource;
for (i = 0; i < permissionList.length; i++) {
permission = permissionList[i];
resource = {
collection: true,
name: permission.name,
properties: {
name: permission.name
}
};
if (path != "") {
registry.put("/_system/governance/permission/admin/" + path + "/" + permission.key, resource);
} else {
registry.put("/_system/governance/permission/admin/" + permission.key, resource);
}
}
}
};
/**
* Private method to be used by addUser() to
* retrieve secondary user stores.
* This needs Authentication since the method access admin services.
*
* @returns Array of secondary user stores.
*/
publicMethods.getSecondaryUserStores = function () {
var returnVal = [];
// To call the userstore admin service, user needs to have admin permission
if (publicMethods.isAuthorized("/permission/admin")) {
var endpoint = devicemgtProps["adminService"] + constants["USER_STORE_CONFIG_ADMIN_SERVICE_END_POINT"];
var wsPayload = "<xsd:getSecondaryRealmConfigurations xmlns:xsd='http://org.apache.axis2/xsd'/>";
serviceInvokers.WS.soapRequest(
"urn:getSecondaryRealmConfigurations",
wsPayload,
endpoint,
function (wsResponse) {
var domainIDs = stringify(wsResponse. * ::['return']. * ::domainId.text());
if (domainIDs != "\"\"") {
var regExpForSearch = new RegExp(constants["USER_STORES_NOISY_CHAR"], "g");
domainIDs = domainIDs.replace(regExpForSearch, "");
returnVal = domainIDs.split(constants["USER_STORES_SPLITTING_CHAR"]);
}
}, function (e) {
log.error("Error retrieving secondary user stores", e);
},
constants["SOAP_VERSION"]);
} else {
if (log.isDebugEnabled()) {
log.debug("User does not have admin permission to get the secondary user store details.");
}
}
return returnVal;
};
return publicMethods;
}();

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var conf = function () {
var cloudConf = application.get("CLOUD_CONF");
if (!cloudConf) {
cloudConf = require("/app/conf/toplink-menu.json");
var pinch = require("/app/modules/conf-reader/pinch.min.js")["pinch"];
var server = require("carbon")["server"];
var process = require("process");
pinch(cloudConf, /^/,
function (path, key, value) {
if ((typeof value === "string") && value.indexOf("%https.ip%") > -1) {
//noinspection JSUnresolvedFunction
return value.replace("%https.ip%", server.address("https"));
} else if ((typeof value === "string") && value.indexOf("%http.ip%") > -1) {
//noinspection JSUnresolvedFunction
return value.replace("%http.ip%", server.address("http"));
} else if ((typeof value === "string") && value.indexOf("%date-year%") > -1) {
var year = new Date().getFullYear();
return value.replace("%date-year%", year);
} else if ((typeof value === "string") && value.indexOf("%server.ip%") > -1) {
var getProperty = require("process").getProperty;
return value.replace("%server.ip%", getProperty("carbon.local.ip"));
} else {
var paramPattern = new RegExp("%(.*?)%", "g");
var out = value;
while ((matches = paramPattern.exec(value)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (matches.index === paramPattern.lastIndex) {
paramPattern.lastIndex++;
}
if (matches.length == 2) {
var property = process.getProperty(matches[1]);
if (property) {
out = out.replace(new RegExp("%" + matches[1] + "%", "g"), property);
}
}
}
return out;
}
}
);
application.put("CLOUD_CONF", cloudConf);
}
return cloudConf;
}();

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var conf = function () {
var conf = application.get("CONF");
if (!conf) {
conf = require("/app/conf/config.json");
var pinch = require("/app/modules/conf-reader/pinch.min.js")["pinch"];
var server = require("carbon")["server"];
var process = require("process");
pinch(conf, /^/,
function (path, key, value) {
if ((typeof value === "string") && value.indexOf("%https.ip%") > -1) {
//noinspection JSUnresolvedFunction
return value.replace("%https.ip%", server.address("https"));
} else if ((typeof value === "string") && value.indexOf("%http.ip%") > -1) {
//noinspection JSUnresolvedFunction
return value.replace("%http.ip%", server.address("http"));
} else if ((typeof value === "string") && value.indexOf("%date-year%") > -1) {
var year = new Date().getFullYear();
return value.replace("%date-year%", year);
} else if ((typeof value === "string") && value.indexOf("%server.ip%") > -1) {
var getProperty = require("process").getProperty;
return value.replace("%server.ip%", getProperty("carbon.local.ip"));
} else {
var paramPattern = new RegExp("%(.*?)%", "g");
var out = value;
while ((matches = paramPattern.exec(value)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (matches.index === paramPattern.lastIndex) {
paramPattern.lastIndex++;
}
if (matches.length == 2) {
var property = process.getProperty(matches[1]);
if (property) {
out = out.replace(new RegExp("%" + matches[1] + "%", "g"), property);
}
}
}
return out;
}
}
);
var DeviceConfigurationManager = Packages.org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager;
conf["serverConfig"] = DeviceConfigurationManager.getInstance().getDeviceManagementConfig();
application.put("CONF", conf);
}
return conf;
}();

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2011 František Hába <hello@frantisekhaba.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the 'Software'), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Reference:- https://github.com/Baggz/Pinch
* */
(function(){var k=function(a,c){return a.length!==c.length?!1:a.every(function(a,b){return c[b]===a})},j=function(a,c,d){var b,e;if("[object Array]"===Object.prototype.toString.call(a)){b=0;for(e=a.length;b<e;b++)c.apply(d,[b,a[b],a])}else for(b in a)a.hasOwnProperty(b)&&c.apply(d,[b,a[b],a])},h=function(a){for(var c=[],d=!1,b=0,e=a.length,f="",g=function(){f&&(c.push(f),f="")};b<e;b++)a[b].match(/\[|\]/)?(g(),d="]"===a[b]?!1:!0):'"'!==a[b]&&"'"!==a[b]&&("."===a[b]&&!d?g():f+=a[b]),b===e-1&&g();return c},
g=function(a,c,d){var b=-1!==["string","object"].indexOf(typeof a),e="string"===typeof c||c&&c.test&&c.exec,f=-1!==["string","object","function"].indexOf(typeof d);b&&e&&f&&("string"===typeof a?(this.instance=JSON.parse(a),this.json=!0):this.instance=a,this.pattern="string"===typeof c?c.replace(/'/g,'"'):c,this.replacement=d,this.createIndex(this.instance))};g.prototype.createIndex=function(a,c){var d=this;this.index=this.index||[];c=c||"";j(a,function(a,e){var f,a=a+"";f=a.match(/^[a-zA-Z]+$/)?c?
c+"."+a:a:a.match(/\d+/)?c+"["+a+"]":c+'["'+a+'"]';d.index.push(f);"object"===typeof e&&d.createIndex(e,f)})};g.prototype.replace=function(){var a=this;j(this.index,function(c,d){if(a.pattern&&a.pattern.test&&a.pattern.exec&&d.match(a.pattern))return a.replaceValue(d);if("string"===typeof a.pattern){var b=h(d),e=h(a.pattern);if(k(b,e))return a.replaceValue(d)}});return this.json?JSON.stringify(this.instance):this.instance};g.prototype.replaceValue=function(a){var c=this,d=h(a);d.reduce(function(b,
e,f){if(f===d.length-1)f="function"===typeof c.replacement?c.replacement(a,e,b[e]):c.replacement,b[e]=f;else return b[e]},this.instance)};var i=function(a,c,d,b){a=(new g(a,c,d)).replace();return"function"===typeof b?b(null,a):a};"undefined"!==typeof module&&module.exports?module.exports=i:"undefined"!==typeof define?define(function(){return i}):this.pinch=i})();

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var USER_SESSION_KEY = "_UUF_USER";
var UNSPECIFIED = "Unspecified";
var httpURL = "httpURL";
var httpsURL = "httpsURL";
var DEVICE_IDENTIFIER = "deviceIdentifier";
var DEVICE_NAME = "name";
var DEVICE_OWNERSHIP = "ownership";
var DEVICE_OWNER = "owner";
var DEVICE_TYPE = "type";
var DEVICE_VENDOR = "vendor";
var DEVICE_MODEL = "model";
var DEVICE_PRODUCT = "PRODUCT";
var DEVICE_OS_VERSION = "osVersion";
var DEVICE_OS_BUILD_DATE = "osBuildDate";
var DEVICE_PROPERTIES = "properties";
var DEVICE_ENROLLMENT_INFO = "enrolmentInfo";
var DEVICE_STATUS = "status";
var DEVICE_INFO = "deviceInfo";
var FEATURE_NAME = "featureName";
var FEATURE_DESCRIPTION = "featureDescription";
var PLATFORM_ANDROID = "android";
var PLATFORM_WINDOWS = "windows";
var PLATFORM_IOS = "ios";
var LANGUAGE_US = "en_US";
var VENDOR_APPLE = "Apple";
var ERRORS = {
"USER_NOT_FOUND": "USER_NOT_FOUND",
"UNKNOWN_ERROR": "Unknown Error"
};
var USER_STORES_NOISY_CHAR = "\"";
var USER_STORES_SPLITTING_CHAR = "\\n";
var USER_STORE_CONFIG_ADMIN_SERVICE_END_POINT =
"/services/UserStoreConfigAdminService.UserStoreConfigAdminServiceHttpsSoap12Endpoint/";
var SOAP_VERSION = 1.2;
var WEB_SERVICE_ADDRESSING_VERSION = 1.0;
var TOKEN_PAIR = "tokenPair";
var ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS = "encodedTenantBasedClientAppCredentials";
var CONTENT_TYPE_IDENTIFIER = "Content-Type";
var ENCODED_TENANT_BASED_WEB_SOCKET_CLIENT_CREDENTIALS = "encodedTenantBasedWebSocketClientCredentials";
var CONTENT_DISPOSITION_IDENTIFIER = "Content-Disposition";
var APPLICATION_JSON = "application/json";
var APPLICATION_ZIP = "application/zip";
var STREAMING_FILES_ACCEPT_HEADERS = ["application/zip", "application/pdf", "application/octet-stream"];
var ACCEPT_IDENTIFIER = "Accept";
var AUTHORIZATION_HEADER= "Authorization";
var BEARER_PREFIX = "Bearer ";
var HTTP_GET = "GET";
var HTTP_POST = "POST";
var HTTP_PUT = "PUT";
var HTTP_DELETE = "DELETE";
var HTTP_CONFLICT = 409;
var HTTP_CREATED = 201;
var CACHED_CREDENTIALS = "tenantBasedCredentials";
var CACHED_CREDENTIALS_FOR_WEBSOCKET_APP = "tenantBasedWebSocketClientCredentials";
var ALLOWED_SCOPES = "scopes";
var SAML_TOKEN_KEY = "samlToken";
var SKIP_WELCOME_SCREEN ="skipWelcomeScreen";

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var carbonModule = require("carbon");
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
//noinspection JSUnresolvedFunction Server
var carbonServer = new carbonModule.server.Server({
tenanted: true,
url: devicemgtProps["httpsURL"] + "/admin"
});
application.put("carbonServer", carbonServer);
var permissions = {
"/permission/admin/Login": ["ui.execute"],
"/permission/admin/manage/api/subscribe": ["ui.execute"],
"/permission/admin/device-mgt/device/api/subscribe": ["ui.execute"],
"/permission/admin/device-mgt/devices/enroll": ["ui.execute"],
"/permission/admin/device-mgt/devices/disenroll": ["ui.execute"],
"/permission/admin/device-mgt/devices/owning-device/view": ["ui.execute"],
"/permission/admin/manage/portal": ["ui.execute"]
};
var adminPermissions = {
"/permission/admin/device-mgt": ["ui.execute"],
"/permission/admin/manage/api": ["ui.execute"],
"/permission/admin/manage/portal": ["ui.execute"]
};
//On Startup, admin user will get both roles: devicemgt-admin and devicemgt-user
//Average user through sign-up will only receive the role: devicemgt-user.
//Admin can setup necessary permissions for the role: devicemgt-user
userModule.addRole("internal/devicemgt-user", ["admin"], permissions);
userModule.addRole("internal/devicemgt-admin", ["admin"], adminPermissions);

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var onSuccess;
var onFail;
(function () {
var log = new Log("/app/modules/login.js");
var constants = require("/app/modules/constants.js");
onSuccess = function (context) {
var utility = require("/app/modules/utility.js").utility;
var apiWrapperUtil = require("/app/modules/oauth/token-handlers.js")["handlers"];
try {
utility.startTenantFlow(context.user);
var APIManagementProviderService = utility.getAPIManagementProviderService();
var isLoaded = APIManagementProviderService.isTierLoaded();
if(!isLoaded && context.input.samlToken) {
session.put(constants.SKIP_WELCOME_SCREEN, false);
session.put(constants.SAML_TOKEN_KEY, context.input.samlToken);
return;
}
} finally {
utility.endTenantFlow();
}
session.put(constants.SKIP_WELCOME_SCREEN, true);
if (context.input.samlToken) {
//apiWrapperUtil.setupTokenPairBySamlGrantType(context.user.username + '@' + context.user.domain, context.input.samlToken);
/**
* Since the user can be verified using the sso.client.js we can use JWT grant type to issue the token for the user.
*/
apiWrapperUtil.setupTokenPairByJWTGrantType(context.user.username + '@' + context.user.domain, context.input.samlToken);
} else {
apiWrapperUtil.setupTokenPairByPasswordGrantType(context.input.username, context.input.password);
}
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var carbonServer = require("carbon").server;
if (!context.input.samlToken) {
(new carbonServer.Server({url: devicemgtProps["adminService"]}))
.login(context.input.username, context.input.password);
}
};
onFail = function (error) {
log.error(error.message);
}
})();

View File

@ -0,0 +1,406 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var utils = function () {
var log = new Log("/app/modules/oauth/token-handler-utils.js");
var deviceMgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var constants = require("/app/modules/constants.js");
var carbon = require("carbon");
var authModule = require("/lib/modules/auth/auth.js").module;
var utility = require('/app/modules/utility.js').utility;
//noinspection JSUnresolvedVariable
var Base64 = Packages.org.apache.commons.codec.binary.Base64;
//noinspection JSUnresolvedVariable
var String = Packages.java.lang.String;
var publicMethods = {};
var privateMethods = {};
publicMethods["encode"] = function (payload) {
//noinspection JSUnresolvedFunction
return String(Base64.encodeBase64(String(payload).getBytes()));
};
publicMethods["decode"] = function (payload) {
//noinspection JSUnresolvedFunction
return String(Base64.decodeBase64(String(payload).getBytes()));
};
publicMethods["getDynamicClientAppCredentials"] = function () {
// setting up dynamic client application properties
var dcAppProperties = {
"applicationType": deviceMgtProps["oauthProvider"]["appRegistration"]["appType"],
"clientName": deviceMgtProps["oauthProvider"]["appRegistration"]["clientName"],
"owner": deviceMgtProps["oauthProvider"]["appRegistration"]["owner"],
"tokenScope": deviceMgtProps["oauthProvider"]["appRegistration"]["tokenScope"],
"grantType": deviceMgtProps["oauthProvider"]["appRegistration"]["grantType"],
"callbackUrl": deviceMgtProps["oauthProvider"]["appRegistration"]["callbackUrl"],
"saasApp" : true
};
// calling dynamic client app registration service endpoint
var requestURL = deviceMgtProps["oauthProvider"]["appRegistration"]
["dynamicClientAppRegistrationServiceURL"];
var requestPayload = dcAppProperties;
var xhr = new XMLHttpRequest();
xhr.open("POST", requestURL, false);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(stringify(requestPayload));
var dynamicClientAppCredentials = {};
if (xhr["status"] == 201 && xhr["responseText"]) {
var responsePayload = parse(xhr["responseText"]);
dynamicClientAppCredentials["clientId"] = responsePayload["client_id"];
dynamicClientAppCredentials["clientSecret"] = responsePayload["client_secret"];
} else if (xhr["status"] == 400) {
log.error("{/app/modules/oauth/token-handler-utils.js - getDynamicClientAppCredentials()} " +
"Bad request. Invalid data provided as dynamic client application properties.");
dynamicClientAppCredentials = null;
} else {
log.error("{/app/modules/oauth/token-handler-utils.js - getDynamicClientAppCredentials()} " +
"Error in retrieving dynamic client credentials.");
dynamicClientAppCredentials = null;
}
// returning dynamic client credentials
return dynamicClientAppCredentials;
};
publicMethods["getTenantBasedClientAppCredentials"] = function (username) {
if (!username) {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving tenant " +
"based client app credentials. No username " +
"as input - getTenantBasedClientAppCredentials(x)");
return null;
} else {
//noinspection JSUnresolvedFunction, JSUnresolvedVariable
var tenantDomain = carbon.server.tenantDomain({username: username});
if (!tenantDomain) {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving tenant " +
"based client application credentials. Unable to obtain a valid tenant domain for provided " +
"username - getTenantBasedClientAppCredentials(x, y)");
return null;
} else {
var cachedTenantBasedClientAppCredentials = privateMethods.
getCachedTenantBasedClientAppCredentials(tenantDomain);
if (cachedTenantBasedClientAppCredentials) {
return cachedTenantBasedClientAppCredentials;
} else {
var adminUsername = deviceMgtProps["adminUser"];
var adminUserTenantId = deviceMgtProps["adminUserTenantId"];
//claims required for jwtAuthenticator.
var claims = {"http://wso2.org/claims/enduserTenantId": adminUserTenantId,
"http://wso2.org/claims/enduser": adminUsername};
var jwtToken = publicMethods.getJwtToken(adminUsername, claims);
// register a tenant based client app at API Manager
var applicationName = deviceMgtProps["oauthProvider"]["appRegistration"]
["clientName"] + "_" + tenantDomain;
var requestURL = deviceMgtProps["oauthProvider"]["appRegistration"]
["apiManagerClientAppRegistrationServiceURL"] +
"?tenantDomain=" + tenantDomain + "&applicationName=" + applicationName;
var xhr = new XMLHttpRequest();
xhr.open("POST", requestURL, false);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("X-JWT-Assertion", "" + jwtToken);
xhr.send();
if (xhr["status"] == 201 && xhr["responseText"]) {
var responsePayload = parse(xhr["responseText"]);
var tenantBasedClientAppCredentials = {};
tenantBasedClientAppCredentials["clientId"] = responsePayload["client_id"];
tenantBasedClientAppCredentials["clientSecret"] = responsePayload["client_secret"];
privateMethods.
setCachedTenantBasedClientAppCredentials(tenantDomain, tenantBasedClientAppCredentials);
return tenantBasedClientAppCredentials;
} else {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving tenant " +
"based client application credentials from API " +
"Manager - getTenantBasedClientAppCredentials(x, y)");
return null;
}
}
}
}
};
publicMethods["getTenantBasedWebSocketClientAppCredentials"] = function (username) {
if (!username) {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving tenant " +
"based client app credentials. No username " +
"as input - getTenantBasedWebSocketClientAppCredentials(x)");
return null;
} else {
//noinspection JSUnresolvedFunction, JSUnresolvedVariable
var tenantDomain = carbon.server.tenantDomain({username: username});
if (!tenantDomain) {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving tenant " +
"based client application credentials. Unable to obtain a valid tenant domain for provided " +
"username - getTenantBasedWebSocketClientAppCredentials(x, y)");
return null;
} else {
var cachedBasedWebsocketClientAppCredentials = privateMethods.
getCachedBasedWebSocketClientAppCredentials(tenantDomain);
if (cachedBasedWebsocketClientAppCredentials) {
return cachedBasedWebsocketClientAppCredentials;
} else {
var adminUsername = deviceMgtProps["adminUser"];
var adminUserTenantId = deviceMgtProps["adminUserTenantId"];
//claims required for jwtAuthenticator.
var claims = {"http://wso2.org/claims/enduserTenantId": adminUserTenantId,
"http://wso2.org/claims/enduser": adminUsername};
var jwtToken = publicMethods.getJwtToken(adminUsername, claims);
// register a tenant based app at API Manager
var applicationName = "websocket_webapp_" + tenantDomain;
var requestURL = (deviceMgtProps["oauthProvider"]["appRegistration"]
["apiManagerClientAppRegistrationServiceURL"]).replace("/tenants","");
var xhr = new XMLHttpRequest();
xhr.open("POST", requestURL, false);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("X-JWT-Assertion", "" + jwtToken);
xhr.send(stringify({applicationName:applicationName, tags:["device_management"],
isAllowedToAllDomains:false, isMappingAnExistingOAuthApp:false, validityPeriod: 3600}));
if (xhr["status"] == 201 && xhr["responseText"]) {
var responsePayload = parse(xhr["responseText"]);
var tenantTenantBasedWebsocketClientAppCredentials = {};
tenantTenantBasedWebsocketClientAppCredentials["clientId"] = responsePayload["client_id"];
tenantTenantBasedWebsocketClientAppCredentials["clientSecret"] =
responsePayload["client_secret"];
privateMethods.setCachedBasedWebSocketClientAppCredentials(tenantDomain,
tenantTenantBasedWebsocketClientAppCredentials);
return tenantTenantBasedWebsocketClientAppCredentials;
} else {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving tenant " +
"based client application credentials from API " +
"Manager - getTenantBasedWebSocketClientAppCredentials(x, y)");
return null;
}
}
}
}
};
privateMethods["setCachedTenantBasedClientAppCredentials"] = function (tenantDomain, clientAppCredentials) {
var cachedTenantBasedClientAppCredentialsMap = application.get(constants["CACHED_CREDENTIALS"]);
if (!cachedTenantBasedClientAppCredentialsMap) {
cachedTenantBasedClientAppCredentialsMap = {};
cachedTenantBasedClientAppCredentialsMap[tenantDomain] = clientAppCredentials;
application.put(constants["CACHED_CREDENTIALS"], cachedTenantBasedClientAppCredentialsMap);
} else if (!cachedTenantBasedClientAppCredentialsMap[tenantDomain]) {
cachedTenantBasedClientAppCredentialsMap[tenantDomain] = clientAppCredentials;
}
};
privateMethods["getCachedTenantBasedClientAppCredentials"] = function (tenantDomain) {
var cachedTenantBasedClientAppCredentialsMap = application.get(constants["CACHED_CREDENTIALS"]);
if (!cachedTenantBasedClientAppCredentialsMap ||
!cachedTenantBasedClientAppCredentialsMap[tenantDomain]) {
return null;
} else {
return cachedTenantBasedClientAppCredentialsMap[tenantDomain];
}
};
privateMethods["getCachedBasedWebSocketClientAppCredentials"] = function (tenantDomain) {
var cachedBasedWebSocketClientAppCredentialsMap
= application.get(constants["CACHED_CREDENTIALS_FOR_WEBSOCKET_APP"]);
if (!cachedBasedWebSocketClientAppCredentialsMap ||
!cachedBasedWebSocketClientAppCredentialsMap[tenantDomain]) {
return null;
} else {
return cachedBasedWebSocketClientAppCredentialsMap[tenantDomain];
}
};
privateMethods["setCachedBasedWebSocketClientAppCredentials"] = function (tenantDomain, clientAppCredentials) {
var cachedBasedWebSocketClientAppCredentialsMap
= application.get(constants["CACHED_CREDENTIALS_FOR_WEBSOCKET_APP"]);
if (!cachedBasedWebSocketClientAppCredentialsMap) {
cachedBasedWebSocketClientAppCredentialsMap = {};
cachedBasedWebSocketClientAppCredentialsMap[tenantDomain] = clientAppCredentials;
application.put(constants["CACHED_CREDENTIALS_FOR_WEBSOCKET_APP"]
, cachedBasedWebSocketClientAppCredentialsMap);
} else if (!cachedBasedWebSocketClientAppCredentialsMap[tenantDomain]) {
cachedBasedWebSocketClientAppCredentialsMap[tenantDomain] = clientAppCredentials;
}
};
publicMethods["getTokenPairAndScopesByPasswordGrantType"] = function (username, password
, encodedClientAppCredentials, scopes) {
if (!username || !password || !encodedClientAppCredentials || !scopes) {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving access token by password " +
"grant type. No username, password, encoded client app credentials or scopes are " +
"found - getTokenPairAndScopesByPasswordGrantType(a, b, c, d)");
return null;
} else {
// calling oauth provider token service endpoint
var requestURL = deviceMgtProps["oauthProvider"]["tokenServiceURL"];
var requestPayload = "grant_type=password&username=" +
username + "&password=" + password + "&scope=" + scopes;
var xhr = new XMLHttpRequest();
xhr.open("POST", requestURL, false);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("Authorization", "Basic " + encodedClientAppCredentials);
xhr.send(requestPayload);
if (xhr["status"] == 200 && xhr["responseText"]) {
var responsePayload = parse(xhr["responseText"]);
var tokenData = {};
tokenData["accessToken"] = responsePayload["access_token"];
tokenData["refreshToken"] = responsePayload["refresh_token"];
tokenData["scopes"] = responsePayload["scope"];
return tokenData;
} else {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving access token " +
"by password grant type - getTokenPairAndScopesByPasswordGrantType(a, b, c, d)");
return null;
}
}
};
publicMethods["getTokenPairAndScopesByJWTGrantType"] = function (assertion, encodedClientAppCredentials, scopes) {
if (!assertion || !encodedClientAppCredentials || !scopes) {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving access token by jwt " +
"grant type. No assertion, encoded client app credentials or scopes are " +
"found - getTokenPairAndScopesByJWTGrantType(x, y, z)");
return null;
} else {
var ssoLoginUser = authModule.ssoLogin(assertion);
if (!ssoLoginUser.user.username) {
return null;
}
var endUsername = ssoLoginUser.user.username + "@" + ssoLoginUser.user.domain;
var JWTClientManagerServicePackagePath =
"org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService";
//noinspection JSUnresolvedFunction, JSUnresolvedVariable
var JWTClientManagerService = carbon.server.osgiService(JWTClientManagerServicePackagePath);
//noinspection JSUnresolvedFunction
var jwtClient = JWTClientManagerService.getJWTClient();
// returning access token by JWT grant type
var tokenInfo = jwtClient.getAccessToken(encodedClientAppCredentials,
endUsername, scopes);
if (tokenInfo) {
var tokenData = {};
tokenData["accessToken"] = tokenInfo.getAccessToken();
tokenData["refreshToken"] = tokenInfo.getRefreshToken();
tokenData["scopes"] = tokenInfo.getScopes();
return tokenData;
} else {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving access token " +
"by jwt grant type - getTokenPairAndScopesByJWTGrantType()");
return null;
}
}
};
publicMethods["getNewTokenPairByRefreshToken"] = function (refreshToken, encodedClientAppCredentials, scopes) {
if (!refreshToken || !encodedClientAppCredentials) {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving new access token " +
"by current refresh token. No refresh token or encoded client app credentials are " +
"found - getNewTokenPairByRefreshToken(x, y, z)");
return null;
} else {
var requestURL = deviceMgtProps["oauthProvider"]["tokenServiceURL"];
var requestPayload = "grant_type=refresh_token&refresh_token=" + refreshToken;
if (scopes) {
requestPayload = requestPayload + "&scope=" + scopes;
}
var xhr = new XMLHttpRequest();
xhr.open("POST", requestURL, false);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("Authorization", "Basic " + encodedClientAppCredentials);
xhr.send(requestPayload);
if (xhr["status"] == 200 && xhr["responseText"]) {
var responsePayload = parse(xhr["responseText"]);
var tokenPair = {};
tokenPair["accessToken"] = responsePayload["access_token"];
tokenPair["refreshToken"] = responsePayload["refresh_token"];
return tokenPair;
} else {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving new access token by " +
"current refresh token - getNewTokenPairByRefreshToken(x, y, z)");
return null;
}
}
};
publicMethods["getAccessTokenByJWTGrantType"] = function (clientAppCredentials) {
if (!clientAppCredentials) {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving new access token " +
"by current refresh token. No client app credentials are found " +
"as input - getAccessTokenByJWTGrantType(x)");
return null;
} else {
var JWTClientManagerServicePackagePath =
"org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService";
//noinspection JSUnresolvedFunction, JSUnresolvedVariable
var JWTClientManagerService = carbon.server.osgiService(JWTClientManagerServicePackagePath);
//noinspection JSUnresolvedFunction
var jwtClient = JWTClientManagerService.getJWTClient();
// returning access token by JWT grant type
return jwtClient.getAccessToken(clientAppCredentials["clientId"], clientAppCredentials["clientSecret"],
deviceMgtProps["oauthProvider"]["appRegistration"]["owner"], null)["accessToken"];
}
};
publicMethods["getJwtToken"] = function (username, claims) {
if (!username) {
log.error("{/app/modules/oauth/token-handler-utils.js} Error in retrieving new jwt token");
return null;
} else {
var JWTClientManagerServicePackagePath =
"org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService";
//noinspection JSUnresolvedFunction, JSUnresolvedVariable
var JWTClientManagerService = carbon.server.osgiService(JWTClientManagerServicePackagePath);
//noinspection JSUnresolvedFunction
var jwtClient = JWTClientManagerService.getJWTClient();
// returning access token by JWT grant type
if (claims) {
return jwtClient.getJwtToken(username, claims);
} else {
return jwtClient.getJwtToken(username);
}
}
};
publicMethods["removeClientAppCredentials"] = function (tenantDomain) {
var cachedTenantBasedClientAppCredentialsMap = application.get(constants["CACHED_CREDENTIALS"]);
if (cachedTenantBasedClientAppCredentialsMap) {
if (cachedTenantBasedClientAppCredentialsMap[tenantDomain]) {
delete cachedTenantBasedClientAppCredentialsMap[tenantDomain];
}
}
};
publicMethods["getUniqueBrowserScope"] = function () {
var deviceScope = "device_" + utility.md5(request.getHeader("User-Agent") + "::" + request.getRemoteAddr());
deviceScope = deviceScope + " ";
return deviceScope;
};
return publicMethods;
}();

View File

@ -0,0 +1,282 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* -----------------------------------------------------
* Following module includes handlers
* at Jaggery Layer for handling OAuth tokens.
* -----------------------------------------------------
*/
var handlers = function () {
var log = new Log("/app/modules/oauth/token-handlers.js");
var tokenUtil = require("/app/modules/oauth/token-handler-utils.js")["utils"];
var constants = require("/app/modules/constants.js");
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var utility = require("/app/modules/utility.js")["utility"];
var publicMethods = {};
var privateMethods = {};
publicMethods["setupTokenPairByPasswordGrantType"] = function (username, password) {
if (!username || !password) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up access token pair by " +
"password grant type. Either username of logged in user, password or both are missing " +
"as input - setupTokenPairByPasswordGrantType(x, y)");
} else {
privateMethods.setUpEncodedTenantBasedClientAppCredentials(username);
privateMethods.setUpEncodedTenantBasedWebSocketClientAppCredentials(username);
var encodedClientAppCredentials = session.get(constants["ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS"]);
if (!encodedClientAppCredentials) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up access token pair by " +
"password grant type. Encoded client credentials are " +
"missing - setupTokenPairByPasswordGrantType(x, y)");
} else {
var tokenData;
// tokenPair will include current access token as well as current refresh token
var arrayOfScopes = devicemgtProps["scopes"];
arrayOfScopes = arrayOfScopes.concat(utility.getDeviceTypesScopesList());
var stringOfScopes = tokenUtil.getUniqueBrowserScope();
arrayOfScopes.forEach(function (entry) {
stringOfScopes += entry + " ";
});
tokenData = tokenUtil.
getTokenPairAndScopesByPasswordGrantType(username,
encodeURIComponent(password), encodedClientAppCredentials, stringOfScopes);
if (!tokenData) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up " +
"token pair by password grant type. Error in token " +
"retrieval - setupTokenPairByPasswordGrantType(x, y)");
} else {
var tokenPair = {};
tokenPair["accessToken"] = tokenData["accessToken"];
tokenPair["refreshToken"] = tokenData["refreshToken"];
// setting up token pair into session context as a string
session.put(constants["TOKEN_PAIR"], stringify(tokenPair));
var scopes = tokenData.scopes.split(" ");
// adding allowed scopes to the session
session.put(constants["ALLOWED_SCOPES"], scopes);
}
}
}
};
publicMethods["setupTokenPairBySamlGrantType"] = function (username, samlToken) {
if (!username || !samlToken) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up access token pair by " +
"saml grant type. Either username of logged in user, samlToken or both are missing " +
"as input - setupTokenPairBySamlGrantType(x, y)");
} else {
privateMethods.setUpEncodedTenantBasedClientAppCredentials(username);
privateMethods.setUpEncodedTenantBasedWebSocketClientAppCredentials(username);
var encodedClientAppCredentials = session.get(constants["ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS"]);
if (!encodedClientAppCredentials) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up access token pair " +
"by saml grant type. Encoded client credentials are " +
"missing - setupTokenPairBySamlGrantType(x, y)");
} else {
var tokenData;
var arrayOfScopes = devicemgtProps["scopes"];
arrayOfScopes = arrayOfScopes.concat(utility.getDeviceTypesScopesList());
var stringOfScopes = tokenUtil.getUniqueBrowserScope();
arrayOfScopes.forEach(function (entry) {
stringOfScopes += entry + " ";
});
// accessTokenPair will include current access token as well as current refresh token
tokenData = tokenUtil.
getTokenPairAndScopesBySAMLGrantType(samlToken, encodedClientAppCredentials, stringOfScopes);
if (!tokenData) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up token " +
"pair by password grant type. Error in token " +
"retrieval - setupTokenPairBySamlGrantType(x, y)");
} else {
var tokenPair = {};
tokenPair["accessToken"] = tokenData["accessToken"];
tokenPair["refreshToken"] = tokenData["refreshToken"];
// setting up access token pair into session context as a string
session.put(constants["TOKEN_PAIR"], stringify(tokenPair));
var scopes = tokenData.scopes.split(" ");
// adding allowed scopes to the session
session.put(constants["ALLOWED_SCOPES"], scopes);
}
}
}
};
publicMethods["setupTokenPairByJWTGrantType"] = function (username, samlToken) {
//samlToken is used to validate then if the user is a valid user then token is issued with JWT Grant Type.
if (!username || !samlToken) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up access token pair by " +
"saml grant type. Either username of logged in user, samlToken or both are missing " +
"as input - setupTokenPairBySamlGrantType(x, y)");
} else {
privateMethods.setUpEncodedTenantBasedClientAppCredentials(username);
privateMethods.setUpEncodedTenantBasedWebSocketClientAppCredentials(username);
var encodedClientAppCredentials = session.get(constants["ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS"]);
if (!encodedClientAppCredentials) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up access token pair " +
"by saml grant type. Encoded client credentials are " +
"missing - setupTokenPairBySamlGrantType(x, y)");
} else {
var tokenData;
var arrayOfScopes = devicemgtProps["scopes"];
arrayOfScopes = arrayOfScopes.concat(utility.getDeviceTypesScopesList());
var stringOfScopes = tokenUtil.getUniqueBrowserScope();
arrayOfScopes.forEach(function (entry) {
stringOfScopes += entry + " ";
});
// accessTokenPair will include current access token as well as current refresh token
tokenData = tokenUtil.
getTokenPairAndScopesByJWTGrantType(samlToken, encodedClientAppCredentials, stringOfScopes);
if (!tokenData) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up token. Error in token " +
"retrieval - setupTokenPairBySamlGrantType(x, y)");
} else {
var tokenPair = {};
tokenPair["accessToken"] = tokenData["accessToken"];
tokenPair["refreshToken"] = tokenData["refreshToken"];
// setting up access token pair into session context as a string
session.put(constants["TOKEN_PAIR"], stringify(tokenPair));
var scopes = tokenData.scopes.split(" ");
// adding allowed scopes to the session
session.put(constants["ALLOWED_SCOPES"], scopes);
}
}
}
};
publicMethods["refreshTokenPair"] = function () {
var currentTokenPair = parse(session.get(constants["TOKEN_PAIR"]));
// currentTokenPair includes current access token as well as current refresh token
var encodedClientAppCredentials = session.get(constants["ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS"]);
if (!currentTokenPair || !encodedClientAppCredentials) {
throw new Error("{/app/modules/oauth/token-handlers.js} Error in refreshing tokens. Either the " +
"token pair, encoded client app credentials or both input are not found under " +
"session context - refreshTokenPair()");
} else {
var newTokenPair = tokenUtil.
getNewTokenPairByRefreshToken(currentTokenPair["refreshToken"], encodedClientAppCredentials);
if (!newTokenPair) {
log.error("{/app/modules/oauth/token-handlers.js} Error in refreshing token pair. " +
"Unable to update session context with new access token pair - refreshTokenPair()");
userModule.logout(function () {
response.sendRedirect(devicemgtProps["appContext"] + "login");
});
} else {
session.put(constants["TOKEN_PAIR"], stringify(newTokenPair));
}
}
};
privateMethods["setUpEncodedTenantBasedClientAppCredentials"] = function (username) {
if (!username) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up encoded tenant based " +
"client credentials to session context. No username of logged in user is found as " +
"input - setUpEncodedTenantBasedClientAppCredentials(x)");
} else {
if (devicemgtProps["gatewayEnabled"]) {
var tenantBasedClientAppCredentials = tokenUtil.getTenantBasedClientAppCredentials(username);
if (!tenantBasedClientAppCredentials) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up encoded tenant " +
"based client credentials to session context as the server is unable " +
"to obtain such credentials - setUpEncodedTenantBasedClientAppCredentials(x)");
} else {
var encodedTenantBasedClientAppCredentials =
tokenUtil.encode(tenantBasedClientAppCredentials["clientId"] + ":" +
tenantBasedClientAppCredentials["clientSecret"]);
// setting up encoded tenant based client credentials to session context.
session.put(constants["ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS"],
encodedTenantBasedClientAppCredentials);
}
} else {
var dynamicClientAppCredentials = tokenUtil.getDynamicClientAppCredentials();
if (!dynamicClientAppCredentials) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up encoded tenant based " +
"client credentials to session context as the server is unable to obtain " +
"dynamic client credentials - setUpEncodedTenantBasedClientAppCredentials(x)");
}
var encodedTenantBasedClientAppCredentials =
tokenUtil.encode(dynamicClientAppCredentials["clientId"] + ":" +
dynamicClientAppCredentials["clientSecret"]);
// setting up encoded tenant based client credentials to session context.
session.put(constants["ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS"],
encodedTenantBasedClientAppCredentials);
}
}
};
privateMethods["setUpEncodedTenantBasedWebSocketClientAppCredentials"] = function (username) {
if (!username) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up encoded tenant based " +
"client credentials to session context. No username of logged in user is found as " +
"input - setUpEncodedTenantBasedWebSocketClientAppCredentials(x)");
} else {
if (devicemgtProps["gatewayEnabled"]) {
var tenantBasedWebSocketClientAppCredentials
= tokenUtil.getTenantBasedWebSocketClientAppCredentials(username);
if (!tenantBasedWebSocketClientAppCredentials) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up encoded tenant " +
"based client credentials to session context as the server is unable " +
"to obtain such credentials - setUpEncodedTenantBasedWebSocketClientAppCredentials(x)");
} else {
var encodedTenantBasedWebSocketClientAppCredentials =
tokenUtil.encode(tenantBasedWebSocketClientAppCredentials["clientId"] + ":" +
tenantBasedWebSocketClientAppCredentials["clientSecret"]);
// setting up encoded tenant based client credentials to session context.
session.put(constants["ENCODED_TENANT_BASED_WEB_SOCKET_CLIENT_CREDENTIALS"],
encodedTenantBasedWebSocketClientAppCredentials);
}
} else {
var dynamicClientAppCredentials = tokenUtil.getDynamicClientAppCredentials();
if (!dynamicClientAppCredentials) {
throw new Error("{/app/modules/oauth/token-handlers.js} Could not set up encoded tenant based " +
"client credentials to session context as the server is unable to obtain " +
"dynamic client credentials - setUpEncodedTenantBasedWebSocketClientAppCredentials(x)");
}
var encodedTenantBasedWebSocketClientAppCredentials =
tokenUtil.encode(dynamicClientAppCredentials["clientId"] + ":" +
dynamicClientAppCredentials["clientSecret"]);
// setting up encoded tenant based client credentials to session context.
session.put(constants["ENCODED_TENANT_BASED_WEB_SOCKET_CLIENT_CREDENTIALS"],
encodedTenantBasedWebSocketClientAppCredentials);
}
}
};
publicMethods["removeClientDetails"] = function () {
var user = session.get(constants.USER_SESSION_KEY);
if (!user) {
log.error("User object was not found in the session");
throw constants.ERRORS.USER_NOT_FOUND;
}
tokenUtil.removeClientAppCredentials(user.domain);
session.remove(constants["ENCODED_TENANT_BASED_WEB_SOCKET_CLIENT_CREDENTIALS"]);
session.remove(constants["ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS"]);
session.remove(constants["TOKEN_PAIR"]);
session.remove(constants["ALLOWED_SCOPES"]);
};
return publicMethods;
}();

View File

@ -0,0 +1,496 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* ----------------------------------------------------------------------------
* Following module includes invokers
* at Jaggery Layer for calling Backend Services, protected by OAuth Tokens.
* These Services include both REST and SOAP Services.
* ----------------------------------------------------------------------------
*/
var invokers = function () {
var log = new Log("/app/modules/oauth/token-protected-service-invokers.js");
var publicXMLHTTPInvokers = {};
var publicHTTPClientInvokers = {};
var privateMethods = {};
var publicWSInvokers = {};
var TOKEN_EXPIRED = "Access token expired";
var TOKEN_INVALID = "Invalid input. Access token validation failed";
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var constants = require("/app/modules/constants.js");
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
var tokenUtil = require("/app/modules/oauth/token-handlers.js")["handlers"];
/**
* This method reads the token pair from the session and return the access token.
* If the token pair is not set in the session, this will return null.
*/
privateMethods.getAccessToken = function () {
if (session) {
var tokenPair = session.get(constants["TOKEN_PAIR"]);
if (tokenPair) {
return parse(tokenPair)["accessToken"];
}
}
return null;
};
/**
* ---------------------------------------------------------------------------
* Start of XML-HTTP-REQUEST based Interceptor implementations
* ---------------------------------------------------------------------------
*/
/**
* This method add Oauth authentication header to outgoing XML-HTTP Requests if Oauth authentication is enabled.
* @param httpMethod HTTP request type.
* @param requestPayload payload/data if exists which is needed to be send.
* @param endpoint Backend REST API url.
* @param responseCallback a function to be called with response retrieved.
* @param count a counter which hold the number of recursive execution
* @param headers a list of name value pairs for additional http headers
*/
privateMethods["execute"] = function (httpMethod, requestPayload, endpoint, responseCallback, count, headers) {
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open(httpMethod, endpoint);
var contentTypeFound = false;
var acceptTypeFound = false;
for (var i in headers) {
xmlHttpRequest.setRequestHeader(headers[i].name, headers[i].value);
if(constants["CONTENT_TYPE_IDENTIFIER"] == headers[i].name){
contentTypeFound = true;
}
if(constants["ACCEPT_IDENTIFIER"] == headers[i].name){
acceptTypeFound = true;
}
}
if (!contentTypeFound) {
xmlHttpRequest.setRequestHeader(constants["CONTENT_TYPE_IDENTIFIER"], constants["APPLICATION_JSON"]);
}
if (!acceptTypeFound) {
xmlHttpRequest.setRequestHeader(constants["ACCEPT_IDENTIFIER"], constants["APPLICATION_JSON"]);
}
if (devicemgtProps["isOAuthEnabled"]) {
var accessToken = privateMethods.getAccessToken();
if (accessToken == null) {
userModule.logout(function () {
response.sendRedirect(devicemgtProps["appContext"] + "login");
});
} else {
xmlHttpRequest.setRequestHeader(constants["AUTHORIZATION_HEADER"],
constants["BEARER_PREFIX"] + accessToken);
}
}
if (requestPayload) {
xmlHttpRequest.send(requestPayload);
} else {
xmlHttpRequest.send();
}
log.debug("Request : " + httpMethod + " " + endpoint);
log.debug("Request payload if any : " + stringify(requestPayload));
log.debug("Response status : " + xmlHttpRequest.status);
log.debug("Response payload if any : " + xmlHttpRequest.responseText);
if (xmlHttpRequest.status == 401) {
if ((xmlHttpRequest.responseText == TOKEN_EXPIRED ||
xmlHttpRequest.responseText == TOKEN_INVALID ) && count < 5) {
tokenUtil.refreshTokenPair();
return privateMethods.execute(httpMethod, requestPayload, endpoint, responseCallback, ++count, headers);
} else if (privateMethods.isInvalidClientCredential(xmlHttpRequest.responseText)) {
log.error("API application has been removed.");
tokenUtil.removeClientDetails();
session.invalidate();
response.sendRedirect(devicemgtProps["appContext"] + "login");
} else if (privateMethods.isInvalidCredential(xmlHttpRequest.responseText)) {
tokenUtil.refreshTokenPair();
return privateMethods.execute(httpMethod, requestPayload, endpoint, responseCallback, ++count, headers);
}
} else {
return responseCallback(xmlHttpRequest);
}
};
/**
* This method verify whether the access token is expired using response payload.
* This is required when using API gateway.
* @param responsePayload response payload.
* return true if it is invalid otherwise false.
*/
privateMethods["isInvalidCredential"] =
function (responsePayload) {
if (responsePayload) {
try {
payload = parse(responsePayload);
if (payload["fault"]["code"] == 900901) {
log.debug("Access token is invalid: " + payload["fault"]["code"]);
log.debug(payload["fault"]["description"]);
return true;
}
} catch (err) {
// do nothing
}
}
return false;
};
/**
* This method verify whether the client credential is removed/blocked using response payload.
* This is required when using API gateway.
* @param responsePayload response payload.
* return true if it is invalid otherwise false.
*/
privateMethods["isInvalidClientCredential"] =
function (responsePayload) {
if (responsePayload) {
try {
payload = parse(responsePayload);
if (payload["fault"]["message"] == "Invalid Credentials") {
return true;
}
} catch (err) {
// do nothing
}
}
return false;
};
/**
* This method add Oauth authentication header to outgoing XML-HTTP Requests if Oauth authentication is enabled.
* @param httpMethod HTTP request type.
* @param requestPayload payload/data if exists which is needed to be send.
* @param endpoint Backend REST API url.
* @param responseCallback a function to be called with response retrieved.
*/
privateMethods["initiateXMLHTTPRequest"] =
function (httpMethod, requestPayload, endpoint, responseCallback, headers) {
return privateMethods.execute(httpMethod, requestPayload, endpoint, responseCallback, 0, headers);
};
/**
* This method invokes return initiateXMLHttpRequest for get calls.
* @param endpoint Backend REST API url.
* @param responseCallback a function to be called with response retrieved.
*/
publicXMLHTTPInvokers["get"] = function (endpoint, responseCallback, headers) {
var requestPayload = null;
return privateMethods.initiateXMLHTTPRequest(constants["HTTP_GET"], requestPayload, endpoint, responseCallback,
headers);
};
/**
* This method invokes return initiateXMLHttpRequest for post calls.
* @param endpoint Backend REST API url.
* @param requestPayload payload/data if exists which is needed to be send.
* @param responseCallback a function to be called with response retrieved.
*/
publicXMLHTTPInvokers["post"] = function (endpoint, requestPayload, responseCallback, headers) {
return privateMethods.initiateXMLHTTPRequest(constants["HTTP_POST"], requestPayload, endpoint, responseCallback,
headers);
};
/**
* This method invokes return initiateXMLHttpRequest for put calls.
* @param endpoint Backend REST API url.
* @param requestPayload payload/data if exists which is needed to be send.
* @param responseCallback a function to be called with response retrieved.
*/
publicXMLHTTPInvokers["put"] = function (endpoint, requestPayload, responseCallback, headers) {
return privateMethods.initiateXMLHTTPRequest(constants["HTTP_PUT"], requestPayload, endpoint, responseCallback,
headers);
};
/**
* This method invokes return initiateXMLHttpRequest for delete calls.
* @param endpoint Backend REST API url.
* @param responseCallback a function to be called with response retrieved.
*/
publicXMLHTTPInvokers["delete"] = function (endpoint, responseCallback, headers) {
var requestPayload = null;
return privateMethods.initiateXMLHTTPRequest(constants["HTTP_DELETE"], requestPayload, endpoint,
responseCallback, headers);
};
/**
* ---------------------------------------------------------------------------
* Start of WS-REQUEST based Interceptor implementations
* ---------------------------------------------------------------------------
*/
/**
* This method add Oauth authentication header to outgoing WS Requests if Oauth authentication is enabled.
* @param action
* @param endpoint service end point to be triggered.
* @param payload soap payload which need to be send.
* @param successCallback a function to be called if the respond if successful.
* @param errorCallback a function to be called if en error is reserved.
* @param soapVersion soapVersion which need to used.
*/
privateMethods["initiateWSRequest"] = function (action, endpoint, successCallback,
errorCallback, soapVersion, payload) {
var ws = require("ws");
//noinspection JSUnresolvedFunction
var wsRequest = new ws.WSRequest();
var options = [];
if (devicemgtProps["isOAuthEnabled"]) {
var accessToken = privateMethods.getAccessToken();
if (accessToken) {
var authenticationHeaderName = String(constants["AUTHORIZATION_HEADER"]);
var authenticationHeaderValue = String(constants["BEARER_PREFIX"] + accessToken);
var headers = [];
var oAuthAuthenticationData = {};
oAuthAuthenticationData.name = authenticationHeaderName;
oAuthAuthenticationData.value = authenticationHeaderValue;
headers.push(oAuthAuthenticationData);
options.HTTPHeaders = headers;
} else {
response.sendRedirect(devicemgtProps["appContext"] + "login");
}
}
options.useSOAP = soapVersion;
options.useWSA = constants["WEB_SERVICE_ADDRESSING_VERSION"];
options.action = action;
var wsResponse;
try {
wsRequest.open(options, endpoint, false);
if (payload) {
wsRequest.send(payload);
} else {
wsRequest.send();
}
wsResponse = wsRequest.responseE4X;
} catch (e) {
return errorCallback(e);
}
return successCallback(wsResponse);
};
/**
* This method invokes return initiateWSRequest for soap calls.
* @param action describes particular soap action.
* @param requestPayload SOAP request payload which is needed to be send.
* @param endpoint service end point to be triggered.
* @param successCallback a function to be called if the respond if successful.
* @param errorCallback a function to be called if en error is reserved.
* @param soapVersion soapVersion which need to used.
*/
publicWSInvokers["soapRequest"] = function (action, requestPayload, endpoint,
successCallback, errorCallback, soapVersion) {
return privateMethods.initiateWSRequest(action, endpoint, successCallback,
errorCallback, soapVersion, requestPayload);
};
/**
* ---------------------------------------------------------------------------
* Start of HTTP-CLIENT-REQUEST based Interceptor implementations
* ---------------------------------------------------------------------------
*/
/**
* This method add Oauth authentication header to outgoing HTTPClient Requests if Oauth authentication is enabled.
* @param method HTTP request type.
* @param url target url.
* @param payload payload/data which need to be send.
* @param successCallback a function to be called if the respond if successful.
* @param errorCallback a function to be called if en error is reserved.
* @param headers a list of name value pairs for additional http headers.
*/
privateMethods["initiateHTTPClientRequest"] =
function (method, url, successCallback, errorCallback, payload, headers) {
//noinspection JSUnresolvedVariable
var HttpClient = Packages.org.apache.commons.httpclient.HttpClient;
var httpMethodObject;
switch (method) {
case constants["HTTP_GET"]:
//noinspection JSUnresolvedVariable
var GetMethod = Packages.org.apache.commons.httpclient.methods.GetMethod;
httpMethodObject = new GetMethod(url);
break;
case constants["HTTP_POST"]:
//noinspection JSUnresolvedVariable
var PostMethod = Packages.org.apache.commons.httpclient.methods.PostMethod;
httpMethodObject = new PostMethod(url);
break;
case constants["HTTP_PUT"]:
//noinspection JSUnresolvedVariable
var PutMethod = Packages.org.apache.commons.httpclient.methods.PutMethod;
httpMethodObject = new PutMethod(url);
break;
case constants["HTTP_DELETE"]:
//noinspection JSUnresolvedVariable
var DeleteMethod = Packages.org.apache.commons.httpclient.methods.DeleteMethod;
httpMethodObject = new DeleteMethod(url);
break;
default:
//noinspection JSUnresolvedFunction
throw new IllegalArgumentException("Invalid HTTP request method: " + method);
}
//noinspection JSUnresolvedVariable
var Header = Packages.org.apache.commons.httpclient.Header;
var contentTypeFound = false;
var acceptTypeFound = false;
var acceptTypeValue = constants["APPLICATION_JSON"];
for (var i in headers) {
var header = new Header();
header.setName(headers[i].name);
header.setValue(headers[i].value);
httpMethodObject.addRequestHeader(header);
if(constants["CONTENT_TYPE_IDENTIFIER"] == headers[i].name){
contentTypeFound = true;
}
if(constants["ACCEPT_IDENTIFIER"] == headers[i].name){
acceptTypeFound = true;
acceptTypeValue = headers[i].value;
}
}
var header = new Header();
if(!contentTypeFound){
header.setName(constants["CONTENT_TYPE_IDENTIFIER"]);
header.setValue(constants["APPLICATION_JSON"]);
//noinspection JSUnresolvedFunction
httpMethodObject.addRequestHeader(header);
}
if(!acceptTypeFound) {
header = new Header();
header.setName(constants["ACCEPT_IDENTIFIER"]);
header.setValue(constants["APPLICATION_JSON"]);
//noinspection JSUnresolvedFunction
httpMethodObject.addRequestHeader(header);
}
if (devicemgtProps["isOAuthEnabled"]) {
var accessToken = privateMethods.getAccessToken();
if (accessToken) {
header = new Header();
header.setName(constants["AUTHORIZATION_HEADER"]);
header.setValue(constants["BEARER_PREFIX"] + accessToken);
//noinspection JSUnresolvedFunction
httpMethodObject.addRequestHeader(header);
} else {
response.sendRedirect(devicemgtProps["appContext"] + "login");
}
}
//noinspection JSUnresolvedFunction
if (payload != null) {
var StringRequestEntity = Packages.org.apache.commons.httpclient.methods.StringRequestEntity;
var stringRequestEntity = new StringRequestEntity(stringify(payload));
//noinspection JSUnresolvedFunction
httpMethodObject.setRequestEntity(stringRequestEntity);
}
var client = new HttpClient();
try {
//noinspection JSUnresolvedFunction
client.executeMethod(httpMethodObject);
//noinspection JSUnresolvedFunction
var status = httpMethodObject.getStatusCode();
if (status >= 200 && status < 300) {
if (constants["STREAMING_FILES_ACCEPT_HEADERS"].indexOf(acceptTypeValue) > -1) {
return successCallback(httpMethodObject.getResponseBodyAsStream(),
httpMethodObject.getResponseHeaders());
} else {
return successCallback(httpMethodObject.getResponseBodyAsString(),
httpMethodObject.getResponseHeaders());
}
} else {
return errorCallback(httpMethodObject.getResponseBodyAsString(),
httpMethodObject.getResponseHeaders());
}
} catch (e) {
return errorCallback(response);
} finally {
//noinspection JSUnresolvedFunction
if (method != constants["HTTP_GET"]) {
method.releaseConnection();
}
}
};
/**
* This method invokes return initiateHTTPClientRequest for get calls.
* @param url target url.
* @param successCallback a function to be called if the respond if successful.
* @param errorCallback a function to be called if en error is reserved.
* @param headers a list of name value pairs for additional http headers.
*/
publicHTTPClientInvokers["get"] = function (url, successCallback, errorCallback, headers) {
var requestPayload = null;
return privateMethods.initiateHTTPClientRequest(constants["HTTP_GET"], url, successCallback, errorCallback,
requestPayload, headers);
};
/**
* This method invokes return initiateHTTPClientRequest for post calls.
* @param url target url.
* @param payload payload/data which need to be send.
* @param successCallback a function to be called if the respond if successful.
* @param errorCallback a function to be called if en error is reserved.
* @param headers a list of name value pairs for additional http headers.
*/
publicHTTPClientInvokers["post"] = function (url, payload, successCallback, errorCallback, headers) {
return privateMethods.initiateHTTPClientRequest(constants["HTTP_POST"], url, successCallback, errorCallback,
payload, headers);
};
/**
* This method invokes return initiateHTTPClientRequest for put calls.
* @param url target url.
* @param payload payload/data which need to be send.
* @param successCallback a function to be called if the respond if successful.
* @param errorCallback a function to be called if en error is reserved.
* @param headers a list of name value pairs for additional http headers.
*/
publicHTTPClientInvokers["put"] = function (url, payload, successCallback, errorCallback, headers) {
return privateMethods.initiateHTTPClientRequest(constants["HTTP_PUT"], url, successCallback, errorCallback,
payload, headers);
};
/**
* This method invokes return initiateHTTPClientRequest for delete calls.
* @param url target url.
* @param successCallback a function to be called if the respond if successful.
* @param errorCallback a function to be called if en error is reserved.
* @param headers a list of name value pairs for additional http headers.
*/
publicHTTPClientInvokers["delete"] = function (url, successCallback, errorCallback, headers) {
var requestPayload = null;
return privateMethods.initiateHTTPClientRequest(constants["HTTP_DELETE"], url, successCallback, errorCallback,
requestPayload, headers);
};
var publicMethods = {};
publicMethods.XMLHttp = publicXMLHTTPInvokers;
publicMethods.WS = publicWSInvokers;
publicMethods.HttpClient = publicHTTPClientInvokers;
return publicMethods;
}();

View File

@ -0,0 +1,396 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var utility;
utility = function () {
var constants = require('/app/modules/constants.js');
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var log = new Log("/app/modules/utility.js");
var JavaClass = Packages.java.lang.Class;
var PrivilegedCarbonContext = Packages.org.wso2.carbon.context.PrivilegedCarbonContext;
var server = require("carbon")["server"];
var getOsgiService = function (className) {
return PrivilegedCarbonContext.getThreadLocalCarbonContext().getOSGiService(JavaClass.forName(className));
};
var deviceTypeConfigMap = {};
var publicMethods = {};
publicMethods.startTenantFlow = function (userInfo) {
var context, carbon = require('carbon');
PrivilegedCarbonContext.startTenantFlow();
context = PrivilegedCarbonContext.getThreadLocalCarbonContext();
context.setTenantDomain(carbon.server.tenantDomain({
tenantId: userInfo.tenantId
}));
context.setTenantId(userInfo.tenantId);
context.setUsername(userInfo.username || null);
};
publicMethods.endTenantFlow = function () {
PrivilegedCarbonContext.endTenantFlow();
};
publicMethods.getDeviceManagementService = function () {
return getOsgiService('org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService');
};
publicMethods.getAPIManagementProviderService = function () {
return getOsgiService('org.wso2.carbon.apimgt.application.extension.APIManagementProviderService');
};
publicMethods.getUserManagementService = function () {
return getOsgiService("org.wso2.carbon.device.mgt.user.core.UserManager");
};
publicMethods.getPolicyManagementService = function () {
return getOsgiService("org.wso2.carbon.policy.mgt.core.PolicyManagerService");
};
publicMethods.getDeviceTypeConfig = function (deviceType) {
var unitName = publicMethods.getTenantedDeviceUnitName(deviceType, "type-view");
if (!unitName) {
return null;
}
if (deviceType in deviceTypeConfigMap) {
return deviceTypeConfigMap[deviceType];
}
var deviceTypeConfig;
var deviceTypeConfigFile = new File("/app/units/" + unitName + "/private/config.json");
if (deviceTypeConfigFile.isExists()) {
try {
deviceTypeConfigFile.open("r");
var config = deviceTypeConfigFile.readAll();
config = config.replace("%https.ip%", server.address("https"));
config = config.replace("%http.ip%", server.address("http"));
deviceTypeConfig = parse(config);
} catch (err) {
log.error("Error while reading device config file for `" + deviceType + "`: " + err);
} finally {
deviceTypeConfigFile.close();
}
}
deviceTypeConfigMap[deviceType] = deviceTypeConfig;
return deviceTypeConfig;
};
publicMethods.getOperationIcon = function (deviceType, operation) {
var unitName = publicMethods.getTenantedDeviceUnitName(deviceType, "type-view");
var iconPath = "/app/units/" + unitName + "/public/images/operations/" + operation + ".png";
var icon = new File(iconPath);
if (icon.isExists()) {
return devicemgtProps["appContext"] + "public/" + unitName + "/images/operations/" + operation + ".png";
} else {
return null;
}
};
publicMethods.getDeviceThumb = function (deviceType) {
var unitName = publicMethods.getTenantedDeviceUnitName(deviceType, "type-view");
if (!unitName) {
unitName = "cdmf.unit.default.device.type.type-view";
}
var iconPath = "/app/units/" + unitName + "/public/images/thumb.png";
var icon = new File(iconPath);
if (icon.isExists()) {
return devicemgtProps["appContext"] + "public/" + unitName + "/images/thumb.png";
} else {
return null;
}
};
publicMethods.getTenantedDeviceUnitName = function (deviceType, unitPostfix) {
var user = session.get(constants.USER_SESSION_KEY);
if (!user) {
log.error("User object was not found in the session");
throw constants.ERRORS.USER_NOT_FOUND;
}
var unitName = user.domain + ".cdmf.unit.device.type." + deviceType + "." + unitPostfix;
if (new File("/app/units/" + unitName).isExists()) {
return unitName;
}
unitName = "cdmf.unit.device.type." + deviceType + "." + unitPostfix;
if (new File("/app/units/" + unitName).isExists()) {
return unitName;
}
return null;
};
publicMethods.getDeviceTypesScopesList = function () {
var dirs = new File("/app/units/").listFiles();
var scopesList = [];
for (var i = 0; i < dirs.length; i++) {
var unitName = dirs[i].getName();
if (unitName.match(/^cdmf\.unit\.device\.type\..*\.type-view$/g)) {
var deviceTypeConfigFile = new File("/app/units/" + unitName + "/private/config.json");
if (deviceTypeConfigFile.isExists()) {
try {
deviceTypeConfigFile.open("r");
var config = deviceTypeConfigFile.readAll();
config = config.replace("%https.ip%", server.address("https"));
config = config.replace("%http.ip%", server.address("http"));
var deviceTypeConfig = parse(config);
if (deviceTypeConfig.deviceType && deviceTypeConfig.deviceType.scopes) {
scopesList = scopesList.concat(deviceTypeConfig.deviceType.scopes);
}
} catch (err) {
log.error("Error while reading device config file for `" + deviceType + "`: " + err);
} finally {
deviceTypeConfigFile.close();
}
}
}
}
return scopesList;
};
/**
* Escapes special characters such as <,>,',",...etc
* This will prevent XSS attacks upon JSON.
* @param text
* @returns {*}
*/
publicMethods.encodeJson = function (text) {
if (text) {
return text
.replace(/\\u003c/g, "&lt;")
.replace(/</g, "&lt;")
.replace(/\\u003e/g, "&gt;")
.replace(/>/g, "&gt;")
.replace(/\\u0027/g, "&#39;")
.replace(/'/g, "&#39;")
.replace(/\\"/g, "&quot;")
.replace(/\\u0022/g, "&quot;");
} else {
return "";
}
};
publicMethods.md5 = function (s) {
function L(k, d) {
return (k << d) | (k >>> (32 - d))
}
function K(G, k) {
var I, d, F, H, x;
F = (G & 2147483648);
H = (k & 2147483648);
I = (G & 1073741824);
d = (k & 1073741824);
x = (G & 1073741823) + (k & 1073741823);
if (I & d) {
return (x ^ 2147483648 ^ F ^ H)
}
if (I | d) {
if (x & 1073741824) {
return (x ^ 3221225472 ^ F ^ H)
} else {
return (x ^ 1073741824 ^ F ^ H)
}
} else {
return (x ^ F ^ H)
}
}
function r(d, F, k) {
return (d & F) | ((~d) & k)
}
function q(d, F, k) {
return (d & k) | (F & (~k))
}
function p(d, F, k) {
return (d ^ F ^ k)
}
function n(d, F, k) {
return (F ^ (d | (~k)))
}
function u(G, F, aa, Z, k, H, I) {
G = K(G, K(K(r(F, aa, Z), k), I));
return K(L(G, H), F)
}
function f(G, F, aa, Z, k, H, I) {
G = K(G, K(K(q(F, aa, Z), k), I));
return K(L(G, H), F)
}
function D(G, F, aa, Z, k, H, I) {
G = K(G, K(K(p(F, aa, Z), k), I));
return K(L(G, H), F)
}
function t(G, F, aa, Z, k, H, I) {
G = K(G, K(K(n(F, aa, Z), k), I));
return K(L(G, H), F)
}
function e(G) {
var Z;
var F = G.length;
var x = F + 8;
var k = (x - (x % 64)) / 64;
var I = (k + 1) * 16;
var aa = Array(I - 1);
var d = 0;
var H = 0;
while (H < F) {
Z = (H - (H % 4)) / 4;
d = (H % 4) * 8;
aa[Z] = (aa[Z] | (G.charCodeAt(H) << d));
H++
}
Z = (H - (H % 4)) / 4;
d = (H % 4) * 8;
aa[Z] = aa[Z] | (128 << d);
aa[I - 2] = F << 3;
aa[I - 1] = F >>> 29;
return aa
}
function B(x) {
var k = "", F = "", G, d;
for (d = 0; d <= 3; d++) {
G = (x >>> (d * 8)) & 255;
F = "0" + G.toString(16);
k = k + F.substr(F.length - 2, 2)
}
return k
}
function J(k) {
k = k.replace(/rn/g, "n");
var d = "";
for (var F = 0; F < k.length; F++) {
var x = k.charCodeAt(F);
if (x < 128) {
d += String.fromCharCode(x)
} else {
if ((x > 127) && (x < 2048)) {
d += String.fromCharCode((x >> 6) | 192);
d += String.fromCharCode((x & 63) | 128)
} else {
d += String.fromCharCode((x >> 12) | 224);
d += String.fromCharCode(((x >> 6) & 63) | 128);
d += String.fromCharCode((x & 63) | 128)
}
}
}
return d
}
var C = Array();
var P, h, E, v, g, Y, X, W, V;
var S = 7, Q = 12, N = 17, M = 22;
var A = 5, z = 9, y = 14, w = 20;
var o = 4, m = 11, l = 16, j = 23;
var U = 6, T = 10, R = 15, O = 21;
s = J(s);
C = e(s);
Y = 1732584193;
X = 4023233417;
W = 2562383102;
V = 271733878;
for (P = 0; P < C.length; P += 16) {
h = Y;
E = X;
v = W;
g = V;
Y = u(Y, X, W, V, C[P + 0], S, 3614090360);
V = u(V, Y, X, W, C[P + 1], Q, 3905402710);
W = u(W, V, Y, X, C[P + 2], N, 606105819);
X = u(X, W, V, Y, C[P + 3], M, 3250441966);
Y = u(Y, X, W, V, C[P + 4], S, 4118548399);
V = u(V, Y, X, W, C[P + 5], Q, 1200080426);
W = u(W, V, Y, X, C[P + 6], N, 2821735955);
X = u(X, W, V, Y, C[P + 7], M, 4249261313);
Y = u(Y, X, W, V, C[P + 8], S, 1770035416);
V = u(V, Y, X, W, C[P + 9], Q, 2336552879);
W = u(W, V, Y, X, C[P + 10], N, 4294925233);
X = u(X, W, V, Y, C[P + 11], M, 2304563134);
Y = u(Y, X, W, V, C[P + 12], S, 1804603682);
V = u(V, Y, X, W, C[P + 13], Q, 4254626195);
W = u(W, V, Y, X, C[P + 14], N, 2792965006);
X = u(X, W, V, Y, C[P + 15], M, 1236535329);
Y = f(Y, X, W, V, C[P + 1], A, 4129170786);
V = f(V, Y, X, W, C[P + 6], z, 3225465664);
W = f(W, V, Y, X, C[P + 11], y, 643717713);
X = f(X, W, V, Y, C[P + 0], w, 3921069994);
Y = f(Y, X, W, V, C[P + 5], A, 3593408605);
V = f(V, Y, X, W, C[P + 10], z, 38016083);
W = f(W, V, Y, X, C[P + 15], y, 3634488961);
X = f(X, W, V, Y, C[P + 4], w, 3889429448);
Y = f(Y, X, W, V, C[P + 9], A, 568446438);
V = f(V, Y, X, W, C[P + 14], z, 3275163606);
W = f(W, V, Y, X, C[P + 3], y, 4107603335);
X = f(X, W, V, Y, C[P + 8], w, 1163531501);
Y = f(Y, X, W, V, C[P + 13], A, 2850285829);
V = f(V, Y, X, W, C[P + 2], z, 4243563512);
W = f(W, V, Y, X, C[P + 7], y, 1735328473);
X = f(X, W, V, Y, C[P + 12], w, 2368359562);
Y = D(Y, X, W, V, C[P + 5], o, 4294588738);
V = D(V, Y, X, W, C[P + 8], m, 2272392833);
W = D(W, V, Y, X, C[P + 11], l, 1839030562);
X = D(X, W, V, Y, C[P + 14], j, 4259657740);
Y = D(Y, X, W, V, C[P + 1], o, 2763975236);
V = D(V, Y, X, W, C[P + 4], m, 1272893353);
W = D(W, V, Y, X, C[P + 7], l, 4139469664);
X = D(X, W, V, Y, C[P + 10], j, 3200236656);
Y = D(Y, X, W, V, C[P + 13], o, 681279174);
V = D(V, Y, X, W, C[P + 0], m, 3936430074);
W = D(W, V, Y, X, C[P + 3], l, 3572445317);
X = D(X, W, V, Y, C[P + 6], j, 76029189);
Y = D(Y, X, W, V, C[P + 9], o, 3654602809);
V = D(V, Y, X, W, C[P + 12], m, 3873151461);
W = D(W, V, Y, X, C[P + 15], l, 530742520);
X = D(X, W, V, Y, C[P + 2], j, 3299628645);
Y = t(Y, X, W, V, C[P + 0], U, 4096336452);
V = t(V, Y, X, W, C[P + 7], T, 1126891415);
W = t(W, V, Y, X, C[P + 14], R, 2878612391);
X = t(X, W, V, Y, C[P + 5], O, 4237533241);
Y = t(Y, X, W, V, C[P + 12], U, 1700485571);
V = t(V, Y, X, W, C[P + 3], T, 2399980690);
W = t(W, V, Y, X, C[P + 10], R, 4293915773);
X = t(X, W, V, Y, C[P + 1], O, 2240044497);
Y = t(Y, X, W, V, C[P + 8], U, 1873313359);
V = t(V, Y, X, W, C[P + 15], T, 4264355552);
W = t(W, V, Y, X, C[P + 6], R, 2734768916);
X = t(X, W, V, Y, C[P + 13], O, 1309151649);
Y = t(Y, X, W, V, C[P + 4], U, 4149444226);
V = t(V, Y, X, W, C[P + 11], T, 3174756917);
W = t(W, V, Y, X, C[P + 2], R, 718787259);
X = t(X, W, V, Y, C[P + 9], O, 3951481745);
Y = K(Y, h);
X = K(X, E);
W = K(W, v);
V = K(V, g)
}
var i = B(Y) + B(X) + B(W) + B(V);
return i.toLowerCase()
};
return publicMethods;
}();

View File

@ -0,0 +1,234 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{unit "cdmf.unit.ui.title" pageTitle="Home"}}
{{#zone "breadcrumbs"}}
<li>
<a href="{{@app.context}}/">
<i class="icon fw fw-home"></i>
</a>
</li>
{{/zone}}
{{#zone "content"}}
<div class="row">
{{#if permissions.VIEW_DASHBOARD}}
<div class="col-md-3 wr-stats-board">
<div class="wr-stats-board-tile">
<div class="tile-name">Devices</div>
<div>
<div class="tile-icon"><i class="fw fw-mobile"></i></div>
<div class="tile-stats">
<span id="device-count">{{deviceCount}}</span>
<span class="tile-stats-free">
<!--suppress HtmlUnknownTarget -->
{{#if deviceCount}}
<a href="{{@app.context}}/devices">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-view fw-stack-1x"></i>
</span>
View
</a>
{{/if}}
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/device/enroll">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-add fw-stack-1x"></i>
</span>
Add
</a>
</span>
</div>
</div>
</div>
</div>
<div class="col-md-3 wr-stats-board">
<div class="wr-stats-board-tile">
<div class="tile-name">Groups</div>
<div>
<div class="tile-icon"><i class="fw fw-group"></i></div>
<div class="tile-stats">
<span id="group-count">{{groupCount}}</span>
<span class="tile-stats-free">
{{#if groupCount}}
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/groups">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-view fw-stack-1x"></i>
</span>
View
</a>
{{/if}}
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/group/add">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-add fw-stack-1x"></i>
</span>
Add
</a>
</span>
</div>
</div>
</div>
</div>
<div class="col-md-3 wr-stats-board">
<div class="wr-stats-board-tile">
<div class="tile-name">Users</div>
<div>
<div class="tile-icon"><i class="fw fw-user"></i></div>
<div class="tile-stats">
<span id="user-count">{{userCount}}</span>
<span class="tile-stats-free">
{{#if userCount}}
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/users">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-view fw-stack-1x"></i>
</span>
View
</a>
{{/if}}
<!--suppress HtmlUnknownTarget -->
{{#unless isCloud}}
<a href="{{@app.context}}/user/add">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-add fw-stack-1x"></i>
</span>
Add
</a>
{{/unless}}
</span>
</div>
</div>
</div>
</div>
<div class="col-md-3 wr-stats-board">
<div class="wr-stats-board-tile">
<div class="tile-name">Policies</div>
<div>
<div class="tile-icon"><i class="fw fw-policy"></i></div>
<div class="tile-stats">
<span id="policy-count">{{policyCount}}</span>
<span class="tile-stats-free">
{{#if policyCount}}
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/policies">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-view fw-stack-1x"></i>
</span>
View
</a>
{{/if}}
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/policy/add">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-add fw-stack-1x"></i>
</span>
Add
</a>
</span>
</div>
</div>
</div>
</div>
<div class="col-md-3 wr-stats-board">
<div class="wr-stats-board-tile">
<div class="tile-name">Roles</div>
<div>
<div class="tile-icon"><i class="fw fw-bookmark"></i></div>
<div class="tile-stats">
<span id="role-count">{{roleCount}}</span>
<span class="tile-stats-free">
{{#if roleCount}}
<!--suppress HtmlUnknownTarget -->
<a id="device-count-view-btn" href="{{@app.context}}/roles">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-view fw-stack-1x"></i>
</span>
View
</a>
{{/if}}
<!--suppress HtmlUnknownTarget -->
{{#unless isCloud}}
<a href="{{@app.context}}/role/add">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-add fw-stack-1x"></i>
</span>
Add
</a>
{{/unless}}
</span>
</div>
</div>
</div>
</div>
{{#if permissions.IS_ADMIN}}
<div class="col-md-3 wr-stats-board">
<div class="wr-stats-board-tile">
<div class="tile-name">Device Types</div>
<div>
<div class="tile-icon"><i class="fw fw-devices"></i></div>
<div class="tile-stats">
<span id="device-types-count">{{deviceTypeCount}}</span>
<span class="tile-stats-free">
<!--suppress HtmlUnknownTarget -->
{{#if deviceTypeCount}}
<a href="{{@app.context}}/device-types">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-view fw-stack-1x"></i>
</span>
View
</a>
{{/if}}
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/device-type/add">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-add fw-stack-1x"></i>
</span>
Add
</a>
</span>
</div>
</div>
</div>
</div>
{{/if}}
{{else}}
<h1 class="page-sub-title">
Permitted None
</h1>
{{/if}}
</div>
{{/zone}}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
function onRequest(context) {
var constants = require("/app/modules/constants.js");
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var deviceModule = require("/app/modules/business-controllers/device.js")["deviceModule"];
var groupModule = require("/app/modules/business-controllers/group.js")["groupModule"];
var policyModule = require("/app/modules/business-controllers/policy.js")["policyModule"];
if(!session.get(constants["TOKEN_PAIR"])){
response.sendRedirect(context.app.context + "/welcome");
return;
}
var user = session.get(constants["USER_SESSION_KEY"]);
var permissions = userModule.getUIPermissions();
if (!permissions.VIEW_DASHBOARD) {
response.sendRedirect(devicemgtProps["appContext"] + "devices");
return;
}
var viewModel = {};
viewModel.permissions = permissions;
viewModel.enrollmentURL = devicemgtProps.enrollmentURL;
viewModel.deviceCount = deviceModule.getDevicesCount();
viewModel.groupCount = groupModule.getGroupCount();
viewModel.userCount = userModule.getUsersCount();
viewModel.policyCount = policyModule.getPoliciesCount();
viewModel.deviceTypeCount = deviceModule.getDeviceTypeCount();
viewModel.isCloud = devicemgtProps.isCloud;
if (devicemgtProps.isCloud) {
viewModel.roleCount = userModule.getFilteredRoles("devicemgt").content.count;
} else {
viewModel.roleCount = userModule.getRolesCount();
}
return viewModel;
}

View File

@ -0,0 +1,5 @@
{
"version": "1.0.0",
"uri": "/",
"layout": "cdmf.layout.default"
}

View File

@ -0,0 +1,541 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{unit "cdmf.unit.ui.title" pageTitle="Device Management"}}
{{unit "cdmf.unit.data-tables-extended"}}
{{unit "cdmf.unit.lib.ui-permissions-utility"}}
{{#zone "breadcrumbs"}}
<li>
<a href="{{@app.context}}/">
<i class="icon fw fw-home"></i>
</a>
</li>
{{#if group}}
<li>
<a href="{{@app.context}}/groups">
Groups
</a>
</li>
<li>
<a href="#">
{{group.name}}
</a>
</li>
{{else}}
<li>
<a href="{{@app.context}}/devices">
Devices
</a>
</li>
{{/if}}
{{/zone}}
{{#zone "navbarActions"}}
{{#if group}}
<li>
<a href="{{@app.context}}/devices" class="cu-btn">
<span class="icon fw-stack">
<i class="fw fw-add fw-stack-1x"></i>
<i class="fw fw-circle-outline fw-stack-2x"></i>
</span>
Assign from My Devices
</a>
</li>
<!--<li>
<a href="{{@app.context}}/group/{{group.id}}/analytics" class="cu-btn">
<span class="icon fw-stack">
<i class="fw fw-statistics fw-stack-1x"></i>
<i class="fw fw-circle-outline fw-stack-2x"></i>
</span>
View Analytics
</a>
</li>-->
{{else}}
{{#if permissions.enroll}}
<li>
<a href="{{@app.context}}/device/enroll" class="cu-btn">
<span class="icon fw-stack">
<i class="fw fw-add fw-stack-1x"></i>
<i class="fw fw-circle-outline fw-stack-2x"></i>
</span>
Enroll Device
</a>
</li>
{{/if}}
{{/if}}
{{/zone}}
{{#zone "content"}}
{{#if group}}
<h1 class="page-sub-title">
{{group.name}} group
</h1>
<div class="row no-gutter add-padding-5x add-margin-top-5x" style="border: 1px solid #e4e4e4;">
<div class="media">
<div id="device_overview">
<div class="media-left col-lg-2">
<div class="icon">
<img src="/devicemgt/public/cdmf.page.groups/images/group-icon.png"
style="background-color: #11375b; height: 152px;">
</div>
</div>
<div class="media-body asset-desc add-padding-left-5x">
<div style="background: #11375B; color: #fff; padding: 10px; margin-bottom: 5px">
Overview
</div>
<table class="table table-responsive table-striped" id="members" style="margin-bottom: 0px;">
<tbody>
<tr role="row" class="even">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">Owner</td>
<td id="group_owner" style="padding:10px 15px;">{{group.owner}}</td>
</tr>
<tr role="row" class="odd">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">Shared with roles</td>
<td style="padding:10px 15px;">
{{#each roles}}
{{this}}<br/>
{{/each}}
</td>
</tr>
<tr role="row" class="even">
<td class="sorting_1" style="padding:10px 15px;width: 15%;">Device Count</td>
<td style="padding:10px 15px;">{{deviceCount}}</td>
</tr>
<tr role="row" class="odd">
<td class="sorting_1" style="padding:10px 15px;width: 15%;">Description</td>
<td style="padding:10px 15px;">{{group.description}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<br/>
<h1 class="page-sub-title add-margin-top-5x">
Devices in {{group.name}} group
</h1>
{{/if}}
<div class="wr-device-list row">
<div class="wr-hidden-operations wr-advance-operations"></div>
<div class="col-md-12 wr-page-content">
<div>
<span id="permission" data-permission="{{permissions.list}}"></span>
{{unit "cdmf.unit.device.operation-mod"}}
{{#if deviceCount}}
<div id="loading-content" class="col-centered">
<i class="fw fw-wso2-logo fw-pulse fw-2x"></i>
Loading devices . . .
<br>
</div>
<div id="device-listing-status" class="raw hidden">
<ul style="list-style-type: none;">
<li class="message message-info">
<h4>
<i class="icon fw fw-info"></i>
<a id="device-listing-status-msg"></a>
</h4>
</li>
</ul>
</div>
<div id="device-table">
<table class="table table-striped table-hover list-table display responsive nowrap data-table grid-view hidden"
id="device-grid">
<thead>
<tr class="sort-row">
<th class="no-sort"></th>
<th>By Device Name</th>
<th>By Owner</th>
<th>By Status</th>
<th>By Platform</th>
<th>By Ownership</th>
<th class="no-sort"></th>
</tr>
<tr class="filter-row filter-box">
<th class="no-sort"></th>
<th data-for="By Device name" class="text-filter"></th>
<th data-for="By Owner" class="text-filter"></th>
<th data-for="By Status" class="select-filter"></th>
<th data-for="By Platform" class="select-filter data-platform"></th>
<th data-for="By Ownership" class="select-filter"></th>
<th class="no-sort"></th>
</tr>
<tr class="bulk-action-row hidden">
<th colspan="7">
<ul class="tiles">
{{#unless group}}
<li>
<a href="#" style="width: 100px;height: 80px;;"
data-click-event="remove-form"
class="btn square-element add-devices-to-group-link"
data-toggle="modal" data-target="#modalDemo">
<span class="icon fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-group fw-stack-1x"></i>
</span>
Add to Group
</a>
</li>
{{/unless}}
<li>
<a href="#" style="width: 100px;height: 80px;"
data-click-event="remove-form"
class="btn square-element remove-device-link"
data-toggle="modal" data-target="#modalDemo">
<span class="icon fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-delete fw-stack-1x"></i>
</span>
{{#if group}}
Remove from group
{{else}}
Remove Device
{{/if}}
</a>
</li>
</ul>
</th>
</tr>
</thead>
<tbody id="ast-container">
<br class="c-both"/>
</tbody>
</table>
</div>
<br class="c-both"/>
<div id="content-filter-types" style="display: none">
<div class="sort-title">Sort By</div>
<div class="sort-options">
<a href="#">By Device Name<span class="ico-sort-asc"></span></a>
<a href="#">By Owner</a>
<a href="#">By Status</a>
<a href="#">By Platform</a>
<a href="#">By Ownership</a>
</div>
</div>
{{else}}
<div id="ast-container" class="ast-container list-view">
<div class="ctrl-info-panel col-centered text-center wr-login">
<h3 class="text-muted">
<i class="fw fw-devices fw-3x"></i>
</h3>
<h3 class="text-muted">
You don't have any
{{#if group}}
devices assigned to this group. Let's add a device!
{{else}}
enrolled devices. Let's enroll a device!
{{/if}}
</h3>
<h3>
{{#if group}}
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/devices" class="btn-operations btn-default">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-add fw-stack-1x"></i>
</span>
Assign from My Devices
</a>
{{else}}
{{#if permissions.enroll}}
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/device/enroll" class="btn-operations btn-default">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-add fw-stack-1x"></i>
</span>
Enroll New Device
</a>
{{/if}}
{{/if}}
</h3>
</div>
</div>
{{/if}}
<div id="group-device-modal-content" class="hide">
<div class="modal-header">
<h3 class="pull-left modal-title">
<span>
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-group fw-stack-1x"></i>
</span> Device Grouping
</span>
</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fw fw-cancel"></i></button>
</div>
<div class="modal-body add-margin-top-2x add-margin-bottom-2x">
<div id="notification-error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>
</div>
<div id="user-groups">Loading...</div>
</div>
<div class="modal-footer">
<div class="buttons">
<a href="{{@app.context}}/group/add" id="group-add-link" class="btn-operations">
Add device group
</a>
<a href="#" id="group-device-add-link" class="btn-operations">
Add devices to group
</a>
<a href="#" id="group-device-update-link" class="btn-operations">
Update assignment
</a>
<a href="#" id="group-device-cancel-link" class="btn-operations btn-default">
Cancel
</a>
</div>
</div>
</div>
<div id="group-associate-device-200-content" class="hide">
<div class="modal-content">
<div class="row">
<div class="col-md-3 col-centered">
<h3>Successfully added the device/s to the group!</h3>
</div>
</div>
</div>
</div>
<div id="remove-device-modal-content" class="hide">
<div class="modal-header">
<h3 class="pull-left modal-title">
{{#if group}}
Do you really want to remove this device(s) from '{{group.name}}' group?
{{else}}
Do you really want to remove this device(s) from your Devices?
{{/if}}
</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fw fw-cancel"></i>
</button>
</div>
<div class="modal-body add-margin-top-2x add-margin-bottom-2x">
</div>
<div class="modal-footer">
<div class="buttons">
<a href="#" id="remove-device-yes-link" class="btn-operations">
Yes
</a>
<a href="#" id="remove-device-cancel-link" class="btn-operations">
Cancel
</a>
</div>
</div>
</div>
<div id="remove-device-200-content" class="hide">
<div class="modal-header">
<h3 class="pull-left modal-title">
{{#if group}}
Successfully removed from '{{group.name}}' group.
{{else}}
Successfully removed.
{{/if}}
</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fw fw-cancel"></i>
</button>
</div>
<div class="modal-body add-margin-top-2x add-margin-bottom-2x">
</div>
<div class="modal-footer">
<div class="buttons">
<a href="javascript:hidePopup()" class="btn-operations">Ok</a>
</div>
</div>
</div>
<div id="edit-device-modal-content" class="hide">
<div class="modal-header">
<h3 class="pull-left modal-title">
Please enter a new name for the device.
</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fw fw-cancel"></i>
</button>
</div>
<div class="modal-body add-margin-top-2x add-margin-bottom-2x">
<input id="edit-device-name" style="color:#3f3f3f;padding:5px" type="text"
value=""
placeholder="Type here" size="60">
</div>
<div class="modal-footer">
<div class="buttons">
<a href="#" id="edit-device-yes-link" class="btn-operations">
Rename
</a>
<a href="#" id="edit-device-cancel-link" class="btn-operations">
Cancel
</a>
</div>
</div>
</div>
<div id="edit-device-200-content" class="hide">
<div class="modal-header">
<h3 class="pull-left modal-title">
Device was successfully updated.
</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fw fw-cancel"></i>
</button>
</div>
<div class="modal-body add-margin-top-2x add-margin-bottom-2x">
</div>
<div class="modal-footer">
<div class="buttons">
<div id="edit-device-200-content" class="hide">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>Device was successfully updated.</h3>
</div>
</div>
</div>
</div>
<div id="device-400-content" class="hide">
<div class="modal-content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>Exception at backend. Try Later.</h3>
<br/>
<div class="buttons">
<a href="#" id="device-400-link" class="btn-operations">
Ok
</a>
</div>
</div>
</div>
</div>
</div>
<div id="device-403-content" class="hide">
<div class="modal-content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>Operation not permitted.</h3>
<br/>
<div class="buttons">
<a href="#" id="device-403-link" class="btn-operations">
Ok
</a>
</div>
</div>
</div>
</div>
</div>
<div id="group-404-content" class="hide">
<div class="modal-content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>
You don't have any device group to add this device. Let's add a new
device group!</h3>
<br/>
<div class="buttons">
<a href="{{@app.context}}/group/add" class="btn-operations">
Add New Group
</a>
<a href="#" id="cancel-link" class="btn-operations">
Cancel
</a>
</div>
</div>
</div>
</div>
</div>
<div id="device-409-content" class="hide">
<div class="modal-content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>Device does not exist.</h3>
<br/>
<div class="buttons">
<a href="#" id="remove-device-409-link" class="btn-operations">
Ok
</a>
</div>
</div>
</div>
</div>
</div>
<div id="no-device-selected" class="hide">
<div class="modal-content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>Please select one or more devices in order to perform this
operation.</h3>
<br/>
<div class="buttons">
<a href="#" id="no-device-selected-link" class="btn-operations">
Ok
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{/zone}}
{{#zone "bottomJs"}}
<script id="device-listing" data-server-url="{{serverUrl}}" data-current-user="{{@user.username}}" data-device-types="{{deviceTypes}}"
data-portal-url="{{portalUrl}}" data-user-domain="{{userDomain}}" type="text/x-handlebars-template"></script>
{{js "js/listing.js"}}
{{/zone}}
{{#zone "topCss"}}
<style>
.select2-dropdown {
z-index: 999999999;
}
.wr-input-control {
color: black;
}
.select2-selection__choice {
font-size: medium;
}
</style>
{{/zone}}

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
function onRequest(context) {
var constants = require("/app/modules/constants.js");
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
var deviceModule = require("/app/modules/business-controllers/device.js")["deviceModule"];
var groupModule = require("/app/modules/business-controllers/group.js")["groupModule"];
var groupId = request.getParameter("groupId");
var viewModel = {};
var title = "Devices";
if (groupId) {
var group = groupModule.getGroup(groupId);
if (group) {
title = group.name + " " + title;
viewModel.roles = groupModule.getRolesOfGroup(groupId);
viewModel.group = group;
}
}
viewModel.title = title;
var currentUser = session.get(constants.USER_SESSION_KEY);
if (currentUser) {
viewModel.permissions = {};
var uiPermissions = userModule.getUIPermissions();
viewModel.permissions.list = stringify(uiPermissions);
if (uiPermissions.ADD_DEVICE) {
viewModel.permissions.enroll = true;
}
viewModel.currentUser = currentUser;
var deviceCount = 0;
if (groupId) {
deviceCount = groupModule.getGroupDeviceCount(groupId);
} else {
deviceCount = deviceModule.getDevicesCount();
}
if (deviceCount > 0) {
viewModel.deviceCount = deviceCount;
var utility = require("/app/modules/utility.js").utility;
var typesListResponse = deviceModule.getDeviceTypes();
var deviceTypes = [];
if (typesListResponse["status"] == "success") {
var data = typesListResponse.content.deviceTypes;
if (data) {
for (var i = 0; i < data.length; i++) {
var config = utility.getDeviceTypeConfig(data[i]);
var category = "iot";
var label = data[i];
var analyticsEnabled = "false";
var groupingEnabled = "true";
var analyticsView = null;
if (config) {
var deviceType = config.deviceType;
category = deviceType.category;
label = deviceType.label;
analyticsEnabled = deviceType.analyticsEnabled;
groupingEnabled = deviceType.groupingEnabled;
analyticsView = deviceType.analyticsView;
}
deviceTypes.push({
"type": data[i],
"category": category,
"label": label,
"thumb": utility.getDeviceThumb(data[i]),
"analyticsEnabled": analyticsEnabled,
"groupingEnabled": groupingEnabled,
"analyticsView" : analyticsView
});
}
}
}
viewModel.deviceTypes = stringify(deviceTypes);
}
}
var mdmProps = require("/app/modules/conf-reader/main.js")["conf"];
var serverUrl = mdmProps["httpsURL"];
var portalUrl = mdmProps["portalURL"];
var userDomain = context.user.domain;
viewModel.serverUrl = serverUrl;
viewModel.portalUrl = portalUrl;
viewModel.userDomain = userDomain;
return viewModel;
}

View File

@ -0,0 +1,5 @@
{
"version": "1.0.0",
"uri": "/devices",
"layout": "cdmf.layout.default"
}

View File

@ -0,0 +1,959 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Following function would execute
* when a user clicks on the list item
* initial mode and with out select mode.
*/
function InitiateViewOption(url) {
if ($(".select-enable-btn").text() == "Select" && !$(this).hasClass("btn")) {
url = $(this).parent().data("url");
$(location).attr('href', url);
}
}
(function () {
var cache = {};
var validateAndReturn = function (value) {
return (value == undefined || value == null) ? "Unspecified" : value;
};
Handlebars.registerHelper("deviceMap", function (device) {
device.owner = validateAndReturn(device.owner);
device.ownership = validateAndReturn(device.ownership);
var arr = device.properties;
if (arr) {
device.properties = arr.reduce(function (total, current) {
total[current.name] = validateAndReturn(current.value);
return total;
}, {});
}
});
})();
/*
* Setting-up global variables.
*/
var deviceCheckbox = "#ast-container .ctrl-wr-asset .itm-select input[type='checkbox']";
var assetContainer = "#ast-container";
var deviceListing, currentUser, groupId;
/*
* DOM ready functions.
*/
$(document).ready(function () {
var permissionSet = {};
$.setPermission = function (permission) {
permissionSet[permission] = true;
};
$.hasPermission = function (permission) {
return permissionSet[permission];
};
deviceListing = $("#device-listing");
currentUser = deviceListing.data("current-user");
groupId = getParameterByName("groupId");
/* Adding selected class for selected devices */
$(deviceCheckbox).each(function () {
addDeviceSelectedClass(this);
});
/* for device list sorting drop down */
$(".ctrl-filter-type-switcher").popover({
html: true,
content: function () {
return $("#content-filter-types").html();
}
});
});
/*
* On Select All Device button click function.
*
* @param button: Select All Device button
*/
function selectAllDevices(button) {
if (!$(button).data('select')) {
$(deviceCheckbox).each(function (index) {
$(this).prop('checked', true);
addDeviceSelectedClass(this);
});
$(button).data('select', true);
$(button).html('Deselect All Devices');
} else {
$(deviceCheckbox).each(function (index) {
$(this).prop('checked', false);
addDeviceSelectedClass(this);
});
$(button).data('select', false);
$(button).html('Select All Devices');
}
}
/*
* On listing layout toggle buttons click function.
*
* @param view: Selected view type
* @param selection: Selection button
*/
function changeDeviceView(view, selection) {
$(".view-toggle").each(function () {
$(this).removeClass("selected");
});
$(selection).addClass("selected");
if (view == "list") {
$(assetContainer).addClass("list-view");
} else {
$(assetContainer).removeClass("list-view");
}
}
/*
* Add selected style class to the parent element function.
*
* @param checkbox: Selected checkbox
*/
function addDeviceSelectedClass(checkbox) {
if ($(checkbox).is(":checked")) {
$(checkbox).closest(".ctrl-wr-asset").addClass("selected device-select");
} else {
$(checkbox).closest(".ctrl-wr-asset").removeClass("selected device-select");
}
}
function toTitleCase(str) {
return str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
function loadDevices(searchType, searchParam) {
var serviceURL;
if (groupId && $.hasPermission("LIST_OWN_DEVICES")) {
serviceURL = "/api/device-mgt/v1.0/groups/id/" + groupId + "/devices";
} else if ($.hasPermission("LIST_DEVICES")) {
serviceURL = "/api/device-mgt/v1.0/devices";
} else if (permissionsUtil.hasPermission("LIST_OWN_DEVICES")) {
//Get authenticated users devices
serviceURL = "/api/device-mgt/v1.0/devices?user=" + currentUser;
} else {
$("#loading-content").remove();
$('#device-table').addClass('hidden');
$('#device-listing-status-msg').text('Permission denied.');
$("#device-listing-status").removeClass(' hidden');
return;
}
function getPropertyValue(deviceProperties, propertyName) {
if (!deviceProperties) {
return;
}
var property;
for (var i = 0; i < deviceProperties.length; i++) {
property = deviceProperties[i];
if (property.name == propertyName) {
return property.value;
}
}
return {};
}
function getDeviceTypeLabel(type) {
var deviceTypes = deviceListing.data("deviceTypes");
for (var i = 0; i < deviceTypes.length; i++) {
if (deviceTypes[i].type == type) {
return deviceTypes[i].label;
}
}
return type;
}
function getDeviceTypeCategory(type) {
var deviceTypes = deviceListing.data("deviceTypes");
for (var i = 0; i < deviceTypes.length; i++) {
if (deviceTypes[i].type == type) {
return deviceTypes[i].category;
}
}
return type;
}
function getDeviceTypeThumb(type) {
var deviceTypes = deviceListing.data("deviceTypes");
for (var i = 0; i < deviceTypes.length; i++) {
if (deviceTypes[i].type == type) {
return deviceTypes[i].thumb;
}
}
return type;
}
function analyticsEnabled(type) {
var deviceTypes = deviceListing.data("deviceTypes");
for (var i = 0; i < deviceTypes.length; i++) {
if (deviceTypes[i].type == type) {
var analyticsEnabled = deviceTypes[i].analyticsEnabled;
if (analyticsEnabled == undefined) {
// By default it should be enabled
return true;
}
// In JS Boolean("false") returns TRUE => http://stackoverflow.com/a/264037/1560536
return (analyticsEnabled == "true");
}
}
return true;
}
// Read "analyticsView" from config.json and return value if exists
function getAnalyticsView(type) {
var deviceTypes = deviceListing.data("deviceTypes");
for (var i = 0; i < deviceTypes.length; i++) {
if (deviceTypes[i].type == type) {
var analyticsView = deviceTypes[i].analyticsView;
if (analyticsEnabled == undefined) {
// if undefined go to default analytics view
return "none";
}
return analyticsView;
}
}
return "none";
}
function groupingEnabled(type) {
var deviceTypes = deviceListing.data("deviceTypes");
for (var i = 0; i < deviceTypes.length; i++) {
if (deviceTypes[i].type == type) {
var groupingEnabled = deviceTypes[i].groupingEnabled;
if (groupingEnabled == undefined) {
// By default it should be enabled
return true;
}
// In JS Boolean("false") returns TRUE => http://stackoverflow.com/a/264037/1560536
return (groupingEnabled == "true");
}
}
return true;
}
var columns = [
{
targets: 0,
data: 'name',
class: 'remove-padding icon-only content-fill viewEnabledIcon',
render: function (data, type, row, meta) {
return '<div class="thumbnail icon"><img class="square-element text fw " src="'
+ getDeviceTypeThumb(row.deviceType) + '"/></div>';
}
},
{
targets: 1,
data: 'name',
class: 'viewEnabledIcon',
render: function (name, type, row, meta) {
var model = getPropertyValue(row.properties, 'DEVICE_MODEL');
var vendor = getPropertyValue(row.properties, 'VENDOR');
var html = '<h4>' + name + '</h4>';
if (model) {
html += '<div>(' + vendor + '-' + model + ')</div>';
}
return html;
}
},
{
targets: 2,
data: 'userPattern',
class: 'remove-padding-top viewEnabledIcon'
},
{
targets: 3,
data: 'status',
class: 'remove-padding-top viewEnabledIcon',
render: function (status, type, row, meta) {
var html;
switch (status) {
case 'ACTIVE' :
html = '<span><i class="fw fw-success icon-success"></i> Active</span>';
break;
case 'INACTIVE' :
html = '<span><i class="fw fw-warning icon-warning"></i> Inactive</span>';
break;
case 'BLOCKED' :
html = '<span><i class="fw fw-remove icon-danger"></i> Blocked</span>';
break;
case 'REMOVED' :
html = '<span><i class="fw fw-delete icon-danger"></i> Removed</span>';
break;
case 'UNREACHABLE' :
html = '<span><i class="fw fw-warning icon-warning"></i> Unreachable</span>';
break;
}
return html;
}
},
{
targets: 4,
data: 'type',
class: 'remove-padding-top viewEnabledIcon',
render: function (status, type, row, meta) {
return getDeviceTypeLabel(row.deviceType);
}
},
{
targets: 5,
data: 'ownership',
class: 'remove-padding-top viewEnabledIcon',
render: function (status, type, row, meta) {
if (getDeviceTypeCategory(row.deviceType) == 'mobile') {
return row.ownership;
} else {
return null;
}
}
},
{
targets: 6,
data: 'action-buttons',
class: 'text-right content-fill text-left-on-grid-view no-wrap tooltip-overflow-fix',
render: function (status, type, row, meta) {
var deviceType = row.deviceType;
var deviceIdentifier = row.deviceIdentifier;
var html = '<span></span>';
var portalUrl = $("#device-listing").data("portal-url");
var serverUrl = $("#device-listing").data("server-url");
var userDomain = $("#device-listing").data("userDomain");
var statURL;
if (status != 'REMOVED') {
html = '';
if (analyticsEnabled(row.deviceType)) {
// redirecting to respective analytics view depending on device configs
switch (getAnalyticsView(deviceType)) {
case "DAS" : { statURL =portalUrl + "/portal/t/"+ userDomain+ "/dashboards/android-iot/battery?owner=" +currentUser+"&deviceId=";break;}
default : {statURL=context+ "/device/" + row.deviceType +"/analytics?deviceId="}
}
html += '<a href="' + statURL +
deviceIdentifier + '&deviceName=' + row.name + '" ' + 'data-click-event="remove-form"' +
' class="btn padding-reduce-on-grid-view" data-placement="top" data-toggle="tooltip" data-original-title="Analytics"><span class="fw-stack">' +
'<i class="fw fw-circle-outline fw-stack-2x"></i><i class="fw fw-statistics fw-stack-1x"></i></span>' +
'<span class="hidden-xs hidden-on-grid-view">Analytics</span>';
}
if (!groupId && groupingEnabled(row.deviceType)) {
html +=
'<a href="#" data-click-event="remove-form" class="btn padding-reduce-on-grid-view group-device-link" '
+
'data-deviceid="' + deviceIdentifier + '" data-devicetype="' + deviceType
+ '" data-devicename="' +
row.name + '" data-placement="top" data-toggle="tooltip" data-original-title="Group"><span class="fw-stack"><i class="fw fw-circle-outline fw-stack-2x"></i>' +
'<i class="fw fw-group fw-stack-1x"></i></span>' +
'<span class="hidden-xs hidden-on-grid-view">Group</span></a>';
}
html +=
'<a href="#" data-click-event="remove-form" class="btn padding-reduce-on-grid-view edit-device-link" '
+ 'data-deviceid="' + deviceIdentifier + '" data-devicetype="' + deviceType
+ '" data-devicename="' + row.name + '" data-placement="top" data-toggle="tooltip" data-original-title="Edit">'
+ '<span class="fw-stack"><i class="fw fw-circle-outline fw-stack-2x"></i>'
+ '<i class="fw fw-edit fw-stack-1x"></i></span>'
+ '<span class="hidden-xs hidden-on-grid-view">Edit</span></a>';
var groupOwner = $('#group_owner').text();
if (groupId && groupOwner != "wso2.system.user") {
html +=
'<a href="#" data-click-event="remove-form" class="btn padding-reduce-on-grid-view remove-device-link" '
+ 'data-deviceid="' + deviceIdentifier + '" data-devicetype="' + deviceType
+ '" data-devicename="' + row.name + '" data-placement="top" data-toggle="tooltip" data-original-title="Remove from group">'
+ '<span class="fw-stack"><i class="fw fw-circle-outline fw-stack-2x"></i>'
+ '<i class="fw fw-delete fw-stack-1x"></i></span>'
+ '<span class="hidden-xs hidden-on-grid-view">Remove from group</span>';
} else {
html +=
'<a href="#" data-click-event="remove-form" class="btn padding-reduce-on-grid-view remove-device-link" '
+ 'data-deviceid="' + deviceIdentifier + '" data-devicetype="' + deviceType
+ '" data-devicename="' + row.name + '" data-placement="top" data-toggle="tooltip" data-original-title="Delete">'
+ '<span class="fw-stack"><i class="fw fw-circle-outline fw-stack-2x"></i>'
+ '<i class="fw fw-delete fw-stack-1x"></i></span>'
+ '<span class="hidden-xs hidden-on-grid-view">Delete</span>';
}
}
return html;
}
}
];
var fnCreatedRow = function (row, data, dataIndex) {
if(data.status != "REMOVED"){
$(row).attr('data-type', 'selectable');
}else{
$(row).attr('data-type', 'non-selectable');
}
$(row).attr('data-deviceid', htmlspecialchars(data.deviceIdentifier));
$(row).attr('data-devicetype', htmlspecialchars(data.deviceType));
$(row).attr('data-url', context + '/device/' + htmlspecialchars(data.deviceType) + '?id=' + htmlspecialchars(data.deviceIdentifier));
var model = htmlspecialchars(getPropertyValue(data.properties, 'DEVICE_MODEL'));
var vendor = htmlspecialchars(getPropertyValue(data.properties, 'VENDOR'));
var owner = htmlspecialchars(data.userPattern);
var status = htmlspecialchars(data.status);
var ownership = htmlspecialchars(data.ownership);
var deviceType = htmlspecialchars(data.deviceType);
var category = getDeviceTypeCategory(deviceType);
$.each($('td', row), function (colIndex) {
switch (colIndex) {
case 1:
$(this).attr('data-search', model + ',' + vendor);
$(this).attr('data-display', model);
break;
case 2:
$(this).attr('data-grid-label', "Owner");
$(this).attr('data-search', owner);
$(this).attr('data-display', owner);
break;
case 3:
$(this).attr('data-grid-label', "Status");
$(this).attr('data-search', status);
$(this).attr('data-display', status);
break;
case 4:
$(this).attr('data-grid-label', "Type");
$(this).attr('data-search', deviceType);
$(this).attr('data-display', getDeviceTypeLabel(deviceType));
break;
case 5:
if (category == 'mobile') {
$(this).attr('data-grid-label', "Ownership");
$(this).attr('data-search', ownership);
$(this).attr('data-display', ownership);
}
break;
}
});
};
function htmlspecialchars(text) {
return jQuery('<div/>').text(text).html();
}
var dataFilter = function (data) {
data = JSON.parse(data);
var objects = [];
$(data.devices).each(function (index) {
objects.push(
{
model: getPropertyValue(data.devices[index].properties, "DEVICE_MODEL"),
vendor: getPropertyValue(data.devices[index].properties, "VENDOR"),
userPattern: data.devices[index].enrolmentInfo.owner,
status: data.devices[index].enrolmentInfo.status,
ownership: data.devices[index].enrolmentInfo.ownership,
deviceType: data.devices[index].type,
deviceIdentifier: data.devices[index].deviceIdentifier,
name: data.devices[index].name
}
);
});
var json = {
"recordsTotal": data.count,
"recordsFiltered": data.count,
"data": objects
};
return JSON.stringify(json);
};
$('#device-grid').datatables_extended_serverside_paging(
null,
serviceURL,
dataFilter,
columns,
fnCreatedRow,
function () {
$(".icon .text").res_text(0.2);
$('#device-grid').removeClass('hidden');
$("#loading-content").remove();
attachDeviceEvents();
// if ($('.advance-search').length < 1) {
// $(this).closest('.dataTables_wrapper').find('div[id$=_filter] input')
// .after('<a href="' + context + '/devices/search"' +
// ' class="advance-search add-padding-3x">Advance Search</a>');
// }
}, {
"placeholder": "Search By Device Name",
"searchKey": "name"
}
);
$(deviceCheckbox).click(function () {
addDeviceSelectedClass(this);
});
}
function openCollapsedNav() {
$('.wr-hidden-nav-toggle-btn').addClass('active');
$('#hiddenNav').slideToggle('slideDown', function () {
if ($(this).css('display') == 'none') {
$('.wr-hidden-nav-toggle-btn').removeClass('active');
}
});
}
/*
* DOM ready functions.
*/
$(document).ready(function () {
/* Adding selected class for selected devices */
$(deviceCheckbox).each(function () {
addDeviceSelectedClass(this);
});
var permissionList = $("#permission").data("permission");
for (var key in permissionList) {
if (permissionList.hasOwnProperty(key)) {
$.setPermission(key);
}
}
loadDevices();
/* for device list sorting drop down */
$(".ctrl-filter-type-switcher").popover({
html: true,
content: function () {
return $("#content-filter-types").html();
}
});
/* for data tables*/
$('[data-toggle="tooltip"]').tooltip();
$("[data-toggle=popover]").popover();
$(".ctrl-filter-type-switcher").popover({
html: true,
content: function () {
return $('#content-filter-types').html();
}
});
$('#nav').affix({
offset: {
top: $('header').height()
}
});
});
var modalPopup = ".modal";
var modalPopupContainer = modalPopup + " .modal-content";
var modalPopupContent = modalPopup + " .modal-content";
var body = "body";
/*
* set popup maximum height function.
*/
function setPopupMaxHeight() {
$(modalPopupContent).css('max-height', ($(body).height() - ($(body).height() / 100 * 30)));
$(modalPopupContainer).css('margin-top', (-($(modalPopupContainer).height() / 2)));
}
/*
* show popup function.
*/
function showPopup() {
$(modalPopup).modal('show');
}
/*
* hide popup function.
*/
function hidePopup() {
$(modalPopupContent).html("");
$(modalPopupContent).removeClass("operation-data");
$(modalPopup).modal('hide');
$('body').removeClass('modal-open').css('padding-right', '0px');
$('.modal-backdrop').remove();
}
function markAlreadyAssignedGroups(deviceId, deviceType) {
var successCallback = function (data, textStatus, xhr) {
data = JSON.parse(data);
if (xhr.status == 200) {
if (data.length > 0) {
var selectedValues = [];
for (var i = 0; i < data.length; i++) {
if (data[i].owner != "wso2.system.user") {
selectedValues.push(data[i].id);
}
}
$("#groups").val(selectedValues).trigger("change");
}
} else {
displayErrors(xhr);
}
};
invokerUtil.get("/api/device-mgt/v1.0/groups/device?deviceId=" + deviceId + "&deviceType=" + deviceType,
successCallback, function (message) {
displayErrors(message);
});
}
/**
* Following functions should be triggered after AJAX request is made.
*/
function attachDeviceEvents() {
/**
* Following click function would execute
* when a user clicks on "Group" link
* on Device Management page in WSO2 DeviceMgt Console.
*/
if ($("a.group-device-link").length > 0) {
$("a.group-device-link").click(function () {
var deviceId = $(this).data("deviceid");
var deviceType = $(this).data("devicetype");
$(modalPopupContent).html($('#group-device-modal-content').html());
$('#user-groups').html(
'<div style="height:100px" data-state="loading" data-loading-text="Loading..." data-loading-style="icon-only" data-loading-inverse="true"></div>');
$("a#group-device-update-link").hide();
showPopup();
var serviceURL;
if ($.hasPermission("LIST_ALL_GROUPS")) {
serviceURL = "/api/device-mgt/v1.0/admin/groups?limit=100";
} else if ($.hasPermission("LIST_GROUPS")) {
//Get authenticated users groups
serviceURL = "/api/device-mgt/v1.0/groups?limit=100";
}
invokerUtil.get(serviceURL, function (data) {
$("a#group-device-add-link").hide();
var groups = JSON.parse(data);
var html = '';
var hasGroups = false;
for (var i = 0; i < groups.deviceGroups.length; i++) {
if (groups.deviceGroups[i].owner != "wso2.system.user") {
html += '<option value="' + groups.deviceGroups[i].id + '">' + groups.deviceGroups[i].name + '</option>';
hasGroups = true;
}
}
if (hasGroups) {
html = '<br/><h4>Please select device group(s)</h4><br/>' +
'<div class="wr-input-control">' +
'<select id="groups" class="form-control select2" multiple="multiple">' +
html + '</select></div>';
markAlreadyAssignedGroups(deviceId, deviceType);
$("a#group-device-update-link").show();
$("a#group-add-link").hide();
} else {
$("a#group-device-update-link").hide();
$("a#group-add-link").show();
html += '<br/><h4>You don\'t have any existing device groups. Please add new device group first.</h4>'
}
$('#user-groups').html(html);
$("select.select2[multiple=multiple]").select2({
tags: false
});
$("a#group-device-update-link").click(function () {
var deviceIdentifier = {"id": deviceId, "type": deviceType};
var deviceGroupIds = $("#groups").val();
if (!deviceGroupIds) {
deviceGroupIds = [];
}
var deviceToGroupsAssignment = {
deviceIdentifier: deviceIdentifier,
deviceGroupIds: deviceGroupIds
};
serviceURL = "/api/device-mgt/v1.0/groups/device/assign";
invokerUtil.post(serviceURL, deviceToGroupsAssignment, function (data) {
$(modalPopupContent).html($('#group-associate-device-200-content').html());
setTimeout(function () {
hidePopup();
location.reload(false);
}, 2000);
}, function (jqXHR) {
displayDeviceErrors(jqXHR);
});
});
}, function (jqXHR) {
if (jqXHR.status == 404) {
$(modalPopupContent).html($('#group-404-content').html());
$("a#cancel-link").click(function () {
hidePopup();
});
} else {
displayDeviceErrors(jqXHR);
}
});
$("a#group-device-cancel-link").click(function () {
hidePopup();
});
});
}
/**
* Following click function would execute
* when a user clicks on "Remove" link
* on Device Management page in WSO2 MDM Console.
*/
$("a.remove-device-link").click(function () {
var deviceIdentifiers = [];
var deviceId = $(this).data("deviceid");
var deviceType = $(this).data("devicetype");
if (deviceId && deviceType) {
deviceIdentifiers = [{"id": deviceId, "type": deviceType}];
} else {
deviceIdentifiers = getSelectedDevices();
}
if (deviceIdentifiers.length == 0) {
$(modalPopupContent).html($('#no-device-selected').html());
$("a#no-device-selected-link").click(function () {
hidePopup();
});
showPopup();
return;
}
$(modalPopupContent).html($('#remove-device-modal-content').html());
showPopup();
$("a#remove-device-yes-link").click(function () {
if (groupId) {
var serviceURL = "/api/device-mgt/v1.0/groups/id/" + groupId + "/devices/remove";
invokerUtil.post(serviceURL, deviceIdentifiers, function (message) {
$(modalPopupContent).html($('#remove-device-200-content').html());
setTimeout(function () {
hidePopup();
location.reload(false);
}, 2000);
}, function (jqXHR) {
displayDeviceErrors(jqXHR);
});
} else {
removeDevices(deviceIdentifiers);
}
});
$("a#remove-device-cancel-link").click(function () {
hidePopup();
});
});
/**
* Following click function would execute
* when a user clicks on "Edit" link
* on Device Management page in WSO2 MDM Console.
*/
$("a.edit-device-link").click(function () {
var deviceId = $(this).data("deviceid");
var deviceType = $(this).data("devicetype");
var deviceName = $(this).data("devicename");
var serviceURL = "/api/device-mgt/v1.0/devices/type/" + deviceType + "/id/" + deviceId + "/rename";
$(modalPopupContent).html($('#edit-device-modal-content').html());
$('#edit-device-name').val(deviceName);
showPopup();
$("a#edit-device-yes-link").click(function () {
var newDeviceName = $('#edit-device-name').val();
var request = {};
request['name'] = newDeviceName;
invokerUtil.post(serviceURL, request, function (message) {
$(modalPopupContent).html($('#edit-device-200-content').html());
setTimeout(function () {
hidePopup();
location.reload(false);
}, 2000);
}, function (jqXHR) {
displayDeviceErrors(jqXHR);
});
});
$("a#edit-device-cancel-link").click(function () {
hidePopup();
});
});
/**
* Following click function would execute
* when a user clicks on "Add to Group" link
* on Device Management page in WSO2 devicemgt Console.
*/
$("a.add-devices-to-group-link").click(function () {
$("a#group-device-update-link").hide();
var deviceIdentifiers = getSelectedDevices();
if (deviceIdentifiers.length == 0) {
$(modalPopupContent).html($('#no-device-selected').html());
$("a#no-device-selected-link").click(function () {
hidePopup();
});
showPopup();
return;
}
$(modalPopupContent).html($('#group-device-modal-content').html());
$('#user-groups').html(
'<div style="height:100px" data-state="loading" data-loading-text="Loading..." data-loading-style="icon-only" data-loading-inverse="true"></div>');
$("a#group-device-add-link").hide();
showPopup();
var serviceURL;
if ($.hasPermission("LIST_ALL_GROUPS")) {
serviceURL = "/api/device-mgt/v1.0/admin/groups?limit=100";
} else if ($.hasPermission("LIST_GROUPS")) {
//Get authenticated users groups
serviceURL = "/api/device-mgt/v1.0/groups?limit=100";
}
invokerUtil.get(serviceURL, function (data) {
var groups = JSON.parse(data);
var html = '';
var hasGroups = false;
for (var i = 0; i < groups.deviceGroups.length; i++) {
if (groups.deviceGroups[i].owner != "wso2.system.user") {
html += '<option value="' + groups.deviceGroups[i].id + '">' +
groups.deviceGroups[i].name + '</option>';
hasGroups = true;
}
}
if (hasGroups) {
html = '<br /><select id="assign-group-selector" style="color:#3f3f3f;padding:5px;width:250px;">' +
html + '</select>';
$("a#group-add-link").hide();
$("a#group-device-add-link").show();
} else {
html += '<br/><h4>You don\'t have any existing device groups. Please add new device group first.</h4>';
$("a#group-add-link").show();
$("a#group-device-add-link").hide();
}
$('#user-groups').html(html);
$("a#group-device-add-link").click(function () {
var selectedGroup = $('#assign-group-selector').val();
serviceURL = "/api/device-mgt/v1.0/groups/id/" + selectedGroup + "/devices/add";
invokerUtil.post(serviceURL, deviceIdentifiers, function (data) {
$(modalPopupContent).html($('#group-associate-device-200-content').html());
setTimeout(function () {
hidePopup();
location.reload(false);
}, 2000);
}, function (jqXHR) {
displayDeviceErrors(jqXHR);
});
});
}, function (jqXHR) {
if (jqXHR.status == 404) {
$(modalPopupContent).html($('#group-404-content').html());
$("a#cancel-link").click(function () {
hidePopup();
});
} else {
displayDeviceErrors(jqXHR);
}
});
$("a#group-device-cancel-link").click(function () {
hidePopup();
});
});
}
function removeDevices(deviceIdentifiers) {
var serviceURL = "/api/device-mgt/v1.0/devices/type/" + deviceIdentifiers[0].type + "/id/" + deviceIdentifiers[0].id;
invokerUtil.delete(serviceURL, function (message) {
if (deviceIdentifiers.length > 1) {
deviceIdentifiers.slice(1, deviceIdentifiers.length);
removeDevices(deviceIdentifiers);
} else {
$(modalPopupContent).html($('#remove-device-200-content').html());
setTimeout(function () {
hidePopup();
location.reload(false);
}, 2000);
}
}, function (jqXHR) {
displayDeviceErrors(jqXHR);
});
}
function displayDeviceErrors(jqXHR) {
showPopup();
if (jqXHR.status == 400) {
$(modalPopupContent).html($('#device-400-content').html());
$("a#device-400-link").click(function () {
hidePopup();
});
} else if (jqXHR.status == 403) {
$(modalPopupContent).html($('#device-403-content').html());
$("a#device-403-link").click(function () {
hidePopup();
});
} else if (jqXHR.status == 409) {
$(modalPopupContent).html($('#device-409-content').html());
$("a#device-409-link").click(function () {
hidePopup();
});
} else {
$(modalPopupContent).html($('#device-unexpected-error-content').html());
$("a#device-unexpected-error-link").click(function () {
hidePopup();
});
console.log("Error code: " + jqXHR.status);
}
}
function getParameterByName(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
/*
* Function to get selected devices.
*/
function getSelectedDevices() {
var deviceList = [];
var thisTable = $(".DTTT_selected").closest('.dataTables_wrapper').find('.dataTable').dataTable();
thisTable.api().rows().every(function () {
if ($(this.node()).hasClass('DTTT_selected')) {
deviceList.push(
{
"id": $(thisTable.api().row(this).node()).data('deviceid'),
"type": $(thisTable.api().row(this).node()).data('devicetype')
}
);
}
});
return deviceList;
}

View File

@ -0,0 +1,114 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{unit "cdmf.unit.ui.title" pageTitle="Device Type Management"}}
{{unit "cdmf.unit.data-tables-extended"}}
{{unit "cdmf.unit.ui.modal"}}
{{#zone "topCss"}}
{{css "css/custom.css"}}
{{/zone}}
{{#zone "breadcrumbs"}}
<li>
<a href="{{@app.context}}/">
<i class="icon fw fw-home"></i>
</a>
</li>
<li>
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/device-types">
Device Types
</a>
</li>
{{/zone}}
{{#zone "navbarActions"}}
{{#unless isCloud}}
<li>
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/device-type/add" class="cu-btn">
<span class="icon fw-stack">
<i class="fw fw-add fw-stack-1x"></i>
<i class="fw fw-circle-outline fw-stack-2x"></i>
</span>
Create Device Type
</a>
</li>
{{/unless}}
{{/zone}}
{{#zone "content"}}
{{#if hasDeviceTypes}}
<div id="loading-content" class="col-centered">
<input type="hidden" id="can-edit" value="true"/>
<i class="fw fw-settings fw-spin fw-2x"></i>
Loading device types . . .
<br>
</div>
<div id="devicetype-table">
<table class="table table-striped table-hover list-table display responsive nowrap data-table grid-view"
id="devicetype-grid">
<thead>
<tr class="sort-row">
<th>By Device Type Name</th>
</tr>
<tr class="bulk-action-row">
<th colspan="3"></th>
</tr>
</thead>
<tbody id="ast-container" data-app-context="{{@app.context}}/"></tbody>
</table>
</div>
<div id="content-filter-types" style="display: none">
<div class="sort-title">Sort By</div>
<div class="sort-options">
<!--suppress HtmlUnknownTag -->
<th>By Device Type name</th>
</div>
</div>
{{else}}
<div id="user-created-msg" class="container col-centered wr-content">
<div class="wr-form">
<p class="page-sub-title">You Haven't created device types yet.</p>
<br>Please click <b>"Create a Device Type"</b>, if you wish to create a device type.
<hr/>
<a href="{{@app.context}}/device-type/add" class="cu-btn-inner">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-add fw-stack-1x"></i>
</span>
Create Device Type
</a>
</div>
</div>
{{/if}}
{{/zone}}
{{#zone "bottomJs"}}
<!--suppress HtmlUnknownTarget -->
<script id="devicetype-listing" data-current-user="{{@user.username}}"
src="{{@page.publicUri}}/templates/devicetype-listing.hbs" type="text/x-handlebars-template"></script>
{{js "js/devicetype-listing.js"}}
{{/zone}}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
function onRequest(context) {
var deviceModule = require("/app/modules/business-controllers/device.js")["deviceModule"];
var deviceMgtProps = require("/app/modules/conf-reader/main.js")["conf"];
context["permissions"] = userModule.getUIPermissions();
if (userModule.isAuthorized("/permission/admin/device-mgt/admin/device-type")) {
context["editPermitted"] = true;
}
var deviceTypeCount = deviceModule.getDeviceTypeCount();
if (deviceTypeCount > 0) {
context["hasDeviceTypes"] = true;
} else {
context["hasDeviceTypes"] = false;
}
return context;
}

View File

@ -0,0 +1,5 @@
{
"version": "1.0.0",
"uri": "/device-types",
"layout": "cdmf.layout.default"
}

View File

@ -0,0 +1,207 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var loadDeviceTypeBasedActionURL = function (action, deviceTypeName) {
href = $("#ast-container").data("app-context") + "device-type/" + action + "?type=" + encodeURIComponent(deviceTypeName);
$(location).attr('href', href);
};
$(function () {
var sortableElem = '.wr-sortable';
$(sortableElem).sortable({
beforeStop: function () {
$(this).sortable('toArray');
}
});
$(sortableElem).disableSelection();
});
var apiBasePath = "/api/device-mgt/v1.0";
var modalPopup = ".modal";
var modalPopupContainer = modalPopup + " .modal-content";
var modalPopupContent = modalPopup + " .modal-content";
var body = "body";
var isInit = true;
var isCloud = false;
/**
*
* Fires the res_text when ever a data table redraw occurs making
* the font icons change the size to respective screen resolution.
*
*/
$(document).on('draw.dt', function () {
$(".icon .text").res_text(0.2);
});
/*
* set popup maximum height function.
*/
function setPopupMaxHeight() {
$(modalPopupContent).css('max-height', ($(body).height() - ($(body).height() / 100 * 30)));
$(modalPopupContainer).css('margin-top', (-($(modalPopupContainer).height() / 2)));
}
/*
* show popup function.
*/
function showPopup() {
$(modalPopup).modal('show');
//setPopupMaxHeight();
}
/*
* hide popup function.
*/
function hidePopup() {
$(modalPopupContent).html('');
$(modalPopup).modal('hide');
$('body').removeClass('modal-open').css('padding-right', '0px');
$('.modal-backdrop').remove();
}
/**
* Following function would execute
* when a user clicks on the list item
* initial mode and with out select mode.
*/
function InitiateViewOption() {
// $(location).attr('href', $(this).data("url"));
}
function htmlspecialchars(text) {
return jQuery('<div/>').text(text).html();
}
function loadDeviceTypes() {
var loadingContent = $("#loading-content");
loadingContent.show();
var dataFilter = function (data) {
data = JSON.parse(data);
var objects = [];
$(data).each(function (index) {
objects.push(
{
name: htmlspecialchars(data[index].name),
DT_RowId: "devicetype-" + htmlspecialchars(data[index].name),
metaDefinition: (data[index].deviceTypeMetaDefinition ? true : false)
}
)
});
var json = {
"recordsTotal": data.length,
"recordsFiltered": data.length,
"data": objects
};
return JSON.stringify(json);
};
//noinspection JSUnusedLocalSymbols
var fnCreatedRow = function (nRow, aData, iDataIndex) {
$(nRow).attr('data-type', 'selectable');
};
//noinspection JSUnusedLocalSymbols
var columns = [
{
class: "remove-padding content-fill",
data: null,
defaultContent: "<div class='thumbnail icon'>" +
"<i class='square-element text fw fw-devices' style='font-size: 74px;'></i>" +
"</div>"
},
{
class: "",
data: "name",
render: function (name, type, row, meta) {
return '<h4>' + name.replace("devicemgt", "") + '</h4>';
}
},
{
class: "text-right content-fill text-left-on-grid-view no-wrap",
data: null,
render: function (data, type, row, meta) {
var isCloud = false;
if ($('#is-cloud').length > 0) {
isCloud = true;
}
var innerhtml = '';
if (data.metaDefinition) {
var editLink = '<a onclick="javascript:loadDeviceTypeBasedActionURL(\'edit\', \'' + data.name + '\')" ' +
'data-devicetype="' + data.name + '" ' +
'data-click-event="edit-form" ' +
'class="btn padding-reduce-on-grid-view edit-devicetype-link">' +
'<span class="fw-stack">' +
'<i class="fw fw-circle-outline fw-stack-2x"></i>' +
'<i class="fw fw-devices fw-stack-1x"></i>' +
'<span class="fw-stack fw-move-right fw-move-bottom">' +
'<i class="fw fw-circle fw-stack-2x fw-stroke fw-inverse"></i>' +
'<i class="fw fw-circle fw-stack-2x"></i><i class="fw fw-edit fw-stack-1x fw-inverse"></i>' +
'</span>' +
'</span>' +
'<span class="hidden-xs hidden-on-grid-view">Edit</span>' +
'</a>';
var editEventLink = '<a onclick="javascript:loadDeviceTypeBasedActionURL(\'edit-event\', \'' + data.name + '\')" ' +
'data-devicetype="' + data.name + '" ' +
'data-click-event="edit-form" ' +
'class="btn padding-reduce-on-grid-view edit-event-link">' +
'<span class="fw-stack">' +
'<i class="fw fw-circle-outline fw-stack-2x"></i>' +
'<i class="fw fw-document fw-stack-1x"></i>' +
'<span class="fw-stack fw-move-right fw-move-bottom">' +
'<i class="fw fw-circle fw-stack-2x fw-stroke fw-inverse"></i>' +
'<i class="fw fw-circle fw-stack-2x"></i><i class="fw fw-edit fw-stack-1x fw-inverse"></i>' +
'</span>' +
'</span>' +
'<span class="hidden-xs hidden-on-grid-view">Edit Event</span>' +
'</a>';
innerhtml = editLink + editEventLink;
}
return innerhtml;
}
}
];
var options = {
"placeholder": "Search By Device Type Name",
"searchKey": "filter",
"searching": false
};
var settings = {
"sorting": false
};
var deviceTypeApiUrl = '/api/device-mgt/v1.0/admin/device-types';
$('#devicetype-grid').datatables_extended_serverside_paging(settings, deviceTypeApiUrl, dataFilter, columns, fnCreatedRow, null, options);
loadingContent.hide();
}
$(document).ready(function () {
loadDeviceTypes();
});

View File

@ -0,0 +1,44 @@
{{#each deviceTypes}}
<tr data-type="selectable" id="devicetype-{{deviceTypeName}}">
<td class="remove-padding icon-only content-fill">
<div class="thumbnail icon">
<i class="square-element text fw fw-user"></i>
</div>
</td>
<td class="remove-padding-top">{{deviceTypeName}}</td>
<td class="text-right content-fill text-left-on-grid-view no-wrap">
{{#if canEdit}}
<a onclick="javascript:loadDeviceTypeBasedActionURL('edit', '{{deviceTypeName}}')" data-devicetype="{{deviceTypeName}}"
data-click-event="edit-form"
class="btn padding-reduce-on-grid-view edit-devicetype-link" title="Edit Device Type">
<span class="fw-stack fw-lg">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-user fw-stack-1x"></i>
<span class="fw-stack fw-move-right fw-move-bottom">
<i class="fw fw-circle fw-stack-2x fw-stroke fw-inverse"></i>
<i class="fw fw-circle fw-stack-2x"></i>
<i class="fw fw-edit fw-stack-1x fw-inverse"></i>
</span>
</span>
<span class="hidden-xs hidden-on-grid-view">Edit</span>
</a>
<a onclick="javascript:loadDeviceTypeBasedActionURL('edit-event', '{{deviceTypeName}}')"
data-devicetype="{{deviceTypeName}}"
data-click-event="edit-form" class="btn padding-reduce-on-grid-view edit-permission-link"
title="Edit Device Event">
<span class="fw-stack fw-lg">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-security-policy fw-stack-1x"></i>
<span class="fw-stack fw-move-right fw-move-bottom">
<i class="fw fw-circle fw-stack-2x fw-stroke fw-inverse"></i>
<i class="fw fw-circle fw-stack-2x"></i>
<i class="fw fw-edit fw-stack-1x fw-inverse"></i>
</span>
</span>
<span class="hidden-xs hidden-on-grid-view">Edit Event</span>
</a>
{{/if}}
</td>
</tr>
{{/each}}

View File

@ -0,0 +1,43 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may
obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{unit "cdmf.unit.ui.title" pageTitle="Policy Management | View Policy"}}
{{#zone "breadcrumbs"}}
<li>
<a href="{{@app.context}}/">
<i class="icon fw fw-home"></i>
</a>
</li>
<li>
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/policies">
Policies
</a>
</li>
<li>
<a href="#">
View
</a>
</li>
{{/zone}}
{{#zone "content"}}
{{unit "cdmf.unit.device.operation-mod"}}
{{unit "cdmf.unit.effective-policy.view"}}
{{/zone}}

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
function onRequest(context) {
var utility = require("/app/modules/utility.js")["utility"];
var deviceType = context.uriParams.deviceType;
var deviceId = context.uriParams.deviceId;
return {"deviceTypePolicyView": utility.getTenantedDeviceUnitName(deviceType, deviceId,"policy-view")};
}

View File

@ -0,0 +1,5 @@
{
"version": "1.0.0",
"uri": "/policy/effective-policy/",
"layout": "cdmf.layout.default"
}

View File

@ -0,0 +1,22 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{#zone "title"}}Error | {{@app.conf.appName}}{{/zone}}
{{#zone "messageDescription"}}
{{@page.params.status}} - {{@page.params.message}}
{{/zone}}

View File

@ -0,0 +1,5 @@
{
"version": "1.0.0",
"uri": "/errors/default",
"layout" : "cdmf.layout.error"
}

View File

@ -0,0 +1,420 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{unit "cdmf.unit.ui.title" pageTitle="Group Management"}}
{{unit "cdmf.unit.ui.content.title" pageHeader="Groups"}}
{{unit "cdmf.unit.lib.service-invoker-utility"}}
{{unit "cdmf.unit.data-tables-extended"}}
{{unit "cdmf.unit.lib.ui-permissions-utility"}}
{{#zone "breadcrumbs"}}
<li>
<a href="{{@app.context}}/">
<i class="icon fw fw-home"></i>
</a>
</li>
<li>
<a href="{{@app.context}}/groups">
Groups
</a>
</li>
{{/zone}}
{{#zone "navbarActions"}}
{{#if permissions.ADD_GROUP}}
<li>
<a href="{{@app.context}}/group/add" class="cu-btn">
<span class="icon fw-stack">
<i class="fw fw-add fw-stack-1x"></i>
<i class="fw fw-circle-outline fw-stack-2x"></i>
</span>
Add Group
</a>
</li>
{{/if}}
{{/zone}}
{{#zone "content"}}
<div class="wr-device-list row">
<div class="wr-hidden-operations wr-advance-operations"></div>
<div class="col-md-12 wr-page-content">
<div>
<span id="permission" data-permission="{{permissions.list}}"></span>
<span id="isCloud" data-isCloud="{{isCloud}}"></span>
{{#if groupCount}}
<div class="container-fluid" id="group-listing" data-current-user="{{@user.username}}">
<table class="table table-striped table-hover list-table display responsive nowrap data-table table-selectable grid-view"
id="group-grid">
<thead>
<tr class="sort-row">
<th class="no-sort"></th>
<th class="no-sort"></th>
<th>By Owner</th>
<th class="no-sort"></th>
</tr>
<tr class="filter-row filter-box">
<th></th>
<th></th>
<th data-for="By Owner" class="text-filter"></th>
<th></th>
</tr>
</thead>
<tbody id="ast-container"></tbody>
</table>
</div>
{{else}}
<div id="ast-container" class="ast-container list-view">
<div class="ctrl-info-panel col-centered text-center wr-login">
<h3 class="text-muted">
<i class="fw fw-group fw-3x"></i>
</h3>
<h3 class="text-muted">You don't have any device group. Let's add a new device group!</h3>
{{#if permissions.ADD_GROUP}}
<h3>
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/group/add" class="btn-operations btn-default">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-add fw-stack-1x"></i>
</span>
Add New Group
</a>
</h3>
{{/if}}
</div>
</div>
{{/if}}
<div id="share-group-w1-modal-content" class="hide">
<div class="modal-header">
<h3 class="pull-left modal-title">
<span>
<span class="fw-stack add-margin-right-2x">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-share fw-stack-1x"></i>
</span> Group Sharing
</span>
</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fw fw-cancel"></i></button>
</div>
<div class="modal-body add-margin-top-2x add-margin-bottom-2x">
<div id="notification-error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>
</div>
<div id="user-names">
<h4>
Select user role(s)
</h4>
<div class="wr-input-control" id="rolesListing">
</div>
</div>
</div>
<div class="modal-footer">
<div class="buttons">
{{#if permissions.ADD_ROLE}}
<a href="{{@app.context}}/role/add" class="btn-operations">
New Role
</a>
<a href="#" id="share-group-new-role-from-selection" class="btn-operations">
New Role from Selection
</a>
{{/if}}
<a href="#" id="share-group-next-link" class="btn-operations">
Share
</a>
</div>
</div>
</div>
<div id="share-group-w3-modal-content" class="hide">
<div class="modal-header">
<h3 class="pull-left modal-title">
<span>
<span class="fw-stack add-margin-right-2x">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-bookmark fw-stack-1x"></i>
</span> Group Sharing Role
</span>
</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fw fw-cancel"></i></button>
</div>
<div class="modal-body add-margin-top-2x add-margin-bottom-2x">
<div id="notification-error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>
</div>
<div id="user-roles">
<h4>
Enter Group Sharing Role Name
<br><br>
<input type="text" id="group-sharing-role-name"
class="form-control modal-input operationDataKeys" />
<br><br>
</h4>
</div>
</div>
<div class="modal-footer">
<div class="buttons">
<a href="#" id="share-group-w3-yes-link" class="btn-operations">
OK
</a>
<a href="#" id="share-group-w3-cancel-link" class="btn-operations btn-default">
Cancel
</a>
</div>
</div>
</div>
<div id="share-group-200-content" class="hide">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>Group successfully updated.</h3>
<br />
Do you want to manage users?
<div class="buttons">
<a href="{{@app.context}}/users" id="share-group-add-users-yes-link"
class="btn-operations">
Yes
</a>
<a href="#" class="btn-operations btn-default" data-dismiss="modal">
No
</a>
</div>
<br />
</div>
</div>
</div>
</div>
<div id="create-combined-role-200-content" class="hide">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>New Role was successfully created.</h3>
<br />
Do you need to Add / Remove users from the chosen roles?
<div class="buttons">
<a href="{{@app.context}}/users" id="share-group-add-users-yes-link"
class="btn-operations">
Yes
</a>
<a href="#" class="btn-operations btn-default" data-dismiss="modal">
No
</a>
</div>
<br />
</div>
</div>
</div>
</div>
<div id="remove-group-modal-content" class="hide">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>Do you really want to remove this group from your Group List?</h3>
<div class="buttons">
<a href="#" id="remove-group-yes-link" class="btn-operations">
Yes
</a>
<a href="#" id="remove-group-cancel-link" class="btn-operations btn-default"
data-dismiss="modal">
Cancel
</a>
</div>
</div>
</div>
</div>
</div>
<div id="remove-group-200-content" class="hide">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>Group was successfully removed.</h3>
</div>
</div>
</div>
</div>
<div id="edit-group-modal-content" class="hide">
<div class="modal-header">
<h3 class="pull-left modal-title">
<span>
<span class="fw-stack add-margin-right-2x">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-edit fw-stack-1x"></i>
</span> Update Group
</span>
</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fw fw-cancel"></i></button>
</div>
<div class="modal-body add-margin-top-2x add-margin-bottom-2x">
<div id="notification-error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>
</div>
<div>
<h4>
Enter new name and description for the group
<br><br>
<input type="text" id="edit-group-name"
class="form-control modal-input operationDataKeys" placeholder="Group Name"
value="">
</input>
<br>
<input type="text" id="edit-group-description"
class="form-control modal-input operationDataKeys"
placeholder="Group Description" value="">
</h4>
</div>
</div>
<div class="modal-footer">
<div class="buttons">
<a href="#" id="edit-group-yes-link" class="btn-operations">
Update
</a>
<a href="#" id="edit-group-cancel-link" class="btn-operations btn-default">
Cancel
</a>
</div>
</div>
</div>
<div id="edit-group-200-content" class="hide">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>Group was successfully updated.</h3>
</div>
</div>
</div>
</div>
<div id="group-400-content" class="hide">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3 id="error-msg">Bad Request. Please contact your administrator.</h3>
<div class="buttons">
<a href="#" id="group-400-link" class="btn-operations">
Ok
</a>
</div>
</div>
</div>
</div>
</div>
<div id="group-403-content" class="hide">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>Operation not permitted.</h3>
<div class="buttons">
<a href="#" id="group-403-link" class="btn-operations">
Ok
</a>
</div>
</div>
</div>
</div>
</div>
<div id="group-404-content" class="hide">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3 id="group-404-message">Not found.</h3>
<div class="buttons">
<a href="#" id="group-404-link" class="btn-operations">
Ok
</a>
</div>
</div>
</div>
</div>
</div>
<div id="group-409-content" class="hide">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>Group does not exist.</h3>
<div class="buttons">
<a href="#" id="group-409-link" class="btn-operations">
Ok
</a>
</div>
</div>
</div>
</div>
</div>
<div id="group-unexpected-error-content" class="hide">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3 id="unexp-error-msg">Unexpected error occurred!</h3>
<div class="buttons">
<a href="#" id="group-unexpected-error-link" class="btn-operations">
Ok
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{/zone}}
{{#zone "bottomJs"}}
{{#if groupCount}}
{{js "js/listing.js"}}
{{/if}}
{{/zone}}
{{#zone "topCss"}}
<style>
.select2-dropdown {
z-index: 999999999;
}
.wr-input-control {
color: black;
}
.select2-selection__choice {
font-size: medium;
}
</style>
{{/zone}}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
function onRequest(context) {
var groupModule = require("/app/modules/business-controllers/group.js")["groupModule"];
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
var constants = require("/app/modules/constants.js");
var deviceMgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var currentUser = session.get(constants.USER_SESSION_KEY);
var page = {};
if (currentUser) {
page.permissions = userModule.getUIPermissions();
page.permissions.list = stringify(page.permissions);
page.currentUser = currentUser;
var groupCount = groupModule.getGroupCount();
if (groupCount > 0) {
page.groupCount = groupCount;
}
}
page.isCloud = deviceMgtProps.isCloud;
return page;
}

View File

@ -0,0 +1,5 @@
{
"version": "1.0.0",
"uri": "/groups",
"layout": "cdmf.layout.default"
}

View File

@ -0,0 +1,646 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Following function would execute
* when a user clicks on the list item
* initial mode and with out select mode.
*/
function InitiateViewOption(url) {
if ($(".select-enable-btn").text() == "Select") {
url = $(this).parent().data("url");
$(location).attr('href', url);
}
}
(function () {
var cache = {};
var validateAndReturn = function (value) {
return (value == undefined || value == null) ? "Unspecified" : value;
};
Handlebars.registerHelper("deviceMap", function (device) {
device.owner = validateAndReturn(device.owner);
device.ownership = validateAndReturn(device.ownership);
var arr = device.properties;
if (arr) {
device.properties = arr.reduce(function (total, current) {
total[current.name] = validateAndReturn(current.value);
return total;
}, {});
}
});
})();
/*
* Setting-up global variables.
*/
var groupCheckbox = "#ast-container .ctrl-wr-asset .itm-select input[type='checkbox']";
var assetContainer = "#ast-container";
/*
* On Select All Groups button click function.
*
* @param button: Select All Groups button
*/
function selectAllDevices(button) {
if (!$(button).data('select')) {
$(groupCheckbox).each(function (index) {
$(this).prop('checked', true);
addGroupSelectedClass(this);
});
$(button).data('select', true);
$(button).html('Deselect All Groups');
} else {
$(groupCheckbox).each(function (index) {
$(this).prop('checked', false);
addGroupSelectedClass(this);
});
$(button).data('select', false);
$(button).html('Select All Groups');
}
}
/*
* On listing layout toggle buttons click function.
*
* @param view: Selected view type
* @param selection: Selection button
*/
function changeDeviceView(view, selection) {
$(".view-toggle").each(function () {
$(this).removeClass("selected");
});
$(selection).addClass("selected");
if (view == "list") {
$(assetContainer).addClass("list-view");
} else {
$(assetContainer).removeClass("list-view");
}
}
/*
* Add selected style class to the parent element function.
*
* @param checkbox: Selected checkbox
*/
function addGroupSelectedClass(checkbox) {
if ($(checkbox).is(":checked")) {
$(checkbox).closest(".ctrl-wr-asset").addClass("selected device-select");
} else {
$(checkbox).closest(".ctrl-wr-asset").removeClass("selected device-select");
}
}
function toTitleCase(str) {
return str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
function htmlspecialchars(text) {
return jQuery('<div/>').text(text).html();
}
function loadGroups() {
var groupListing = $("#group-listing");
var currentUser = groupListing.data("currentUser");
var serviceURL;
if ($.hasPermission("LIST_ALL_GROUPS")) {
serviceURL = "/api/device-mgt/v1.0/admin/groups";
} else if ($.hasPermission("LIST_GROUPS")) {
//Get authenticated users groups
serviceURL = "/api/device-mgt/v1.0/groups";
} else {
$("#loading-content").remove();
$('#device-table').addClass('hidden');
$('#device-listing-status-msg').text('Permission denied.');
$("#device-listing-status").removeClass(' hidden');
return;
}
var dataFilter = function (data) {
data = JSON.parse(data);
var objects = [];
$(data.deviceGroups).each(function (index) {
objects.push({
groupId: htmlspecialchars(data.deviceGroups[index].id),
name: htmlspecialchars(data.deviceGroups[index].name),
description: htmlspecialchars(data.deviceGroups[index].description),
owner: htmlspecialchars(data.deviceGroups[index].owner)
})
});
var json = {
"recordsTotal": data.count,
"recordsFiltered": data.count,
"data": objects
};
return JSON.stringify(json);
};
var columns = [
{
targets: 0,
data: 'id',
class: 'remove-padding icon-only content-fill viewEnabledIcon',
render: function (data, type, row, meta) {
return '<div class="thumbnail icon"><img class="square-element text fw " ' +
'src="public/cdmf.page.groups/images/group-icon.png"/></div>';
}
},
{
targets: 1,
data: 'name',
class: 'viewEnabledIcon'
},
{
targets: 2,
data: 'owner',
class: 'remove-padding-top viewEnabledIcon'
},
{
targets: 3,
data: 'description',
class: 'remove-padding-top viewEnabledIcon'
},
{
targets: 4,
data: 'id',
class: 'text-right content-fill text-left-on-grid-view no-wrap tooltip-overflow-fix',
render: function (id, type, row, meta) {
var html = '';
if ($.hasPermission("VIEW_GROUP_DEVICES")) {
/*html += '<a href="group/' + row.groupId
+ '/analytics" data-click-event="remove-form" class="btn padding-reduce-on-grid-view">' +
'<span class="fw-stack"><i class="fw fw-circle-outline fw-stack-2x"></i><i class="fw fw-statistics fw-stack-1x"></i></span>'
+
'<span class="hidden-xs hidden-on-grid-view">Analytics</span></a>';*/
}
if (row.owner != "wso2.system.user") {
if ($.hasPermission("SHARE_GROUP")) {
html +=
'<a href="#" data-click-event="remove-form" class="btn padding-reduce-on-grid-view share-group-link" data-group-id="'
+ row.groupId + '" ' +
'data-group-owner="' + row.owner
+ '" data-placement="top" data-toggle="tooltip" data-original-title="Share"><span class="fw-stack"><i class="fw fw-circle-outline fw-stack-2x"></i><i class="fw fw-share fw-stack-1x"></i></span>'
+
'<span class="hidden-xs hidden-on-grid-view">Share</span></a>';
}
if ($.hasPermission("UPDATE_GROUP")) {
html +=
'<a href="#" data-click-event="remove-form" class="btn padding-reduce-on-grid-view edit-group-link" data-group-name="'
+ row.name + '" ' +
'data-group-owner="' + row.owner + '" data-group-description="' + row.description
+ '" data-group-id="' + row.groupId
+ '" data-placement="top" data-toggle="tooltip" data-original-title="Edit"><span class="fw-stack"><i class="fw fw-circle-outline fw-stack-2x"></i>' +
'<i class="fw fw-edit fw-stack-1x"></i></span><span class="hidden-xs hidden-on-grid-view">Edit</span></a>';
}
if ($.hasPermission("REMOVE_GROUP")) {
html +=
'<a href="#" data-click-event="remove-form" class="btn padding-reduce-on-grid-view remove-group-link" data-group-id="'
+ row.groupId + '" ' +
'data-group-owner="' + row.owner
+ '" data-placement="top" data-toggle="tooltip" data-original-title="Delete"><span class="fw-stack"><i class="fw fw-circle-outline fw-stack-2x"></i><i class="fw fw-delete fw-stack-1x"></i>'
+
'</span><span class="hidden-xs hidden-on-grid-view">Delete</span></a>';
}
}
return html;
}
}
];
var fnCreatedRow = function (row, data) {
$(row).attr('data-type', 'selectable');
if ($.hasPermission("VIEW_GROUP_DEVICES")) {
$(row).attr('data-url', 'devices?groupId=' + data.groupId + '&groupName=' + data.name);
}
$.each($('td', row), function (colIndex) {
switch (colIndex) {
case 1:
//TODO : The following line encodes html entities.
//Should check for a better solution since it only changes the presentation layer
$(this).text($("<textarea></textarea>").html(this.innerText).text())
$(this).attr('data-grid-label', "Name");
$(this).attr('data-search', data.name);
$(this).attr('data-display', data.name);
break;
case 2:
$(this).attr('data-grid-label', "Owner");
$(this).attr('data-search', data.owner);
$(this).attr('data-display', data.owner);
break;
case 3:
$(this).attr('data-grid-label', "Description");
$(this).attr('data-search', data.description);
$(this).attr('data-display', data.description);
break;
}
});
};
$('#group-grid').datatables_extended_serverside_paging(
null,
serviceURL,
dataFilter,
columns,
fnCreatedRow,
function (oSettings) {
$(".icon .text").res_text(0.2);
attachEvents();
var thisTable = $(this).closest('.dataTables_wrapper').find('.dataTable').dataTable();
thisTable.removeClass("table-selectable");
},
{
"placeholder": "Search By Group Name",
"searchKey": "name"
}
);
$(groupCheckbox).click(function () {
addGroupSelectedClass(this);
});
}
function openCollapsedNav() {
$('.wr-hidden-nav-toggle-btn').addClass('active');
$('#hiddenNav').slideToggle('slideDown', function () {
if ($(this).css('display') == 'none') {
$('.wr-hidden-nav-toggle-btn').removeClass('active');
}
});
}
/*
* DOM ready functions.
*/
$(document).ready(function () {
/* Adding selected class for selected devices */
$(groupCheckbox).each(function () {
addGroupSelectedClass(this);
});
var permissionSet = {};
//This method is used to setup permission for device listing
$.setPermission = function (permission) {
permissionSet[permission] = true;
};
$.hasPermission = function (permission) {
return permissionSet[permission];
};
var permissionList = $("#permission").data("permission");
for (var key in permissionList) {
if (permissionList.hasOwnProperty(key)) {
$.setPermission(key);
}
}
loadGroups();
//$('#device-grid').datatables_extended();
/* for device list sorting drop down */
$(".ctrl-filter-type-switcher").popover(
{
html: true,
content: function () {
return $("#content-filter-types").html();
}
}
);
/* for data tables*/
$('[data-toggle="tooltip"]').tooltip();
$("[data-toggle=popover]").popover();
$(".ctrl-filter-type-switcher").popover(
{
html: true,
content: function () {
return $('#content-filter-types').html();
}
}
);
$('#nav').affix(
{
offset: {
top: $('header').height()
}
}
);
});
var modalPopup = ".modal";
var modalPopupContainer = modalPopup + " .modal-content";
var modalPopupContent = modalPopup + " .modal-content";
var body = "body";
/*
* set popup maximum height function.
*/
function setPopupMaxHeight() {
$(modalPopupContent).css('max-height', ($(body).height() - ($(body).height() / 100 * 30)));
$(modalPopupContainer).css('margin-top', (-($(modalPopupContainer).height() / 2)));
}
/*
* show popup function.
*/
function showPopup() {
$(modalPopup).modal('show');
}
/*
* hide popup function.
*/
function hidePopup() {
$(modalPopupContent).html("");
$(modalPopupContent).removeClass("operation-data");
$(modalPopup).modal('hide');
$('body').removeClass('modal-open').css('padding-right', '0px');
$('.modal-backdrop').remove();
}
/**
* Following functions should be triggered after AJAX request is made.
*/
function attachEvents() {
/**
* Following click function would execute
* when a user clicks on "Share" link
* on Group Management page in WSO2 Device Management Server Console.
*/
$("a.share-group-link").click(function () {
var groupId = $(this).data("group-id");
var groupOwner = $(this).data("group-owner");
$(modalPopupContent).html($('#share-group-w1-modal-content').html());
showPopup();
listAllRoles(groupId);
var shareGroupNextLink = $("a#share-group-next-link");
shareGroupNextLink.click(function () {
var roles = $("#roles").val();
updateGroupShare(groupId, roles);
});
var shareGroupNewRoleFromSelectionLink = $("a#share-group-new-role-from-selection");
shareGroupNewRoleFromSelectionLink.click(function () {
var roles = $("#roles").val();
if (roles && roles.length >= 2) {
addNewRole(roles);
} else {
var errorMsgWrapper = "#notification-error-msg";
var errorMsg = "#notification-error-msg span";
$(errorMsg).text("To create a new role with the combination of roles, at least two roles should be" +
" selected.");
$(errorMsgWrapper).removeClass("hidden");
}
});
});
/**
* Following click function would execute
* when a user clicks on "Remove" link
* on Group Management page in WSO2 IoT Server Console.
*/
$("a.remove-group-link").click(function () {
var groupId = $(this).data("group-id");
var groupOwner = $(this).data("group-owner");
$(modalPopupContent).html($('#remove-group-modal-content').html());
showPopup();
$("a#remove-group-yes-link").click(function () {
var successCallback = function (data, textStatus, xhr) {
if (xhr.status == 200) {
$(modalPopupContent).html($('#remove-group-200-content').html());
setTimeout(function () {
hidePopup();
location.reload(false);
}, 2000);
} else {
displayErrors(xhr);
}
};
invokerUtil.delete("/api/device-mgt/v1.0/groups/id/" + groupId,
successCallback, function (message) {
displayErrors(message);
});
});
$("a#remove-group-cancel-link").click(function () {
hidePopup();
});
});
/**
* Following click function would execute
* when a user clicks on "Edit" link
* on Device Management page in WSO2 MDM Console.
*/
$("a.edit-group-link").click(function () {
var groupId = $(this).data("group-id");
var groupName = $(this).data("group-name");
var groupOwner = $(this).data("group-owner");
var groupDescription = $(this).data("group-description");
$(modalPopupContent).html($('#edit-group-modal-content').html());
$('#edit-group-name').val(groupName);
$('#edit-group-description').val(groupDescription);
showPopup();
$("a#edit-group-yes-link").click(function () {
var newGroupName = $('#edit-group-name').val();
var newGroupDescription = $('#edit-group-description').val();
var group = {"name": newGroupName, "description": newGroupDescription, "owner": groupOwner};
var successCallback = function (data, textStatus, xhr) {
if (xhr.status == 200) {
$(modalPopupContent).html($('#edit-group-200-content').html());
setTimeout(function () {
hidePopup();
location.reload(false);
}, 2000);
} else {
displayErrors(xhr);
}
};
invokerUtil.put("/api/device-mgt/v1.0/groups/id/" + groupId, group,
successCallback, function (message) {
displayErrors(message);
});
});
$("a#edit-group-cancel-link").click(function () {
hidePopup();
});
});
}
function markAlreadySavedUsersRoles(groupId) {
var successCallback = function (data, textStatus, xhr) {
data = JSON.parse(data);
if (xhr.status == 200) {
if (data.roles.length > 0) {
var selectedValues = [];
for (var i = 0; i < data.roles.length; i++) {
selectedValues.push(data.roles[i]);
}
$("#roles").val(selectedValues).trigger("change");
} else {
return;
}
} else {
displayErrors(xhr);
}
};
invokerUtil.get("/api/device-mgt/v1.0/groups/id/" + groupId + "/roles",
successCallback, function (message) {
displayErrors(message);
});
}
function listAllRoles(groupId) {
var successCallback = function (data, textStatus, xhr) {
data = JSON.parse(data);
if (xhr.status == 200) {
if (data.roles.length > 0) {
var html = '<select id="roles" class="form-control select2" multiple="multiple">';
for (var i = 0; i < data.roles.length; i++) {
html += '<option value="' + data.roles[i] + '">' + data.roles[i] + '</option>';
}
html += '</select>';
$("#rolesListing").html(html);
markAlreadySavedUsersRoles(groupId);
$("select.select2[multiple=multiple]").select2({tags: false}).on("select2:select", function () {
$("#notification-error-msg").addClass("hidden");
});
} else {
$("#rolesListing").html("No roles available");
}
} else {
displayErrors(xhr);
}
};
invokerUtil.get("/api/device-mgt/v1.0/roles?offset=0&limit=100&user-store=all",
successCallback, function (message) {
displayErrors(message);
});
}
function addNewRole(roles) {
$(modalPopupContent).html($('#share-group-w3-modal-content').html());
$("a#share-group-w3-yes-link").click(function () {
var roleName = $('#group-sharing-role-name').val();
if (roleName) {
createNewCombinedRole(roleName, roles);
} else {
var errorMsgWrapper = "#notification-error-msg";
var errorMsg = "#notification-error-msg span";
$(errorMsg).text("Role name cannot be empty.");
$(errorMsgWrapper).removeClass("hidden");
}
});
$("a#share-group-w3-cancel-link").click(function () {
hidePopup();
});
}
function togglePermissionAction(element) {
$(element).data('value', 'checked');
var icon = $(element).find("i")[1];
if ($(icon).hasClass('fw-minus')) {
$(icon).removeClass('fw-minus');
$(icon).addClass('fw-add');
$(element).data('value', 'unchecked');
} else {
$(icon).removeClass('fw-add');
$(icon).addClass('fw-minus');
$(element).data('value', 'checked');
}
}
function updateGroupShare(groupId, roles) {
var successCallback = function (data) {
$(modalPopupContent).html($('#share-group-200-content').html());
};
invokerUtil.post("/api/device-mgt/v1.0/groups/id/" + groupId + "/share",
roles, successCallback, function (message) {
displayErrors(message);
});
}
function createNewCombinedRole(roleName, roleList) {
var successCallback = function (data, status, jqXHR, isLast) {
$(modalPopupContent).html($('#create-combined-role-200-content').html());
showPopup();
};
invokerUtil.post("/api/device-mgt/v1.0/roles/create-combined-role/" + roleName, roleList,
successCallback, function (message) {
displayErrors(message);
});
}
function displayErrors(jqXHR) {
showPopup();
if (jqXHR.status == 400) {
$(modalPopupContent).html($('#group-400-content').html());
if (jqXHR.responseText) {
$('#error-msg').html(jqXHR.responseText.replace(new RegExp("\"", 'g'), ""));
}
$("a#group-400-link").click(function () {
hidePopup();
});
} else if (jqXHR.status == 403) {
$(modalPopupContent).html($('#group-403-content').html());
$("a#group-403-link").click(function () {
hidePopup();
});
} else if (jqXHR.status == 404) {
$(modalPopupContent).html($('#group-404-content').html());
$("#group-404-message").html(jqXHR.responseText);
$("a#group-404-link").click(function () {
hidePopup();
});
} else if (jqXHR.status == 409) {
$(modalPopupContent).html($('#group-409-content').html());
$("a#group-409-link").click(function () {
hidePopup();
});
} else {
$(modalPopupContent).html($('#group-unexpected-error-content').html());
if (jqXHR.responseText) {
$('#unexp-error-msg').html(jqXHR.responseText.replace(new RegExp("\"", 'g'), ""));
}
$("a#group-unexpected-error-link").click(function () {
hidePopup();
});
console.log("Error code: " + jqXHR.status);
}
}

View File

@ -0,0 +1,34 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{unit "cdmf.unit.data-tables-extended"}}
{{unit "cdmf.unit.ui.title" pageTitle="Notification Listing"}}
{{#zone "breadcrumbs"}}
<li>
<a href="{{@app.context}}/">
<i class="icon fw fw-home"></i>
</a>
</li>
<li>
<a href="{{@app.context}}/notification-listing">
Notifications
</a>
</li>
{{/zone}}
{{#zone "content"}}
{{unit "cdmf.unit.notification.listing"}}
{{/zone}}

View File

@ -0,0 +1,5 @@
{
"version": "1.0.0",
"uri": "/notification-listing",
"layout": "cdmf.layout.default"
}

View File

@ -0,0 +1,57 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{unit "cdmf.unit.ui.title" pageTitle="Home"}}
{{#zone "breadcrumbs"}}
<li>
<a href="{{@app.context}}/">
<i class="icon fw fw-home"></i>
</a>
</li>
{{/zone}}
{{#zone "content"}}
<div class="row">
<div class="col-md-12">
Loading...Please Wait
</div>
</div>
{{/zone}}
{{#zone "bottomJs"}}
<script type="text/javascript">
$('body').attr("data-toggle", "loading")
.attr("data-loading-style", "overlay")
.attr("data-loading-text", "SETTING UP YOUR ENVIRONMENT . . .");
$('[data-toggle="loading"]').loading('show');
</script>
{{js "/js/script.js"}}
{{/zone}}
{{#zone "topCss"}}
<style>
.loading[data-loading-style=overlay] .loading-bg{
background: #11375B;
opacity: 1;
filter: alpha(opacity=100);
}
.loading .loading-animation{
width : 200px;
}
</style>
{{/zone}}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
function onRequest(context) {
var constants = require("/app/modules/constants.js");
var skipWelcomeScreen = session.get(constants.SKIP_WELCOME_SCREEN);
if (skipWelcomeScreen) {
response.sendRedirect(context.app.context + "/");
exit();
}
return {};
}

View File

@ -0,0 +1,5 @@
{
"version": "1.0.0",
"uri": "/welcome",
"layout": "cdmf.layout.loading"
}

View File

@ -0,0 +1,30 @@
var pollingCount = 24;
function poll() {
$.ajax({
url: context + "/api/user/environment-loaded",
type: "GET",
success: function (data) {
if (data.isLoaded) {
window.location = context + "/";
}
},
dataType: "json",
complete: setTimeout(function () {
pollingCount = pollingCount - 1;
if (pollingCount > 0) {
poll();
} else {
$(".loading-animation .logo").hide();
$(".loading-animation").prepend(
'<i class="fw fw-error fw-inverse fw-2x" style="float: left;"></i>');
$(".loading-animation p").css("width", "150%")
.html("Ops... it seems something went wrong.<br/> Refresh the page to retry!");
}
}, 5000),
timeout: 5000
});
}
$(document).ready(function () {
poll();
});

View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Checks if provided input is valid against RegEx input.
*
* @param regExp Regular expression
* @param inputString Input string to check
* @returns {boolean} Returns true if input matches RegEx
*/
function inputIsValid(regExp, inputString) {
regExp = new RegExp(regExp);
return regExp.test(inputString);
}
/**
* Checks if an email address has the valid format or not.
*
* @param email Email address
* @returns {boolean} true if email has the valid format, otherwise false.
*/
function emailIsValid(email) {
var regExp = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
return regExp.test(email);
}
$(document).ready(function(){
/**
* Following click function would execute
* when a user clicks on "Add User" button
* on Add User page in WSO2 Devicemgt Console.
*/
$("button#add-user-btn").click(function () {
var usernameInput = $("input#user_name");
var firstnameInput = $("input#first_name");
var lastnameInput = $("input#last_name");
var emailInput = $("input#email");
var passwordInput = $("input#password");
var passwordConfirmationInput = $("input#password_confirmation");
var username = usernameInput.val().trim();
var firstname = firstnameInput.val();
var lastname = lastnameInput.val();
var emailAddress = emailInput.val();
var password = passwordInput.val();
var passwordConfirmation = passwordConfirmationInput.val();
var errorMsgWrapper = "#user-create-error-msg";
var errorMsg = "#user-create-error-msg span";
if (!firstname) {
$(errorMsg).text("Firstname is a required field. It cannot be empty.");
$(errorMsgWrapper).removeClass("hidden");
} else if (!inputIsValid(firstnameInput.data("regex"), firstname)) {
$(errorMsg).text(firstnameInput.data("errormsg"));
$(errorMsgWrapper).removeClass("hidden");
} else if (!lastname) {
$(errorMsg).text("Lastname is a required field. It cannot be empty.");
$(errorMsgWrapper).removeClass("hidden");
} else if (!inputIsValid(lastnameInput.data("regex"), lastname)) {
$(errorMsg).text(lastnameInput.data("errormsg"));
$(errorMsgWrapper).removeClass("hidden");
} else if (!username) {
$(errorMsg).text("Username is a required field. It cannot be empty.");
$(errorMsgWrapper).removeClass("hidden");
} else if (!inputIsValid(usernameInput.data("regex"), username)) {
$(errorMsg).text(usernameInput.data("errormsg"));
$(errorMsgWrapper).removeClass("hidden");
} else if (!emailAddress) {
$(errorMsg).text("Email is a required field. It cannot be empty.");
$(errorMsgWrapper).removeClass("hidden");
} else if (!emailIsValid(emailAddress)) {
$(errorMsg).text(emailInput.data("errormsg"));
$(errorMsgWrapper).removeClass("hidden");
} else if (!password) {
$(errorMsg).text("Password is a required field. It cannot be empty.");
$(errorMsgWrapper).removeClass("hidden");
} else if (password.length < 6) {
$(errorMsg).text("Password should contain a minimum of 6 characters.");
$(errorMsgWrapper).removeClass("hidden");
} else if (password != passwordConfirmation) {
$(errorMsg).text("Please enter the same password for confirmation.");
$(errorMsgWrapper).removeClass("hidden");
} else {
$(errorMsgWrapper).addClass("hidden");
$("#add-user-btn").prop('disabled', true);
var addUserFormData = {};
addUserFormData.username = username;
addUserFormData.firstname = firstname;
addUserFormData.lastname = lastname;
addUserFormData.emailAddress = emailAddress;
addUserFormData.password = $("input#password").val();
addUserFormData.userRoles = null;
var context = $(".form-login-box").data("context");
var addUserAPI = context + "/api/user/register";
$.ajax({
type: 'POST',
url: addUserAPI,
contentType: 'application/json',
data: JSON.stringify(addUserFormData),
success: function (data) {
$("#add-user-btn").prop('disabled', false);
if (data == 200) {
$('.wr-validation-summary strong').html(
"<i class=\"icon fw fw-success\"></i> Successfully Submitted.");
$('.wr-validation-summary').removeClass("alert-danger");
$('.wr-validation-summary').addClass("alert-success");
} else if (data == 201) {
$('.wr-validation-summary strong').html(
"<i class=\"icon fw fw-success\"></i> User created succssfully. You will be " +
"redirected to login page.");
$('.wr-validation-summary').removeClass("alert-danger");
$('.wr-validation-summary').addClass("alert-success");
$("#add-user-btn").prop('disabled', true);
setTimeout(function () {
window.location = context + "/login";
}, 2000);
} else if (data == 400) {
$('.wr-validation-summary strong').html(
"<i class=\"icon fw fw-error\"></i> Exception at backend.");
$('.wr-validation-summary').removeClass("alert-danger");
$('.wr-validation-summary').addClass("alert-warning");
} else if (data == 403) {
$('.wr-validation-summary strong').html("Action not permitted.");
} else if (data == 409) {
$('.wr-validation-summary strong').html(
"<i class=\"icon fw fw-info\"></i> User name already exists.");
$('.wr-validation-summary').removeClass("alert-default");
$('.wr-validation-summary').addClass("alert-success");
}
$('.wr-validation-summary').removeClass("hidden");
$('#password').val('');
$('#password_confirmation').val('');
},
error: function (err) {
$("#add-user-btn").prop('disabled', false);
$('.wr-validation-summary strong').html(
"<i class=\"icon fw fw-error\"></i> An unexpected error occurred.");
$('.wr-validation-summary').removeClass("hidden");
}
});
}
});
});

View File

@ -0,0 +1,75 @@
{{unit "cdmf.unit.ui.title" pageTitle="Register"}}
{{#zone "content"}}
{{unit "uuf.unit.lib.form-validation"}}
<div class="container col-xs-12 col-sm-10 col-md-8 col-lg-6 col-centered wr-content wr-login col-centered sign-panel">
<p class="page-sub-title">Register</p>
<p>Create new account on WSO2 IoT (All fields are required.)</p>
<hr/>
<!-- validation -->
<div class="wr-validation-summary hidden alert alert-danger">
<strong></strong>
<button type="button" class="close" aria-label="close" data-dismiss="alert">
<span aria-hidden="true">
<i class="fw fw-cancel"></i>
</span>
</button>
</div>
<div class="form-login-box" id="registerForm" data-context="{{@app.context}}">
<div id="user-create-error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>
</div>
<div class="form-group">
<label for="first_name" class="wr-input-label">First Name</label>
<input type="text" id="first_name" name="first_name" class="form-control" placeholder="First Name"
data-regex="{{firstnameJSRegEx}}" data-lengthmsg="{{usernameHelpText}}"
data-errormsg="{{firstnameRegExViolationErrorMsg}}"/>
</div>
<div class="form-group">
<label for="last_name" class="wr-input-label">Last Name</label>
<input type="text" id="last_name" name="last_name" class="form-control" placeholder="Last Name"
data-regex="{{lastnameJSRegEx}}" data-errormsg="{{lastnameRegExViolationErrorMsg}}"/>
</div>
<div class="form-group">
<label for="user_name" class="wr-input-label">Username ( {{usernameHelpText}} )</label>
<input type="text" id="user_name" name="user_name" class="form-control" placeholder="Username"
data-regex="{{usernameJSRegEx}}" data-errormsg="{{usernameRegExViolationErrorMsg}}"/>
</div>
<div class="form-group">
<label for="email" class="wr-input-label">Email</label>
<input type="text" id="email" name="email" class="form-control" placeholder="Email"
data-regex="{{emailJSRegEx}}" data-errormsg="{{emailRegExViolationErrorMsg}}"/>
</div>
<div class="form-group">
<label for="password" class="wr-input-label">Password</label>
<input type="password" id="password" name="password" class="form-control"
placeholder="Password"/>
</div>
<div class="form-group">
<label for="password_confirmation" class="wr-input-label">Confirm Password</label>
<input type="password" id="password_confirmation" name="password_confirmation"
class="form-control" placeholder="Confirm Password"/>
</div>
<div class="wr-input-control">
<button class="wr-btn" id="add-user-btn">Register</button>
<button class="wr-btn" onclick="document.location.href='{{@app.context}}/login';">
Cancel
</button>
</div>
</div>
</div>
{{/zone}}
{{#zone "bottomJs"}}
{{js "js/validate-register.js"}}
{{/zone}}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Returns the dynamic state to be populated by add-user page.
*
* @param context Object that gets updated with the dynamic state of this page to be presented
* @returns {*} A context object that returns the dynamic state of this page to be presented
*/
function onRequest(context) {
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var page = {};
page["usernameJSRegEx"] = devicemgtProps.userValidationConfig.usernameJSRegEx;
page["usernameHelpText"] = devicemgtProps.userValidationConfig.usernameHelpMsg;
page["usernameRegExViolationErrorMsg"] = devicemgtProps.userValidationConfig.usernameRegExViolationErrorMsg;
page["firstnameJSRegEx"] = devicemgtProps.userValidationConfig.firstnameJSRegEx;
page["firstnameRegExViolationErrorMsg"] = devicemgtProps.userValidationConfig.firstnameRegExViolationErrorMsg;
page["lastnameJSRegEx"] = devicemgtProps.userValidationConfig.lastnameJSRegEx;
page["lastnameRegExViolationErrorMsg"] = devicemgtProps.userValidationConfig.lastnameRegExViolationErrorMsg;
page["emailJSRegEx"] = devicemgtProps.userValidationConfig.emailJSRegEx;
page["emailRegExViolationErrorMsg"] = devicemgtProps.userValidationConfig.emailRegExViolationErrorMsg;
return page;
}

View File

@ -0,0 +1,6 @@
{
"version": "1.0.0",
"uri": "/register",
"isAnonymous": true,
"layout": "uuf.layout.sign-in"
}

View File

@ -0,0 +1,275 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var loadRoleBasedActionURL = function (action, rolename) {
href = $("#ast-container").data("app-context") + "role/" + action + "/?rolename=" + encodeURIComponent(rolename);
$(location).attr('href', href);
};
$(function () {
var sortableElem = '.wr-sortable';
$(sortableElem).sortable({
beforeStop: function () {
$(this).sortable('toArray');
}
});
$(sortableElem).disableSelection();
});
var apiBasePath = "/api/device-mgt/v1.0";
var modalPopup = ".modal";
var modalPopupContainer = modalPopup + " .modal-content";
var modalPopupContent = modalPopup + " .modal-content";
var body = "body";
var isInit = true;
var isCloud = false;
/**
*
* Fires the res_text when ever a data table redraw occurs making
* the font icons change the size to respective screen resolution.
*
*/
$(document).on('draw.dt', function () {
$(".icon .text").res_text(0.2);
});
/*
* set popup maximum height function.
*/
function setPopupMaxHeight() {
$(modalPopupContent).css('max-height', ($(body).height() - ($(body).height() / 100 * 30)));
$(modalPopupContainer).css('margin-top', (-($(modalPopupContainer).height() / 2)));
}
/*
* show popup function.
*/
function showPopup() {
$(modalPopup).modal('show');
//setPopupMaxHeight();
}
/*
* hide popup function.
*/
function hidePopup() {
$(modalPopupContent).html('');
$(modalPopup).modal('hide');
$('body').removeClass('modal-open').css('padding-right', '0px');
$('.modal-backdrop').remove();
}
/**
* Following function would execute
* when a user clicks on the list item
* initial mode and with out select mode.
*/
function InitiateViewOption() {
// $(location).attr('href', $(this).data("url"));
}
function htmlspecialchars(text) {
return jQuery('<div/>').text(text).html();
}
function loadRoles() {
isCloud = $("#role-table").data("cloud");
var loadingContent = $("#loading-content");
loadingContent.show();
var dataFilter = function (data) {
data = JSON.parse(data);
var objects = [];
var count = 0;
$(data.roles).each(function (index) {
objects.push(
{
name: htmlspecialchars(data.roles[index]),
DT_RowId: "role-" + htmlspecialchars(data.roles[index])
}
)
});
var json = {
"recordsTotal": data.count,
"recordsFiltered": data.count,
"data": objects
};
return JSON.stringify(json);
};
//noinspection JSUnusedLocalSymbols
var fnCreatedRow = function (nRow, aData, iDataIndex) {
$(nRow).attr('data-type', 'selectable');
};
//noinspection JSUnusedLocalSymbols
var columns = [
{
class: "remove-padding icon-only content-fill",
data: null,
defaultContent: "<div class='thumbnail icon'>" +
"<i class='square-element text fw fw-bookmark' style='font-size: 74px;'></i>" +
"</div>"
},
{
class: "",
data: "name",
render: function (name, type, row, meta) {
return '<h4>' + name.replace("devicemgt", ""); + '</h4>';
}
},
{
class: "text-right content-fill text-left-on-grid-view no-wrap",
data: null,
render: function (data, type, row, meta) {
var isCloud = false;
if ($('#is-cloud').length > 0) {
isCloud = true;
}
var innerhtml = '';
var editLink = '<a onclick="javascript:loadRoleBasedActionURL(\'edit\', \'' + data.name + '\')" ' +
'data-role="' + data.name + '" ' +
'data-click-event="edit-form" ' +
'class="btn padding-reduce-on-grid-view edit-role-link">' +
'<span class="fw-stack">' +
'<i class="fw fw-circle-outline fw-stack-2x"></i>' +
'<i class="fw fw-bookmark fw-stack-1x"></i>' +
'<span class="fw-stack fw-move-right fw-move-bottom">' +
'<i class="fw fw-circle fw-stack-2x fw-stroke fw-inverse"></i>' +
'<i class="fw fw-circle fw-stack-2x"></i><i class="fw fw-edit fw-stack-1x fw-inverse"></i>' +
'</span>' +
'</span>' +
'<span class="hidden-xs hidden-on-grid-view">Edit</span>' +
'</a>';
var editPermissionLink = '<a onclick="javascript:loadRoleBasedActionURL(\'edit-permission\', \'' + data.name + '\')" ' +
'data-role="' + data.name + '" ' +
'data-click-event="edit-form" ' +
'class="btn padding-reduce-on-grid-view edit-permission-link">' +
'<span class="fw-stack">' +
'<i class="fw fw-circle-outline fw-stack-2x"></i>' +
'<i class="fw fw-security-policy fw-stack-1x"></i>' +
'<span class="fw-stack fw-move-right fw-move-bottom">' +
'<i class="fw fw-circle fw-stack-2x fw-stroke fw-inverse"></i>' +
'<i class="fw fw-circle fw-stack-2x"></i><i class="fw fw-edit fw-stack-1x fw-inverse"></i>' +
'</span>' +
'</span>' +
'<span class="hidden-xs hidden-on-grid-view">Edit Permission</span>' +
'</a>';
var removeLink = '<a data-role="' + data.name + '" ' +
'data-click-event="remove-form" ' +
'class="btn padding-reduce-on-grid-view remove-role-link">' +
'<span class="fw-stack">' +
'<i class="fw fw-circle-outline fw-stack-2x"></i>' +
'<i class="fw fw-delete fw-stack-1x"></i>' +
'</span>' +
'<span class="hidden-xs hidden-on-grid-view">Remove</span>' +
'</a>';
if (!isCloud) {
innerhtml = editLink + editPermissionLink + removeLink;
}
return innerhtml;
}
}
];
var options = {
"placeholder": "Search By Role Name",
"searchKey": "filter"
};
var settings = {
"sorting": false
};
var roleApiUrl = '/api/device-mgt/v1.0/roles?user-store=all';
if (isCloud) {
roleApiUrl = '/api/device-mgt/v1.0/roles/filter/devicemgt?user-store=all';
}
$('#role-grid').datatables_extended_serverside_paging(settings, roleApiUrl, dataFilter, columns, fnCreatedRow, null, options);
loadingContent.hide();
}
/**
* Following click function would execute
* when a user clicks on "Remove" link
* on Role Listing page in WSO2 Devicemgt Console.
*/
$("#role-grid").on("click", ".remove-role-link", function () {
var role = $(this).data("role");
var userStore;
if (role.indexOf('/') > 0) {
userStore = role.substr(0, role.indexOf('/'));
role = role.substr(role.indexOf('/') + 1);
}
var removeRoleAPI = apiBasePath + "/roles/" + encodeURIComponent(role);
if (userStore) {
removeRoleAPI += "?user-store=" + encodeURIComponent(userStore);
}
modalDialog.header('Do you really want to remove this role ?');
modalDialog.footer('<div class="buttons"><a href="#" id="remove-role-yes-link" class="btn-operations">Remove</a>' +
'<a href="#" id="remove-role-cancel-link" class="btn-operations btn-default">Cancel</a></div>');
modalDialog.show();
$("a#remove-role-yes-link").click(function () {
invokerUtil.delete(
removeRoleAPI,
function () {
if (userStore) {
role = userStore + '/' + role;
}
$('[id="role-' + role + '"]').remove();
modalDialog.header('Done. Role was successfully removed.');
modalDialog.footer('<div class="buttons"><a href="#" id="remove-role-success-link" ' +
'class="btn-operations">Ok</a></div>');
$("a#remove-role-success-link").click(function () {
modalDialog.hide();
});
},
function () {
// $(modalPopupContent).html($('#remove-role-error-content').html());
modalDialog.header('An unexpected error occurred. Please try again later.');
modalDialog.footer('<div class="buttons"><a href="#" id="remove-role-error-link" ' +
'class="btn-operations">Ok</a></div>');
modalDialog.showAsError();
$("a#remove-role-error-link").click(function () {
modalDialog.hide();
});
}
);
});
$("a#remove-role-cancel-link").click(function () {
modalDialog.hide();
});
});
$(document).ready(function () {
loadRoles();
});

View File

@ -0,0 +1,56 @@
{{#each roles}}
<tr data-type="selectable" id="role-{{roleName}}">
<td class="remove-padding icon-only content-fill">
<div class="thumbnail icon">
<i class="square-element text fw fw-user"></i>
</div>
</td>
<td class="remove-padding-top">{{roleName}}</td>
<td class="text-right content-fill text-left-on-grid-view no-wrap">
{{#unequal adminRole roleName }}
{{#if canEdit}}
<a onclick="javascript:loadRoleBasedActionURL('edit', '{{roleName}}')" data-role="{{roleName}}"
data-click-event="edit-form"
class="btn padding-reduce-on-grid-view edit-role-link" title="Edit Role">
<span class="fw-stack fw-lg">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-user fw-stack-1x"></i>
<span class="fw-stack fw-move-right fw-move-bottom">
<i class="fw fw-circle fw-stack-2x fw-stroke fw-inverse"></i>
<i class="fw fw-circle fw-stack-2x"></i>
<i class="fw fw-edit fw-stack-1x fw-inverse"></i>
</span>
</span>
<span class="hidden-xs hidden-on-grid-view">Edit</span>
</a>
<a onclick="javascript:loadRoleBasedActionURL('edit-permission', '{{roleName}}')"
data-role="{{roleName}}"
data-click-event="edit-form" class="btn padding-reduce-on-grid-view edit-permission-link"
title="Edit Role Permissions">
<span class="fw-stack fw-lg">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-security-policy fw-stack-1x"></i>
<span class="fw-stack fw-move-right fw-move-bottom">
<i class="fw fw-circle fw-stack-2x fw-stroke fw-inverse"></i>
<i class="fw fw-circle fw-stack-2x"></i>
<i class="fw fw-edit fw-stack-1x fw-inverse"></i>
</span>
</span>
<span class="hidden-xs hidden-on-grid-view">Edit Permission</span>
</a>
{{/if}}
{{#if canRemove}}
<a data-role="{{roleName}}" data-click-event="remove-form"
class="btn padding-reduce-on-grid-view remove-role-link" title="Remove Role">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-delete fw-stack-1x"></i>
</span>
<span class="hidden-xs hidden-on-grid-view">Remove</span>
</a>
{{/if}}
{{/unequal}}
</td>
</tr>
{{/each}}

View File

@ -0,0 +1,120 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{unit "cdmf.unit.ui.title" pageTitle="Role Management"}}
{{unit "cdmf.unit.data-tables-extended"}}
{{unit "cdmf.unit.ui.modal"}}
{{#zone "breadcrumbs"}}
<li>
<a href="{{@app.context}}/">
<i class="icon fw fw-home"></i>
</a>
</li>
<li>
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/roles">
Roles
</a>
</li>
{{/zone}}
{{#zone "navbarActions"}}
{{#unless isCloud}}
<li>
<!--suppress HtmlUnknownTarget -->
<a href="{{@app.context}}/role/add" class="cu-btn">
<span class="icon fw-stack">
<i class="fw fw-add fw-stack-1x"></i>
<i class="fw fw-circle-outline fw-stack-2x"></i>
</span>
Add Role
</a>
</li>
{{/unless}}
{{/zone}}
{{#zone "content"}}
{{#if hasRoles}}
<div id="loading-content" class="col-centered">
{{#unless isCloud}}
{{#if removePermitted}}
<input type="hidden" id="can-remove" value="true"/>
{{/if}}
{{#if editPermitted}}
<input type="hidden" id="can-edit" value="true"/>
{{/if}}
{{/unless}}
{{#if isCloud}}
<input type="hidden" id="is-cloud" value="true"/>
{{/if}}
<i class="fw fw-settings fw-spin fw-2x"></i>
Loading roles . . .
<br>
</div>
<div id="role-table" data-cloud={{isCloud}} data-role={{adminRole}}>
<table class="table table-striped table-hover list-table display responsive nowrap data-table grid-view"
id="role-grid">
<thead>
<tr class="sort-row">
<th>By Role Name</th>
</tr>
<tr class="bulk-action-row">
<th colspan="3"></th>
</tr>
</thead>
<tbody id="ast-container" data-app-context="{{@app.context}}/"></tbody>
</table>
</div>
<div id="content-filter-types" style="display: none">
<div class="sort-title">Sort By</div>
<div class="sort-options">
<!--suppress HtmlUnknownTag -->
<th>By Role name</th>
</div>
</div>
{{else}}
<div id="user-created-msg" class="container col-centered wr-content">
<div class="wr-form">
<p class="page-sub-title">You Haven't created roles yet.</p>
<br>Please click <b>"Add A New Role"</b>, if you wish to add a role.
<hr/>
<a href="{{@app.context}}/role/add" class="cu-btn-inner">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-add fw-stack-1x"></i>
</span>
Add Role
</a>
</div>
</div>
{{/if}}
{{/zone}}
{{#zone "bottomJs"}}
<!--suppress HtmlUnknownTarget -->
<script id="role-listing" data-current-user="{{@user.username}}"
src="{{@page.publicUri}}/templates/role-listing.hbs" type="text/x-handlebars-template"></script>
{{js "js/role-listing.js"}}
{{/zone}}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
function onRequest(context) {
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
var deviceMgtProps = require("/app/modules/conf-reader/main.js")["conf"];
context["permissions"] = userModule.getUIPermissions();
if (userModule.isAuthorized("/permission/admin/device-mgt/roles/delete")) {
context["removePermitted"] = true;
}
if (userModule.isAuthorized("/permission/admin/device-mgt/roles/update")) {
context["editPermitted"] = true;
}
if (userModule.isAuthorized("/permission/admin/device-mgt/roles/remove")) {
context["removePermitted"] = true;
}
context["adminRole"] = deviceMgtProps["adminRole"];
context["isCloud"] = deviceMgtProps["isCloud"];
var roleCount = userModule.getRolesCount()
if (deviceMgtProps["isCloud"]) {
roleCount = userModule.getFilteredRoles("devicemgt").content.count;
}
if (roleCount > 0) {
context["hasRoles"] = true;
} else {
context["hasRoles"] = false;
}
return context;
}

View File

@ -0,0 +1,5 @@
{
"version": "1.0.0",
"uri": "/roles",
"layout": "cdmf.layout.default"
}

View File

@ -0,0 +1,63 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{#zone "title"}}{{! to override parent page title }}{{/zone}}
{{unit "cdmf.unit.ui.title" pageTitle="Login"}}
{{#zone "content"}}
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-4 col-sm-offset-3 col-md-offset-3 col-lg-offset-4">
<p class="page-sub-title">Login</p>
<hr />
{{#if message}}
<div class="alert alert-danger" style="padding-right: 15px;">
<i class="icon fw fw-warning"></i> {{message}}!
</div>
{{/if}}
<div class="panel-body">
<form id="signInForm" method="POST" action="{{loginActionUrl}}">
<div class="form-group">
<label for="username">Username *</label>
<input type="text" name="username" class="form-control" placeholder="Enter your username"
autofocus="autofocus" required="required" />
</div>
<div class="form-group">
<label for="password">Password *</label>
<input type="password" name="password" class="form-control" autocomplete="off"
placeholder="Enter your password" required="required" />
</div>
{{#if sessionDataKey}}
<input type="hidden" name="sessionDataKey" value="{{sessionDataKey}}" />
{{/if}}
{{#if referer}}
<input type="hidden" name="referer" value="{{referer}}" />
{{/if}}
<div class="wr-input-control wr-btn-grp">
<button class="wr-btn btn-download-agent">
Log in
</button>
<div id="register-link-wrapper" style="float: right; padding-top: 10px;">
<a href="{{@app.context}}/register" class="pull-right create-account">Create an account</a>
</div>
</div>
</form>
</div>
</div>
</div>
{{/zone}}

View File

@ -0,0 +1,32 @@
function onRequest(context) {
var authModuleConfigs = context.app.conf["authModule"];
var sessionDataKey = request.getParameter("sessionDataKey");
var authFailure = request.getParameter("authFailure");
//if sso enabled and sessionDataKey is empty redirect
var ssoConfigs = authModuleConfigs["sso"];
if (ssoConfigs && (ssoConfigs["enabled"].toString() == "true") && !sessionDataKey) {
// SSO is enabled in Auth module.
var redirectUri = context.app.context + "/uuf/login";
var queryString = request.getQueryString();
if (queryString && (queryString.length > 0)) {
redirectUri = redirectUri + "?" + queryString;
}
response.sendRedirect(encodeURI(redirectUri));
exit();
}
var viewModel = {};
var loginActionUrl = context.app.context + "/uuf/login";
if (sessionDataKey) {
loginActionUrl = "/commonauth";
}
if (authFailure) {
viewModel.message = "Login failed! Please recheck the username and password and try again.";
}
viewModel.sessionDataKey = sessionDataKey;
viewModel.loginActionUrl = loginActionUrl;
return viewModel;
}

View File

@ -0,0 +1,6 @@
{
"version": "1.0.0",
"layout": "uuf.layout.sign-in",
"uri": "/login",
"isAnonymous": true
}

View File

@ -0,0 +1,5 @@
{
"version": "1.0.0",
"uri": "/logout",
"extends": "uuf.page.sign-out"
}

View File

@ -0,0 +1,38 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{#zone "title"}}Error | {{@app.conf.appName}}{{/zone}}
{{#zone "breadcrumbs"}}
<li>
<a href="{{@app.context}}/">
<i class="icon fw fw-home"></i>
</a>
</li>
{{/zone}}
{{#zone "content"}}
<div class="message message-danger">
<h4><i class="icon fw fw-error"></i>An Error Occurred!</h4>
<div style="padding-left: 25px;">
<h5><b>HTTP Status : {{@page.params.status}}</b></h5>
<p style="white-space: pre-wrap;">{{@page.params.message}}</p>
</div>
</div>
{{/zone}}

View File

@ -0,0 +1,6 @@
{
"version": "1.0.0",
"uri": "/error/default",
"layout": "uuf.layout.default",
"isAnonymous": true
}

View File

@ -0,0 +1,24 @@
$(document).ready(function(){
$("#signInForm").validate({
rules: {
username: {
required: true,
minlength: 3
},
password: {
required: true,
minlength: 3
}
},
messages: {
username: {
required: "Please enter a username",
minlength: "Your username must consist of at least 3 characters"
},
password: {
required: "Please provide a password",
minlength: "Your password must be at least 3 characters long"
}
}
});
});

View File

@ -0,0 +1,63 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{#zone "title"}}Sign In | {{@app.conf.appName}}{{/zone}}
{{~#zone "content"}}
<div class="col-sm-7 col-md-4 center-block" style="float: none; margin-top: 10%;">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
{{#defineZone "signIn-title" scope="protected"}}Sign In to UUF Template App{{/defineZone}}
</h4>
</div>
<div class="panel-body">
{{#if message}}
<div id="_uuf_login-error-msg" class="alert alert-danger">
<i class="icon fw fw-warning"></i> {{message}}!
</div>
{{/if}}
<form id="signInForm" method="POST"
class="{{defineZone "signInForm-class" scope="protected"}}"
action="{{#defineZone "signInForm-action" scope="protected"}}{{@app.context}}/uuf/login{{/defineZone}}">
<div class="form-group">
<input type="text" name="username" class="form-control"
placeholder="User Name" required="required" autofocus="autofocus" />
</div>
<div class="form-group">
<input type="password" name="password" class="form-control" autocomplete="off"
placeholder="Password" required="required" />
</div>
{{#if referer}}
<input type="hidden" name="referer" value="{{referer}}" />
{{/if}}
<div class="form-group" style="padding-top: 10px;">
<input type="submit" name="signInBtn" class="btn btn-primary btn-block"
value="Sign In" />
</div>
{{defineZone "signInForm-below" scope="protected"}}
</form>
</div>
</div>
</div>
{{/zone}}
{{! sign-in form validation}}
{{~unit "uuf.unit.lib.form-validation"}}
{{~#zone "bottomJs"}}
{{~js "js/sign-in-validations.js"}}
{{/zone}}

View File

@ -0,0 +1,30 @@
function onRequest(context) {
var authModuleConfigs = context.app.conf["authModule"];
if (authModuleConfigs && (authModuleConfigs["enabled"].toString() == "true")) {
// Auth module is enabled.
if (context.user) {
// User is already logged in.
response.sendRedirect(context.app.context + "/");
exit();
} else {
// User is not logged in.
var ssoConfigs = authModuleConfigs["sso"];
if (ssoConfigs && (ssoConfigs["enabled"].toString() == "true")) {
// SSO is enabled in Auth module.
var redirectUri = context.app.context + "/uuf/login";
var queryString = request.getQueryString();
if (queryString && (queryString.length > 0)) {
redirectUri = redirectUri + "?" + queryString;
}
response.sendRedirect(encodeURI(redirectUri));
exit();
} else {
// Generic login process is enabled.
return {
message: request.getParameter("error"),
referer: request.getParameter("referer")
};
}
}
}
}

View File

@ -0,0 +1,6 @@
{
"version": "1.0.0",
"uri": "/signin",
"layout": "uuf.layout.sign-in",
"isAnonymous": true
}

View File

@ -0,0 +1,18 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{! This template won't be rendered. So nothing is here }}

View File

@ -0,0 +1,16 @@
function onRequest(context) {
var authModuleConfigs = context.app.conf["authModule"];
if (authModuleConfigs && (authModuleConfigs["enabled"].toString() == "true")) {
// Auth module is enabled.
if (context.user) {
// User is logged in.
response.sendRedirect(context.app.context + "/uuf/logout");
exit();
} else {
// User is already logged out.
response.sendRedirect(context.app.context + "/");
exit();
}
}
}

View File

@ -0,0 +1,5 @@
{
"version": "1.0.0",
"uri": "/signout",
"layout": "uuf.layout.sign-in"
}

View File

@ -0,0 +1,45 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{#zone "title"}}Sign In | {{@app.conf.appName}}{{/zone}}
{{unit "uuf.unit.theme"}}
{{unit "uuf.unit.header.logo"}}{{unit "uuf.unit.header"}}
{{unit "uuf.unit.footer"}}
{{#zone "content"}}
<div class="jumbotron">
<p>
You are now being redirected to Identity Server. If the redirection fails, please click
on the button below.
</p>
<div>
<form method="post" action="{{@page.params.identityProviderUrl}}">
<input type="hidden" name="SAMLRequest"
value="{{@page.params.encodedSAMLAuthRequest}}" />
<input type="hidden" name="RelayState" value="{{@page.params.relayState}}" />
<input type="hidden" name="SSOAuthSessionID" value="{{@page.params.sessionId}}" />
<button type="submit" class="btn btn-primary">Redirect manually</button>
</form>
</div>
</div>
{{/zone}}
{{#zone "bottomJs"}}
<script type="text/javascript">document.forms[0].submit();</script>
{{/zone}}

View File

@ -0,0 +1,6 @@
{
"version": "1.0.0",
"uri": "//",
"layout": "uuf.layout.sign-in",
"isAnonymous": true
}

View File

@ -0,0 +1,48 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{#zone "topCss"}}
{{css "css/daterangepicker.css"}}
{{/zone}}
<span id="device-type-details" data-devicetypes="{{deviceTypes}}"></span>
<div id="rangeSliderWrapper" class="pull-right col-lg-9">
<div id="dateRangePickerContainer">
<div class="btn-group" role="group">
<button id="hour-btn" type="button"
class="btn btn-default date-range">Hour
</button>
<button id="h12-btn" type="button"
class="btn btn-default date-range">12 Hours
</button>
<button id="h24-btn" type="button"
class="btn btn-default date-range">24 Hours
</button>
<button id="h48-btn" type="button"
class="btn btn-default date-range">48 Hours
</button>
<button id="date-range" type="button"
class="btn btn-default date-range last-child"
data-toggle="popup"
title="Click to set custom date range"></button>
</div>
</div>
</div>
{{#zone "bottomJs"}}
{{js "js/moment.js"}}
{{js "js/jquery.daterangepicker.js"}}
{{js "js/date-picker.js"}}
{{/zone}}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
function onRequest(context) {
var deviceTypes = context.unit.params.deviceTypes;
var deviceType = context.uriParams.deviceType;
var deviceTypesList = [];
if (deviceTypes) {
for (var i = 0; i < deviceTypes.length; i++) {
deviceTypesList.push(deviceTypes[i].type);
}
} else if (deviceType) {
deviceTypesList.push(deviceType);
}
return {"deviceTypes": stringify(deviceTypesList)};
}

View File

@ -0,0 +1,361 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
.date-picker {
width: 170px;
height: 25px;
padding: 0;
border: 0;
line-height: 25px;
padding-left: 10px;
font-size: 12px;
font-family: Arial;
font-weight: bold;
cursor: pointer;
color: #303030;
position: relative;
z-index: 2;
}
.date-picker-wrapper {
position: absolute;
z-index: 1;
border: 1px solid #bfbfbf;
background-color: #efefef;
width: 448px;
padding: 5px 12px;
font-size: 12px;
line-height: 20px;
color: #aaa;
font-family: Arial;
box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.5);
}
.date-picker-wrapper.single-date {
width: auto;
}
.date-picker-wrapper.no-shortcuts {
padding-bottom: 12px;
}
.date-picker-wrapper .footer {
display: none;
font-size: 11px;
padding-top: 3px;
}
.date-picker-wrapper b {
color: #666;
font-weight: 700;
}
.date-picker-wrapper a {
color: rgb(107, 180, 214);
text-decoration: underline;
}
.date-picker-wrapper .month-wrapper {
border: 1px solid #bfbfbf;
border-radius: 3px;
background-color: #fff;
padding: 5px;
cursor: default;
position: relative;
_overflow: hidden;
}
.date-picker-wrapper .month-wrapper table {
width: 190px;
float: left;
}
.date-picker-wrapper .month-wrapper table.month2 {
width: 190px;
float: right;
}
.date-picker-wrapper .month-wrapper table th,
.date-picker-wrapper .month-wrapper table td {
vertical-align: middle;
text-align: center;
line-height: 14px;
margin: 0px;
padding: 0px;
}
.date-picker-wrapper .month-wrapper table .day {
height: 19px;
line-height: 19px;
font-size: 12px;
margin-bottom: 1px;
color: #999;
cursor: default;
}
.date-picker-wrapper .month-wrapper table div.day.lastMonth,
.date-picker-wrapper .month-wrapper table div.day.nextMonth {
color: #999;
cursor: default;
}
.date-picker-wrapper .month-wrapper table .day.checked {
background-color: rgb(156, 219, 247);
}
.date-picker-wrapper .month-wrapper table .week-name {
height: 20px;
line-height: 20px;
font-weight: 100;
}
.date-picker-wrapper .month-wrapper table .day.has-tooltip {
cursor: help !important;
}
.date-picker-wrapper .month-wrapper table .day.toMonth.valid {
color: #333;
cursor: pointer;
}
.date-picker-wrapper .month-wrapper table .day.real-today {
background-color: rgb(255, 230, 132);
}
.date-picker-wrapper .month-wrapper table .day.real-today.checked {
background-color: rgb(112, 204, 213);
}
.date-picker-wrapper table .caption {
height: 40px;
}
.date-picker-wrapper table .caption .next,
.date-picker-wrapper table .caption .prev {
padding: 0 5px;
cursor: pointer;
}
.date-picker-wrapper table .caption .next:hover,
.date-picker-wrapper table .caption .prev:hover {
background-color: #ccc;
color: white;
}
.date-picker-wrapper .gap {
position: absolute;
display: none;
top: 0px;
left: 204px;
z-index: 1;
width: 15px;
height: 100%;
background-color: red;
font-size: 0;
line-height: 0;
}
.date-picker-wrapper .gap .gap-lines {
height: 100%;
overflow: hidden;
}
.date-picker-wrapper .gap .gap-line {
height: 15px;
width: 15px;
position: relative;
}
.date-picker-wrapper .gap .gap-line .gap-1 {
z-index: 1;
height: 0;
border-left: 8px solid white;
border-top: 8px solid #eee;
border-bottom: 8px solid #eee;
}
.date-picker-wrapper .gap .gap-line .gap-2 {
position: absolute;
right: 0;
top: 0px;
z-index: 2;
height: 0;
border-left: 8px solid transparent;
border-top: 8px solid white;
}
.date-picker-wrapper .gap .gap-line .gap-3 {
position: absolute;
right: 0;
top: 8px;
z-index: 2;
height: 0;
border-left: 8px solid transparent;
border-bottom: 8px solid white;
}
.date-picker-wrapper .gap .gap-top-mask {
width: 6px;
height: 1px;
position: absolute;
top: -1px;
left: 1px;
background-color: #eee;
z-index: 3;
}
.date-picker-wrapper .gap .gap-bottom-mask {
width: 6px;
height: 1px;
position: absolute;
bottom: -1px;
left: 7px;
background-color: #eee;
z-index: 3;
}
.date-picker-wrapper .selected-days {
display: none;
}
.date-picker-wrapper .drp_top-bar {
line-height: 40px;
height: 40px;
position: relative;
}
.date-picker-wrapper .drp_top-bar .error-top {
display: none;
}
.date-picker-wrapper .drp_top-bar .normal-top {
display: none;
}
.date-picker-wrapper .drp_top-bar .default-top {
display: block;
}
.date-picker-wrapper .drp_top-bar.error .default-top {
display: none;
}
.date-picker-wrapper .drp_top-bar.error .error-top {
display: block;
color: red;
}
.date-picker-wrapper .drp_top-bar.normal .default-top {
display: none;
}
.date-picker-wrapper .drp_top-bar.normal .normal-top {
display: block;
}
.date-picker-wrapper .drp_top-bar .apply-btn {
position: absolute;
right: 0px;
top: 6px;
padding: 3px 5px;
margin: 0;
font-size: 12px;
border-radius: 4px;
cursor: pointer;
color: #d9eef7;
border: solid 1px #0076a3;
background: #0095cd;
background: -webkit-gradient(linear, left top, left bottom, from(#00adee), to(#0078a5));
background: -moz-linear-gradient(top, #00adee, #0078a5);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00adee', endColorstr='#0078a5');
color: white;
}
.date-picker-wrapper .drp_top-bar .apply-btn.disabled {
pointer-events: none;
color: #606060;
border: solid 1px #b7b7b7;
background: #fff;
background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#ededed));
background: -moz-linear-gradient(top, #fff, #ededed);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed');
}
/*time styling*/
.time {
position: relative;
}
.time input[type=range] {
vertical-align: middle;
}
.time1, .time2 {
width: 180px;
padding: 0 5px;
text-align: center;
}
.time1 {
float: left;
}
.time2 {
float: right;
}
.hour, .minute {
text-align: left;
}
.hide {
display: none;
}
input.hour-range, input.minute-range {
width: 150px;
}
#dateRangePickerContainer .date-range, #dateRangePickerContainer .input-append {
background: none !important;
}
#date-range {
padding-right: 30px;
width: 300px;
height: 100%;
display: inline-block;
}
#dateRangePickerContainer {
float: right;
}
.date-range {
border: 1px solid #ccc;
}
#dateRangePickerContainer button.active {
background-color: #e6e6e6 !important;
}
#dateRangePickerContainer .btn-default:hover {
background-color: #b2b2b2;
border-color: #000000;
}

View File

@ -0,0 +1,125 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var fromDate, toDate, currentDay = new Date();
var startDate = new Date(currentDay.getTime() - (60 * 60 * 24 * 100));
var endDate = new Date(currentDay.getTime());
function initDate() {
currentDay = new Date();
}
var DateRange = convertDate(startDate) + " to " + convertDate(endDate);
$(document).ready(function () {
initDate();
var configObject = {
startOfWeek: 'monday',
separator: ' to ',
format: 'YYYY-MM-DD HH:mm',
autoClose: false,
time: {
enabled: true
},
shortcuts: 'hide',
endDate: currentDay,
maxDays: 2,
getValue: function () {
return this.value;
},
setValue: function (s) {
this.value = s;
}
};
$('#date-range').html(DateRange);
$('#date-range').dateRangePicker(configObject)
.bind('datepicker-apply', function (event, dateRange) {
$(this).addClass('active');
$(this).siblings().removeClass('active');
fromDate = dateRange.date1 != "Invalid Date" ? dateRange.date1.getTime() / 1000 : null;
toDate = dateRange.date2 != "Invalid Date" ? dateRange.date2.getTime() / 1000 : null;
drawGraph(fromDate, toDate);
}
);
setDateTime(currentDay.getTime() - 3600000, currentDay.getTime());
$('#hour-btn').addClass('active');
});
//hour
$('#hour-btn').on('click', function () {
initDate();
setDateTime(currentDay.getTime() - 3600000, currentDay.getTime());
});
//12 hours
$('#h12-btn').on('click', function () {
initDate();
setDateTime(currentDay.getTime() - (3600000 * 12), currentDay.getTime());
});
//24 hours
$('#h24-btn').on('click', function () {
initDate();
setDateTime(currentDay.getTime() - (3600000 * 24), currentDay.getTime());
});
//48 hours
$('#h48-btn').on('click', function () {
initDate();
setDateTime(currentDay.getTime() - (3600000 * 48), currentDay.getTime());
});
$('body').on('click', '.btn-group button', function (e) {
$(this).addClass('active');
$(this).siblings().removeClass('active');
});
function setDateTime(from, to) {
fromDate = from;
toDate = to;
startDate = new Date(from);
endDate = new Date(to);
DateRange = convertDate(startDate) + " to " + convertDate(endDate);
$('#date-range').html(DateRange);
var tzOffset = new Date().getTimezoneOffset() * 60 / 1000;
from += tzOffset;
to += tzOffset;
// Implement drawGraph_<device type name> method in your UI unit for analytics.
var deviceTypes = $("#device-type-details").data("devicetypes");
for (var i = 0; i < deviceTypes.length; i++){
try{
window["drawGraph_" + deviceTypes](parseInt(from / 1000), parseInt(to / 1000));
}catch(e){
}
try{
window["drawTable"](parseInt(from / 1000), parseInt(to / 1000));
}catch(e){
}
}
}
function convertDate(date) {
var month = date.getMonth() + 1;
var day = date.getDate();
var hour = date.getHours();
var minute = date.getMinutes();
return date.getFullYear() + '-' + (('' + month).length < 2 ? '0' : '') + month + '-' +
(('' + day).length < 2 ? '0' : '') + day + " " + (('' + hour).length < 2 ? '0' : '') +
hour + ":" + (('' + minute).length < 2 ? '0' : '') + minute;
}

View File

@ -0,0 +1,28 @@
{{!
Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
WSO2 Inc. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
}}
{{#zone "topCss"}}
{{~css "css/dataTables.bootstrap.css"}}
{{~css "css/dataTables.responsive.css"}}
{{/zone}}
{{#zone "bottomJs"}}
{{~js "js/jquery.dataTables.min.js"}}
{{~js "js/dataTables.bootstrap.js"}}
{{~js "js/dataTables.responsive.min.js"}}
{{~js "js/dataTables.extended.js"}}
{{~js "js/dataTables.extended.serversidepaging.js"}}
{{/zone}}

View File

@ -0,0 +1,372 @@
div.dataTables_length label {
font-weight: normal;
text-align: left;
white-space: nowrap;
}
div.dataTables_length select {
width: 75px;
display: inline-block;
}
div.dataTables_filter {
text-align: right;
}
div.dataTables_filter label {
font-weight: normal;
white-space: nowrap;
text-align: left;
}
div.dataTables_filter input {
margin-left: 0.5em;
display: inline-block;
width: auto;
}
div.dataTables_info {
padding-top: 8px;
white-space: nowrap;
}
div.dataTables_paginate {
margin: 0;
white-space: nowrap;
text-align: right;
}
div.dataTables_paginate ul.pagination {
margin: 2px 0;
white-space: nowrap;
}
@media screen and (max-width: 767px) {
div.dataTables_wrapper > div.row > div,
div.dataTables_length,
div.dataTables_filter,
div.dataTables_info,
div.dataTables_paginate {
text-align: center;
}
div.DTTT {
margin-bottom: 0.5em;
}
}
table.dataTable td,
table.dataTable th {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
table.dataTable {
clear: both;
margin-top: 6px !important;
margin-bottom: 6px !important;
max-width: none !important;
}
table.dataTable thead .sorting,
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc,
table.dataTable thead .sorting_asc_disabled,
table.dataTable thead .sorting_desc_disabled {
cursor: pointer;
position: relative;
}
table.dataTable thead .sorting:after,
table.dataTable thead .sorting_asc:after,
table.dataTable thead .sorting_desc:after {
position: absolute;
top: 8px;
right: 8px;
display: block;
font-family: 'Glyphicons Halflings';
opacity: 0.5;
}
table.dataTable thead .sorting:after {
opacity: 0.2;
content: "\e150"; /* sort */
}
table.dataTable thead .sorting_asc:after {
content: "\e155"; /* sort-by-attributes */
}
table.dataTable thead .sorting_desc:after {
content: "\e156"; /* sort-by-attributes-alt */
}
div.dataTables_scrollBody table.dataTable thead .sorting:after,
div.dataTables_scrollBody table.dataTable thead .sorting_asc:after,
div.dataTables_scrollBody table.dataTable thead .sorting_desc:after {
display: none;
}
table.dataTable thead .sorting_asc_disabled:after,
table.dataTable thead .sorting_desc_disabled:after {
color: #eee;
}
table.dataTable thead > tr > th {
padding-right: 30px;
}
table.dataTable th:active {
outline: none;
}
/* Condensed */
table.dataTable.table-condensed thead > tr > th {
padding-right: 20px;
}
table.dataTable.table-condensed thead .sorting:after,
table.dataTable.table-condensed thead .sorting_asc:after,
table.dataTable.table-condensed thead .sorting_desc:after {
top: 6px;
right: 6px;
}
/* Scrolling */
div.dataTables_scrollHead table {
margin-bottom: 0 !important;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
div.dataTables_scrollHead table thead tr:last-child th:first-child,
div.dataTables_scrollHead table thead tr:last-child td:first-child {
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
div.dataTables_scrollBody table {
border-top: none;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
div.dataTables_scrollBody tbody tr:first-child th,
div.dataTables_scrollBody tbody tr:first-child td {
border-top: none;
}
div.dataTables_scrollFoot table {
margin-top: 0 !important;
border-top: none;
}
/* Frustratingly the border-collapse:collapse used by Bootstrap makes the column
width calculations when using scrolling impossible to align columns. We have
to use separate
*/
table.table-bordered.dataTable {
border-collapse: separate !important;
}
table.table-bordered thead th,
table.table-bordered thead td {
border-left-width: 0;
border-top-width: 0;
}
table.table-bordered tbody th,
table.table-bordered tbody td {
border-left-width: 0;
border-bottom-width: 0;
}
table.table-bordered tfoot th,
table.table-bordered tfoot td {
border-left-width: 0;
border-bottom-width: 0;
}
table.table-bordered th:last-child,
table.table-bordered td:last-child {
border-right-width: 0;
}
div.dataTables_scrollHead table.table-bordered {
border-bottom-width: 0;
}
/*
* TableTools styles
*/
.table.dataTable tbody tr.active td,
.table.dataTable tbody tr.active th {
background-color: #08C;
color: white;
}
.table.dataTable tbody tr.active:hover td,
.table.dataTable tbody tr.active:hover th {
background-color: #0075b0 !important;
}
.table.dataTable tbody tr.active th > a,
.table.dataTable tbody tr.active td > a {
color: white;
}
.table-striped.dataTable tbody tr.active:nth-child(odd) td,
.table-striped.dataTable tbody tr.active:nth-child(odd) th {
background-color: #017ebc;
}
table.DTTT_selectable tbody tr {
cursor: pointer;
}
div.DTTT .btn:hover {
text-decoration: none !important;
}
ul.DTTT_dropdown.dropdown-menu {
z-index: 2003;
}
ul.DTTT_dropdown.dropdown-menu a {
color: #333 !important; /* needed only when demo_page.css is included */
}
ul.DTTT_dropdown.dropdown-menu li {
position: relative;
}
ul.DTTT_dropdown.dropdown-menu li:hover a {
background-color: #0088cc;
color: white !important;
}
div.DTTT_collection_background {
z-index: 2002;
}
/* TableTools information display */
div.DTTT_print_info {
position: fixed;
top: 50%;
left: 50%;
width: 400px;
height: 150px;
margin-left: -200px;
margin-top: -75px;
text-align: center;
color: #333;
padding: 10px 30px;
opacity: 0.95;
background-color: white;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 6px;
-webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.5);
box-shadow: 0 3px 7px rgba(0, 0, 0, 0.5);
}
div.DTTT_print_info h6 {
font-weight: normal;
font-size: 28px;
line-height: 28px;
margin: 1em;
}
div.DTTT_print_info p {
font-size: 14px;
line-height: 20px;
}
div.dataTables_processing {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 60px;
margin-left: -50%;
margin-top: -25px;
padding-top: 20px;
padding-bottom: 20px;
text-align: center;
font-size: 1.2em;
background-color: white;
background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0)), color-stop(25%, rgba(255,255,255,0.9)), color-stop(75%, rgba(255,255,255,0.9)), color-stop(100%, rgba(255,255,255,0)));
background: -webkit-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);
background: -moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);
background: -ms-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);
background: -o-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);
background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);
}
/*
* FixedColumns styles
*/
div.DTFC_LeftHeadWrapper table,
div.DTFC_LeftFootWrapper table,
div.DTFC_RightHeadWrapper table,
div.DTFC_RightFootWrapper table,
table.DTFC_Cloned tr.even {
background-color: white;
margin-bottom: 0;
}
div.DTFC_RightHeadWrapper table ,
div.DTFC_LeftHeadWrapper table {
border-bottom: none !important;
margin-bottom: 0 !important;
border-top-right-radius: 0 !important;
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
div.DTFC_RightHeadWrapper table thead tr:last-child th:first-child,
div.DTFC_RightHeadWrapper table thead tr:last-child td:first-child,
div.DTFC_LeftHeadWrapper table thead tr:last-child th:first-child,
div.DTFC_LeftHeadWrapper table thead tr:last-child td:first-child {
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
div.DTFC_RightBodyWrapper table,
div.DTFC_LeftBodyWrapper table {
border-top: none;
margin: 0 !important;
}
div.DTFC_RightBodyWrapper tbody tr:first-child th,
div.DTFC_RightBodyWrapper tbody tr:first-child td,
div.DTFC_LeftBodyWrapper tbody tr:first-child th,
div.DTFC_LeftBodyWrapper tbody tr:first-child td {
border-top: none;
}
div.DTFC_RightFootWrapper table,
div.DTFC_LeftFootWrapper table {
border-top: none;
margin-top: 0 !important;
}
div.DTFC_LeftBodyWrapper table.dataTable thead .sorting:after,
div.DTFC_LeftBodyWrapper table.dataTable thead .sorting_asc:after,
div.DTFC_LeftBodyWrapper table.dataTable thead .sorting_desc:after,
div.DTFC_RightBodyWrapper table.dataTable thead .sorting:after,
div.DTFC_RightBodyWrapper table.dataTable thead .sorting_asc:after,
div.DTFC_RightBodyWrapper table.dataTable thead .sorting_desc:after {
display: none;
}
/*
* FixedHeader styles
*/
div.FixedHeader_Cloned table {
margin: 0 !important
}

View File

@ -0,0 +1,106 @@
table.dataTable.dtr-inline.collapsed > tbody > tr > td:first-child,
table.dataTable.dtr-inline.collapsed > tbody > tr > th:first-child {
position: relative;
padding-left: 30px;
cursor: pointer;
}
table.dataTable.dtr-inline.collapsed > tbody > tr > td:first-child:before,
table.dataTable.dtr-inline.collapsed > tbody > tr > th:first-child:before {
top: 8px;
left: 4px;
height: 16px;
width: 16px;
display: block;
position: absolute;
color: white;
border: 2px solid white;
border-radius: 16px;
text-align: center;
line-height: 14px;
box-shadow: 0 0 3px #444;
box-sizing: content-box;
content: '+';
background-color: #31b131;
}
table.dataTable.dtr-inline.collapsed > tbody > tr > td:first-child.dataTables_empty:before,
table.dataTable.dtr-inline.collapsed > tbody > tr > th:first-child.dataTables_empty:before {
display: none;
}
table.dataTable.dtr-inline.collapsed > tbody > tr.parent > td:first-child:before,
table.dataTable.dtr-inline.collapsed > tbody > tr.parent > th:first-child:before {
content: '-';
background-color: #d33333;
}
table.dataTable.dtr-inline.collapsed > tbody > tr.child td:before {
display: none;
}
table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td:first-child,
table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th:first-child {
padding-left: 27px;
}
table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td:first-child:before,
table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th:first-child:before {
top: 5px;
left: 4px;
height: 14px;
width: 14px;
border-radius: 14px;
line-height: 12px;
}
table.dataTable.dtr-column > tbody > tr > td.control,
table.dataTable.dtr-column > tbody > tr > th.control {
position: relative;
cursor: pointer;
}
table.dataTable.dtr-column > tbody > tr > td.control:before,
table.dataTable.dtr-column > tbody > tr > th.control:before {
top: 50%;
left: 50%;
height: 16px;
width: 16px;
margin-top: -10px;
margin-left: -10px;
display: block;
position: absolute;
color: white;
border: 2px solid white;
border-radius: 16px;
text-align: center;
line-height: 14px;
box-shadow: 0 0 3px #444;
box-sizing: content-box;
content: '+';
background-color: #31b131;
}
table.dataTable.dtr-column > tbody > tr.parent td.control:before,
table.dataTable.dtr-column > tbody > tr.parent th.control:before {
content: '-';
background-color: #d33333;
}
table.dataTable > tbody > tr.child {
padding: 0.5em 1em;
}
table.dataTable > tbody > tr.child:hover {
background: transparent !important;
}
table.dataTable > tbody > tr.child ul {
display: inline-block;
list-style-type: none;
margin: 0;
padding: 0;
}
table.dataTable > tbody > tr.child ul li {
border-bottom: 1px solid #efefef;
padding: 0.5em 0;
}
table.dataTable > tbody > tr.child ul li:first-child {
padding-top: 0;
}
table.dataTable > tbody > tr.child ul li:last-child {
border-bottom: none;
}
table.dataTable > tbody > tr.child span.dtr-title {
display: inline-block;
min-width: 75px;
font-weight: bold;
}

View File

@ -0,0 +1,206 @@
/*! DataTables Bootstrap 3 integration
* ©2011-2014 SpryMedia Ltd - datatables.net/license
*/
/**
* DataTables integration for Bootstrap 3. This requires Bootstrap 3 and
* DataTables 1.10 or newer.
*
* This file sets the defaults and adds options to DataTables to style its
* controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap
* for further information.
*/
(function(window, document, undefined){
var factory = function( $, DataTable ) {
"use strict";
/* Set the defaults for DataTables initialisation */
$.extend( true, DataTable.defaults, {
dom:
"<'row'<'col-sm-6'l><'col-sm-6'f>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
renderer: 'bootstrap'
} );
/* Default class modification */
$.extend( DataTable.ext.classes, {
sWrapper: "dataTables_wrapper form-inline dt-bootstrap",
sFilterInput: "form-control input-sm",
sLengthSelect: "form-control input-sm"
} );
/* Bootstrap paging button renderer */
DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {
var api = new DataTable.Api( settings );
var classes = settings.oClasses;
var lang = settings.oLanguage.oPaginate;
var btnDisplay, btnClass, counter=0;
var attach = function( container, buttons ) {
var i, ien, node, button;
var clickHandler = function ( e ) {
e.preventDefault();
if ( !$(e.currentTarget).hasClass('disabled') ) {
api.page( e.data.action ).draw( false );
}
};
for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
button = buttons[i];
if ( $.isArray( button ) ) {
attach( container, button );
}
else {
btnDisplay = '';
btnClass = '';
switch ( button ) {
case 'ellipsis':
btnDisplay = '&hellip;';
btnClass = 'disabled';
break;
case 'first':
btnDisplay = lang.sFirst;
btnClass = button + (page > 0 ?
'' : ' disabled');
break;
case 'previous':
btnDisplay = lang.sPrevious;
btnClass = button + (page > 0 ?
'' : ' disabled');
break;
case 'next':
btnDisplay = lang.sNext;
btnClass = button + (page < pages-1 ?
'' : ' disabled');
break;
case 'last':
btnDisplay = lang.sLast;
btnClass = button + (page < pages-1 ?
'' : ' disabled');
break;
default:
btnDisplay = button + 1;
btnClass = page === button ?
'active' : '';
break;
}
if ( btnDisplay ) {
node = $('<li>', {
'class': classes.sPageButton+' '+btnClass,
'id': idx === 0 && typeof button === 'string' ?
settings.sTableId +'_'+ button :
null
} )
.append( $('<a>', {
'href': '#',
'aria-controls': settings.sTableId,
'data-dt-idx': counter,
'tabindex': settings.iTabIndex
} )
.html( btnDisplay )
)
.appendTo( container );
settings.oApi._fnBindAction(
node, {action: button}, clickHandler
);
counter++;
}
}
}
};
// IE9 throws an 'unknown error' if document.activeElement is used
// inside an iframe or frame.
var activeEl;
try {
// Because this approach is destroying and recreating the paging
// elements, focus is lost on the select button which is bad for
// accessibility. So we want to restore focus once the draw has
// completed
activeEl = $(document.activeElement).data('dt-idx');
}
catch (e) {}
attach(
$(host).empty().html('<ul class="pagination"/>').children('ul'),
buttons
);
if ( activeEl ) {
$(host).find( '[data-dt-idx='+activeEl+']' ).focus();
}
};
/*
* TableTools Bootstrap compatibility
* Required TableTools 2.1+
*/
if ( DataTable.TableTools ) {
// Set the classes that TableTools uses to something suitable for Bootstrap
$.extend( true, DataTable.TableTools.classes, {
"container": "DTTT btn-group",
"buttons": {
"normal": "btn btn-default",
"disabled": "disabled"
},
"collection": {
"container": "DTTT_dropdown dropdown-menu",
"buttons": {
"normal": "",
"disabled": "disabled"
}
},
"print": {
"info": "DTTT_print_info"
},
"select": {
"row": "active"
}
} );
// Have the collection use a bootstrap compatible drop down
$.extend( true, DataTable.TableTools.DEFAULTS.oTags, {
"collection": {
"container": "ul",
"button": "li",
"liner": "a"
}
} );
}
}; // /factory
// Define as an AMD module if possible
if ( typeof define === 'function' && define.amd ) {
define( ['jquery', 'datatables'], factory );
}
else if ( typeof exports === 'object' ) {
// Node/CommonJS
factory( require('jquery'), require('datatables') );
}
else if ( jQuery ) {
// Otherwise simply initialise as normal, stopping multiple evaluation
factory( jQuery, jQuery.fn.dataTable );
}
})(window, document);

View File

@ -0,0 +1,294 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* ========================================================================
* datatables_extended function
* ======================================================================== */
$.fn.datatables_extended = function(settings){
var elem = $(this);
// EMM related function
if (InitiateViewOption) {
$(document).on('click','.viewEnabledIcon',InitiateViewOption);
}
//--- End of EMM related codes
/*
* Work around for accessing settings params inside datatable functions
*/
if(settings != null && settings.sorting != null && settings.sorting != undefined && settings.sorting){
elem.addClass('sorting-enabled');
}else{
elem.addClass('sorting-disabled');
}
$(elem).DataTable(
$.extend({},{
bSortCellsTop: true,
responsive: false,
autoWidth: false,
dom:'<"dataTablesTop"' +
'f' +
'<"dataTables_toolbar">' +
'>' +
'rt' +
'<"dataTablesBottom"' +
'lip' +
'>',
language: {
searchPlaceholder: 'Search ...',
search: ''
},
initComplete: function(){
this.api().columns().every(function(){
var column = this;
var filterColumn = $('.filter-row th', elem);
/**
* Create & add select/text filters to each column
*/
if (filterColumn.eq(column.index()).hasClass('select-filter')) {
var select = $('<select class="form-control"><option value="">All</option></select>')
.appendTo(filterColumn.eq(column.index()).empty())
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex(
$(this).val()
);
column
//.search(val ? '^' + val + '$' : '', true, false)
.search(val ? val : '', true, false)
.draw();
if (filterColumn.eq(column.index()).hasClass('data-platform')){
if(val == null || val == undefined || val == ""){
$("#operation-bar").hide();
$( "#operation-guide" ).show();
$( "#operation-bar" ).addClass("hidden");
}else{
$( "#operation-guide" ).hide();
$( "#operation-bar" ).removeClass("hidden");
$("#operation-bar").show();
//TODO: Enable after adding iot operations bar
//loadOperationBar(val);
}
}
});
$(column).each(function () {
if ($(column.nodes()).attr('data-search')) {
var values = [];
column.nodes().unique().sort().each(function (d, j) {
var title = $(d).attr('data-display');
var value = $(d).attr('data-search');
if ($.inArray(value, values) < 0) {
values.push(value);
if (value !== undefined) {
select.append('<option value="' + value + '">' + title + '</option>')
}
}
});
}
else {
column.data().unique().sort().each(function (d, j) {
select.append('<option value="' + d + '">' + d + '</option>')
});
}
});
}
else if (filterColumn.eq(column.index()).hasClass('text-filter')) {
var title = filterColumn.eq(column.index()).attr('data-for');
$(filterColumn.eq(column.index()).empty()).html('<input type="text" class="form-control" placeholder="Search ' + title + '" />');
filterColumn.eq(column.index()).find('input').on('keyup change', function () {
column.search($(this).val()).draw();
if($('.dataTables_empty').length > 0) {
$('.bulk-action-row').addClass("hidden");
} else {
$('.bulk-action-row').removeClass("hidden");
}
});
}
});
/**
* search input default styles override
*/
var search_input = $(this).closest('.dataTables_wrapper').find('div[id$=_filter] input');
search_input.before('<i class="fw fw-search search-icon"></i>').removeClass('input-sm');
/**
* create sorting dropdown menu for list table advance operations
*/
var table = this;
if(table.hasClass('sorting-enabled')){
var dropdownmenu = $('<ul class="dropdown-menu arrow arrow-top-right dark sort-list add-margin-top-2x"><li class="dropdown-header">Sort by</li></ul>');
$('.sort-row th', elem).each(function () {
if (!$(this).hasClass('no-sort')) {
dropdownmenu.append('<li><a href="#' + $(this).html() + '" data-column="' + $(this).index() + '">' + $(this).html() + '</a></li>');
}
});
}
function getAdvanceToolBar(){
if (!table.hasClass('no-toolbar')) {
if (table.hasClass('sorting-enabled')) {
return '<ul class="nav nav-pills navbar-right remove-margin" role="tablist">' +
'<li><button data-click-event="toggle-selectable" class="btn btn-default btn-primary select-enable-btn">Select</li>' +
'<li><button data-click-event="toggle-selected" id="dt-select-all" class="btn btn-default btn-primary disabled">Select All</li>' +
'<li><button data-click-event="toggle-list-view" data-view="grid" class="btn btn-default"><i class="fw fw-grid"></i></button></li>' +
'<li><button data-click-event="toggle-list-view" data-view="list" class="btn btn-default"><i class="fw fw-list"></i></button></li>' +
'<li><button class="btn btn-default" data-toggle="dropdown"><i class="fw fw-sort"></i></button>' + dropdownmenu[0].outerHTML + '</li>' +
'</ul>'
} else {
return '<ul class="nav nav-pills navbar-right remove-margin" role="tablist">' +
'<li><button data-click-event="toggle-selectable" class="btn btn-default btn-primary select-enable-btn">Select</li>' +
'<li><button data-click-event="toggle-selected" id="dt-select-all" class="btn btn-default btn-primary disabled">Select All</li>' +
'<li><button data-click-event="toggle-list-view" data-view="grid" class="btn btn-default"><i class="fw fw-grid"></i></button></li>' +
'<li><button data-click-event="toggle-list-view" data-view="list" class="btn btn-default"><i class="fw fw-list"></i></button></li>' +
'</ul>'
}
}else{
return '';
}
}
/**
* append advance operations to list table toolbar
*/
$('.dataTable.list-table').closest('.dataTables_wrapper').find('.dataTablesTop .dataTables_toolbar').html(
getAdvanceToolBar()
);
/**
* sorting dropdown menu select function
*/
$('.dataTables_wrapper .sort-list li a').click(function() {
$(this).closest('li').siblings('li').find('a').removeClass('sorting_asc').removeClass('sorting_desc');
var thisTable = $(this).closest('.dataTables_wrapper').find('.dataTable').dataTable();
if (!($(this).hasClass('sorting_asc')) && !($(this).hasClass('sorting_desc'))) {
$(this).addClass('sorting_asc');
thisTable.fnSort( [ [$(this).attr('data-column'),'asc'] ] );
}
else if($(this).hasClass('sorting_asc')) {
$(this).switchClass('sorting_asc', 'sorting_desc');
thisTable.fnSort( [ [$(this).attr('data-column'),'desc'] ] );
}
else if($(this).hasClass('sorting_desc')) {
$(this).switchClass('sorting_desc', 'sorting_asc');
thisTable.fnSort( [ [$(this).attr('data-column'),'asc'] ] );
}
});
var rowSelectedClass = 'DTTT_selected selected';
/**
* Enable/Disable selection on rows
*/
$('.dataTables_wrapper [data-click-event=toggle-selectable]').click(function () {
var button = this,
thisTable = $(this).closest('.dataTables_wrapper').find('.dataTable').dataTable();
if ($(button).html() == 'Select') {
thisTable.addClass("table-selectable");
$(button).addClass("active").html('Cancel');
$(button).parent().next().children("button").removeClass("disabled");
// EMM related code
$(document).off('click','.viewEnabledIcon');
//--- End of EMM related codes
} else if ($(button).html() == 'Cancel'){
$('.bulk-action-row').addClass('hidden');
thisTable.removeClass("table-selectable");
$(button).addClass("active").html('Select');
$(button).parent().next().children().addClass("disabled");
// EMM related function
$(document).on('click','.viewEnabledIcon',InitiateViewOption);
//--- End of EMM related codes
}
});
/**
* select/deselect all rows function
*/
$('.dataTables_wrapper [data-click-event=toggle-selected]').click(function() {
var button = this,
thisTable = $(this).closest('.dataTables_wrapper').find('.dataTable').dataTable();
if(!$(button).hasClass('disabled')){
if($(button).html() == 'Select All') {
thisTable.api().rows().every(function () {
$(this.node()).addClass(rowSelectedClass);
$(button).html('Deselect All');
});
}
else if($(button).html() == 'Deselect All') {
thisTable.api().rows().every(function () {
$(this.node()).removeClass(rowSelectedClass);
$(button).html('Select All');
});
}
}
});
/**
* on row click select/deselect row function
*/
$('body').on('click', '[data-type=selectable]', function(){
var rowSelectedClass = 'DTTT_selected selected';
$(this).toggleClass(rowSelectedClass);
if ($('.table-selectable .DTTT_selected').length > 0) {
$('.bulk-action-row').removeClass('hidden');
} else {
$('.bulk-action-row').addClass('hidden');
}
var button = this,
thisTable = $(this).closest('.dataTables_wrapper').find('.dataTable').dataTable();
thisTable.api().rows().every(function () {
if(!$(this.node()).hasClass(rowSelectedClass)){
$(button).closest('.dataTables_wrapper').find('[data-click-event=toggle-selected]').html('Select All');
}
});
});
/**
* list table list/grid view toggle function
*/
var toggleButton = $('[data-click-event=toggle-list-view]');
toggleButton.click(function(){
if($(this).attr('data-view') == 'grid') {
$(this).closest('.dataTables_wrapper').find('.dataTable').addClass('grid-view');
//$(this).closest('li').hide();
//$(this).closest('li').siblings().show();
}
else {
$(this).closest('.dataTables_wrapper').find('.dataTable').removeClass('grid-view');
//$(this).closest('li').hide();
//$(this).closest('li').siblings().show();
}
});
}
},settings)
);
};

View File

@ -0,0 +1,360 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* =========================================================
* data-tables extended function (Server-side Pagination)
* =========================================================
*/
/**
* @namespace $
* The $ is just a function.
* It is actually an alias for the function called jQuery.
* For ex: $(this) means jQuery(this) and S.fn.x means jQuery.fn.x
*/
$.fn.datatables_extended_serverside_paging = function (settings, url, dataFilter,
columns, fnCreatedRow, fnDrawCallback, options) {
var elem = $(this);
// EMM related function
if (InitiateViewOption) {
$(document).on('click', '.viewEnabledIcon', InitiateViewOption);
}
//--- End of EMM related codes
/*
* Work around for accessing settings params inside datatable functions
*/
if (settings != null && settings.sorting != null && settings.sorting != undefined && settings.sorting) {
elem.addClass('sorting-enabled');
} else {
elem.addClass('sorting-disabled');
}
var deviceType;
var ownership;
var searching = true;
if (options) {
if (typeof options.searching !== 'undefined') {
searching = options.searching;
}
}
//--- End of EMM related codes
$(elem).DataTable(
$.extend({}, {
serverSide: true,
processing: false,
searching: searching,
ordering: false,
filter: false,
bSortCellsTop: true,
ajax: {
url: context + "/api/data-tables/invoker",
data: function (params) {
var i;
var searchParams = {};
for (i = 0; i < params.columns.length; i++) {
searchParams[params.columns[i].data] = encodeURIComponent(params.columns[i].search.value);
}
if (options) {
searchParams[options.searchKey] = encodeURIComponent(params.search.value);
}
params.filter = JSON.stringify(searchParams);
params.offset = params.start;
params.limit = params.length;
// if(params.search.value){
// params.filter = params.search.value;
// }
params.url = url;
//Remove this line to add url parameters which is included by data tables it self
delete params.columns;
},
dataFilter: dataFilter
},
columns: columns,
responsive: false,
autoWidth: false,
dom: '<"dataTablesTop"' +
'f' +
'<"dataTables_toolbar">' +
'>' +
'rt' +
'<"dataTablesBottom"' +
'lip' +
'>',
language: {
searchPlaceholder: options.placeholder,
search: ''
},
fnCreatedRow: fnCreatedRow,
"fnDrawCallback": fnDrawCallback,
initComplete: function () {
//loading tooltips
if (typeof $.fn.tooltip == 'function') {
$('[data-toggle="tooltip"]').tooltip();
} else {
console.warn('Warning : Dependency missing - Bootstrap Tooltip Library');
}
this.api().columns().every(function () {
var column = this;
var filterColumn = $('.filter-row th', elem);
/**
* Create & add select/text filters to each column
*/
if (filterColumn.eq(column.index()).hasClass('select-filter')) {
var select = $('<select class="form-control"><option value="">All</option></select>')
.appendTo(filterColumn.eq(column.index()).empty())
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex(
$(this).val()
);
column
//.search(val ? '^' + val + '$' : '', true, false)
.search(val ? val : '', true, false)
.draw();
if (filterColumn.eq(column.index()).hasClass('data-platform')) {
deviceType = val;
if (!deviceType || !ownership) {
$("#operation-bar").addClass("hidden");
$("#operation-guide").removeClass("hidden");
} else {
$("#operation-guide").addClass("hidden");
$("#operation-bar").removeClass("hidden");
//noinspection JSUnresolvedFunction
if (deviceType && ownership) {
loadOperationBar(deviceType, ownership, operationBarModeConstants.BULK);
}
}
}
if (filterColumn.eq(column.index()).hasClass('data-ownership')) {
ownership = val;
if (!deviceType || !ownership) {
$("#operation-bar").addClass("hidden");
$("#operation-guide").removeClass("hidden");
} else {
$("#operation-guide").addClass("hidden");
$("#operation-bar").removeClass("hidden");
//noinspection JSUnresolvedFunction
if (deviceType && ownership) {
loadOperationBar(deviceType, ownership, operationBarModeConstants.BULK);
}
}
}
});
$(column).each(function () {
if ($(column.nodes()).attr('data-search')) {
var titles = [];
column.nodes().unique().sort().each(function (d, j) {
var title = $(d).attr('data-display');
var searchVal = $(d).attr('data-search');
if ($.inArray(title, titles) < 0) {
titles.push(title);
if (title !== undefined) {
select.append('<option value="' + searchVal + '">' + title + '</option>')
}
}
});
} else {
column.data().unique().sort().each(function (d, j) {
select.append('<option value="' + d + '">' + d + '</option>')
});
}
});
} else if (filterColumn.eq(column.index()).hasClass('text-filter')) {
var title = filterColumn.eq(column.index()).attr('data-for');
$(filterColumn.eq(column.index()).empty()).html('<input type="text" class="form-control" placeholder="Search ' + title + '" />');
//noinspection SpellCheckingInspection
filterColumn.eq(column.index()).find('input').on('keyup', function () {
column.search($(this).val()).draw();
});
}
});
/**
* search input default styles override
*/
var search_input = $(this).closest('.dataTables_wrapper').find('div[id$=_filter] input');
search_input.before('<i class="fw fw-search search-icon"></i>').removeClass('input-sm');
/**
* create sorting dropdown menu for list table advance operations
*/
var table = this;
if (table.hasClass('sorting-enabled')) {
var dropdownmenu = $('<ul class="dropdown-menu arrow arrow-top-right dark sort-list add-margin-top-2x"><li class="dropdown-header">Sort by</li></ul>');
$('.sort-row th', elem).each(function () {
if (!$(this).hasClass('no-sort')) {
dropdownmenu.append('<li><a href="#' + $(this).html() + '" data-column="' + $(this).index() + '">' + $(this).html() + '</a></li>');
}
});
}
function getAdvanceToolBar() {
if (table.hasClass('sorting-enabled')) {
return '<ul class="nav nav-pills navbar-right remove-margin" role="tablist">' +
'<li><button data-click-event="toggle-selectable" class="btn btn-default btn-primary select-enable-btn">Select</li>' +
'<li><button data-click-event="toggle-selected" id="dt-select-all" class="btn btn-default btn-primary disabled">Select All</li>' +
'<li><button data-click-event="toggle-list-view" data-view="grid" class="btn btn-default"><i class="fw fw-grid"></i></button></li>' +
'<li><button data-click-event="toggle-list-view" data-view="list" class="btn btn-default"><i class="fw fw-list"></i></button></li>' +
'<li><button class="btn btn-default" data-toggle="dropdown"><i class="fw fw-sort"></i></button>' + dropdownmenu[0].outerHTML + '</li>' +
'</ul>'
} else {
return '<ul class="nav nav-pills navbar-right remove-margin" role="tablist">' +
'<li><button data-click-event="toggle-selectable" class="btn btn-default btn-primary select-enable-btn">Select</li>' +
'<li><button data-click-event="toggle-selected" id="dt-select-all" class="btn btn-default btn-primary disabled">Select All</li>' +
'<li><button data-click-event="toggle-list-view" data-view="grid" class="btn btn-default"><i class="fw fw-grid"></i></button></li>' +
'<li><button data-click-event="toggle-list-view" data-view="list" class="btn btn-default"><i class="fw fw-list"></i></button></li>' +
'</ul>'
}
;
}
/**
* append advance operations to list table toolbar
*/
$('.dataTable.list-table').closest('.dataTables_wrapper').find('.dataTablesTop .dataTables_toolbar').html(
getAdvanceToolBar()
);
/**
* sorting dropdown menu select function
*/
$('.dataTables_wrapper .sort-list li a').click(function () {
$(this).closest('li').siblings('li').find('a').removeClass('sorting_asc').removeClass('sorting_desc');
var thisTable = $(this).closest('.dataTables_wrapper').find('.dataTable').dataTable();
if (!($(this).hasClass('sorting_asc')) && !($(this).hasClass('sorting_desc'))) {
$(this).addClass('sorting_asc');
thisTable.fnSort([[$(this).attr('data-column'), 'asc']]);
}
else if ($(this).hasClass('sorting_asc')) {
$(this).switchClass('sorting_asc', 'sorting_desc');
thisTable.fnSort([[$(this).attr('data-column'), 'desc']]);
}
else if ($(this).hasClass('sorting_desc')) {
$(this).switchClass('sorting_desc', 'sorting_asc');
thisTable.fnSort([[$(this).attr('data-column'), 'asc']]);
}
});
var rowSelectedClass = 'DTTT_selected selected';
/**
* Enable/Disable selection on rows
*/
$('.dataTables_wrapper [data-click-event=toggle-selectable]').click(function () {
var button = this,
thisTable = $(this).closest('.dataTables_wrapper').find('.dataTable').dataTable();
if ($(button).html() == 'Select') {
thisTable.addClass("table-selectable");
$(button).addClass("active").html('Cancel');
$(button).parent().next().children("button").removeClass("disabled");
// EMM related code
$(document).off('click', '.viewEnabledIcon');
//--- End of EMM related codes
} else if ($(button).html() == 'Cancel') {
$('.bulk-action-row').addClass('hidden');
thisTable.removeClass("table-selectable");
$(button).addClass("active").html('Select');
$(button).parent().next().children().addClass("disabled");
$('.DTTT_selected.selected').removeClass(rowSelectedClass);
// EMM related function
$(document).on('click', '.viewEnabledIcon', InitiateViewOption);
//--- End of EMM related codes
}
});
/**
* select/deselect all rows function
*/
$('.dataTables_wrapper [data-click-event=toggle-selected]').click(function () {
var button = this,
thisTable = $(this).closest('.dataTables_wrapper').find('.dataTable').dataTable();
if (!$(button).hasClass('disabled')) {
if ($(button).html() == 'Select All') {
thisTable.api().rows().every(function () {
$(this.node()).addClass(rowSelectedClass);
$(button).html('Deselect All');
});
}
else if ($(button).html() == 'Deselect All') {
thisTable.api().rows().every(function () {
$(this.node()).removeClass(rowSelectedClass);
$(button).html('Select All');
});
}
}
});
/**
* on row click select/deselect row function
*/
$('body').on('click', '[data-type=selectable]', function () {
var rowSelectedClass = 'DTTT_selected selected';
$(this).toggleClass(rowSelectedClass);
if ($('.table-selectable .DTTT_selected').length > 0) {
$('.bulk-action-row').removeClass('hidden');
} else {
$('.bulk-action-row').addClass('hidden');
}
var button = this,
thisTable = $(this).closest('.dataTables_wrapper').find('.dataTable').dataTable();
thisTable.api().rows().every(function () {
if (!$(this.node()).hasClass(rowSelectedClass)) {
$(button).closest('.dataTables_wrapper').find('[data-click-event=toggle-selected]').html('Select All');
}
});
});
/**
* list table list/grid view toggle function
*/
var toggleButton = $('[data-click-event=toggle-list-view]');
toggleButton.click(function () {
if ($(this).attr('data-view') == 'grid') {
$(this).closest('.dataTables_wrapper').find('.dataTable').addClass('grid-view');
//$(this).closest('li').hide();
//$(this).closest('li').siblings().show();
}
else {
$(this).closest('.dataTables_wrapper').find('.dataTable').removeClass('grid-view');
//$(this).closest('li').hide();
//$(this).closest('li').siblings().show();
}
})
}
}, settings)
);
};

View File

@ -0,0 +1,19 @@
/*!
Responsive 1.0.6
2014-2015 SpryMedia Ltd - datatables.net/license
*/
(function(n,p){var o=function(e,k){var h=function(d,a){if(!k.versionCheck||!k.versionCheck("1.10.1"))throw"DataTables Responsive requires DataTables 1.10.1 or newer";this.s={dt:new k.Api(d),columns:[]};this.s.dt.settings()[0].responsive||(a&&"string"===typeof a.details&&(a.details={type:a.details}),this.c=e.extend(!0,{},h.defaults,k.defaults.responsive,a),d.responsive=this,this._constructor())};h.prototype={_constructor:function(){var d=this,a=this.s.dt;a.settings()[0]._responsive=this;e(n).on("resize.dtr orientationchange.dtr",
a.settings()[0].oApi._fnThrottle(function(){d._resize()}));a.on("destroy.dtr",function(){e(n).off("resize.dtr orientationchange.dtr draw.dtr")});this.c.breakpoints.sort(function(a,c){return a.width<c.width?1:a.width>c.width?-1:0});this._classLogic();this._resizeAuto();var c=this.c.details;c.type&&(d._detailsInit(),this._detailsVis(),a.on("column-visibility.dtr",function(){d._detailsVis()}),a.on("draw.dtr",function(){a.rows({page:"current"}).iterator("row",function(b,c){var f=a.row(c);if(f.child.isShown()){var i=
d.c.details.renderer(a,c);f.child(i,"child").show()}})}),e(a.table().node()).addClass("dtr-"+c.type));this._resize()},_columnsVisiblity:function(d){var a=this.s.dt,c=this.s.columns,b,g,f=e.map(c,function(a){return a.auto&&null===a.minWidth?!1:!0===a.auto?"-":-1!==e.inArray(d,a.includeIn)}),i=0;b=0;for(g=f.length;b<g;b++)!0===f[b]&&(i+=c[b].minWidth);b=a.settings()[0].oScroll;b=b.sY||b.sX?b.iBarWidth:0;a=a.table().container().offsetWidth-b-i;b=0;for(g=f.length;b<g;b++)c[b].control&&(a-=c[b].minWidth);
i=!1;b=0;for(g=f.length;b<g;b++)"-"===f[b]&&!c[b].control&&(i||0>a-c[b].minWidth?(i=!0,f[b]=!1):f[b]=!0,a-=c[b].minWidth);a=!1;b=0;for(g=c.length;b<g;b++)if(!c[b].control&&!c[b].never&&!f[b]){a=!0;break}b=0;for(g=c.length;b<g;b++)c[b].control&&(f[b]=a);-1===e.inArray(!0,f)&&(f[0]=!0);return f},_classLogic:function(){var d=this,a=this.c.breakpoints,c=this.s.dt.columns().eq(0).map(function(a){a=this.column(a).header().className;return{className:a,includeIn:[],auto:!1,control:!1,never:a.match(/\bnever\b/)?
!0:!1}}),b=function(a,b){var d=c[a].includeIn;-1===e.inArray(b,d)&&d.push(b)},g=function(f,g,e,j){if(e)if("max-"===e){j=d._find(g).width;g=0;for(e=a.length;g<e;g++)a[g].width<=j&&b(f,a[g].name)}else if("min-"===e){j=d._find(g).width;g=0;for(e=a.length;g<e;g++)a[g].width>=j&&b(f,a[g].name)}else{if("not-"===e){g=0;for(e=a.length;g<e;g++)-1===a[g].name.indexOf(j)&&b(f,a[g].name)}}else c[f].includeIn.push(g)};c.each(function(b,c){for(var d=b.className.split(" "),j=!1,h=0,k=d.length;h<k;h++){var l=e.trim(d[h]);
if("all"===l){j=!0;b.includeIn=e.map(a,function(a){return a.name});return}if("none"===l||"never"===l){j=!0;return}if("control"===l){j=!0;b.control=!0;return}e.each(a,function(a,b){var d=b.name.split("-"),e=l.match(RegExp("(min\\-|max\\-|not\\-)?("+d[0]+")(\\-[_a-zA-Z0-9])?"));e&&(j=!0,e[2]===d[0]&&e[3]==="-"+d[1]?g(c,b.name,e[1],e[2]+e[3]):e[2]===d[0]&&!e[3]&&g(c,b.name,e[1],e[2]))})}j||(b.auto=!0)});this.s.columns=c},_detailsInit:function(){var d=this,a=this.s.dt,c=this.c.details;"inline"===c.type&&
(c.target="td:first-child");var b=c.target;e(a.table().body()).on("click","string"===typeof b?b:"td",function(){if(e(a.table().node()).hasClass("collapsed")&&a.row(e(this).closest("tr")).length){if(typeof b==="number"){var c=b<0?a.columns().eq(0).length+b:b;if(a.cell(this).index().column!==c)return}c=a.row(e(this).closest("tr"));if(c.child.isShown()){c.child(false);e(c.node()).removeClass("parent")}else{var f=d.c.details.renderer(a,c[0]);c.child(f,"child").show();e(c.node()).addClass("parent")}}})},
_detailsVis:function(){var d=this,a=this.s.dt,c=a.columns().indexes().filter(function(b){var c=a.column(b);return c.visible()?null:e(c.header()).hasClass("never")?null:b}),b=!0;if(0===c.length||1===c.length&&this.s.columns[c[0]].control)b=!1;b?a.rows({page:"current"}).eq(0).each(function(b){b=a.row(b);if(b.child()){var c=d.c.details.renderer(a,b[0]);!1===c?b.child.hide():b.child(c,"child").show()}}):a.rows({page:"current"}).eq(0).each(function(b){a.row(b).child.hide()})},_find:function(d){for(var a=
this.c.breakpoints,c=0,b=a.length;c<b;c++)if(a[c].name===d)return a[c]},_resize:function(){var d=this.s.dt,a=e(n).width(),c=this.c.breakpoints,b=c[0].name,g=this.s.columns,f;for(f=c.length-1;0<=f;f--)if(a<=c[f].width){b=c[f].name;break}var i=this._columnsVisiblity(b),c=!1;f=0;for(a=g.length;f<a;f++)if(!1===i[f]&&!g[f].never){c=!0;break}e(d.table().node()).toggleClass("collapsed",c);d.columns().eq(0).each(function(a,b){d.column(a).visible(i[b])})},_resizeAuto:function(){var d=this.s.dt,a=this.s.columns;
if(this.c.auto&&-1!==e.inArray(!0,e.map(a,function(a){return a.auto}))){d.table().node();var c=d.table().node().cloneNode(!1),b=e(d.table().header().cloneNode(!1)).appendTo(c),g=e(d.table().body().cloneNode(!1)).appendTo(c);e(d.table().footer()).clone(!1).appendTo(c);d.rows({page:"current"}).indexes().flatten().each(function(a){var b=d.row(a).node().cloneNode(!0);d.columns(":hidden").flatten().length&&e(b).append(d.cells(a,":hidden").nodes().to$().clone());e(b).appendTo(g)});var f=d.columns().header().to$().clone(!1);
e("<tr/>").append(f).appendTo(b);"inline"===this.c.details.type&&e(c).addClass("dtr-inline collapsed");c=e("<div/>").css({width:1,height:1,overflow:"hidden"}).append(c);c.find("th.never, td.never").remove();c.insertBefore(d.table().node());d.columns().eq(0).each(function(b){a[b].minWidth=f[b].offsetWidth||0});c.remove()}}};h.breakpoints=[{name:"desktop",width:Infinity},{name:"tablet-l",width:1024},{name:"tablet-p",width:768},{name:"mobile-l",width:480},{name:"mobile-p",width:320}];h.defaults={breakpoints:h.breakpoints,
auto:!0,details:{renderer:function(d,a){var c=d.cells(a,":hidden").eq(0).map(function(a){var c=e(d.column(a.column).header()),a=d.cell(a).index();if(c.hasClass("control")||c.hasClass("never"))return"";var f=d.settings()[0],f=f.oApi._fnGetCellData(f,a.row,a.column,"display");(c=c.text())&&(c+=":");return'<li data-dtr-index="'+a.column+'"><span class="dtr-title">'+c+'</span> <span class="dtr-data">'+f+"</span></li>"}).toArray().join("");return c?e('<ul data-dtr-index="'+a+'"/>').append(c):!1},target:0,
type:"inline"}};var m=e.fn.dataTable.Api;m.register("responsive()",function(){return this});m.register("responsive.index()",function(d){d=e(d);return{column:d.data("dtr-index"),row:d.parent().data("dtr-index")}});m.register("responsive.rebuild()",function(){return this.iterator("table",function(d){d._responsive&&d._responsive._classLogic()})});m.register("responsive.recalc()",function(){return this.iterator("table",function(d){d._responsive&&(d._responsive._resizeAuto(),d._responsive._resize())})});
h.version="1.0.6";e.fn.dataTable.Responsive=h;e.fn.DataTable.Responsive=h;e(p).on("init.dt.dtr",function(d,a){if("dt"===d.namespace&&(e(a.nTable).hasClass("responsive")||e(a.nTable).hasClass("dt-responsive")||a.oInit.responsive||k.defaults.responsive)){var c=a.oInit.responsive;!1!==c&&new h(a,e.isPlainObject(c)?c:{})}});return h};"function"===typeof define&&define.amd?define(["jquery","datatables"],o):"object"===typeof exports?o(require("jquery"),require("datatables")):jQuery&&!jQuery.fn.dataTable.Responsive&&
o(jQuery,jQuery.fn.dataTable)})(window,document);

View File

@ -0,0 +1,163 @@
/*!
DataTables 1.10.9
©2008-2015 SpryMedia Ltd - datatables.net/license
*/
(function(Fa,T,k){var S=function(h){function X(a){var b,c,d={};h.each(a,function(e){if((b=e.match(/^([^A-Z]+?)([A-Z])/))&&-1!=="a aa ai ao as b fn i m o s ".indexOf(b[1]+" "))c=e.replace(b[0],b[2].toLowerCase()),d[c]=e,"o"===b[1]&&X(a[e])});a._hungarianMap=d}function I(a,b,c){a._hungarianMap||X(a);var d;h.each(b,function(e){d=a._hungarianMap[e];if(d!==k&&(c||b[d]===k))"o"===d.charAt(0)?(b[d]||(b[d]={}),h.extend(!0,b[d],b[e]),I(a[d],b[d],c)):b[d]=b[e]})}function S(a){var b=m.defaults.oLanguage,c=a.sZeroRecords;
!a.sEmptyTable&&(c&&"No data available in table"===b.sEmptyTable)&&F(a,a,"sZeroRecords","sEmptyTable");!a.sLoadingRecords&&(c&&"Loading..."===b.sLoadingRecords)&&F(a,a,"sZeroRecords","sLoadingRecords");a.sInfoThousands&&(a.sThousands=a.sInfoThousands);(a=a.sDecimal)&&cb(a)}function db(a){A(a,"ordering","bSort");A(a,"orderMulti","bSortMulti");A(a,"orderClasses","bSortClasses");A(a,"orderCellsTop","bSortCellsTop");A(a,"order","aaSorting");A(a,"orderFixed","aaSortingFixed");A(a,"paging","bPaginate");
A(a,"pagingType","sPaginationType");A(a,"pageLength","iDisplayLength");A(a,"searching","bFilter");"boolean"===typeof a.sScrollX&&(a.sScrollX=a.sScrollX?"100%":"");if(a=a.aoSearchCols)for(var b=0,c=a.length;b<c;b++)a[b]&&I(m.models.oSearch,a[b])}function eb(a){A(a,"orderable","bSortable");A(a,"orderData","aDataSort");A(a,"orderSequence","asSorting");A(a,"orderDataType","sortDataType");var b=a.aDataSort;b&&!h.isArray(b)&&(a.aDataSort=[b])}function fb(a){if(!m.__browser){var b={};m.__browser=b;var c=
h("<div/>").css({position:"fixed",top:0,left:0,height:1,width:1,overflow:"hidden"}).append(h("<div/>").css({position:"absolute",top:1,left:1,width:100,overflow:"scroll"}).append(h("<div/>").css({width:"100%",height:10}))).appendTo("body"),d=c.children(),e=d.children();b.barWidth=d[0].offsetWidth-d[0].clientWidth;b.bScrollOversize=100===e[0].offsetWidth&&100!==d[0].clientWidth;b.bScrollbarLeft=1!==Math.round(e.offset().left);b.bBounding=c[0].getBoundingClientRect().width?!0:!1;c.remove()}h.extend(a.oBrowser,
m.__browser);a.oScroll.iBarWidth=m.__browser.barWidth}function gb(a,b,c,d,e,f){var g,i=!1;c!==k&&(g=c,i=!0);for(;d!==e;)a.hasOwnProperty(d)&&(g=i?b(g,a[d],d,a):a[d],i=!0,d+=f);return g}function Ga(a,b){var c=m.defaults.column,d=a.aoColumns.length,c=h.extend({},m.models.oColumn,c,{nTh:b?b:T.createElement("th"),sTitle:c.sTitle?c.sTitle:b?b.innerHTML:"",aDataSort:c.aDataSort?c.aDataSort:[d],mData:c.mData?c.mData:d,idx:d});a.aoColumns.push(c);c=a.aoPreSearchCols;c[d]=h.extend({},m.models.oSearch,c[d]);
la(a,d,h(b).data())}function la(a,b,c){var b=a.aoColumns[b],d=a.oClasses,e=h(b.nTh);if(!b.sWidthOrig){b.sWidthOrig=e.attr("width")||null;var f=(e.attr("style")||"").match(/width:\s*(\d+[pxem%]+)/);f&&(b.sWidthOrig=f[1])}c!==k&&null!==c&&(eb(c),I(m.defaults.column,c),c.mDataProp!==k&&!c.mData&&(c.mData=c.mDataProp),c.sType&&(b._sManualType=c.sType),c.className&&!c.sClass&&(c.sClass=c.className),h.extend(b,c),F(b,c,"sWidth","sWidthOrig"),c.iDataSort!==k&&(b.aDataSort=[c.iDataSort]),F(b,c,"aDataSort"));
var g=b.mData,i=P(g),j=b.mRender?P(b.mRender):null,c=function(a){return"string"===typeof a&&-1!==a.indexOf("@")};b._bAttrSrc=h.isPlainObject(g)&&(c(g.sort)||c(g.type)||c(g.filter));b.fnGetData=function(a,b,c){var d=i(a,b,k,c);return j&&b?j(d,b,a,c):d};b.fnSetData=function(a,b,c){return Q(g)(a,b,c)};"number"!==typeof g&&(a._rowReadObject=!0);a.oFeatures.bSort||(b.bSortable=!1,e.addClass(d.sSortableNone));a=-1!==h.inArray("asc",b.asSorting);c=-1!==h.inArray("desc",b.asSorting);!b.bSortable||!a&&!c?
(b.sSortingClass=d.sSortableNone,b.sSortingClassJUI=""):a&&!c?(b.sSortingClass=d.sSortableAsc,b.sSortingClassJUI=d.sSortJUIAscAllowed):!a&&c?(b.sSortingClass=d.sSortableDesc,b.sSortingClassJUI=d.sSortJUIDescAllowed):(b.sSortingClass=d.sSortable,b.sSortingClassJUI=d.sSortJUI)}function Y(a){if(!1!==a.oFeatures.bAutoWidth){var b=a.aoColumns;Ha(a);for(var c=0,d=b.length;c<d;c++)b[c].nTh.style.width=b[c].sWidth}b=a.oScroll;(""!==b.sY||""!==b.sX)&&Z(a);w(a,null,"column-sizing",[a])}function $(a,b){var c=
aa(a,"bVisible");return"number"===typeof c[b]?c[b]:null}function ba(a,b){var c=aa(a,"bVisible"),c=h.inArray(b,c);return-1!==c?c:null}function ca(a){return aa(a,"bVisible").length}function aa(a,b){var c=[];h.map(a.aoColumns,function(a,e){a[b]&&c.push(e)});return c}function Ia(a){var b=a.aoColumns,c=a.aoData,d=m.ext.type.detect,e,f,g,i,j,h,l,r,q;e=0;for(f=b.length;e<f;e++)if(l=b[e],q=[],!l.sType&&l._sManualType)l.sType=l._sManualType;else if(!l.sType){g=0;for(i=d.length;g<i;g++){j=0;for(h=c.length;j<
h;j++){q[j]===k&&(q[j]=B(a,j,e,"type"));r=d[g](q[j],a);if(!r&&g!==d.length-1)break;if("html"===r)break}if(r){l.sType=r;break}}l.sType||(l.sType="string")}}function hb(a,b,c,d){var e,f,g,i,j,n,l=a.aoColumns;if(b)for(e=b.length-1;0<=e;e--){n=b[e];var r=n.targets!==k?n.targets:n.aTargets;h.isArray(r)||(r=[r]);f=0;for(g=r.length;f<g;f++)if("number"===typeof r[f]&&0<=r[f]){for(;l.length<=r[f];)Ga(a);d(r[f],n)}else if("number"===typeof r[f]&&0>r[f])d(l.length+r[f],n);else if("string"===typeof r[f]){i=0;
for(j=l.length;i<j;i++)("_all"==r[f]||h(l[i].nTh).hasClass(r[f]))&&d(i,n)}}if(c){e=0;for(a=c.length;e<a;e++)d(e,c[e])}}function L(a,b,c,d){var e=a.aoData.length,f=h.extend(!0,{},m.models.oRow,{src:c?"dom":"data",idx:e});f._aData=b;a.aoData.push(f);for(var g=a.aoColumns,i=0,j=g.length;i<j;i++)g[i].sType=null;a.aiDisplayMaster.push(e);b=a.rowIdFn(b);b!==k&&(a.aIds[b]=f);(c||!a.oFeatures.bDeferRender)&&Ja(a,e,c,d);return e}function ma(a,b){var c;b instanceof h||(b=h(b));return b.map(function(b,e){c=
Ka(a,e);return L(a,c.data,e,c.cells)})}function B(a,b,c,d){var e=a.iDraw,f=a.aoColumns[c],g=a.aoData[b]._aData,i=f.sDefaultContent,c=f.fnGetData(g,d,{settings:a,row:b,col:c});if(c===k)return a.iDrawError!=e&&null===i&&(J(a,0,"Requested unknown parameter "+("function"==typeof f.mData?"{function}":"'"+f.mData+"'")+" for row "+b,4),a.iDrawError=e),i;if((c===g||null===c)&&null!==i)c=i;else if("function"===typeof c)return c.call(g);return null===c&&"display"==d?"":c}function ib(a,b,c,d){a.aoColumns[c].fnSetData(a.aoData[b]._aData,
d,{settings:a,row:b,col:c})}function La(a){return h.map(a.match(/(\\.|[^\.])+/g)||[""],function(a){return a.replace(/\\./g,".")})}function P(a){if(h.isPlainObject(a)){var b={};h.each(a,function(a,c){c&&(b[a]=P(c))});return function(a,c,f,g){var i=b[c]||b._;return i!==k?i(a,c,f,g):a}}if(null===a)return function(a){return a};if("function"===typeof a)return function(b,c,f,g){return a(b,c,f,g)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var c=function(a,b,
f){var g,i;if(""!==f){i=La(f);for(var j=0,n=i.length;j<n;j++){f=i[j].match(da);g=i[j].match(U);if(f){i[j]=i[j].replace(da,"");""!==i[j]&&(a=a[i[j]]);g=[];i.splice(0,j+1);i=i.join(".");if(h.isArray(a)){j=0;for(n=a.length;j<n;j++)g.push(c(a[j],b,i))}a=f[0].substring(1,f[0].length-1);a=""===a?g:g.join(a);break}else if(g){i[j]=i[j].replace(U,"");a=a[i[j]]();continue}if(null===a||a[i[j]]===k)return k;a=a[i[j]]}}return a};return function(b,e){return c(b,e,a)}}return function(b){return b[a]}}function Q(a){if(h.isPlainObject(a))return Q(a._);
if(null===a)return function(){};if("function"===typeof a)return function(b,d,e){a(b,"set",d,e)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var b=function(a,d,e){var e=La(e),f;f=e[e.length-1];for(var g,i,j=0,n=e.length-1;j<n;j++){g=e[j].match(da);i=e[j].match(U);if(g){e[j]=e[j].replace(da,"");a[e[j]]=[];f=e.slice();f.splice(0,j+1);g=f.join(".");if(h.isArray(d)){i=0;for(n=d.length;i<n;i++)f={},b(f,d[i],g),a[e[j]].push(f)}else a[e[j]]=d;return}i&&(e[j]=e[j].replace(U,
""),a=a[e[j]](d));if(null===a[e[j]]||a[e[j]]===k)a[e[j]]={};a=a[e[j]]}if(f.match(U))a[f.replace(U,"")](d);else a[f.replace(da,"")]=d};return function(c,d){return b(c,d,a)}}return function(b,d){b[a]=d}}function Ma(a){return D(a.aoData,"_aData")}function na(a){a.aoData.length=0;a.aiDisplayMaster.length=0;a.aiDisplay.length=0;a.aIds={}}function oa(a,b,c){for(var d=-1,e=0,f=a.length;e<f;e++)a[e]==b?d=e:a[e]>b&&a[e]--; -1!=d&&c===k&&a.splice(d,1)}function ea(a,b,c,d){var e=a.aoData[b],f,g=function(c,d){for(;c.childNodes.length;)c.removeChild(c.firstChild);
c.innerHTML=B(a,b,d,"display")};if("dom"===c||(!c||"auto"===c)&&"dom"===e.src)e._aData=Ka(a,e,d,d===k?k:e._aData).data;else{var i=e.anCells;if(i)if(d!==k)g(i[d],d);else{c=0;for(f=i.length;c<f;c++)g(i[c],c)}}e._aSortData=null;e._aFilterData=null;g=a.aoColumns;if(d!==k)g[d].sType=null;else{c=0;for(f=g.length;c<f;c++)g[c].sType=null;Na(a,e)}}function Ka(a,b,c,d){var e=[],f=b.firstChild,g,i,j=0,n,l=a.aoColumns,r=a._rowReadObject,d=d!==k?d:r?{}:[],q=function(a,b){if("string"===typeof a){var c=a.indexOf("@");
-1!==c&&(c=a.substring(c+1),Q(a)(d,b.getAttribute(c)))}},jb=function(a){if(c===k||c===j)i=l[j],n=h.trim(a.innerHTML),i&&i._bAttrSrc?(Q(i.mData._)(d,n),q(i.mData.sort,a),q(i.mData.type,a),q(i.mData.filter,a)):r?(i._setter||(i._setter=Q(i.mData)),i._setter(d,n)):d[j]=n;j++};if(f)for(;f;){g=f.nodeName.toUpperCase();if("TD"==g||"TH"==g)jb(f),e.push(f);f=f.nextSibling}else{e=b.anCells;g=0;for(var o=e.length;g<o;g++)jb(e[g])}if(b=f?b:b.nTr)(b=b.getAttribute("id"))&&Q(a.rowId)(d,b);return{data:d,cells:e}}
function Ja(a,b,c,d){var e=a.aoData[b],f=e._aData,g=[],i,j,h,l,r;if(null===e.nTr){i=c||T.createElement("tr");e.nTr=i;e.anCells=g;i._DT_RowIndex=b;Na(a,e);l=0;for(r=a.aoColumns.length;l<r;l++){h=a.aoColumns[l];j=c?d[l]:T.createElement(h.sCellType);g.push(j);if(!c||h.mRender||h.mData!==l)j.innerHTML=B(a,b,l,"display");h.sClass&&(j.className+=" "+h.sClass);h.bVisible&&!c?i.appendChild(j):!h.bVisible&&c&&j.parentNode.removeChild(j);h.fnCreatedCell&&h.fnCreatedCell.call(a.oInstance,j,B(a,b,l),f,b,l)}w(a,
"aoRowCreatedCallback",null,[i,f,b])}e.nTr.setAttribute("role","row")}function Na(a,b){var c=b.nTr,d=b._aData;if(c){var e=a.rowIdFn(d);e&&(c.id=e);d.DT_RowClass&&(e=d.DT_RowClass.split(" "),b.__rowc=b.__rowc?pa(b.__rowc.concat(e)):e,h(c).removeClass(b.__rowc.join(" ")).addClass(d.DT_RowClass));d.DT_RowAttr&&h(c).attr(d.DT_RowAttr);d.DT_RowData&&h(c).data(d.DT_RowData)}}function kb(a){var b,c,d,e,f,g=a.nTHead,i=a.nTFoot,j=0===h("th, td",g).length,n=a.oClasses,l=a.aoColumns;j&&(e=h("<tr/>").appendTo(g));
b=0;for(c=l.length;b<c;b++)f=l[b],d=h(f.nTh).addClass(f.sClass),j&&d.appendTo(e),a.oFeatures.bSort&&(d.addClass(f.sSortingClass),!1!==f.bSortable&&(d.attr("tabindex",a.iTabIndex).attr("aria-controls",a.sTableId),Oa(a,f.nTh,b))),f.sTitle!=d[0].innerHTML&&d.html(f.sTitle),Pa(a,"header")(a,d,f,n);j&&fa(a.aoHeader,g);h(g).find(">tr").attr("role","row");h(g).find(">tr>th, >tr>td").addClass(n.sHeaderTH);h(i).find(">tr>th, >tr>td").addClass(n.sFooterTH);if(null!==i){a=a.aoFooter[0];b=0;for(c=a.length;b<
c;b++)f=l[b],f.nTf=a[b].cell,f.sClass&&h(f.nTf).addClass(f.sClass)}}function ga(a,b,c){var d,e,f,g=[],i=[],j=a.aoColumns.length,n;if(b){c===k&&(c=!1);d=0;for(e=b.length;d<e;d++){g[d]=b[d].slice();g[d].nTr=b[d].nTr;for(f=j-1;0<=f;f--)!a.aoColumns[f].bVisible&&!c&&g[d].splice(f,1);i.push([])}d=0;for(e=g.length;d<e;d++){if(a=g[d].nTr)for(;f=a.firstChild;)a.removeChild(f);f=0;for(b=g[d].length;f<b;f++)if(n=j=1,i[d][f]===k){a.appendChild(g[d][f].cell);for(i[d][f]=1;g[d+j]!==k&&g[d][f].cell==g[d+j][f].cell;)i[d+
j][f]=1,j++;for(;g[d][f+n]!==k&&g[d][f].cell==g[d][f+n].cell;){for(c=0;c<j;c++)i[d+c][f+n]=1;n++}h(g[d][f].cell).attr("rowspan",j).attr("colspan",n)}}}}function M(a){var b=w(a,"aoPreDrawCallback","preDraw",[a]);if(-1!==h.inArray(!1,b))C(a,!1);else{var b=[],c=0,d=a.asStripeClasses,e=d.length,f=a.oLanguage,g=a.iInitDisplayStart,i="ssp"==y(a),j=a.aiDisplay;a.bDrawing=!0;g!==k&&-1!==g&&(a._iDisplayStart=i?g:g>=a.fnRecordsDisplay()?0:g,a.iInitDisplayStart=-1);var g=a._iDisplayStart,n=a.fnDisplayEnd();
if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++,C(a,!1);else if(i){if(!a.bDestroying&&!lb(a))return}else a.iDraw++;if(0!==j.length){f=i?a.aoData.length:n;for(i=i?0:g;i<f;i++){var l=j[i],r=a.aoData[l];null===r.nTr&&Ja(a,l);l=r.nTr;if(0!==e){var q=d[c%e];r._sRowStripe!=q&&(h(l).removeClass(r._sRowStripe).addClass(q),r._sRowStripe=q)}w(a,"aoRowCallback",null,[l,r._aData,c,i]);b.push(l);c++}}else c=f.sZeroRecords,1==a.iDraw&&"ajax"==y(a)?c=f.sLoadingRecords:f.sEmptyTable&&0===a.fnRecordsTotal()&&(c=f.sEmptyTable),
b[0]=h("<tr/>",{"class":e?d[0]:""}).append(h("<td />",{valign:"top",colSpan:ca(a),"class":a.oClasses.sRowEmpty}).html(c))[0];w(a,"aoHeaderCallback","header",[h(a.nTHead).children("tr")[0],Ma(a),g,n,j]);w(a,"aoFooterCallback","footer",[h(a.nTFoot).children("tr")[0],Ma(a),g,n,j]);d=h(a.nTBody);d.children().detach();d.append(h(b));w(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=!1}}function R(a,b){var c=a.oFeatures,d=c.bFilter;c.bSort&&mb(a);d?ha(a,a.oPreviousSearch):a.aiDisplay=
a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);a._drawHold=b;M(a);a._drawHold=!1}function nb(a){var b=a.oClasses,c=h(a.nTable),c=h("<div/>").insertBefore(c),d=a.oFeatures,e=h("<div/>",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)});a.nHolding=c[0];a.nTableWrapper=e[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var f=a.sDom.split(""),g,i,j,n,l,r,q=0;q<f.length;q++){g=null;i=f[q];if("<"==i){j=h("<div/>")[0];n=f[q+1];if("'"==n||'"'==n){l="";for(r=2;f[q+r]!=n;)l+=
f[q+r],r++;"H"==l?l=b.sJUIHeader:"F"==l&&(l=b.sJUIFooter);-1!=l.indexOf(".")?(n=l.split("."),j.id=n[0].substr(1,n[0].length-1),j.className=n[1]):"#"==l.charAt(0)?j.id=l.substr(1,l.length-1):j.className=l;q+=r}e.append(j);e=h(j)}else if(">"==i)e=e.parent();else if("l"==i&&d.bPaginate&&d.bLengthChange)g=ob(a);else if("f"==i&&d.bFilter)g=pb(a);else if("r"==i&&d.bProcessing)g=qb(a);else if("t"==i)g=rb(a);else if("i"==i&&d.bInfo)g=sb(a);else if("p"==i&&d.bPaginate)g=tb(a);else if(0!==m.ext.feature.length){j=
m.ext.feature;r=0;for(n=j.length;r<n;r++)if(i==j[r].cFeature){g=j[r].fnInit(a);break}}g&&(j=a.aanFeatures,j[i]||(j[i]=[]),j[i].push(g),e.append(g))}c.replaceWith(e);a.nHolding=null}function fa(a,b){var c=h(b).children("tr"),d,e,f,g,i,j,n,l,r,q;a.splice(0,a.length);f=0;for(j=c.length;f<j;f++)a.push([]);f=0;for(j=c.length;f<j;f++){d=c[f];for(e=d.firstChild;e;){if("TD"==e.nodeName.toUpperCase()||"TH"==e.nodeName.toUpperCase()){l=1*e.getAttribute("colspan");r=1*e.getAttribute("rowspan");l=!l||0===l||
1===l?1:l;r=!r||0===r||1===r?1:r;g=0;for(i=a[f];i[g];)g++;n=g;q=1===l?!0:!1;for(i=0;i<l;i++)for(g=0;g<r;g++)a[f+g][n+i]={cell:e,unique:q},a[f+g].nTr=d}e=e.nextSibling}}}function qa(a,b,c){var d=[];c||(c=a.aoHeader,b&&(c=[],fa(c,b)));for(var b=0,e=c.length;b<e;b++)for(var f=0,g=c[b].length;f<g;f++)if(c[b][f].unique&&(!d[f]||!a.bSortCellsTop))d[f]=c[b][f].cell;return d}function ra(a,b,c){w(a,"aoServerParams","serverParams",[b]);if(b&&h.isArray(b)){var d={},e=/(.*?)\[\]$/;h.each(b,function(a,b){var c=
b.name.match(e);c?(c=c[0],d[c]||(d[c]=[]),d[c].push(b.value)):d[b.name]=b.value});b=d}var f,g=a.ajax,i=a.oInstance,j=function(b){w(a,null,"xhr",[a,b,a.jqXHR]);c(b)};if(h.isPlainObject(g)&&g.data){f=g.data;var n=h.isFunction(f)?f(b,a):f,b=h.isFunction(f)&&n?n:h.extend(!0,b,n);delete g.data}n={data:b,success:function(b){var c=b.error||b.sError;c&&J(a,0,c);a.json=b;j(b)},dataType:"json",cache:!1,type:a.sServerMethod,error:function(b,c){var d=w(a,null,"xhr",[a,null,a.jqXHR]);-1===h.inArray(!0,d)&&("parsererror"==
c?J(a,0,"Invalid JSON response",1):4===b.readyState&&J(a,0,"Ajax error",7));C(a,!1)}};a.oAjaxData=b;w(a,null,"preXhr",[a,b]);a.fnServerData?a.fnServerData.call(i,a.sAjaxSource,h.map(b,function(a,b){return{name:b,value:a}}),j,a):a.sAjaxSource||"string"===typeof g?a.jqXHR=h.ajax(h.extend(n,{url:g||a.sAjaxSource})):h.isFunction(g)?a.jqXHR=g.call(i,b,j,a):(a.jqXHR=h.ajax(h.extend(n,g)),g.data=f)}function lb(a){return a.bAjaxDataGet?(a.iDraw++,C(a,!0),ra(a,ub(a),function(b){vb(a,b)}),!1):!0}function ub(a){var b=
a.aoColumns,c=b.length,d=a.oFeatures,e=a.oPreviousSearch,f=a.aoPreSearchCols,g,i=[],j,n,l,r=V(a);g=a._iDisplayStart;j=!1!==d.bPaginate?a._iDisplayLength:-1;var q=function(a,b){i.push({name:a,value:b})};q("sEcho",a.iDraw);q("iColumns",c);q("sColumns",D(b,"sName").join(","));q("iDisplayStart",g);q("iDisplayLength",j);var k={draw:a.iDraw,columns:[],order:[],start:g,length:j,search:{value:e.sSearch,regex:e.bRegex}};for(g=0;g<c;g++)n=b[g],l=f[g],j="function"==typeof n.mData?"function":n.mData,k.columns.push({data:j,
name:n.sName,searchable:n.bSearchable,orderable:n.bSortable,search:{value:l.sSearch,regex:l.bRegex}}),q("mDataProp_"+g,j),d.bFilter&&(q("sSearch_"+g,l.sSearch),q("bRegex_"+g,l.bRegex),q("bSearchable_"+g,n.bSearchable)),d.bSort&&q("bSortable_"+g,n.bSortable);d.bFilter&&(q("sSearch",e.sSearch),q("bRegex",e.bRegex));d.bSort&&(h.each(r,function(a,b){k.order.push({column:b.col,dir:b.dir});q("iSortCol_"+a,b.col);q("sSortDir_"+a,b.dir)}),q("iSortingCols",r.length));b=m.ext.legacy.ajax;return null===b?a.sAjaxSource?
i:k:b?i:k}function vb(a,b){var c=sa(a,b),d=b.sEcho!==k?b.sEcho:b.draw,e=b.iTotalRecords!==k?b.iTotalRecords:b.recordsTotal,f=b.iTotalDisplayRecords!==k?b.iTotalDisplayRecords:b.recordsFiltered;if(d){if(1*d<a.iDraw)return;a.iDraw=1*d}na(a);a._iRecordsTotal=parseInt(e,10);a._iRecordsDisplay=parseInt(f,10);d=0;for(e=c.length;d<e;d++)L(a,c[d]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=!1;M(a);a._bInitComplete||ta(a,b);a.bAjaxDataGet=!0;C(a,!1)}function sa(a,b){var c=h.isPlainObject(a.ajax)&&
a.ajax.dataSrc!==k?a.ajax.dataSrc:a.sAjaxDataProp;return"data"===c?b.aaData||b[c]:""!==c?P(c)(b):b}function pb(a){var b=a.oClasses,c=a.sTableId,d=a.oLanguage,e=a.oPreviousSearch,f=a.aanFeatures,g='<input type="search" class="'+b.sFilterInput+'"/>',i=d.sSearch,i=i.match(/_INPUT_/)?i.replace("_INPUT_",g):i+g,b=h("<div/>",{id:!f.f?c+"_filter":null,"class":b.sFilter}).append(h("<label/>").append(i)),f=function(){var b=!this.value?"":this.value;b!=e.sSearch&&(ha(a,{sSearch:b,bRegex:e.bRegex,bSmart:e.bSmart,
bCaseInsensitive:e.bCaseInsensitive}),a._iDisplayStart=0,M(a))},g=null!==a.searchDelay?a.searchDelay:"ssp"===y(a)?400:0,j=h("input",b).val(e.sSearch).attr("placeholder",d.sSearchPlaceholder).bind("keyup.DT search.DT input.DT paste.DT cut.DT",g?ua(f,g):f).bind("keypress.DT",function(a){if(13==a.keyCode)return!1}).attr("aria-controls",c);h(a.nTable).on("search.dt.DT",function(b,c){if(a===c)try{j[0]!==T.activeElement&&j.val(e.sSearch)}catch(d){}});return b[0]}function ha(a,b,c){var d=a.oPreviousSearch,
e=a.aoPreSearchCols,f=function(a){d.sSearch=a.sSearch;d.bRegex=a.bRegex;d.bSmart=a.bSmart;d.bCaseInsensitive=a.bCaseInsensitive};Ia(a);if("ssp"!=y(a)){wb(a,b.sSearch,c,b.bEscapeRegex!==k?!b.bEscapeRegex:b.bRegex,b.bSmart,b.bCaseInsensitive);f(b);for(b=0;b<e.length;b++)xb(a,e[b].sSearch,b,e[b].bEscapeRegex!==k?!e[b].bEscapeRegex:e[b].bRegex,e[b].bSmart,e[b].bCaseInsensitive);yb(a)}else f(b);a.bFiltered=!0;w(a,null,"search",[a])}function yb(a){for(var b=m.ext.search,c=a.aiDisplay,d,e,f=0,g=b.length;f<
g;f++){for(var i=[],j=0,n=c.length;j<n;j++)e=c[j],d=a.aoData[e],b[f](a,d._aFilterData,e,d._aData,j)&&i.push(e);c.length=0;h.merge(c,i)}}function xb(a,b,c,d,e,f){if(""!==b)for(var g=a.aiDisplay,d=Qa(b,d,e,f),e=g.length-1;0<=e;e--)b=a.aoData[g[e]]._aFilterData[c],d.test(b)||g.splice(e,1)}function wb(a,b,c,d,e,f){var d=Qa(b,d,e,f),e=a.oPreviousSearch.sSearch,f=a.aiDisplayMaster,g;0!==m.ext.search.length&&(c=!0);g=zb(a);if(0>=b.length)a.aiDisplay=f.slice();else{if(g||c||e.length>b.length||0!==b.indexOf(e)||
a.bSorted)a.aiDisplay=f.slice();b=a.aiDisplay;for(c=b.length-1;0<=c;c--)d.test(a.aoData[b[c]]._sFilterRow)||b.splice(c,1)}}function Qa(a,b,c,d){a=b?a:va(a);c&&(a="^(?=.*?"+h.map(a.match(/"[^"]+"|[^ ]+/g)||[""],function(a){if('"'===a.charAt(0))var b=a.match(/^"(.*)"$/),a=b?b[1]:a;return a.replace('"',"")}).join(")(?=.*?")+").*$");return RegExp(a,d?"i":"")}function va(a){return a.replace(Yb,"\\$1")}function zb(a){var b=a.aoColumns,c,d,e,f,g,i,j,h,l=m.ext.type.search;c=!1;d=0;for(f=a.aoData.length;d<
f;d++)if(h=a.aoData[d],!h._aFilterData){i=[];e=0;for(g=b.length;e<g;e++)c=b[e],c.bSearchable?(j=B(a,d,e,"filter"),l[c.sType]&&(j=l[c.sType](j)),null===j&&(j=""),"string"!==typeof j&&j.toString&&(j=j.toString())):j="",j.indexOf&&-1!==j.indexOf("&")&&(wa.innerHTML=j,j=Zb?wa.textContent:wa.innerText),j.replace&&(j=j.replace(/[\r\n]/g,"")),i.push(j);h._aFilterData=i;h._sFilterRow=i.join(" ");c=!0}return c}function Ab(a){return{search:a.sSearch,smart:a.bSmart,regex:a.bRegex,caseInsensitive:a.bCaseInsensitive}}
function Bb(a){return{sSearch:a.search,bSmart:a.smart,bRegex:a.regex,bCaseInsensitive:a.caseInsensitive}}function sb(a){var b=a.sTableId,c=a.aanFeatures.i,d=h("<div/>",{"class":a.oClasses.sInfo,id:!c?b+"_info":null});c||(a.aoDrawCallback.push({fn:Cb,sName:"information"}),d.attr("role","status").attr("aria-live","polite"),h(a.nTable).attr("aria-describedby",b+"_info"));return d[0]}function Cb(a){var b=a.aanFeatures.i;if(0!==b.length){var c=a.oLanguage,d=a._iDisplayStart+1,e=a.fnDisplayEnd(),f=a.fnRecordsTotal(),
g=a.fnRecordsDisplay(),i=g?c.sInfo:c.sInfoEmpty;g!==f&&(i+=" "+c.sInfoFiltered);i+=c.sInfoPostFix;i=Db(a,i);c=c.fnInfoCallback;null!==c&&(i=c.call(a.oInstance,a,d,e,f,g,i));h(b).html(i)}}function Db(a,b){var c=a.fnFormatNumber,d=a._iDisplayStart+1,e=a._iDisplayLength,f=a.fnRecordsDisplay(),g=-1===e;return b.replace(/_START_/g,c.call(a,d)).replace(/_END_/g,c.call(a,a.fnDisplayEnd())).replace(/_MAX_/g,c.call(a,a.fnRecordsTotal())).replace(/_TOTAL_/g,c.call(a,f)).replace(/_PAGE_/g,c.call(a,g?1:Math.ceil(d/
e))).replace(/_PAGES_/g,c.call(a,g?1:Math.ceil(f/e)))}function ia(a){var b,c,d=a.iInitDisplayStart,e=a.aoColumns,f;c=a.oFeatures;var g=a.bDeferLoading;if(a.bInitialised){nb(a);kb(a);ga(a,a.aoHeader);ga(a,a.aoFooter);C(a,!0);c.bAutoWidth&&Ha(a);b=0;for(c=e.length;b<c;b++)f=e[b],f.sWidth&&(f.nTh.style.width=u(f.sWidth));w(a,null,"preInit",[a]);R(a);e=y(a);if("ssp"!=e||g)"ajax"==e?ra(a,[],function(c){var f=sa(a,c);for(b=0;b<f.length;b++)L(a,f[b]);a.iInitDisplayStart=d;R(a);C(a,!1);ta(a,c)},a):(C(a,!1),
ta(a))}else setTimeout(function(){ia(a)},200)}function ta(a,b){a._bInitComplete=!0;(b||a.oInit.aaData)&&Y(a);w(a,"aoInitComplete","init",[a,b])}function Ra(a,b){var c=parseInt(b,10);a._iDisplayLength=c;Sa(a);w(a,null,"length",[a,c])}function ob(a){for(var b=a.oClasses,c=a.sTableId,d=a.aLengthMenu,e=h.isArray(d[0]),f=e?d[0]:d,d=e?d[1]:d,e=h("<select/>",{name:c+"_length","aria-controls":c,"class":b.sLengthSelect}),g=0,i=f.length;g<i;g++)e[0][g]=new Option(d[g],f[g]);var j=h("<div><label/></div>").addClass(b.sLength);
a.aanFeatures.l||(j[0].id=c+"_length");j.children().append(a.oLanguage.sLengthMenu.replace("_MENU_",e[0].outerHTML));h("select",j).val(a._iDisplayLength).bind("change.DT",function(){Ra(a,h(this).val());M(a)});h(a.nTable).bind("length.dt.DT",function(b,c,d){a===c&&h("select",j).val(d)});return j[0]}function tb(a){var b=a.sPaginationType,c=m.ext.pager[b],d="function"===typeof c,e=function(a){M(a)},b=h("<div/>").addClass(a.oClasses.sPaging+b)[0],f=a.aanFeatures;d||c.fnInit(a,b,e);f.p||(b.id=a.sTableId+
"_paginate",a.aoDrawCallback.push({fn:function(a){if(d){var b=a._iDisplayStart,j=a._iDisplayLength,h=a.fnRecordsDisplay(),l=-1===j,b=l?0:Math.ceil(b/j),j=l?1:Math.ceil(h/j),h=c(b,j),k,l=0;for(k=f.p.length;l<k;l++)Pa(a,"pageButton")(a,f.p[l],l,h,b,j)}else c.fnUpdate(a,e)},sName:"pagination"}));return b}function Ta(a,b,c){var d=a._iDisplayStart,e=a._iDisplayLength,f=a.fnRecordsDisplay();0===f||-1===e?d=0:"number"===typeof b?(d=b*e,d>f&&(d=0)):"first"==b?d=0:"previous"==b?(d=0<=e?d-e:0,0>d&&(d=0)):"next"==
b?d+e<f&&(d+=e):"last"==b?d=Math.floor((f-1)/e)*e:J(a,0,"Unknown paging action: "+b,5);b=a._iDisplayStart!==d;a._iDisplayStart=d;b&&(w(a,null,"page",[a]),c&&M(a));return b}function qb(a){return h("<div/>",{id:!a.aanFeatures.r?a.sTableId+"_processing":null,"class":a.oClasses.sProcessing}).html(a.oLanguage.sProcessing).insertBefore(a.nTable)[0]}function C(a,b){a.oFeatures.bProcessing&&h(a.aanFeatures.r).css("display",b?"block":"none");w(a,null,"processing",[a,b])}function rb(a){var b=h(a.nTable);b.attr("role",
"grid");var c=a.oScroll;if(""===c.sX&&""===c.sY)return a.nTable;var d=c.sX,e=c.sY,f=a.oClasses,g=b.children("caption"),i=g.length?g[0]._captionSide:null,j=h(b[0].cloneNode(!1)),n=h(b[0].cloneNode(!1)),l=b.children("tfoot");c.sX&&"100%"===b.attr("width")&&b.removeAttr("width");l.length||(l=null);j=h("<div/>",{"class":f.sScrollWrapper}).append(h("<div/>",{"class":f.sScrollHead}).css({overflow:"hidden",position:"relative",border:0,width:d?!d?null:u(d):"100%"}).append(h("<div/>",{"class":f.sScrollHeadInner}).css({"box-sizing":"content-box",
width:c.sXInner||"100%"}).append(j.removeAttr("id").css("margin-left",0).append("top"===i?g:null).append(b.children("thead"))))).append(h("<div/>",{"class":f.sScrollBody}).css({position:"relative",overflow:"auto",width:!d?null:u(d)}).append(b));l&&j.append(h("<div/>",{"class":f.sScrollFoot}).css({overflow:"hidden",border:0,width:d?!d?null:u(d):"100%"}).append(h("<div/>",{"class":f.sScrollFootInner}).append(n.removeAttr("id").css("margin-left",0).append("bottom"===i?g:null).append(b.children("tfoot")))));
var b=j.children(),k=b[0],f=b[1],q=l?b[2]:null;if(d)h(f).on("scroll.DT",function(){var a=this.scrollLeft;k.scrollLeft=a;l&&(q.scrollLeft=a)});h(f).css(e&&c.bCollapse?"max-height":"height",e);a.nScrollHead=k;a.nScrollBody=f;a.nScrollFoot=q;a.aoDrawCallback.push({fn:Z,sName:"scrolling"});return j[0]}function Z(a){var b=a.oScroll,c=b.sX,d=b.sXInner,e=b.sY,b=b.iBarWidth,f=h(a.nScrollHead),g=f[0].style,i=f.children("div"),j=i[0].style,n=i.children("table"),i=a.nScrollBody,l=h(i),k=i.style,q=h(a.nScrollFoot).children("div"),
m=q.children("table"),o=h(a.nTHead),E=h(a.nTable),p=E[0],t=p.style,N=a.nTFoot?h(a.nTFoot):null,Eb=a.oBrowser,w=Eb.bScrollOversize,s,v,O,x,y=[],z=[],A=[],B,C=function(a){a=a.style;a.paddingTop="0";a.paddingBottom="0";a.borderTopWidth="0";a.borderBottomWidth="0";a.height=0};E.children("thead, tfoot").remove();x=o.clone().prependTo(E);o=o.find("tr");v=x.find("tr");x.find("th, td").removeAttr("tabindex");N&&(O=N.clone().prependTo(E),s=N.find("tr"),O=O.find("tr"));c||(k.width="100%",f[0].style.width="100%");
h.each(qa(a,x),function(b,c){B=$(a,b);c.style.width=a.aoColumns[B].sWidth});N&&H(function(a){a.style.width=""},O);f=E.outerWidth();if(""===c){t.width="100%";if(w&&(E.find("tbody").height()>i.offsetHeight||"scroll"==l.css("overflow-y")))t.width=u(E.outerWidth()-b);f=E.outerWidth()}else""!==d&&(t.width=u(d),f=E.outerWidth());H(C,v);H(function(a){A.push(a.innerHTML);y.push(u(h(a).css("width")))},v);H(function(a,b){a.style.width=y[b]},o);h(v).height(0);N&&(H(C,O),H(function(a){z.push(u(h(a).css("width")))},
O),H(function(a,b){a.style.width=z[b]},s),h(O).height(0));H(function(a,b){a.innerHTML='<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+A[b]+"</div>";a.style.width=y[b]},v);N&&H(function(a,b){a.innerHTML="";a.style.width=z[b]},O);if(E.outerWidth()<f){s=i.scrollHeight>i.offsetHeight||"scroll"==l.css("overflow-y")?f+b:f;if(w&&(i.scrollHeight>i.offsetHeight||"scroll"==l.css("overflow-y")))t.width=u(s-b);(""===c||""!==d)&&J(a,1,"Possible column misalignment",6)}else s="100%";k.width=
u(s);g.width=u(s);N&&(a.nScrollFoot.style.width=u(s));!e&&w&&(k.height=u(p.offsetHeight+b));c=E.outerWidth();n[0].style.width=u(c);j.width=u(c);d=E.height()>i.clientHeight||"scroll"==l.css("overflow-y");e="padding"+(Eb.bScrollbarLeft?"Left":"Right");j[e]=d?b+"px":"0px";N&&(m[0].style.width=u(c),q[0].style.width=u(c),q[0].style[e]=d?b+"px":"0px");l.scroll();if((a.bSorted||a.bFiltered)&&!a._drawHold)i.scrollTop=0}function H(a,b,c){for(var d=0,e=0,f=b.length,g,i;e<f;){g=b[e].firstChild;for(i=c?c[e].firstChild:
null;g;)1===g.nodeType&&(c?a(g,i,d):a(g,d),d++),g=g.nextSibling,i=c?i.nextSibling:null;e++}}function Ha(a){var b=a.nTable,c=a.aoColumns,d=a.oScroll,e=d.sY,f=d.sX,g=d.sXInner,i=c.length,j=aa(a,"bVisible"),n=h("th",a.nTHead),l=b.getAttribute("width"),k=b.parentNode,q=!1,m,o,p;p=a.oBrowser;d=p.bScrollOversize;(m=b.style.width)&&-1!==m.indexOf("%")&&(l=m);for(m=0;m<j.length;m++)o=c[j[m]],null!==o.sWidth&&(o.sWidth=Fb(o.sWidthOrig,k),q=!0);if(d||!q&&!f&&!e&&i==ca(a)&&i==n.length)for(m=0;m<i;m++){if(j=
$(a,m))c[j].sWidth=u(n.eq(m).width())}else{i=h(b).clone().css("visibility","hidden").removeAttr("id");i.find("tbody tr").remove();var t=h("<tr/>").appendTo(i.find("tbody"));i.find("thead, tfoot").remove();i.append(h(a.nTHead).clone()).append(h(a.nTFoot).clone());i.find("tfoot th, tfoot td").css("width","");n=qa(a,i.find("thead")[0]);for(m=0;m<j.length;m++)o=c[j[m]],n[m].style.width=null!==o.sWidthOrig&&""!==o.sWidthOrig?u(o.sWidthOrig):"";if(a.aoData.length)for(m=0;m<j.length;m++)q=j[m],o=c[q],h(Gb(a,
q)).clone(!1).append(o.sContentPadding).appendTo(t);q=h("<div/>").css(f||e?{position:"absolute",top:0,left:0,height:1,right:0,overflow:"hidden"}:{}).append(i).appendTo(k);f&&g?i.width(g):f?(i.css("width","auto"),i.width()<k.clientWidth&&i.width(k.clientWidth)):e?i.width(k.clientWidth):l&&i.width(l);if(f){for(m=g=0;m<j.length;m++)o=c[j[m]],e=p.bBounding?n[m].getBoundingClientRect().width:h(n[m]).outerWidth(),g+=null===o.sWidthOrig?e:parseInt(o.sWidth,10)+e-h(n[m]).width();i.width(u(g));b.style.width=
u(g)}for(m=0;m<j.length;m++)if(o=c[j[m]],p=h(n[m]).width())o.sWidth=u(p);b.style.width=u(i.css("width"));q.remove()}l&&(b.style.width=u(l));if((l||f)&&!a._reszEvt)b=function(){h(Fa).bind("resize.DT-"+a.sInstance,ua(function(){Y(a)}))},d?setTimeout(b,1E3):b(),a._reszEvt=!0}function ua(a,b){var c=b!==k?b:200,d,e;return function(){var b=this,g=+new Date,i=arguments;d&&g<d+c?(clearTimeout(e),e=setTimeout(function(){d=k;a.apply(b,i)},c)):(d=g,a.apply(b,i))}}function Fb(a,b){if(!a)return 0;var c=h("<div/>").css("width",
u(a)).appendTo(b||T.body),d=c[0].offsetWidth;c.remove();return d}function Gb(a,b){var c=Hb(a,b);if(0>c)return null;var d=a.aoData[c];return!d.nTr?h("<td/>").html(B(a,c,b,"display"))[0]:d.anCells[b]}function Hb(a,b){for(var c,d=-1,e=-1,f=0,g=a.aoData.length;f<g;f++)c=B(a,f,b,"display")+"",c=c.replace($b,""),c.length>d&&(d=c.length,e=f);return e}function u(a){return null===a?"0px":"number"==typeof a?0>a?"0px":a+"px":a.match(/\d$/)?a+"px":a}function V(a){var b,c,d=[],e=a.aoColumns,f,g,i,j;b=a.aaSortingFixed;
c=h.isPlainObject(b);var n=[];f=function(a){a.length&&!h.isArray(a[0])?n.push(a):h.merge(n,a)};h.isArray(b)&&f(b);c&&b.pre&&f(b.pre);f(a.aaSorting);c&&b.post&&f(b.post);for(a=0;a<n.length;a++){j=n[a][0];f=e[j].aDataSort;b=0;for(c=f.length;b<c;b++)g=f[b],i=e[g].sType||"string",n[a]._idx===k&&(n[a]._idx=h.inArray(n[a][1],e[g].asSorting)),d.push({src:j,col:g,dir:n[a][1],index:n[a]._idx,type:i,formatter:m.ext.type.order[i+"-pre"]})}return d}function mb(a){var b,c,d=[],e=m.ext.type.order,f=a.aoData,g=
0,i,j=a.aiDisplayMaster,h;Ia(a);h=V(a);b=0;for(c=h.length;b<c;b++)i=h[b],i.formatter&&g++,Ib(a,i.col);if("ssp"!=y(a)&&0!==h.length){b=0;for(c=j.length;b<c;b++)d[j[b]]=b;g===h.length?j.sort(function(a,b){var c,e,g,i,j=h.length,k=f[a]._aSortData,m=f[b]._aSortData;for(g=0;g<j;g++)if(i=h[g],c=k[i.col],e=m[i.col],c=c<e?-1:c>e?1:0,0!==c)return"asc"===i.dir?c:-c;c=d[a];e=d[b];return c<e?-1:c>e?1:0}):j.sort(function(a,b){var c,g,i,j,k=h.length,m=f[a]._aSortData,p=f[b]._aSortData;for(i=0;i<k;i++)if(j=h[i],
c=m[j.col],g=p[j.col],j=e[j.type+"-"+j.dir]||e["string-"+j.dir],c=j(c,g),0!==c)return c;c=d[a];g=d[b];return c<g?-1:c>g?1:0})}a.bSorted=!0}function Jb(a){for(var b,c,d=a.aoColumns,e=V(a),a=a.oLanguage.oAria,f=0,g=d.length;f<g;f++){c=d[f];var i=c.asSorting;b=c.sTitle.replace(/<.*?>/g,"");var j=c.nTh;j.removeAttribute("aria-sort");c.bSortable&&(0<e.length&&e[0].col==f?(j.setAttribute("aria-sort","asc"==e[0].dir?"ascending":"descending"),c=i[e[0].index+1]||i[0]):c=i[0],b+="asc"===c?a.sSortAscending:
a.sSortDescending);j.setAttribute("aria-label",b)}}function Ua(a,b,c,d){var e=a.aaSorting,f=a.aoColumns[b].asSorting,g=function(a,b){var c=a._idx;c===k&&(c=h.inArray(a[1],f));return c+1<f.length?c+1:b?null:0};"number"===typeof e[0]&&(e=a.aaSorting=[e]);c&&a.oFeatures.bSortMulti?(c=h.inArray(b,D(e,"0")),-1!==c?(b=g(e[c],!0),null===b&&1===e.length&&(b=0),null===b?e.splice(c,1):(e[c][1]=f[b],e[c]._idx=b)):(e.push([b,f[0],0]),e[e.length-1]._idx=0)):e.length&&e[0][0]==b?(b=g(e[0]),e.length=1,e[0][1]=f[b],
e[0]._idx=b):(e.length=0,e.push([b,f[0]]),e[0]._idx=0);R(a);"function"==typeof d&&d(a)}function Oa(a,b,c,d){var e=a.aoColumns[c];Va(b,{},function(b){!1!==e.bSortable&&(a.oFeatures.bProcessing?(C(a,!0),setTimeout(function(){Ua(a,c,b.shiftKey,d);"ssp"!==y(a)&&C(a,!1)},0)):Ua(a,c,b.shiftKey,d))})}function xa(a){var b=a.aLastSort,c=a.oClasses.sSortColumn,d=V(a),e=a.oFeatures,f,g;if(e.bSort&&e.bSortClasses){e=0;for(f=b.length;e<f;e++)g=b[e].src,h(D(a.aoData,"anCells",g)).removeClass(c+(2>e?e+1:3));e=0;
for(f=d.length;e<f;e++)g=d[e].src,h(D(a.aoData,"anCells",g)).addClass(c+(2>e?e+1:3))}a.aLastSort=d}function Ib(a,b){var c=a.aoColumns[b],d=m.ext.order[c.sSortDataType],e;d&&(e=d.call(a.oInstance,a,b,ba(a,b)));for(var f,g=m.ext.type.order[c.sType+"-pre"],i=0,h=a.aoData.length;i<h;i++)if(c=a.aoData[i],c._aSortData||(c._aSortData=[]),!c._aSortData[b]||d)f=d?e[i]:B(a,i,b,"sort"),c._aSortData[b]=g?g(f):f}function ya(a){if(a.oFeatures.bStateSave&&!a.bDestroying){var b={time:+new Date,start:a._iDisplayStart,
length:a._iDisplayLength,order:h.extend(!0,[],a.aaSorting),search:Ab(a.oPreviousSearch),columns:h.map(a.aoColumns,function(b,d){return{visible:b.bVisible,search:Ab(a.aoPreSearchCols[d])}})};w(a,"aoStateSaveParams","stateSaveParams",[a,b]);a.oSavedState=b;a.fnStateSaveCallback.call(a.oInstance,a,b)}}function Kb(a){var b,c,d=a.aoColumns;if(a.oFeatures.bStateSave){var e=a.fnStateLoadCallback.call(a.oInstance,a);if(e&&e.time&&(b=w(a,"aoStateLoadParams","stateLoadParams",[a,e]),-1===h.inArray(!1,b)&&(b=
a.iStateDuration,!(0<b&&e.time<+new Date-1E3*b)&&d.length===e.columns.length))){a.oLoadedState=h.extend(!0,{},e);e.start!==k&&(a._iDisplayStart=e.start,a.iInitDisplayStart=e.start);e.length!==k&&(a._iDisplayLength=e.length);e.order!==k&&(a.aaSorting=[],h.each(e.order,function(b,c){a.aaSorting.push(c[0]>=d.length?[0,c[1]]:c)}));e.search!==k&&h.extend(a.oPreviousSearch,Bb(e.search));b=0;for(c=e.columns.length;b<c;b++){var f=e.columns[b];f.visible!==k&&(d[b].bVisible=f.visible);f.search!==k&&h.extend(a.aoPreSearchCols[b],
Bb(f.search))}w(a,"aoStateLoaded","stateLoaded",[a,e])}}}function za(a){var b=m.settings,a=h.inArray(a,D(b,"nTable"));return-1!==a?b[a]:null}function J(a,b,c,d){c="DataTables warning: "+(a?"table id="+a.sTableId+" - ":"")+c;d&&(c+=". For more information about this error, please see http://datatables.net/tn/"+d);if(b)Fa.console&&console.log&&console.log(c);else if(b=m.ext,b=b.sErrMode||b.errMode,a&&w(a,null,"error",[a,d,c]),"alert"==b)alert(c);else{if("throw"==b)throw Error(c);"function"==typeof b&&
b(a,d,c)}}function F(a,b,c,d){h.isArray(c)?h.each(c,function(c,d){h.isArray(d)?F(a,b,d[0],d[1]):F(a,b,d)}):(d===k&&(d=c),b[c]!==k&&(a[d]=b[c]))}function Lb(a,b,c){var d,e;for(e in b)b.hasOwnProperty(e)&&(d=b[e],h.isPlainObject(d)?(h.isPlainObject(a[e])||(a[e]={}),h.extend(!0,a[e],d)):a[e]=c&&"data"!==e&&"aaData"!==e&&h.isArray(d)?d.slice():d);return a}function Va(a,b,c){h(a).bind("click.DT",b,function(b){a.blur();c(b)}).bind("keypress.DT",b,function(a){13===a.which&&(a.preventDefault(),c(a))}).bind("selectstart.DT",
function(){return!1})}function z(a,b,c,d){c&&a[b].push({fn:c,sName:d})}function w(a,b,c,d){var e=[];b&&(e=h.map(a[b].slice().reverse(),function(b){return b.fn.apply(a.oInstance,d)}));null!==c&&(b=h.Event(c+".dt"),h(a.nTable).trigger(b,d),e.push(b.result));return e}function Sa(a){var b=a._iDisplayStart,c=a.fnDisplayEnd(),d=a._iDisplayLength;b>=c&&(b=c-d);b-=b%d;if(-1===d||0>b)b=0;a._iDisplayStart=b}function Pa(a,b){var c=a.renderer,d=m.ext.renderer[b];return h.isPlainObject(c)&&c[b]?d[c[b]]||d._:"string"===
typeof c?d[c]||d._:d._}function y(a){return a.oFeatures.bServerSide?"ssp":a.ajax||a.sAjaxSource?"ajax":"dom"}function Aa(a,b){var c=[],c=Mb.numbers_length,d=Math.floor(c/2);b<=c?c=W(0,b):a<=d?(c=W(0,c-2),c.push("ellipsis"),c.push(b-1)):(a>=b-1-d?c=W(b-(c-2),b):(c=W(a-d+2,a+d-1),c.push("ellipsis"),c.push(b-1)),c.splice(0,0,"ellipsis"),c.splice(0,0,0));c.DT_el="span";return c}function cb(a){h.each({num:function(b){return Ba(b,a)},"num-fmt":function(b){return Ba(b,a,Wa)},"html-num":function(b){return Ba(b,
a,Ca)},"html-num-fmt":function(b){return Ba(b,a,Ca,Wa)}},function(b,c){v.type.order[b+a+"-pre"]=c;b.match(/^html\-/)&&(v.type.search[b+a]=v.type.search.html)})}function Nb(a){return function(){var b=[za(this[m.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return m.ext.internal[a].apply(this,b)}}var m,v,t,p,s,Xa={},Ob=/[\r\n]/g,Ca=/<.*?>/g,ac=/^[\w\+\-]/,bc=/[\w\+\-]$/,Yb=RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"),Wa=/[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi,
K=function(a){return!a||!0===a||"-"===a?!0:!1},Pb=function(a){var b=parseInt(a,10);return!isNaN(b)&&isFinite(a)?b:null},Qb=function(a,b){Xa[b]||(Xa[b]=RegExp(va(b),"g"));return"string"===typeof a&&"."!==b?a.replace(/\./g,"").replace(Xa[b],"."):a},Ya=function(a,b,c){var d="string"===typeof a;if(K(a))return!0;b&&d&&(a=Qb(a,b));c&&d&&(a=a.replace(Wa,""));return!isNaN(parseFloat(a))&&isFinite(a)},Rb=function(a,b,c){return K(a)?!0:!(K(a)||"string"===typeof a)?null:Ya(a.replace(Ca,""),b,c)?!0:null},D=function(a,
b,c){var d=[],e=0,f=a.length;if(c!==k)for(;e<f;e++)a[e]&&a[e][b]&&d.push(a[e][b][c]);else for(;e<f;e++)a[e]&&d.push(a[e][b]);return d},ja=function(a,b,c,d){var e=[],f=0,g=b.length;if(d!==k)for(;f<g;f++)a[b[f]][c]&&e.push(a[b[f]][c][d]);else for(;f<g;f++)e.push(a[b[f]][c]);return e},W=function(a,b){var c=[],d;b===k?(b=0,d=a):(d=b,b=a);for(var e=b;e<d;e++)c.push(e);return c},Sb=function(a){for(var b=[],c=0,d=a.length;c<d;c++)a[c]&&b.push(a[c]);return b},pa=function(a){var b=[],c,d,e=a.length,f,g=0;
d=0;a:for(;d<e;d++){c=a[d];for(f=0;f<g;f++)if(b[f]===c)continue a;b.push(c);g++}return b},A=function(a,b,c){a[b]!==k&&(a[c]=a[b])},da=/\[.*?\]$/,U=/\(\)$/,wa=h("<div>")[0],Zb=wa.textContent!==k,$b=/<.*?>/g;m=function(a){this.$=function(a,b){return this.api(!0).$(a,b)};this._=function(a,b){return this.api(!0).rows(a,b).data()};this.api=function(a){return a?new t(za(this[v.iApiIndex])):new t(this)};this.fnAddData=function(a,b){var c=this.api(!0),d=h.isArray(a)&&(h.isArray(a[0])||h.isPlainObject(a[0]))?
c.rows.add(a):c.row.add(a);(b===k||b)&&c.draw();return d.flatten().toArray()};this.fnAdjustColumnSizing=function(a){var b=this.api(!0).columns.adjust(),c=b.settings()[0],d=c.oScroll;a===k||a?b.draw(!1):(""!==d.sX||""!==d.sY)&&Z(c)};this.fnClearTable=function(a){var b=this.api(!0).clear();(a===k||a)&&b.draw()};this.fnClose=function(a){this.api(!0).row(a).child.hide()};this.fnDeleteRow=function(a,b,c){var d=this.api(!0),a=d.rows(a),e=a.settings()[0],h=e.aoData[a[0][0]];a.remove();b&&b.call(this,e,h);
(c===k||c)&&d.draw();return h};this.fnDestroy=function(a){this.api(!0).destroy(a)};this.fnDraw=function(a){this.api(!0).draw(a)};this.fnFilter=function(a,b,c,d,e,h){e=this.api(!0);null===b||b===k?e.search(a,c,d,h):e.column(b).search(a,c,d,h);e.draw()};this.fnGetData=function(a,b){var c=this.api(!0);if(a!==k){var d=a.nodeName?a.nodeName.toLowerCase():"";return b!==k||"td"==d||"th"==d?c.cell(a,b).data():c.row(a).data()||null}return c.data().toArray()};this.fnGetNodes=function(a){var b=this.api(!0);
return a!==k?b.row(a).node():b.rows().nodes().flatten().toArray()};this.fnGetPosition=function(a){var b=this.api(!0),c=a.nodeName.toUpperCase();return"TR"==c?b.row(a).index():"TD"==c||"TH"==c?(a=b.cell(a).index(),[a.row,a.columnVisible,a.column]):null};this.fnIsOpen=function(a){return this.api(!0).row(a).child.isShown()};this.fnOpen=function(a,b,c){return this.api(!0).row(a).child(b,c).show().child()[0]};this.fnPageChange=function(a,b){var c=this.api(!0).page(a);(b===k||b)&&c.draw(!1)};this.fnSetColumnVis=
function(a,b,c){a=this.api(!0).column(a).visible(b);(c===k||c)&&a.columns.adjust().draw()};this.fnSettings=function(){return za(this[v.iApiIndex])};this.fnSort=function(a){this.api(!0).order(a).draw()};this.fnSortListener=function(a,b,c){this.api(!0).order.listener(a,b,c)};this.fnUpdate=function(a,b,c,d,e){var h=this.api(!0);c===k||null===c?h.row(b).data(a):h.cell(b,c).data(a);(e===k||e)&&h.columns.adjust();(d===k||d)&&h.draw();return 0};this.fnVersionCheck=v.fnVersionCheck;var b=this,c=a===k,d=this.length;
c&&(a={});this.oApi=this.internal=v.internal;for(var e in m.ext.internal)e&&(this[e]=Nb(e));this.each(function(){var e={},e=1<d?Lb(e,a,!0):a,g=0,i,j=this.getAttribute("id"),n=!1,l=m.defaults,r=h(this);if("table"!=this.nodeName.toLowerCase())J(null,0,"Non-table node initialisation ("+this.nodeName+")",2);else{db(l);eb(l.column);I(l,l,!0);I(l.column,l.column,!0);I(l,h.extend(e,r.data()));var q=m.settings,g=0;for(i=q.length;g<i;g++){var p=q[g];if(p.nTable==this||p.nTHead.parentNode==this||p.nTFoot&&
p.nTFoot.parentNode==this){g=e.bRetrieve!==k?e.bRetrieve:l.bRetrieve;if(c||g)return p.oInstance;if(e.bDestroy!==k?e.bDestroy:l.bDestroy){p.oInstance.fnDestroy();break}else{J(p,0,"Cannot reinitialise DataTable",3);return}}if(p.sTableId==this.id){q.splice(g,1);break}}if(null===j||""===j)this.id=j="DataTables_Table_"+m.ext._unique++;var o=h.extend(!0,{},m.models.oSettings,{sDestroyWidth:r[0].style.width,sInstance:j,sTableId:j});o.nTable=this;o.oApi=b.internal;o.oInit=e;q.push(o);o.oInstance=1===b.length?
b:r.dataTable();db(e);e.oLanguage&&S(e.oLanguage);e.aLengthMenu&&!e.iDisplayLength&&(e.iDisplayLength=h.isArray(e.aLengthMenu[0])?e.aLengthMenu[0][0]:e.aLengthMenu[0]);e=Lb(h.extend(!0,{},l),e);F(o.oFeatures,e,"bPaginate bLengthChange bFilter bSort bSortMulti bInfo bProcessing bAutoWidth bSortClasses bServerSide bDeferRender".split(" "));F(o,e,["asStripeClasses","ajax","fnServerData","fnFormatNumber","sServerMethod","aaSorting","aaSortingFixed","aLengthMenu","sPaginationType","sAjaxSource","sAjaxDataProp",
"iStateDuration","sDom","bSortCellsTop","iTabIndex","fnStateLoadCallback","fnStateSaveCallback","renderer","searchDelay","rowId",["iCookieDuration","iStateDuration"],["oSearch","oPreviousSearch"],["aoSearchCols","aoPreSearchCols"],["iDisplayLength","_iDisplayLength"],["bJQueryUI","bJUI"]]);F(o.oScroll,e,[["sScrollX","sX"],["sScrollXInner","sXInner"],["sScrollY","sY"],["bScrollCollapse","bCollapse"]]);F(o.oLanguage,e,"fnInfoCallback");z(o,"aoDrawCallback",e.fnDrawCallback,"user");z(o,"aoServerParams",
e.fnServerParams,"user");z(o,"aoStateSaveParams",e.fnStateSaveParams,"user");z(o,"aoStateLoadParams",e.fnStateLoadParams,"user");z(o,"aoStateLoaded",e.fnStateLoaded,"user");z(o,"aoRowCallback",e.fnRowCallback,"user");z(o,"aoRowCreatedCallback",e.fnCreatedRow,"user");z(o,"aoHeaderCallback",e.fnHeaderCallback,"user");z(o,"aoFooterCallback",e.fnFooterCallback,"user");z(o,"aoInitComplete",e.fnInitComplete,"user");z(o,"aoPreDrawCallback",e.fnPreDrawCallback,"user");o.rowIdFn=P(e.rowId);fb(o);j=o.oClasses;
e.bJQueryUI?(h.extend(j,m.ext.oJUIClasses,e.oClasses),e.sDom===l.sDom&&"lfrtip"===l.sDom&&(o.sDom='<"H"lfr>t<"F"ip>'),o.renderer)?h.isPlainObject(o.renderer)&&!o.renderer.header&&(o.renderer.header="jqueryui"):o.renderer="jqueryui":h.extend(j,m.ext.classes,e.oClasses);r.addClass(j.sTable);o.iInitDisplayStart===k&&(o.iInitDisplayStart=e.iDisplayStart,o._iDisplayStart=e.iDisplayStart);null!==e.iDeferLoading&&(o.bDeferLoading=!0,g=h.isArray(e.iDeferLoading),o._iRecordsDisplay=g?e.iDeferLoading[0]:e.iDeferLoading,
o._iRecordsTotal=g?e.iDeferLoading[1]:e.iDeferLoading);var t=o.oLanguage;h.extend(!0,t,e.oLanguage);""!==t.sUrl&&(h.ajax({dataType:"json",url:t.sUrl,success:function(a){S(a);I(l.oLanguage,a);h.extend(true,t,a);ia(o)},error:function(){ia(o)}}),n=!0);null===e.asStripeClasses&&(o.asStripeClasses=[j.sStripeOdd,j.sStripeEven]);var g=o.asStripeClasses,s=r.children("tbody").find("tr").eq(0);-1!==h.inArray(!0,h.map(g,function(a){return s.hasClass(a)}))&&(h("tbody tr",this).removeClass(g.join(" ")),o.asDestroyStripes=
g.slice());q=[];g=this.getElementsByTagName("thead");0!==g.length&&(fa(o.aoHeader,g[0]),q=qa(o));if(null===e.aoColumns){p=[];g=0;for(i=q.length;g<i;g++)p.push(null)}else p=e.aoColumns;g=0;for(i=p.length;g<i;g++)Ga(o,q?q[g]:null);hb(o,e.aoColumnDefs,p,function(a,b){la(o,a,b)});if(s.length){var u=function(a,b){return a.getAttribute("data-"+b)!==null?b:null};h(s[0]).children("th, td").each(function(a,b){var c=o.aoColumns[a];if(c.mData===a){var d=u(b,"sort")||u(b,"order"),e=u(b,"filter")||u(b,"search");
if(d!==null||e!==null){c.mData={_:a+".display",sort:d!==null?a+".@data-"+d:k,type:d!==null?a+".@data-"+d:k,filter:e!==null?a+".@data-"+e:k};la(o,a)}}})}var v=o.oFeatures;e.bStateSave&&(v.bStateSave=!0,Kb(o,e),z(o,"aoDrawCallback",ya,"state_save"));if(e.aaSorting===k){q=o.aaSorting;g=0;for(i=q.length;g<i;g++)q[g][1]=o.aoColumns[g].asSorting[0]}xa(o);v.bSort&&z(o,"aoDrawCallback",function(){if(o.bSorted){var a=V(o),b={};h.each(a,function(a,c){b[c.src]=c.dir});w(o,null,"order",[o,a,b]);Jb(o)}});z(o,
"aoDrawCallback",function(){(o.bSorted||y(o)==="ssp"||v.bDeferRender)&&xa(o)},"sc");g=r.children("caption").each(function(){this._captionSide=r.css("caption-side")});i=r.children("thead");0===i.length&&(i=h("<thead/>").appendTo(this));o.nTHead=i[0];i=r.children("tbody");0===i.length&&(i=h("<tbody/>").appendTo(this));o.nTBody=i[0];i=r.children("tfoot");if(0===i.length&&0<g.length&&(""!==o.oScroll.sX||""!==o.oScroll.sY))i=h("<tfoot/>").appendTo(this);0===i.length||0===i.children().length?r.addClass(j.sNoFooter):
0<i.length&&(o.nTFoot=i[0],fa(o.aoFooter,o.nTFoot));if(e.aaData)for(g=0;g<e.aaData.length;g++)L(o,e.aaData[g]);else(o.bDeferLoading||"dom"==y(o))&&ma(o,h(o.nTBody).children("tr"));o.aiDisplay=o.aiDisplayMaster.slice();o.bInitialised=!0;!1===n&&ia(o)}});b=null;return this};var Tb=[],x=Array.prototype,cc=function(a){var b,c,d=m.settings,e=h.map(d,function(a){return a.nTable});if(a){if(a.nTable&&a.oApi)return[a];if(a.nodeName&&"table"===a.nodeName.toLowerCase())return b=h.inArray(a,e),-1!==b?[d[b]]:
null;if(a&&"function"===typeof a.settings)return a.settings().toArray();"string"===typeof a?c=h(a):a instanceof h&&(c=a)}else return[];if(c)return c.map(function(){b=h.inArray(this,e);return-1!==b?d[b]:null}).toArray()};t=function(a,b){if(!(this instanceof t))return new t(a,b);var c=[],d=function(a){(a=cc(a))&&(c=c.concat(a))};if(h.isArray(a))for(var e=0,f=a.length;e<f;e++)d(a[e]);else d(a);this.context=pa(c);b&&h.merge(this,b);this.selector={rows:null,cols:null,opts:null};t.extend(this,this,Tb)};
m.Api=t;h.extend(t.prototype,{any:function(){return 0!==this.count()},concat:x.concat,context:[],count:function(){return this.flatten().length},each:function(a){for(var b=0,c=this.length;b<c;b++)a.call(this,this[b],b,this);return this},eq:function(a){var b=this.context;return b.length>a?new t(b[a],this[a]):null},filter:function(a){var b=[];if(x.filter)b=x.filter.call(this,a,this);else for(var c=0,d=this.length;c<d;c++)a.call(this,this[c],c,this)&&b.push(this[c]);return new t(this.context,b)},flatten:function(){var a=
[];return new t(this.context,a.concat.apply(a,this.toArray()))},join:x.join,indexOf:x.indexOf||function(a,b){for(var c=b||0,d=this.length;c<d;c++)if(this[c]===a)return c;return-1},iterator:function(a,b,c,d){var e=[],f,g,h,j,n,l=this.context,m,q,p=this.selector;"string"===typeof a&&(d=c,c=b,b=a,a=!1);g=0;for(h=l.length;g<h;g++){var o=new t(l[g]);if("table"===b)f=c.call(o,l[g],g),f!==k&&e.push(f);else if("columns"===b||"rows"===b)f=c.call(o,l[g],this[g],g),f!==k&&e.push(f);else if("column"===b||"column-rows"===
b||"row"===b||"cell"===b){q=this[g];"column-rows"===b&&(m=Da(l[g],p.opts));j=0;for(n=q.length;j<n;j++)f=q[j],f="cell"===b?c.call(o,l[g],f.row,f.column,g,j):c.call(o,l[g],f,g,j,m),f!==k&&e.push(f)}}return e.length||d?(a=new t(l,a?e.concat.apply([],e):e),b=a.selector,b.rows=p.rows,b.cols=p.cols,b.opts=p.opts,a):this},lastIndexOf:x.lastIndexOf||function(a,b){return this.indexOf.apply(this.toArray.reverse(),arguments)},length:0,map:function(a){var b=[];if(x.map)b=x.map.call(this,a,this);else for(var c=
0,d=this.length;c<d;c++)b.push(a.call(this,this[c],c));return new t(this.context,b)},pluck:function(a){return this.map(function(b){return b[a]})},pop:x.pop,push:x.push,reduce:x.reduce||function(a,b){return gb(this,a,b,0,this.length,1)},reduceRight:x.reduceRight||function(a,b){return gb(this,a,b,this.length-1,-1,-1)},reverse:x.reverse,selector:null,shift:x.shift,sort:x.sort,splice:x.splice,toArray:function(){return x.slice.call(this)},to$:function(){return h(this)},toJQuery:function(){return h(this)},
unique:function(){return new t(this.context,pa(this))},unshift:x.unshift});t.extend=function(a,b,c){if(c.length&&b&&(b instanceof t||b.__dt_wrapper)){var d,e,f,g=function(a,b,c){return function(){var d=b.apply(a,arguments);t.extend(d,d,c.methodExt);return d}};d=0;for(e=c.length;d<e;d++)f=c[d],b[f.name]="function"===typeof f.val?g(a,f.val,f):h.isPlainObject(f.val)?{}:f.val,b[f.name].__dt_wrapper=!0,t.extend(a,b[f.name],f.propExt)}};t.register=p=function(a,b){if(h.isArray(a))for(var c=0,d=a.length;c<
d;c++)t.register(a[c],b);else for(var e=a.split("."),f=Tb,g,i,c=0,d=e.length;c<d;c++){g=(i=-1!==e[c].indexOf("()"))?e[c].replace("()",""):e[c];var j;a:{j=0;for(var n=f.length;j<n;j++)if(f[j].name===g){j=f[j];break a}j=null}j||(j={name:g,val:{},methodExt:[],propExt:[]},f.push(j));c===d-1?j.val=b:f=i?j.methodExt:j.propExt}};t.registerPlural=s=function(a,b,c){t.register(a,c);t.register(b,function(){var a=c.apply(this,arguments);return a===this?this:a instanceof t?a.length?h.isArray(a[0])?new t(a.context,
a[0]):a[0]:k:a})};p("tables()",function(a){var b;if(a){b=t;var c=this.context;if("number"===typeof a)a=[c[a]];else var d=h.map(c,function(a){return a.nTable}),a=h(d).filter(a).map(function(){var a=h.inArray(this,d);return c[a]}).toArray();b=new b(a)}else b=this;return b});p("table()",function(a){var a=this.tables(a),b=a.context;return b.length?new t(b[0]):a});s("tables().nodes()","table().node()",function(){return this.iterator("table",function(a){return a.nTable},1)});s("tables().body()","table().body()",
function(){return this.iterator("table",function(a){return a.nTBody},1)});s("tables().header()","table().header()",function(){return this.iterator("table",function(a){return a.nTHead},1)});s("tables().footer()","table().footer()",function(){return this.iterator("table",function(a){return a.nTFoot},1)});s("tables().containers()","table().container()",function(){return this.iterator("table",function(a){return a.nTableWrapper},1)});p("draw()",function(a){return this.iterator("table",function(b){"page"===
a?M(b):("string"===typeof a&&(a="full-hold"===a?!1:!0),R(b,!1===a))})});p("page()",function(a){return a===k?this.page.info().page:this.iterator("table",function(b){Ta(b,a)})});p("page.info()",function(){if(0===this.context.length)return k;var a=this.context[0],b=a._iDisplayStart,c=a._iDisplayLength,d=a.fnRecordsDisplay(),e=-1===c;return{page:e?0:Math.floor(b/c),pages:e?1:Math.ceil(d/c),start:b,end:a.fnDisplayEnd(),length:c,recordsTotal:a.fnRecordsTotal(),recordsDisplay:d,serverSide:"ssp"===y(a)}});
p("page.len()",function(a){return a===k?0!==this.context.length?this.context[0]._iDisplayLength:k:this.iterator("table",function(b){Ra(b,a)})});var Ub=function(a,b,c){if(c){var d=new t(a);d.one("draw",function(){c(d.ajax.json())})}if("ssp"==y(a))R(a,b);else{C(a,!0);var e=a.jqXHR;e&&4!==e.readyState&&e.abort();ra(a,[],function(c){na(a);for(var c=sa(a,c),d=0,e=c.length;d<e;d++)L(a,c[d]);R(a,b);C(a,!1)})}};p("ajax.json()",function(){var a=this.context;if(0<a.length)return a[0].json});p("ajax.params()",
function(){var a=this.context;if(0<a.length)return a[0].oAjaxData});p("ajax.reload()",function(a,b){return this.iterator("table",function(c){Ub(c,!1===b,a)})});p("ajax.url()",function(a){var b=this.context;if(a===k){if(0===b.length)return k;b=b[0];return b.ajax?h.isPlainObject(b.ajax)?b.ajax.url:b.ajax:b.sAjaxSource}return this.iterator("table",function(b){h.isPlainObject(b.ajax)?b.ajax.url=a:b.ajax=a})});p("ajax.url().load()",function(a,b){return this.iterator("table",function(c){Ub(c,!1===b,a)})});
var Za=function(a,b,c,d,e){var f=[],g,i,j,n,l,m;j=typeof b;if(!b||"string"===j||"function"===j||b.length===k)b=[b];j=0;for(n=b.length;j<n;j++){i=b[j]&&b[j].split?b[j].split(","):[b[j]];l=0;for(m=i.length;l<m;l++)(g=c("string"===typeof i[l]?h.trim(i[l]):i[l]))&&g.length&&(f=f.concat(g))}a=v.selector[a];if(a.length){j=0;for(n=a.length;j<n;j++)f=a[j](d,e,f)}return pa(f)},$a=function(a){a||(a={});a.filter&&a.search===k&&(a.search=a.filter);return h.extend({search:"none",order:"current",page:"all"},a)},
ab=function(a){for(var b=0,c=a.length;b<c;b++)if(0<a[b].length)return a[0]=a[b],a[0].length=1,a.length=1,a.context=[a.context[b]],a;a.length=0;return a},Da=function(a,b){var c,d,e,f=[],g=a.aiDisplay;c=a.aiDisplayMaster;var i=b.search;d=b.order;e=b.page;if("ssp"==y(a))return"removed"===i?[]:W(0,c.length);if("current"==e){c=a._iDisplayStart;for(d=a.fnDisplayEnd();c<d;c++)f.push(g[c])}else if("current"==d||"applied"==d)f="none"==i?c.slice():"applied"==i?g.slice():h.map(c,function(a){return-1===h.inArray(a,
g)?a:null});else if("index"==d||"original"==d){c=0;for(d=a.aoData.length;c<d;c++)"none"==i?f.push(c):(e=h.inArray(c,g),(-1===e&&"removed"==i||0<=e&&"applied"==i)&&f.push(c))}return f};p("rows()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=$a(b),c=this.iterator("table",function(c){var e=b;return Za("row",a,function(a){var b=Pb(a);if(b!==null&&!e)return[b];var i=Da(c,e);if(b!==null&&h.inArray(b,i)!==-1)return[b];if(!a)return i;if(typeof a==="function")return h.map(i,function(b){var e=
c.aoData[b];return a(b,e._aData,e.nTr)?b:null});b=Sb(ja(c.aoData,i,"nTr"));if(a.nodeName&&h.inArray(a,b)!==-1)return[a._DT_RowIndex];if(typeof a==="string"&&a.charAt(0)==="#"){i=c.aIds[a.replace(/^#/,"")];if(i!==k)return[i.idx]}return h(b).filter(a).map(function(){return this._DT_RowIndex}).toArray()},c,e)},1);c.selector.rows=a;c.selector.opts=b;return c});p("rows().nodes()",function(){return this.iterator("row",function(a,b){return a.aoData[b].nTr||k},1)});p("rows().data()",function(){return this.iterator(!0,
"rows",function(a,b){return ja(a.aoData,b,"_aData")},1)});s("rows().cache()","row().cache()",function(a){return this.iterator("row",function(b,c){var d=b.aoData[c];return"search"===a?d._aFilterData:d._aSortData},1)});s("rows().invalidate()","row().invalidate()",function(a){return this.iterator("row",function(b,c){ea(b,c,a)})});s("rows().indexes()","row().index()",function(){return this.iterator("row",function(a,b){return b},1)});s("rows().ids()","row().id()",function(a){for(var b=[],c=this.context,
d=0,e=c.length;d<e;d++)for(var f=0,g=this[d].length;f<g;f++){var h=c[d].rowIdFn(c[d].aoData[this[d][f]]._aData);b.push((!0===a?"#":"")+h)}return new t(c,b)});s("rows().remove()","row().remove()",function(){var a=this;this.iterator("row",function(b,c,d){var e=b.aoData,f=e[c];e.splice(c,1);for(var g=0,h=e.length;g<h;g++)null!==e[g].nTr&&(e[g].nTr._DT_RowIndex=g);oa(b.aiDisplayMaster,c);oa(b.aiDisplay,c);oa(a[d],c,!1);Sa(b);c=b.rowIdFn(f._aData);c!==k&&delete b.aIds[c]});this.iterator("table",function(a){for(var c=
0,d=a.aoData.length;c<d;c++)a.aoData[c].idx=c});return this});p("rows.add()",function(a){var b=this.iterator("table",function(b){var c,f,g,h=[];f=0;for(g=a.length;f<g;f++)c=a[f],c.nodeName&&"TR"===c.nodeName.toUpperCase()?h.push(ma(b,c)[0]):h.push(L(b,c));return h},1),c=this.rows(-1);c.pop();h.merge(c,b);return c});p("row()",function(a,b){return ab(this.rows(a,b))});p("row().data()",function(a){var b=this.context;if(a===k)return b.length&&this.length?b[0].aoData[this[0]]._aData:k;b[0].aoData[this[0]]._aData=
a;ea(b[0],this[0],"data");return this});p("row().node()",function(){var a=this.context;return a.length&&this.length?a[0].aoData[this[0]].nTr||null:null});p("row.add()",function(a){a instanceof h&&a.length&&(a=a[0]);var b=this.iterator("table",function(b){return a.nodeName&&"TR"===a.nodeName.toUpperCase()?ma(b,a)[0]:L(b,a)});return this.row(b[0])});var bb=function(a,b){var c=a.context;if(c.length&&(c=c[0].aoData[b!==k?b:a[0]])&&c._details)c._details.remove(),c._detailsShow=k,c._details=k},Vb=function(a,
b){var c=a.context;if(c.length&&a.length){var d=c[0].aoData[a[0]];if(d._details){(d._detailsShow=b)?d._details.insertAfter(d.nTr):d._details.detach();var e=c[0],f=new t(e),g=e.aoData;f.off("draw.dt.DT_details column-visibility.dt.DT_details destroy.dt.DT_details");0<D(g,"_details").length&&(f.on("draw.dt.DT_details",function(a,b){e===b&&f.rows({page:"current"}).eq(0).each(function(a){a=g[a];a._detailsShow&&a._details.insertAfter(a.nTr)})}),f.on("column-visibility.dt.DT_details",function(a,b){if(e===
b)for(var c,d=ca(b),f=0,h=g.length;f<h;f++)c=g[f],c._details&&c._details.children("td[colspan]").attr("colspan",d)}),f.on("destroy.dt.DT_details",function(a,b){if(e===b)for(var c=0,d=g.length;c<d;c++)g[c]._details&&bb(f,c)}))}}};p("row().child()",function(a,b){var c=this.context;if(a===k)return c.length&&this.length?c[0].aoData[this[0]]._details:k;if(!0===a)this.child.show();else if(!1===a)bb(this);else if(c.length&&this.length){var d=c[0],c=c[0].aoData[this[0]],e=[],f=function(a,b){if(h.isArray(a)||
a instanceof h)for(var c=0,k=a.length;c<k;c++)f(a[c],b);else a.nodeName&&"tr"===a.nodeName.toLowerCase()?e.push(a):(c=h("<tr><td/></tr>").addClass(b),h("td",c).addClass(b).html(a)[0].colSpan=ca(d),e.push(c[0]))};f(a,b);c._details&&c._details.remove();c._details=h(e);c._detailsShow&&c._details.insertAfter(c.nTr)}return this});p(["row().child.show()","row().child().show()"],function(){Vb(this,!0);return this});p(["row().child.hide()","row().child().hide()"],function(){Vb(this,!1);return this});p(["row().child.remove()",
"row().child().remove()"],function(){bb(this);return this});p("row().child.isShown()",function(){var a=this.context;return a.length&&this.length?a[0].aoData[this[0]]._detailsShow||!1:!1});var dc=/^(.+):(name|visIdx|visible)$/,Wb=function(a,b,c,d,e){for(var c=[],d=0,f=e.length;d<f;d++)c.push(B(a,e[d],b));return c};p("columns()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=$a(b),c=this.iterator("table",function(c){var e=a,f=b,g=c.aoColumns,i=D(g,"sName"),j=D(g,"nTh");return Za("column",
e,function(a){var b=Pb(a);if(a==="")return W(g.length);if(b!==null)return[b>=0?b:g.length+b];if(typeof a==="function"){var e=Da(c,f);return h.map(g,function(b,f){return a(f,Wb(c,f,0,0,e),j[f])?f:null})}var k=typeof a==="string"?a.match(dc):"";if(k)switch(k[2]){case "visIdx":case "visible":b=parseInt(k[1],10);if(b<0){var m=h.map(g,function(a,b){return a.bVisible?b:null});return[m[m.length+b]]}return[$(c,b)];case "name":return h.map(i,function(a,b){return a===k[1]?b:null})}else return h(j).filter(a).map(function(){return h.inArray(this,
j)}).toArray()},c,f)},1);c.selector.cols=a;c.selector.opts=b;return c});s("columns().header()","column().header()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTh},1)});s("columns().footer()","column().footer()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTf},1)});s("columns().data()","column().data()",function(){return this.iterator("column-rows",Wb,1)});s("columns().dataSrc()","column().dataSrc()",function(){return this.iterator("column",
function(a,b){return a.aoColumns[b].mData},1)});s("columns().cache()","column().cache()",function(a){return this.iterator("column-rows",function(b,c,d,e,f){return ja(b.aoData,f,"search"===a?"_aFilterData":"_aSortData",c)},1)});s("columns().nodes()","column().nodes()",function(){return this.iterator("column-rows",function(a,b,c,d,e){return ja(a.aoData,e,"anCells",b)},1)});s("columns().visible()","column().visible()",function(a,b){return this.iterator("column",function(c,d){if(a===k)return c.aoColumns[d].bVisible;
var e=c.aoColumns,f=e[d],g=c.aoData,i,j,m;if(a!==k&&f.bVisible!==a){if(a){var l=h.inArray(!0,D(e,"bVisible"),d+1);i=0;for(j=g.length;i<j;i++)m=g[i].nTr,e=g[i].anCells,m&&m.insertBefore(e[d],e[l]||null)}else h(D(c.aoData,"anCells",d)).detach();f.bVisible=a;ga(c,c.aoHeader);ga(c,c.aoFooter);if(b===k||b)Y(c),(c.oScroll.sX||c.oScroll.sY)&&Z(c);w(c,null,"column-visibility",[c,d,a]);ya(c)}})});s("columns().indexes()","column().index()",function(a){return this.iterator("column",function(b,c){return"visible"===
a?ba(b,c):c},1)});p("columns.adjust()",function(){return this.iterator("table",function(a){Y(a)},1)});p("column.index()",function(a,b){if(0!==this.context.length){var c=this.context[0];if("fromVisible"===a||"toData"===a)return $(c,b);if("fromData"===a||"toVisible"===a)return ba(c,b)}});p("column()",function(a,b){return ab(this.columns(a,b))});p("cells()",function(a,b,c){h.isPlainObject(a)&&(a.row===k?(c=a,a=null):(c=b,b=null));h.isPlainObject(b)&&(c=b,b=null);if(null===b||b===k)return this.iterator("table",
function(b){var d=a,e=$a(c),f=b.aoData,g=Da(b,e),i=Sb(ja(f,g,"anCells")),j=h([].concat.apply([],i)),l,m=b.aoColumns.length,n,p,t,s,u,v;return Za("cell",d,function(a){var c=typeof a==="function";if(a===null||a===k||c){n=[];p=0;for(t=g.length;p<t;p++){l=g[p];for(s=0;s<m;s++){u={row:l,column:s};if(c){v=f[l];a(u,B(b,l,s),v.anCells?v.anCells[s]:null)&&n.push(u)}else n.push(u)}}return n}return h.isPlainObject(a)?[a]:j.filter(a).map(function(a,b){if(b.parentNode)l=b.parentNode._DT_RowIndex;else{a=0;for(t=
f.length;a<t;a++)if(h.inArray(b,f[a].anCells)!==-1){l=a;break}}return{row:l,column:h.inArray(b,f[l].anCells)}}).toArray()},b,e)});var d=this.columns(b,c),e=this.rows(a,c),f,g,i,j,m,l=this.iterator("table",function(a,b){f=[];g=0;for(i=e[b].length;g<i;g++){j=0;for(m=d[b].length;j<m;j++)f.push({row:e[b][g],column:d[b][j]})}return f},1);h.extend(l.selector,{cols:b,rows:a,opts:c});return l});s("cells().nodes()","cell().node()",function(){return this.iterator("cell",function(a,b,c){return(a=a.aoData[b].anCells)?
a[c]:k},1)});p("cells().data()",function(){return this.iterator("cell",function(a,b,c){return B(a,b,c)},1)});s("cells().cache()","cell().cache()",function(a){a="search"===a?"_aFilterData":"_aSortData";return this.iterator("cell",function(b,c,d){return b.aoData[c][a][d]},1)});s("cells().render()","cell().render()",function(a){return this.iterator("cell",function(b,c,d){return B(b,c,d,a)},1)});s("cells().indexes()","cell().index()",function(){return this.iterator("cell",function(a,b,c){return{row:b,
column:c,columnVisible:ba(a,c)}},1)});s("cells().invalidate()","cell().invalidate()",function(a){return this.iterator("cell",function(b,c,d){ea(b,c,a,d)})});p("cell()",function(a,b,c){return ab(this.cells(a,b,c))});p("cell().data()",function(a){var b=this.context,c=this[0];if(a===k)return b.length&&c.length?B(b[0],c[0].row,c[0].column):k;ib(b[0],c[0].row,c[0].column,a);ea(b[0],c[0].row,"data",c[0].column);return this});p("order()",function(a,b){var c=this.context;if(a===k)return 0!==c.length?c[0].aaSorting:
k;"number"===typeof a?a=[[a,b]]:h.isArray(a[0])||(a=Array.prototype.slice.call(arguments));return this.iterator("table",function(b){b.aaSorting=a.slice()})});p("order.listener()",function(a,b,c){return this.iterator("table",function(d){Oa(d,a,b,c)})});p(["columns().order()","column().order()"],function(a){var b=this;return this.iterator("table",function(c,d){var e=[];h.each(b[d],function(b,c){e.push([c,a])});c.aaSorting=e})});p("search()",function(a,b,c,d){var e=this.context;return a===k?0!==e.length?
e[0].oPreviousSearch.sSearch:k:this.iterator("table",function(e){e.oFeatures.bFilter&&ha(e,h.extend({},e.oPreviousSearch,{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===d?!0:d}),1)})});s("columns().search()","column().search()",function(a,b,c,d){return this.iterator("column",function(e,f){var g=e.aoPreSearchCols;if(a===k)return g[f].sSearch;e.oFeatures.bFilter&&(h.extend(g[f],{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===d?!0:d}),ha(e,
e.oPreviousSearch,1))})});p("state()",function(){return this.context.length?this.context[0].oSavedState:null});p("state.clear()",function(){return this.iterator("table",function(a){a.fnStateSaveCallback.call(a.oInstance,a,{})})});p("state.loaded()",function(){return this.context.length?this.context[0].oLoadedState:null});p("state.save()",function(){return this.iterator("table",function(a){ya(a)})});m.versionCheck=m.fnVersionCheck=function(a){for(var b=m.version.split("."),a=a.split("."),c,d,e=0,f=
a.length;e<f;e++)if(c=parseInt(b[e],10)||0,d=parseInt(a[e],10)||0,c!==d)return c>d;return!0};m.isDataTable=m.fnIsDataTable=function(a){var b=h(a).get(0),c=!1;h.each(m.settings,function(a,e){var f=e.nScrollHead?h("table",e.nScrollHead)[0]:null,g=e.nScrollFoot?h("table",e.nScrollFoot)[0]:null;if(e.nTable===b||f===b||g===b)c=!0});return c};m.tables=m.fnTables=function(a){var b=!1;h.isPlainObject(a)&&(b=a.api,a=a.visible);var c=h.map(m.settings,function(b){if(!a||a&&h(b.nTable).is(":visible"))return b.nTable});
return b?new t(c):c};m.util={throttle:ua,escapeRegex:va};m.camelToHungarian=I;p("$()",function(a,b){var c=this.rows(b).nodes(),c=h(c);return h([].concat(c.filter(a).toArray(),c.find(a).toArray()))});h.each(["on","one","off"],function(a,b){p(b+"()",function(){var a=Array.prototype.slice.call(arguments);a[0].match(/\.dt\b/)||(a[0]+=".dt");var d=h(this.tables().nodes());d[b].apply(d,a);return this})});p("clear()",function(){return this.iterator("table",function(a){na(a)})});p("settings()",function(){return new t(this.context,
this.context)});p("init()",function(){var a=this.context;return a.length?a[0].oInit:null});p("data()",function(){return this.iterator("table",function(a){return D(a.aoData,"_aData")}).flatten()});p("destroy()",function(a){a=a||!1;return this.iterator("table",function(b){var c=b.nTableWrapper.parentNode,d=b.oClasses,e=b.nTable,f=b.nTBody,g=b.nTHead,i=b.nTFoot,j=h(e),f=h(f),k=h(b.nTableWrapper),l=h.map(b.aoData,function(a){return a.nTr}),p;b.bDestroying=!0;w(b,"aoDestroyCallback","destroy",[b]);a||
(new t(b)).columns().visible(!0);k.unbind(".DT").find(":not(tbody *)").unbind(".DT");h(Fa).unbind(".DT-"+b.sInstance);e!=g.parentNode&&(j.children("thead").detach(),j.append(g));i&&e!=i.parentNode&&(j.children("tfoot").detach(),j.append(i));b.aaSorting=[];b.aaSortingFixed=[];xa(b);h(l).removeClass(b.asStripeClasses.join(" "));h("th, td",g).removeClass(d.sSortable+" "+d.sSortableAsc+" "+d.sSortableDesc+" "+d.sSortableNone);b.bJUI&&(h("th span."+d.sSortIcon+", td span."+d.sSortIcon,g).detach(),h("th, td",
g).each(function(){var a=h("div."+d.sSortJUIWrapper,this);h(this).append(a.contents());a.detach()}));f.children().detach();f.append(l);g=a?"remove":"detach";j[g]();k[g]();!a&&c&&(c.insertBefore(e,b.nTableReinsertBefore),j.css("width",b.sDestroyWidth).removeClass(d.sTable),(p=b.asDestroyStripes.length)&&f.children().each(function(a){h(this).addClass(b.asDestroyStripes[a%p])}));c=h.inArray(b,m.settings);-1!==c&&m.settings.splice(c,1)})});h.each(["column","row","cell"],function(a,b){p(b+"s().every()",
function(a){return this.iterator(b,function(d,e,f,g,h){a.call((new t(d))[b](e,"cell"===b?f:k),e,f,g,h)})})});p("i18n()",function(a,b,c){var d=this.context[0],a=P(a)(d.oLanguage);a===k&&(a=b);c!==k&&h.isPlainObject(a)&&(a=a[c]!==k?a[c]:a._);return a.replace("%d",c)});m.version="1.10.9";m.settings=[];m.models={};m.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};m.models.oRow={nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,_sRowStripe:"",src:null,
idx:-1};m.models.oColumn={idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std",sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null};m.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,
25,50,100],aoColumns:null,aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bJQueryUI:!1,bLengthChange:!0,bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1,fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(a){return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,this.oLanguage.sThousands)},fnHeaderCallback:null,
fnInfoCallback:null,fnInitComplete:null,fnPreDrawCallback:null,fnRowCallback:null,fnServerData:null,fnServerParams:null,fnStateLoadCallback:function(a){try{return JSON.parse((-1===a.iStateDuration?sessionStorage:localStorage).getItem("DataTables_"+a.sInstance+"_"+location.pathname))}catch(b){}},fnStateLoadParams:null,fnStateLoaded:null,fnStateSaveCallback:function(a,b){try{(-1===a.iStateDuration?sessionStorage:localStorage).setItem("DataTables_"+a.sInstance+"_"+location.pathname,JSON.stringify(b))}catch(c){}},
fnStateSaveParams:null,iStateDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",sSortDescending:": activate to sort column descending"},oPaginate:{sFirst:"First",sLast:"Last",sNext:"Next",sPrevious:"Previous"},sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",
sInfoPostFix:"",sDecimal:"",sThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sSearchPlaceholder:"",sUrl:"",sZeroRecords:"No matching records found"},oSearch:h.extend({},m.models.oSearch),sAjaxDataProp:"data",sAjaxSource:null,sDom:"lfrtip",searchDelay:null,sPaginationType:"simple_numbers",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null,rowId:"DT_RowId"};X(m.defaults);m.defaults.column={aDataSort:null,
iDataSort:-1,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};X(m.defaults.column);m.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,
iBarWidth:0,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1,bScrollbarLeft:!1,bBounding:!1,barWidth:0},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aIds:{},aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,asDestroyStripes:[],sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],
aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,searchDelay:null,sPaginationType:"two_button",iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,bAjaxDataGet:!0,jqXHR:null,json:k,oAjaxData:k,fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,
iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,bJUI:null,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return"ssp"==y(this)?1*this._iRecordsTotal:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==y(this)?1*this._iRecordsDisplay:this.aiDisplay.length},fnDisplayEnd:function(){var a=this._iDisplayLength,b=this._iDisplayStart,c=b+a,d=this.aiDisplay.length,e=
this.oFeatures,f=e.bPaginate;return e.bServerSide?!1===f||-1===a?b+d:Math.min(b+a,this._iRecordsDisplay):!f||c>d||-1===a?d:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,aLastSort:[],oPlugins:{},rowIdFn:null,rowId:null};m.ext=v={buttons:{},classes:{},errMode:"alert",feature:[],search:[],selector:{cell:[],column:[],row:[]},internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:m.fnVersionCheck,
iApiIndex:0,oJUIClasses:{},sVersion:m.version};h.extend(v,{afnFiltering:v.search,aTypes:v.type.detect,ofnSearch:v.type.search,oSort:v.type.order,afnSortData:v.order,aoFeatures:v.feature,oApi:v.internal,oStdClasses:v.classes,oPagination:v.pager});h.extend(m.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",
sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",
sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sJUIHeader:"",sJUIFooter:""});var Ea="",Ea="",G=Ea+"ui-state-default",ka=Ea+"css_right ui-icon ui-icon-",Xb=Ea+"fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix";h.extend(m.ext.oJUIClasses,m.ext.classes,{sPageButton:"fg-button ui-button "+G,sPageButtonActive:"ui-state-disabled",
sPageButtonDisabled:"ui-state-disabled",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sSortAsc:G+" sorting_asc",sSortDesc:G+" sorting_desc",sSortable:G+" sorting",sSortableAsc:G+" sorting_asc_disabled",sSortableDesc:G+" sorting_desc_disabled",sSortableNone:G+" sorting_disabled",sSortJUIAsc:ka+"triangle-1-n",sSortJUIDesc:ka+"triangle-1-s",sSortJUI:ka+"carat-2-n-s",sSortJUIAscAllowed:ka+"carat-1-n",sSortJUIDescAllowed:ka+"carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",
sSortIcon:"DataTables_sort_icon",sScrollHead:"dataTables_scrollHead "+G,sScrollFoot:"dataTables_scrollFoot "+G,sHeaderTH:G,sFooterTH:G,sJUIHeader:Xb+" ui-corner-tl ui-corner-tr",sJUIFooter:Xb+" ui-corner-bl ui-corner-br"});var Mb=m.ext.pager;h.extend(Mb,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},numbers:function(a,b){return[Aa(a,b)]},simple_numbers:function(a,b){return["previous",Aa(a,b),"next"]},full_numbers:function(a,b){return["first",
"previous",Aa(a,b),"next","last"]},_numbers:Aa,numbers_length:7});h.extend(!0,m.ext.renderer,{pageButton:{_:function(a,b,c,d,e,f){var g=a.oClasses,i=a.oLanguage.oPaginate,j,k,l=0,m=function(b,d){var p,q,t,s,u=function(b){Ta(a,b.data.action,true)};p=0;for(q=d.length;p<q;p++){s=d[p];if(h.isArray(s)){t=h("<"+(s.DT_el||"div")+"/>").appendTo(b);m(t,s)}else{j=null;k="";switch(s){case "ellipsis":b.append('<span class="ellipsis">&#x2026;</span>');break;case "first":j=i.sFirst;k=s+(e>0?"":" "+g.sPageButtonDisabled);
break;case "previous":j=i.sPrevious;k=s+(e>0?"":" "+g.sPageButtonDisabled);break;case "next":j=i.sNext;k=s+(e<f-1?"":" "+g.sPageButtonDisabled);break;case "last":j=i.sLast;k=s+(e<f-1?"":" "+g.sPageButtonDisabled);break;default:j=s+1;k=e===s?g.sPageButtonActive:""}if(j!==null){t=h("<a>",{"class":g.sPageButton+" "+k,"aria-controls":a.sTableId,"data-dt-idx":l,tabindex:a.iTabIndex,id:c===0&&typeof s==="string"?a.sTableId+"_"+s:null}).html(j).appendTo(b);Va(t,{action:s},u);l++}}}},p;try{p=h(b).find(T.activeElement).data("dt-idx")}catch(t){}m(h(b).empty(),
d);p&&h(b).find("[data-dt-idx="+p+"]").focus()}}});h.extend(m.ext.type.detect,[function(a,b){var c=b.oLanguage.sDecimal;return Ya(a,c)?"num"+c:null},function(a){if(a&&!(a instanceof Date)&&(!ac.test(a)||!bc.test(a)))return null;var b=Date.parse(a);return null!==b&&!isNaN(b)||K(a)?"date":null},function(a,b){var c=b.oLanguage.sDecimal;return Ya(a,c,!0)?"num-fmt"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Rb(a,c)?"html-num"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Rb(a,c,
!0)?"html-num-fmt"+c:null},function(a){return K(a)||"string"===typeof a&&-1!==a.indexOf("<")?"html":null}]);h.extend(m.ext.type.search,{html:function(a){return K(a)?a:"string"===typeof a?a.replace(Ob," ").replace(Ca,""):""},string:function(a){return K(a)?a:"string"===typeof a?a.replace(Ob," "):a}});var Ba=function(a,b,c,d){if(0!==a&&(!a||"-"===a))return-Infinity;b&&(a=Qb(a,b));a.replace&&(c&&(a=a.replace(c,"")),d&&(a=a.replace(d,"")));return 1*a};h.extend(v.type.order,{"date-pre":function(a){return Date.parse(a)||
0},"html-pre":function(a){return K(a)?"":a.replace?a.replace(/<.*?>/g,"").toLowerCase():a+""},"string-pre":function(a){return K(a)?"":"string"===typeof a?a.toLowerCase():!a.toString?"":a.toString()},"string-asc":function(a,b){return a<b?-1:a>b?1:0},"string-desc":function(a,b){return a<b?1:a>b?-1:0}});cb("");h.extend(!0,m.ext.renderer,{header:{_:function(a,b,c,d){h(a.nTable).on("order.dt.DT",function(e,f,g,h){if(a===f){e=c.idx;b.removeClass(c.sSortingClass+" "+d.sSortAsc+" "+d.sSortDesc).addClass(h[e]==
"asc"?d.sSortAsc:h[e]=="desc"?d.sSortDesc:c.sSortingClass)}})},jqueryui:function(a,b,c,d){h("<div/>").addClass(d.sSortJUIWrapper).append(b.contents()).append(h("<span/>").addClass(d.sSortIcon+" "+c.sSortingClassJUI)).appendTo(b);h(a.nTable).on("order.dt.DT",function(e,f,g,h){if(a===f){e=c.idx;b.removeClass(d.sSortAsc+" "+d.sSortDesc).addClass(h[e]=="asc"?d.sSortAsc:h[e]=="desc"?d.sSortDesc:c.sSortingClass);b.find("span."+d.sSortIcon).removeClass(d.sSortJUIAsc+" "+d.sSortJUIDesc+" "+d.sSortJUI+" "+
d.sSortJUIAscAllowed+" "+d.sSortJUIDescAllowed).addClass(h[e]=="asc"?d.sSortJUIAsc:h[e]=="desc"?d.sSortJUIDesc:c.sSortingClassJUI)}})}}});m.render={number:function(a,b,c,d,e){return{display:function(f){if("number"!==typeof f&&"string"!==typeof f)return f;var g=0>f?"-":"",f=Math.abs(parseFloat(f)),h=parseInt(f,10),f=c?b+(f-h).toFixed(c).substring(2):"";return g+(d||"")+h.toString().replace(/\B(?=(\d{3})+(?!\d))/g,a)+f+(e||"")}}}};h.extend(m.ext.internal,{_fnExternApiFunc:Nb,_fnBuildAjax:ra,_fnAjaxUpdate:lb,
_fnAjaxParameters:ub,_fnAjaxUpdateDraw:vb,_fnAjaxDataSrc:sa,_fnAddColumn:Ga,_fnColumnOptions:la,_fnAdjustColumnSizing:Y,_fnVisibleToColumnIndex:$,_fnColumnIndexToVisible:ba,_fnVisbleColumns:ca,_fnGetColumns:aa,_fnColumnTypes:Ia,_fnApplyColumnDefs:hb,_fnHungarianMap:X,_fnCamelToHungarian:I,_fnLanguageCompat:S,_fnBrowserDetect:fb,_fnAddData:L,_fnAddTr:ma,_fnNodeToDataIndex:function(a,b){return b._DT_RowIndex!==k?b._DT_RowIndex:null},_fnNodeToColumnIndex:function(a,b,c){return h.inArray(c,a.aoData[b].anCells)},
_fnGetCellData:B,_fnSetCellData:ib,_fnSplitObjNotation:La,_fnGetObjectDataFn:P,_fnSetObjectDataFn:Q,_fnGetDataMaster:Ma,_fnClearTable:na,_fnDeleteIndex:oa,_fnInvalidate:ea,_fnGetRowElements:Ka,_fnCreateTr:Ja,_fnBuildHead:kb,_fnDrawHead:ga,_fnDraw:M,_fnReDraw:R,_fnAddOptionsHtml:nb,_fnDetectHeader:fa,_fnGetUniqueThs:qa,_fnFeatureHtmlFilter:pb,_fnFilterComplete:ha,_fnFilterCustom:yb,_fnFilterColumn:xb,_fnFilter:wb,_fnFilterCreateSearch:Qa,_fnEscapeRegex:va,_fnFilterData:zb,_fnFeatureHtmlInfo:sb,_fnUpdateInfo:Cb,
_fnInfoMacros:Db,_fnInitialise:ia,_fnInitComplete:ta,_fnLengthChange:Ra,_fnFeatureHtmlLength:ob,_fnFeatureHtmlPaginate:tb,_fnPageChange:Ta,_fnFeatureHtmlProcessing:qb,_fnProcessingDisplay:C,_fnFeatureHtmlTable:rb,_fnScrollDraw:Z,_fnApplyToChildren:H,_fnCalculateColumnWidths:Ha,_fnThrottle:ua,_fnConvertToWidth:Fb,_fnGetWidestNode:Gb,_fnGetMaxLenString:Hb,_fnStringToCss:u,_fnSortFlatten:V,_fnSort:mb,_fnSortAria:Jb,_fnSortListener:Ua,_fnSortAttachListener:Oa,_fnSortingClasses:xa,_fnSortData:Ib,_fnSaveState:ya,
_fnLoadState:Kb,_fnSettingsFromNode:za,_fnLog:J,_fnMap:F,_fnBindAction:Va,_fnCallbackReg:z,_fnCallbackFire:w,_fnLengthOverflow:Sa,_fnRenderer:Pa,_fnDataSource:y,_fnRowAttributes:Na,_fnCalculateEnd:function(){}});h.fn.dataTable=m;h.fn.dataTableSettings=m.settings;h.fn.dataTableExt=m.ext;h.fn.DataTable=function(a){return h(this).dataTable(a).api()};h.each(m,function(a,b){h.fn.DataTable[a]=b});return h.fn.dataTable};"function"===typeof define&&define.amd?define("datatables",["jquery"],S):"object"===
typeof exports?module.exports=S(require("jquery")):jQuery&&!jQuery.fn.dataTable&&S(jQuery)})(window,document);

Some files were not shown because too many files have changed in this diff Show More