forked from community/device-mgt-core
parent
6004ee7264
commit
dd1c2e4338
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.wso2.carbon.device.mgt.core.dto;
|
||||||
|
|
||||||
|
import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This holds the information of the registered device management service against the device type
|
||||||
|
* definition loaded timestamp. This is used to handle device type update scenario.
|
||||||
|
*/
|
||||||
|
public class DeviceManagementServiceHolder {
|
||||||
|
|
||||||
|
private DeviceManagementService deviceManagementService;
|
||||||
|
private long timestamp;
|
||||||
|
|
||||||
|
public DeviceManagementServiceHolder(DeviceManagementService deviceManagementService) {
|
||||||
|
this.deviceManagementService = deviceManagementService;
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceManagementService getDeviceManagementService() {
|
||||||
|
return deviceManagementService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(long timestamp) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,226 @@
|
|||||||
|
{{!
|
||||||
|
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.
|
||||||
|
}}
|
||||||
|
{{unit "cdmf.unit.ui.title" pageTitle="Device Type Management"}}
|
||||||
|
|
||||||
|
{{#zone "topCss"}}
|
||||||
|
{{css "css/devicetype.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>
|
||||||
|
<li>
|
||||||
|
<!--suppress HtmlUnknownTarget -->
|
||||||
|
<a href="{{@app.context}}/device-type/add">
|
||||||
|
Add
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{/zone}}
|
||||||
|
|
||||||
|
{{#zone "content"}}
|
||||||
|
{{#if canManage}}
|
||||||
|
<!-- content/body -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<!-- content -->
|
||||||
|
<div id="devicetype-create-form" class="container col-centered wr-content">
|
||||||
|
<div class="wr-form">
|
||||||
|
<p class="page-sub-title">Create Device Type</p>
|
||||||
|
<hr/>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="wr-steps">
|
||||||
|
<div class="col-md-6 col-xs-6">
|
||||||
|
<div class="itm-wiz itm-wiz-current" data-step="policy-platform">
|
||||||
|
<div class="wiz-no">1</div>
|
||||||
|
<div class="wiz-lbl hidden-xs"><span>Create A Device Type</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-xs-6">
|
||||||
|
<div class="itm-wiz" data-step="policy-profile">
|
||||||
|
<div class="wiz-no">2</div>
|
||||||
|
<div class="wiz-lbl hidden-xs"><span>Create Device Event</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br class="c-both"/>
|
||||||
|
</div>
|
||||||
|
<br/><br/>
|
||||||
|
<hr/>
|
||||||
|
<div id="devicetype-create-error-msg" class="alert alert-danger hidden" role="alert">
|
||||||
|
<i class="icon fw fw-error"></i><span></span>
|
||||||
|
</div>
|
||||||
|
<label class="wr-input-label">
|
||||||
|
Device Type Name *
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<input aria-describedby="basic-addon1" type="text" id="deviceTypeName"
|
||||||
|
data-error-msg="invalid device type name" class="form-control"/>
|
||||||
|
|
||||||
|
<label class="wr-input-label">
|
||||||
|
Device Type Description *
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<textarea aria-describedby="basic-addon1" type="text" id="deviceTypeDescription"
|
||||||
|
data-error-msg="invalid device type description"
|
||||||
|
class="form-control" rows="3" cols="70"></textarea>
|
||||||
|
|
||||||
|
<label class="wr-input-label">Push Notification Transport</label>
|
||||||
|
<div class="wr-input-control">
|
||||||
|
<!--suppress HtmlFormInputWithoutLabel -->
|
||||||
|
<select id="pushNotification" class="form-control select">
|
||||||
|
<option>NONE</option>
|
||||||
|
<option>MQTT</option>
|
||||||
|
<option>FCM</option>
|
||||||
|
<option>APNS</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="wr-input-label">
|
||||||
|
Features
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="form-group feature_field_wrapper">
|
||||||
|
<div class="dontfloat" name="deviceFeature">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" id="feature-name" placeholder="name"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<input type="text" class="form-control" id="feature-code" placeholder="code"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<textarea aria-describedby="basic-addon1" type="text" id="feature-description"
|
||||||
|
placeholder="description"
|
||||||
|
data-error-msg="invalid feature description"
|
||||||
|
class="form-control" rows="1" cols="30"></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default add_feature_button"><i class="fa fa-plus"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<label class="wr-input-label">
|
||||||
|
Device Attributes
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="form-group attribute_field_wrapper">
|
||||||
|
<div class="dontfloat">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" name="attribute[]" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default add_button"><i class="fa fa-plus"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hidden-div">
|
||||||
|
<div id="add-attribute-field" name="add-attribute-field">
|
||||||
|
<div class="dontfloat">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" name="attribute[]" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default remove_button"><i class="fa fa-minus"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="wr-input-label">
|
||||||
|
Initial Operation(Feature Code, Trigger operation when device enrolls)
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="form-group operation_field_wrapper">
|
||||||
|
<div class="dontfloat">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" name="operation[]" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default add_operation_button"><i class="fa fa-plus"></i></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="hidden-div">
|
||||||
|
<div id="add-operation-field" name="add-operation-field">
|
||||||
|
<div class="dontfloat">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" name="operation[]" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default remove_operation_button"><i class="fa fa-minus"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<label class="wr-input-label">Claimable</label>
|
||||||
|
<input aria-describedby="basic-addon1" type="checkbox" id="deviceClaimable"/>
|
||||||
|
</br>
|
||||||
|
|
||||||
|
<label class="wr-input-label">Policy Monitoring</label>
|
||||||
|
<input aria-describedby="basic-addon1" type="checkbox" id="policyMonitoring"/>
|
||||||
|
</br>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="roleNameError hidden glyphicon glyphicon-remove form-control-feedback"></span>
|
||||||
|
<label class="error roleNameEmpty hidden" for="summary">
|
||||||
|
Device Type Name is required, should be in minimum 3 characters long and not include any
|
||||||
|
whitespaces or "@" character or "/" character.
|
||||||
|
</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<button id="add-devicetype-btn" class="wr-btn">Add Device Type</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="devicetype-created-msg" class="container col-centered wr-content hidden">
|
||||||
|
<div class="wr-form">
|
||||||
|
<p class="page-sub-title">Devicetype was added successfully.</p>
|
||||||
|
<b>"View Device Type List"</b> to complete the process and go back to the devie type list.
|
||||||
|
<hr/>
|
||||||
|
<button class="wr-btn" onclick="window.location.href='{{@app.context}}/device-types'">
|
||||||
|
View Device Type List
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /content -->
|
||||||
|
<div id="app-context" data-app-context="{{@app.context}}" class="hidden"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /content/body -->
|
||||||
|
{{else}}
|
||||||
|
<h1 class="page-sub-title">
|
||||||
|
Permission Denied
|
||||||
|
</h1>
|
||||||
|
<br>
|
||||||
|
You not authorized to create device type.
|
||||||
|
<br>
|
||||||
|
{{/if}}
|
||||||
|
{{/zone}}
|
||||||
|
|
||||||
|
{{#zone "bottomJs"}}
|
||||||
|
{{js "js/bottomJs.js"}}
|
||||||
|
{{/zone}}
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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"];
|
||||||
|
var displayData = {};
|
||||||
|
|
||||||
|
if (userModule.isAuthorized("/permission/admin/device-mgt/admin/device-type")) {
|
||||||
|
displayData.canManage = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return displayData;
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"uri": "/device-type/add",
|
||||||
|
"layout": "cdmf.layout.default"
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
.dontfloat {
|
||||||
|
clear:both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-div {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
var validateInline = {};
|
||||||
|
var clearInline = {};
|
||||||
|
|
||||||
|
var apiBasePath = "/api/device-mgt/v1.0";
|
||||||
|
|
||||||
|
|
||||||
|
var enableInlineError = function (inputField, errorMsg, errorSign) {
|
||||||
|
var fieldIdentifier = "#" + inputField;
|
||||||
|
var errorMsgIdentifier = "#" + inputField + " ." + errorMsg;
|
||||||
|
var errorSignIdentifier = "#" + inputField + " ." + errorSign;
|
||||||
|
|
||||||
|
if (inputField) {
|
||||||
|
$(fieldIdentifier).addClass(" has-error has-feedback");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMsg) {
|
||||||
|
$(errorMsgIdentifier).removeClass(" hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorSign) {
|
||||||
|
$(errorSignIdentifier).removeClass(" hidden");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var disableInlineError = function (inputField, errorMsg, errorSign) {
|
||||||
|
var fieldIdentifier = "#" + inputField;
|
||||||
|
var errorMsgIdentifier = "#" + inputField + " ." + errorMsg;
|
||||||
|
var errorSignIdentifier = "#" + inputField + " ." + errorSign;
|
||||||
|
|
||||||
|
if (inputField) {
|
||||||
|
$(fieldIdentifier).removeClass(" has-error has-feedback");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMsg) {
|
||||||
|
$(errorMsgIdentifier).addClass(" hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorSign) {
|
||||||
|
$(errorSignIdentifier).addClass(" hidden");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function formatRepo(user) {
|
||||||
|
if (user.loading) {
|
||||||
|
return user.text
|
||||||
|
}
|
||||||
|
if (!user.username) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var markup = '<div class="clearfix">' +
|
||||||
|
'<div class="col-sm-8">' +
|
||||||
|
'<div class="clearfix">' +
|
||||||
|
'<div class="col-sm-4">' + user.username + '</div>';
|
||||||
|
if (user.name || user.name != undefined) {
|
||||||
|
markup += '<div class="col-sm-8"> ( ' + user.name + ' )</div>';
|
||||||
|
}
|
||||||
|
markup += '</div></div></div>';
|
||||||
|
return markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatRepoSelection(user) {
|
||||||
|
return user.username || user.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
var appContext = $("#app-context").data("app-context");
|
||||||
|
|
||||||
|
var maxField = 100; //Input fields increment limitation
|
||||||
|
var addButton = $('.add_button'); //Add button selector
|
||||||
|
var wrapper = $('.attribute_field_wrapper'); //Input field wrapper
|
||||||
|
var fieldHTML = $('#add-attribute-field').html(); //New input field html
|
||||||
|
$(addButton).click(function(){ //Once add button is clicked
|
||||||
|
$(wrapper).append(fieldHTML); // Add field html
|
||||||
|
});
|
||||||
|
$(wrapper).on('click', '.remove_button', function(e){ //Once remove button is clicked
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).parent('div').remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
var addOperationButton = $('.add_operation_button'); //Add button selector
|
||||||
|
var operationWrapper = $('.operation_field_wrapper'); //Input field wrapper
|
||||||
|
var operationFieldHTML = $('#add-operation-field').html(); //New input field html
|
||||||
|
$(addOperationButton).click(function(){ //Once add button is clicked
|
||||||
|
$(operationWrapper).append(operationFieldHTML); // Add field html
|
||||||
|
});
|
||||||
|
$(operationWrapper).on('click', '.remove_operation_button', function(e){ //Once remove button is clicked
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).parent('div').remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var addFeatureButton = $('.add_feature_button'); //Add button selector
|
||||||
|
var featureWrapper = $('.feature_field_wrapper'); //Input field wrapper
|
||||||
|
$(addFeatureButton).click(function(){ //Once add button is clicked
|
||||||
|
var featureFieldHtml = ' <div class="dontfloat" name ="deviceFeature"> <div class="col-xs-3"> <input type="text"' +
|
||||||
|
' class="form-control" id="feature-name" placeholder="name"/> </div> <div class="col-xs-4"> ' +
|
||||||
|
'<input type="text" class="form-control" id="feature-code" placeholder="code"/> </div> ' +
|
||||||
|
'<div class="col-xs-4"> <textarea aria-describedby="basic-addon1" type="text" ' +
|
||||||
|
'id="feature-description" placeholder="description"data-error-msg="invalid ' +
|
||||||
|
'feature description"class="form-control" rows="1" cols="30"></textarea> </div> ' +
|
||||||
|
'<button type="button" class="btn btn-default remove_feature_button"><i class="fa fa-minus"></i></button> </div>'
|
||||||
|
$(featureWrapper).append(featureFieldHtml); // Add field html
|
||||||
|
});
|
||||||
|
$(featureWrapper).on('click', '.remove_feature_button', function(e){ //Once remove button is clicked
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).parent('div').remove(); //Remove field html
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Following click function would execute
|
||||||
|
* when a user clicks on "Add Device type" button.
|
||||||
|
*/
|
||||||
|
$("button#add-devicetype-btn").click(function () {
|
||||||
|
|
||||||
|
var errorMsgWrapper = "#devicetype-create-error-msg";
|
||||||
|
var errorMsg = "#devicetype-create-error-msg span";
|
||||||
|
var deviceType = {};
|
||||||
|
var deviceTypeName = $("#deviceTypeName").val();
|
||||||
|
var deviceTypeDescription = $("#deviceTypeDescription").val();
|
||||||
|
if (!deviceTypeName || deviceTypeName.trim() == "" ) {
|
||||||
|
$(errorMsg).text("Device Type Name Cannot be empty.");
|
||||||
|
$(errorMsgWrapper).removeClass("hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deviceTypeDescription || deviceTypeDescription.trim() == "" ) {
|
||||||
|
$(errorMsg).text("Device Type Description Cannot be empty.");
|
||||||
|
$(errorMsgWrapper).removeClass("hidden");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceType.name = deviceTypeName.trim();
|
||||||
|
deviceType.deviceTypeMetaDefinition = {}
|
||||||
|
deviceType.deviceTypeMetaDefinition.description = deviceTypeDescription.trim();
|
||||||
|
|
||||||
|
var pushNotification = $("#pushNotification").val();
|
||||||
|
if (pushNotification != "NONE") {
|
||||||
|
deviceType.deviceTypeMetaDefinition.pushNotificationConfig = {};
|
||||||
|
deviceType.deviceTypeMetaDefinition.pushNotificationConfig.scheduled = true;
|
||||||
|
deviceType.deviceTypeMetaDefinition.pushNotificationConfig.type = pushNotification;
|
||||||
|
}
|
||||||
|
|
||||||
|
var propertyValues = [];
|
||||||
|
$('input[name^="attribute"]').each(function() {
|
||||||
|
var propertyValue = $(this).val();
|
||||||
|
if (propertyValue.trim() != "") {
|
||||||
|
propertyValues.push(propertyValue.trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deviceType.deviceTypeMetaDefinition.properties = propertyValues;
|
||||||
|
|
||||||
|
var operationValues = [];
|
||||||
|
$('input[name^="operation"]').each(function() {
|
||||||
|
var operationValue = $(this).val();
|
||||||
|
if (operationValue.trim() != "") {
|
||||||
|
operationValues.push(operationValue.trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (operationValues.length > 0) {
|
||||||
|
deviceType.deviceTypeMetaDefinition.initialOperationConfig = {};
|
||||||
|
deviceType.deviceTypeMetaDefinition.initialOperationConfig.operations = operationValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceType.deviceTypeMetaDefinition.policyMonitoringEnabled = $("#policyMonitoring").is(":checked");
|
||||||
|
deviceType.deviceTypeMetaDefinition.claimable = $("#deviceClaimable").is(":checked");;
|
||||||
|
|
||||||
|
var features = [];
|
||||||
|
$('div[name^="deviceFeature"]').each(function() {
|
||||||
|
var featureName = $(this).find("#feature-name").val();
|
||||||
|
var featureCode = $(this).find("#feature-code").val();
|
||||||
|
if (featureName && featureName.trim() != "" && featureCode && featureCode.trim() != "") {
|
||||||
|
var feature = {};
|
||||||
|
feature.name = featureName.trim();
|
||||||
|
feature.code = featureCode.trim();
|
||||||
|
feature.description = $("#feature-description").val();
|
||||||
|
features.push(feature);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deviceType.deviceTypeMetaDefinition.features = features;
|
||||||
|
|
||||||
|
var addRoleAPI = apiBasePath + "/admin/device-types";
|
||||||
|
|
||||||
|
invokerUtil.post(
|
||||||
|
addRoleAPI,
|
||||||
|
deviceType,
|
||||||
|
function (data, textStatus, jqXHR) {
|
||||||
|
if (jqXHR.status == 200) {
|
||||||
|
window.location.href = appContext + "/device-type/edit-event?type=" +
|
||||||
|
encodeURIComponent(deviceTypeName);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (jqXHR) {
|
||||||
|
if (jqXHR.status == 500) {
|
||||||
|
$(errorMsg).text("Unexpected error.");
|
||||||
|
$(errorMsgWrapper).removeClass("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jqXHR.status == 409) {
|
||||||
|
$(errorMsg).text("Device type already exists");
|
||||||
|
$(errorMsgWrapper).removeClass("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,273 @@
|
|||||||
|
{{!
|
||||||
|
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.
|
||||||
|
}}
|
||||||
|
{{unit "cdmf.unit.ui.title" pageTitle="Device Type Management"}}
|
||||||
|
|
||||||
|
{{#zone "topCss"}}
|
||||||
|
{{css "css/devicetype.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>
|
||||||
|
<li>
|
||||||
|
<!--suppress HtmlUnknownTarget -->
|
||||||
|
<a href="{{@app.context}}/device-type/edit?type={{name}}">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{/zone}}
|
||||||
|
|
||||||
|
{{#zone "content"}}
|
||||||
|
{{#if canManage}}
|
||||||
|
<!-- content/body -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<!-- content -->
|
||||||
|
<div id="devicetype-create-form" class="container col-centered wr-content">
|
||||||
|
<div class="wr-form">
|
||||||
|
<p class="page-sub-title">{{name}}</p>
|
||||||
|
<hr/>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div id="devicetype-create-error-msg" class="alert alert-danger hidden" role="alert">
|
||||||
|
<i class="icon fw fw-error"></i><span></span>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<input aria-describedby="basic-addon1" type="text" id="deviceTypeName"
|
||||||
|
data-error-msg="invalid device type name" class="form-control hidden-input" value="{{name}}"/>
|
||||||
|
|
||||||
|
<label class="wr-input-label">
|
||||||
|
Device Type Description *
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<textarea aria-describedby="basic-addon1" type="text" id="deviceTypeDescription"
|
||||||
|
data-error-msg="invalid device type description"
|
||||||
|
class="form-control" rows="3" cols="70">{{type.deviceTypeMetaDefinition.description}}</textarea>
|
||||||
|
|
||||||
|
<label class="wr-input-label">Push Notification Transport</label>
|
||||||
|
<div class="wr-input-control">
|
||||||
|
<!--suppress HtmlFormInputWithoutLabel -->
|
||||||
|
<select id="pushNotification" class="form-control select">
|
||||||
|
{{#if type.deviceTypeMetaDefinition.pushNotificationConfig}}
|
||||||
|
<option>NONE</option>
|
||||||
|
{{#if_eq type.deviceTypeMetaDefinition.pushNotificationConfig.type "MQTT"}}
|
||||||
|
<option selected>MQTT</option>
|
||||||
|
<option>FCM</option>
|
||||||
|
<option>APNS</option>
|
||||||
|
{{/if_eq}}
|
||||||
|
{{#if_eq type.deviceTypeMetaDefinition.pushNotificationConfig.type "FCM"}}
|
||||||
|
<option>MQTT</option>
|
||||||
|
<option selected>FCM</option>
|
||||||
|
<option>APNS</option>
|
||||||
|
{{/if_eq}}
|
||||||
|
{{#if_eq type.deviceTypeMetaDefinition.pushNotificationConfig.type "APNS"}}
|
||||||
|
<option>MQTT</option>
|
||||||
|
<option>FCM</option>
|
||||||
|
<option selected>APNS</option>
|
||||||
|
{{/if_eq}}
|
||||||
|
{{else}}
|
||||||
|
<option selected>NONE</option>
|
||||||
|
<option>MQTT</option>
|
||||||
|
<option>FCM</option>
|
||||||
|
<option>APNS</option>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="wr-input-label">
|
||||||
|
Features
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<div class="form-group feature_field_wrapper">
|
||||||
|
{{#if type.deviceTypeMetaDefinition.features}}
|
||||||
|
{{#each type.deviceTypeMetaDefinition.features}}
|
||||||
|
<div class="dontfloat" name ="deviceFeature">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" id="feature-name" placeholder="name" value="{{this.name}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<input type="text" class="form-control" id="feature-code" placeholder="code" value="{{this.code}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<textarea aria-describedby="basic-addon1" type="text" id="feature-description"
|
||||||
|
placeholder="description"data-error-msg="invalid feature description"class="form-control" rows="1" cols="30">
|
||||||
|
{{this.description}}
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default remove_feature_button"><i class="fa fa-minus"></i></button>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
<div class="dontfloat" name="deviceFeature">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" id="feature-name" placeholder="name"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<input type="text" class="form-control" id="feature-code" placeholder="code"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<textarea aria-describedby="basic-addon1" type="text" id="feature-description"
|
||||||
|
placeholder="description"
|
||||||
|
data-error-msg="invalid feature description"
|
||||||
|
class="form-control" rows="1" cols="30"></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default add_feature_button"><i class="fa fa-plus"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<label class="wr-input-label">
|
||||||
|
Device Attributes
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<div class="form-group attribute_field_wrapper">
|
||||||
|
{{#if type.deviceTypeMetaDefinition.properties}}
|
||||||
|
{{#each type.deviceTypeMetaDefinition.properties}}
|
||||||
|
<div class="dontfloat">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" name="attribute[]" value="{{this}}" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default remove_button"><i class="fa fa-minus"></i></button>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
<div class="dontfloat">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" name="attribute[]" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default add_button"><i class="fa fa-plus"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hidden-div">
|
||||||
|
<div id="add-attribute-field" name="add-attribute-field">
|
||||||
|
<div class="dontfloat">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" name="attribute[]" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default remove_button"><i class="fa fa-minus"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="wr-input-label">
|
||||||
|
Initial Operation(Feature Code, Trigger operation when device enrolls)
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="form-group operation_field_wrapper">
|
||||||
|
{{#if type.deviceTypeMetaDefinition.initialOperationConfig}}
|
||||||
|
{{#each type.deviceTypeMetaDefinition.initialOperationConfig.operations}}
|
||||||
|
<div class="dontfloat">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" name="operation[]" value="{{this}}"/>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default remove_operation_button"><i class="fa fa-minus"></i></button>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
<div class="dontfloat">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" name="operation[]" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default add_operation_button"><i class="fa fa-plus"></i></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="hidden-div">
|
||||||
|
<div id="add-operation-field" name="add-operation-field">
|
||||||
|
<div class="dontfloat">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" name="operation[]" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default remove_operation_button"><i class="fa fa-minus"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<label class="wr-input-label">Claimable</label>
|
||||||
|
<input aria-describedby="basic-addon1" type="checkbox" id="deviceClaimable"
|
||||||
|
{{#if type.deviceTypeMetaDefinition.claimable}}
|
||||||
|
checked
|
||||||
|
{{/if}}/>
|
||||||
|
</br>
|
||||||
|
|
||||||
|
<label class="wr-input-label">Policy Monitoring</label>
|
||||||
|
<input aria-describedby="basic-addon1" type="checkbox" id="policyMonitoring"
|
||||||
|
{{#if type.deviceTypeMetaDefinition.policyMonitoringEnabled}}
|
||||||
|
checked
|
||||||
|
{{/if}}/>
|
||||||
|
</br>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="roleNameError hidden glyphicon glyphicon-remove form-control-feedback"></span>
|
||||||
|
<label class="error roleNameEmpty hidden" for="summary">
|
||||||
|
Device Type Name is required, should be in minimum 3 characters long and not include any
|
||||||
|
whitespaces or "@" character or "/" character.
|
||||||
|
</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<button id="add-devicetype-btn" class="wr-btn">Update</button>
|
||||||
|
<div id="devicetype-create-success-msg" class="alert hidden" role="alert">
|
||||||
|
<i class="icon fw fw-success"></i><span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="devicetype-created-msg" class="container col-centered wr-content hidden">
|
||||||
|
<div class="wr-form">
|
||||||
|
<p class="page-sub-title">Devicetype was added successfully.</p>
|
||||||
|
<b>"View Device Type List"</b> to complete the process and go back to the devie type list.
|
||||||
|
<hr/>
|
||||||
|
<button class="wr-btn" onclick="window.location.href='{{@app.context}}/device-types'">
|
||||||
|
View Device Type List
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /content -->
|
||||||
|
<div id="app-context" data-app-context="{{@app.context}}" class="hidden"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /content/body -->
|
||||||
|
{{else}}
|
||||||
|
<h1 class="page-sub-title">
|
||||||
|
Permission Denied
|
||||||
|
</h1>
|
||||||
|
<br>
|
||||||
|
You not authorized to edit device type.
|
||||||
|
<br>
|
||||||
|
{{/if}}
|
||||||
|
{{/zone}}
|
||||||
|
|
||||||
|
{{#zone "bottomJs"}}
|
||||||
|
{{js "js/bottomJs.js"}}
|
||||||
|
{{/zone}}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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"];
|
||||||
|
var displayData = {};
|
||||||
|
|
||||||
|
|
||||||
|
if (userModule.isAuthorized("/permission/admin/device-mgt/admin/device-type")) {
|
||||||
|
displayData.canManage = true;
|
||||||
|
}
|
||||||
|
context.handlebars.registerHelper('if_eq', function(a, b, opts) {
|
||||||
|
if(a == b) // Or === depending on your needs
|
||||||
|
return opts.fn(this);
|
||||||
|
else
|
||||||
|
return opts.inverse(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
var deviceType = request.getParameter("type");
|
||||||
|
var serviceInvokers = require("/app/modules/oauth/token-protected-service-invokers.js")["invokers"];
|
||||||
|
var restAPIEndpoint = deviceMgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"]
|
||||||
|
+ "/device-types/all/" + deviceType;
|
||||||
|
displayData.name = deviceType;
|
||||||
|
serviceInvokers.XMLHttp.get(
|
||||||
|
restAPIEndpoint,
|
||||||
|
function (restAPIResponse) {
|
||||||
|
if (restAPIResponse["status"] == 200 && restAPIResponse["responseText"]) {
|
||||||
|
var typeData = parse(restAPIResponse["responseText"]);
|
||||||
|
displayData.type = typeData;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return displayData;
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"uri": "/device-type/edit",
|
||||||
|
"layout": "cdmf.layout.default"
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
|
||||||
|
.dontfloat {
|
||||||
|
clear:both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-div {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-input {
|
||||||
|
display: none;
|
||||||
|
}
|
@ -0,0 +1,243 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
var validateInline = {};
|
||||||
|
var clearInline = {};
|
||||||
|
|
||||||
|
var apiBasePath = "/api/device-mgt/v1.0";
|
||||||
|
var domain = $("#domain").val();
|
||||||
|
|
||||||
|
|
||||||
|
var enableInlineError = function (inputField, errorMsg, errorSign) {
|
||||||
|
var fieldIdentifier = "#" + inputField;
|
||||||
|
var errorMsgIdentifier = "#" + inputField + " ." + errorMsg;
|
||||||
|
var errorSignIdentifier = "#" + inputField + " ." + errorSign;
|
||||||
|
|
||||||
|
if (inputField) {
|
||||||
|
$(fieldIdentifier).addClass(" has-error has-feedback");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMsg) {
|
||||||
|
$(errorMsgIdentifier).removeClass(" hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorSign) {
|
||||||
|
$(errorSignIdentifier).removeClass(" hidden");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var disableInlineError = function (inputField, errorMsg, errorSign) {
|
||||||
|
var fieldIdentifier = "#" + inputField;
|
||||||
|
var errorMsgIdentifier = "#" + inputField + " ." + errorMsg;
|
||||||
|
var errorSignIdentifier = "#" + inputField + " ." + errorSign;
|
||||||
|
|
||||||
|
if (inputField) {
|
||||||
|
$(fieldIdentifier).removeClass(" has-error has-feedback");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMsg) {
|
||||||
|
$(errorMsgIdentifier).addClass(" hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorSign) {
|
||||||
|
$(errorSignIdentifier).addClass(" hidden");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatRepo(user) {
|
||||||
|
if (user.loading) {
|
||||||
|
return user.text
|
||||||
|
}
|
||||||
|
if (!user.username) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var markup = '<div class="clearfix">' +
|
||||||
|
'<div class="col-sm-8">' +
|
||||||
|
'<div class="clearfix">' +
|
||||||
|
'<div class="col-sm-4">' + user.username + '</div>';
|
||||||
|
if (user.name || user.name != undefined) {
|
||||||
|
markup += '<div class="col-sm-8"> ( ' + user.name + ' )</div>';
|
||||||
|
}
|
||||||
|
markup += '</div></div></div>';
|
||||||
|
return markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatRepoSelection(user) {
|
||||||
|
return user.username || user.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
var appContext = $("#app-context").data("app-context");
|
||||||
|
|
||||||
|
var addButton = $('.add_button'); //Add button selector
|
||||||
|
var wrapper = $('.attribute_field_wrapper'); //Input field wrapper
|
||||||
|
var fieldHTML = $('#add-attribute-field').html(); //New input field html
|
||||||
|
$(addButton).click(function(){ //Once add button is clicked
|
||||||
|
$(wrapper).append(fieldHTML); // Add field html
|
||||||
|
});
|
||||||
|
$(wrapper).on('click', '.remove_button', function(e){ //Once remove button is clicked
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).parent('div').remove(); //Remove field html
|
||||||
|
});
|
||||||
|
|
||||||
|
var addOperationButton = $('.add_operation_button'); //Add button selector
|
||||||
|
var operationWrapper = $('.operation_field_wrapper'); //Input field wrapper
|
||||||
|
var operationFieldHTML = $('#add-operation-field').html(); //New input field html
|
||||||
|
$(addOperationButton).click(function(){ //Once add button is clicked
|
||||||
|
$(operationWrapper).append(operationFieldHTML); // Add field html
|
||||||
|
});
|
||||||
|
$(operationWrapper).on('click', '.remove_operation_button', function(e){ //Once remove button is clicked
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).parent('div').remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var addFeatureButton = $('.add_feature_button'); //Add button selector
|
||||||
|
var featureWrapper = $('.feature_field_wrapper'); //Input field wrapper
|
||||||
|
$(addFeatureButton).click(function(){ //Once add button is clicked
|
||||||
|
var featureFieldHtml = ' <div class="dontfloat" name ="deviceFeature"> <div class="col-xs-3"> <input type="text"' +
|
||||||
|
' class="form-control" id="feature-name" placeholder="name"/> </div> <div class="col-xs-4"> ' +
|
||||||
|
'<input type="text" class="form-control" id="feature-code" placeholder="code"/> </div> ' +
|
||||||
|
'<div class="col-xs-4"> <textarea aria-describedby="basic-addon1" type="text" ' +
|
||||||
|
'id="feature-description" placeholder="description"data-error-msg="invalid ' +
|
||||||
|
'feature description"class="form-control" rows="1" cols="30"></textarea> </div> ' +
|
||||||
|
'<button type="button" class="btn btn-default remove_feature_button"><i class="fa fa-minus"></i></button> </div>'
|
||||||
|
$(featureWrapper).append(featureFieldHtml); // Add field html
|
||||||
|
|
||||||
|
});
|
||||||
|
$(featureWrapper).on('click', '.remove_feature_button', function(e){ //Once remove button is clicked
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).parent('div').remove(); //Remove field html
|
||||||
|
op--; //Decrement field counter
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Following click function would execute
|
||||||
|
* when a user clicks on "Add Device type" button.
|
||||||
|
*/
|
||||||
|
$("button#add-devicetype-btn").click(function () {
|
||||||
|
|
||||||
|
var errorMsgWrapper = "#devicetype-create-error-msg";
|
||||||
|
var errorMsg = "#devicetype-create-error-msg span";
|
||||||
|
var successMsgWrapper = "#devicetype-create-success-msg";
|
||||||
|
var successMsg = "#devicetype-create-success-msg span";
|
||||||
|
var deviceType = {};
|
||||||
|
var deviceTypeName = $("#deviceTypeName").val();
|
||||||
|
var deviceTypeDescription = $("#deviceTypeDescription").val();
|
||||||
|
if (!deviceTypeName || deviceTypeName.trim() == "" ) {
|
||||||
|
$(errorMsg).text("Device Type Name Cannot be empty.");
|
||||||
|
$(errorMsgWrapper).removeClass("hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deviceTypeDescription || deviceTypeDescription.trim() == "" ) {
|
||||||
|
$(errorMsg).text("Device Type Description Cannot be empty.");
|
||||||
|
$(errorMsgWrapper).removeClass("hidden");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceType.name = deviceTypeName.trim();
|
||||||
|
deviceType.deviceTypeMetaDefinition = {}
|
||||||
|
deviceType.deviceTypeMetaDefinition.description = deviceTypeDescription.trim();
|
||||||
|
|
||||||
|
var pushNotification = $("#pushNotification").val();
|
||||||
|
if (pushNotification != "NONE") {
|
||||||
|
deviceType.deviceTypeMetaDefinition.pushNotificationConfig = {};
|
||||||
|
deviceType.deviceTypeMetaDefinition.pushNotificationConfig.scheduled = true;
|
||||||
|
deviceType.deviceTypeMetaDefinition.pushNotificationConfig.type = pushNotification;
|
||||||
|
}
|
||||||
|
|
||||||
|
var propertyValues = [];
|
||||||
|
$('input[name^="attribute"]').each(function() {
|
||||||
|
var propertyValue = $(this).val();
|
||||||
|
if (propertyValue.trim() != "") {
|
||||||
|
propertyValues.push(propertyValue.trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deviceType.deviceTypeMetaDefinition.properties = propertyValues;
|
||||||
|
|
||||||
|
var operationValues = [];
|
||||||
|
$('input[name^="operation"]').each(function() {
|
||||||
|
var operationValue = $(this).val();
|
||||||
|
if (operationValue.trim() != "") {
|
||||||
|
operationValues.push(operationValue.trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (operationValues.length > 0) {
|
||||||
|
deviceType.deviceTypeMetaDefinition.initialOperationConfig = {};
|
||||||
|
deviceType.deviceTypeMetaDefinition.initialOperationConfig.operations = operationValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceType.deviceTypeMetaDefinition.policyMonitoringEnabled = $("#policyMonitoring").is(":checked");
|
||||||
|
deviceType.deviceTypeMetaDefinition.claimable = $("#deviceClaimable").is(":checked");;
|
||||||
|
|
||||||
|
var features = [];
|
||||||
|
$('div[name^="deviceFeature"]').each(function() {
|
||||||
|
var featureName = $(this).find("#feature-name").val();
|
||||||
|
var featureCode = $(this).find("#feature-code").val();
|
||||||
|
if (featureName && featureName.trim() != "" && featureCode && featureCode.trim() != "") {
|
||||||
|
var feature = {};
|
||||||
|
feature.name = featureName.trim();
|
||||||
|
feature.code = featureCode.trim();
|
||||||
|
feature.description = $("#feature-description").val();
|
||||||
|
features.push(feature);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deviceType.deviceTypeMetaDefinition.features = features;
|
||||||
|
|
||||||
|
var addRoleAPI = apiBasePath + "/admin/device-types";
|
||||||
|
|
||||||
|
invokerUtil.put(
|
||||||
|
addRoleAPI,
|
||||||
|
deviceType,
|
||||||
|
function (data, textStatus, jqXHR) {
|
||||||
|
if (jqXHR.status == 200) {
|
||||||
|
$(successMsg).text("Device type updated.");
|
||||||
|
$(successMsgWrapper).removeClass("hidden");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (jqXHR) {
|
||||||
|
if (jqXHR.status == 500) {
|
||||||
|
$(errorMsg).text("Unexpected error.");
|
||||||
|
$(errorMsgWrapper).removeClass("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jqXHR.status == 409) {
|
||||||
|
$(errorMsg).text("Device type already exists");
|
||||||
|
$(errorMsgWrapper).removeClass("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,160 @@
|
|||||||
|
{{!
|
||||||
|
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.
|
||||||
|
}}
|
||||||
|
{{unit "cdmf.unit.ui.title" pageTitle="Device Type Management"}}
|
||||||
|
|
||||||
|
{{#zone "topCss"}}
|
||||||
|
{{css "css/devicetype.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>
|
||||||
|
<li>
|
||||||
|
<!--suppress HtmlUnknownTarget -->
|
||||||
|
<a href="{{@app.context}}/device-type/edit-event?type={{name}}">
|
||||||
|
Event
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{/zone}}
|
||||||
|
|
||||||
|
{{#zone "content"}}
|
||||||
|
{{#if canManage}}
|
||||||
|
<!-- content/body -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<!-- content -->
|
||||||
|
<div id="devicetype-create-form" class="container col-centered wr-content">
|
||||||
|
<div class="wr-form">
|
||||||
|
<p class="page-sub-title">{{name}}</p>
|
||||||
|
<hr/>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div id="devicetype-create-error-msg" class="alert alert-danger hidden" role="alert">
|
||||||
|
<i class="icon fw fw-error"></i><span></span>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<input aria-describedby="basic-addon1" type="text" id="deviceTypeName"
|
||||||
|
data-error-msg="invalid device type name" class="form-control hidden-input" value="{{name}}"/>
|
||||||
|
|
||||||
|
<label class="wr-input-label">Transport</label>
|
||||||
|
<div class="wr-input-control">
|
||||||
|
<!--suppress HtmlFormInputWithoutLabel -->
|
||||||
|
<select id="transport" class="form-control select">
|
||||||
|
{{#if event}}
|
||||||
|
<option {{selected event.transport "MQTT"}}>MQTT</option>
|
||||||
|
<option {{selected event.transport "HTTP"}}>HTTP</option>
|
||||||
|
{{else}}
|
||||||
|
<option>MQTT</option>
|
||||||
|
<option>HTTP</option>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="wr-input-label">
|
||||||
|
Event Attributes
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<div class="form-group event_field_wrapper">
|
||||||
|
{{#if event.eventAttributes}}
|
||||||
|
{{#each event.eventAttributes.attributes}}
|
||||||
|
<div class="dontfloat" name="deviceEvent">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" id="event-name" placeholder="name" value="{{this.name}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<select class="form-control select" id="event-type">
|
||||||
|
<option {{selected this.type "STRING"}}>STRING</option>
|
||||||
|
<option {{selected this.type "LONG"}}>LONG</option>
|
||||||
|
<option {{selected this.type "BOOL"}}>BOOL</option>
|
||||||
|
<option {{selected this.type "INT"}}>INT</option>
|
||||||
|
<option {{selected this.type "FLOAT"}}>FLOAT</option>
|
||||||
|
<option {{selected this.type "DOUBLE"}}>DOUBLE</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default remove_event_button"><i class="fa fa-minus"></i></button>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
<div class="dontfloat" name="deviceEvent">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input type="text" class="form-control" id="event-name" placeholder="name"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<select class="form-control select" id="event-type">
|
||||||
|
<option>STRING</option>
|
||||||
|
<option>LONG</option>
|
||||||
|
<option>BOOL</option>
|
||||||
|
<option>INT</option>
|
||||||
|
<option>FLOAT</option>
|
||||||
|
<option>DOUBLE</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-default add_event_button"><i class="fa fa-plus"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
{{#if event}}
|
||||||
|
<button id="add-event-btn" class="wr-btn">Update</button>
|
||||||
|
{{else}}
|
||||||
|
<button id="add-event-btn" class="wr-btn">Add</button>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div id="devicetype-create-success-msg" class="alert hidden" role="alert">
|
||||||
|
<i class="icon fw fw-success"></i><span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="devicetype-created-msg" class="container col-centered wr-content hidden">
|
||||||
|
<div class="wr-form">
|
||||||
|
<p class="page-sub-title">Device Type Event was added successfully.</p>
|
||||||
|
<b>"View Device Type List"</b> to complete the process and go back to the devie type list.
|
||||||
|
<hr/>
|
||||||
|
<button class="wr-btn" onclick="window.location.href='{{@app.context}}/device-types'">
|
||||||
|
View Device Type List
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /content -->
|
||||||
|
<div id="app-context" data-app-context="{{@app.context}}" class="hidden"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /content/body -->
|
||||||
|
{{else}}
|
||||||
|
<h1 class="page-sub-title">
|
||||||
|
Permission Denied
|
||||||
|
</h1>
|
||||||
|
<br>
|
||||||
|
You not authorized to edit device type.
|
||||||
|
<br>
|
||||||
|
{{/if}}
|
||||||
|
{{/zone}}
|
||||||
|
|
||||||
|
{{#zone "bottomJs"}}
|
||||||
|
{{js "js/bottomJs.js"}}
|
||||||
|
{{/zone}}
|
@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onRequest(context) {
|
||||||
|
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
|
||||||
|
var deviceMgtProps = require("/app/modules/conf-reader/main.js")["conf"];
|
||||||
|
var displayData = {};
|
||||||
|
|
||||||
|
|
||||||
|
if (userModule.isAuthorized("/permission/admin/device-mgt/admin/device-type")) {
|
||||||
|
displayData.canManage = true;
|
||||||
|
}
|
||||||
|
context.handlebars.registerHelper('selected', function(a, b, opts) {
|
||||||
|
if(a == b) // Or === depending on your needs
|
||||||
|
return "selected";
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
|
||||||
|
var deviceType = request.getParameter("type");
|
||||||
|
var serviceInvokers = require("/app/modules/oauth/token-protected-service-invokers.js")["invokers"];
|
||||||
|
var restAPIEndpoint = deviceMgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"]
|
||||||
|
+ "/events/" + deviceType;
|
||||||
|
displayData.name = deviceType;
|
||||||
|
serviceInvokers.XMLHttp.get(
|
||||||
|
restAPIEndpoint,
|
||||||
|
function (restAPIResponse) {
|
||||||
|
if (restAPIResponse["status"] == 200 && restAPIResponse["responseText"]) {
|
||||||
|
var typeData = parse(restAPIResponse["responseText"]);
|
||||||
|
displayData.event = typeData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return displayData;
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"uri": "/device-type/edit-event",
|
||||||
|
"layout": "cdmf.layout.default"
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
|
||||||
|
.dontfloat {
|
||||||
|
clear:both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-div {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-input {
|
||||||
|
display: none;
|
||||||
|
}
|
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
var validateInline = {};
|
||||||
|
var clearInline = {};
|
||||||
|
|
||||||
|
var apiBasePath = "/api/device-mgt/v1.0";
|
||||||
|
var domain = $("#domain").val();
|
||||||
|
|
||||||
|
|
||||||
|
var enableInlineError = function (inputField, errorMsg, errorSign) {
|
||||||
|
var fieldIdentifier = "#" + inputField;
|
||||||
|
var errorMsgIdentifier = "#" + inputField + " ." + errorMsg;
|
||||||
|
var errorSignIdentifier = "#" + inputField + " ." + errorSign;
|
||||||
|
|
||||||
|
if (inputField) {
|
||||||
|
$(fieldIdentifier).addClass(" has-error has-feedback");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMsg) {
|
||||||
|
$(errorMsgIdentifier).removeClass(" hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorSign) {
|
||||||
|
$(errorSignIdentifier).removeClass(" hidden");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var disableInlineError = function (inputField, errorMsg, errorSign) {
|
||||||
|
var fieldIdentifier = "#" + inputField;
|
||||||
|
var errorMsgIdentifier = "#" + inputField + " ." + errorMsg;
|
||||||
|
var errorSignIdentifier = "#" + inputField + " ." + errorSign;
|
||||||
|
|
||||||
|
if (inputField) {
|
||||||
|
$(fieldIdentifier).removeClass(" has-error has-feedback");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMsg) {
|
||||||
|
$(errorMsgIdentifier).addClass(" hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorSign) {
|
||||||
|
$(errorSignIdentifier).addClass(" hidden");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatRepo(user) {
|
||||||
|
if (user.loading) {
|
||||||
|
return user.text
|
||||||
|
}
|
||||||
|
if (!user.username) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var markup = '<div class="clearfix">' +
|
||||||
|
'<div class="col-sm-8">' +
|
||||||
|
'<div class="clearfix">' +
|
||||||
|
'<div class="col-sm-4">' + user.username + '</div>';
|
||||||
|
if (user.name || user.name != undefined) {
|
||||||
|
markup += '<div class="col-sm-8"> ( ' + user.name + ' )</div>';
|
||||||
|
}
|
||||||
|
markup += '</div></div></div>';
|
||||||
|
return markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatRepoSelection(user) {
|
||||||
|
return user.username || user.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
var appContext = $("#app-context").data("app-context");
|
||||||
|
var addEventButton = $('.add_event_button'); //Add button selector
|
||||||
|
var eventWrapper = $('.event_field_wrapper'); //Input field wrapper
|
||||||
|
$(addEventButton).click(function(){ //Once add button is clicked
|
||||||
|
var eventFieldHtml = ' <div class="dontfloat" name="deviceEvent"> ' +
|
||||||
|
'<div class="col-xs-3"> <input type="text" class="form-control" id="event-name" placeholder="name"/> ' +
|
||||||
|
'</div> <div class="col-xs-4"> <select class="form-control select" id="event-type"> ' +
|
||||||
|
'<option>STRING</option> <option>LONG</option> <option>BOOL</option> <option>INT</option> <option>FLOAT</option> ' +
|
||||||
|
'<option>DOUBLE</option> </select> </div> ' +
|
||||||
|
'<button type="button" class="btn btn-default remove_event_button"><i class="fa fa-minus"></i></button> </div>'
|
||||||
|
$(eventWrapper).append(eventFieldHtml); // Add field html
|
||||||
|
|
||||||
|
});
|
||||||
|
$(eventWrapper).on('click', '.remove_event_button', function(e){ //Once remove button is clicked
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).parent('div').remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Following click function would execute
|
||||||
|
* when a user clicks on "Add Device type" button.
|
||||||
|
*/
|
||||||
|
$("button#add-event-btn").click(function () {
|
||||||
|
|
||||||
|
var errorMsgWrapper = "#devicetype-create-error-msg";
|
||||||
|
var errorMsg = "#devicetype-create-error-msg span";
|
||||||
|
var successMsgWrapper = "#devicetype-create-success-msg";
|
||||||
|
var successMsg = "#devicetype-create-success-msg span";
|
||||||
|
var deviceTypeEvent = {};
|
||||||
|
var deviceTypeName = $("#deviceTypeName").val();
|
||||||
|
var deviceTypeDescription = $("#deviceTypeDescription").val();
|
||||||
|
if (!deviceTypeName || deviceTypeName.trim() == "" ) {
|
||||||
|
$(errorMsg).text("Device Type Name Cannot be empty.");
|
||||||
|
$(errorMsgWrapper).removeClass("hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
deviceTypeEvent.eventAttributes = {};
|
||||||
|
|
||||||
|
deviceTypeEvent.transport = $("#transport").val();
|
||||||
|
|
||||||
|
var attributes = [];
|
||||||
|
$('div[name^="deviceEvent"]').each(function() {
|
||||||
|
var eventName = $(this).find("#event-name").val();
|
||||||
|
var eventType = $(this).find("#event-type").val();
|
||||||
|
if (eventName && eventName.trim() != "" && eventType && eventType.trim() != "" && eventName != "deviceId") {
|
||||||
|
var attribute = {};
|
||||||
|
attribute.name = eventName.trim();
|
||||||
|
attribute.type = eventType.trim();
|
||||||
|
attributes.push(attribute);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deviceTypeEvent.eventAttributes.attributes = attributes;
|
||||||
|
|
||||||
|
var addEventsAPI = apiBasePath + "/events/" + deviceTypeName;
|
||||||
|
|
||||||
|
invokerUtil.post(
|
||||||
|
addEventsAPI,
|
||||||
|
deviceTypeEvent,
|
||||||
|
function (data, textStatus, jqXHR) {
|
||||||
|
if (jqXHR.status == 200) {
|
||||||
|
$(successMsg).text("Device Event Definition added.");
|
||||||
|
$(successMsgWrapper).removeClass("hidden");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (jqXHR) {
|
||||||
|
if (jqXHR.status == 500) {
|
||||||
|
$(errorMsg).text("Unexpected error.");
|
||||||
|
$(errorMsgWrapper).removeClass("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jqXHR.status == 409) {
|
||||||
|
$(errorMsg).text("Device type already exists");
|
||||||
|
$(errorMsgWrapper).removeClass("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,110 @@
|
|||||||
|
{{!
|
||||||
|
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 "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}}
|
@ -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;
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"uri": "/device-types",
|
||||||
|
"layout": "cdmf.layout.default"
|
||||||
|
}
|
@ -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 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 icon-only 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"
|
||||||
|
};
|
||||||
|
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();
|
||||||
|
});
|
@ -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}}
|
Loading…
Reference in new issue