Adding android operations-bar related changes

revert-dabc3590
Ace 8 years ago
parent 215fd51ffb
commit 326d59e7c1

@ -37,10 +37,7 @@
</div>
{{/zone}}
{{#zone "device-detail-properties"}}
<div class="media">
<div class="media-left col-xs-12 col-sm-2 col-md-2 col-lg-2">
<ul class="list-group" role="tablist">
{{#zone "device-view-tabs"}}
<li class="active"><a class="list-group-item" href="#device_statistics" role="tab"
data-toggle="tab" aria-controls="device_statistics">Device
Statistics</a>
@ -50,10 +47,9 @@
</li>
<li><a class="list-group-item" href="#event_log" role="tab" data-toggle="tab"
aria-controls="event_log">Operations Log</a></li>
</ul>
</div>
<div class="media-body add-padding-left-5x remove-padding-xs tab-content">
<div class="panel-group tab-content">
{{/zone}}
{{#zone "device-view-tab-contents"}}
<div class="panel panel-default tab-pane active"
id="device_statistics" role="tabpanel" aria-labelledby="device_statistics">
@ -111,7 +107,5 @@
</div>
</div>
</div>
</div>
</div>
</div>
{{/zone}}

@ -1,19 +1,19 @@
{{unit "cdmf.unit.device.type.android.leaflet"}}
{{unit "cdmf.unit.lib.qrcode"}}
{{unit "cdmf.unit.device.type.android.qr-modal"}}
{{unit "cdmf.unit.device.type.android.operation-bar"}}
{{#if deviceFound}}
{{#if isAuthorized}}
{{#zone "device-details-header"}}
<h1 class="page-sub-title device-id device-select"
data-deviceid="{{deviceView.deviceIdentifier}}" data-type="{{deviceView.type}}"
data-ownership="{{deviceView.ownership}}">
Device {{deviceView.name}}
{{#if deviceView.model}}
data-deviceid="{{device.deviceIdentifier}}" data-type="{{device.type}}"
data-ownership="{{device.ownership}}"
data-owner="{{device.owner}}">
Device {{device.name}}
{{#if device.model}}
<span class="lbl-device">
( {{deviceView.vendor}} {{deviceView.model}} )
( {{device.vendor}} {{device.model}} )
</span>
{{/if}}
</h1>
@ -26,83 +26,84 @@
</div>
<table class="table table-responsive table-striped" id="members">
<tbody>
{{#if deviceView.deviceIdentifier}}
{{#if device.deviceIdentifier}}
<tr role="row" class="odd">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">Device ID</td>
<td style="padding:10px 15px;">{{deviceView.deviceIdentifier}}</td>
<td style="padding:10px 15px;">{{device.deviceIdentifier}}</td>
</tr>
{{/if}}
{{#if deviceView.name}}
{{#if device.name}}
<tr role="row" class="even">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">Name</td>
<td style="padding:10px 15px;">{{deviceView.name}}</td>
<td style="padding:10px 15px;">{{device.name}}</td>
</tr>
{{/if}}
{{#if deviceView.vendor}}
{{#if deviceView.model}}
{{#if device.vendor}}
{{#if device.model}}
<tr role="row" class="odd">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">Model</td>
<td style="padding:10px 15px;">{{deviceView.vendor}} {{deviceView.model}}</td>
<td style="padding:10px 15px;">{{device.vendor}}
{{device.model}}</td>
</tr>
{{/if}}
{{/if}}
{{#if deviceView.status}}
{{#if device.status}}
<tr role="row" class="even">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">Status</td>
<td style="padding:10px 15px;">
{{#equal deviceView.status "ACTIVE"}}<span><i
{{#equal device.status "ACTIVE"}}<span><i
class="fw fw-ok icon-success"></i>Active</span>{{/equal}}
{{#equal deviceView.status "INACTIVE"}}<span><i
{{#equal device.status "INACTIVE"}}<span><i
class="fw fw-warning icon-warning"></i>Inactive</span>{{/equal}}
{{#equal deviceView.status "BLOCKED"}}<span><i
{{#equal device.status "BLOCKED"}}<span><i
class="fw fw-remove icon-danger"></i>Blocked</span>{{/equal}}
{{#equal deviceView.status "REMOVED"}}<span><i
{{#equal device.status "REMOVED"}}<span><i
class="fw fw-delete icon-danger"></i>Removed</span>{{/equal}}
</td>
</tr>
{{/if}}
{{#if deviceView.owner}}
{{#if device.owner}}
<tr role="row" class="odd">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">Owner</td>
<td style="padding:10px 15px;">{{deviceView.owner}}</td>
<td style="padding:10px 15px;">{{device.owner}}</td>
</tr>
{{/if}}
{{#if deviceView.ownership}}
{{#if device.ownership}}
<tr role="row" class="even">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">Ownership</td>
<td style="padding:10px 15px;">{{deviceView.ownership}}</td>
<td style="padding:10px 15px;">{{device.ownership}}</td>
</tr>
{{/if}}
{{#if deviceView.imei}}
{{#if device.imei}}
<tr role="row" class="even">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">IMEI</td>
<td style="padding:10px 15px;">{{deviceView.imei}}</td>
<td style="padding:10px 15px;">{{device.imei}}</td>
</tr>
{{/if}}
{{#if deviceView.udid}}
{{#if device.udid}}
<tr role="row" class="odd">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">UDID</td>
<td style="padding:10px 15px;">{{deviceView.udid}}</td>
<td style="padding:10px 15px;">{{device.udid}}</td>
</tr>
{{/if}}
{{#if deviceView.osBuildDate}}
{{#if device.osBuildDate}}
<tr role="row" class="even">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">Firmware Build
Date
</td>
<td style="padding:10px 15px;">{{deviceView.osBuildDate}}</td>
<td style="padding:10px 15px;">{{device.osBuildDate}}</td>
</tr>
{{/if}}
{{#if deviceView.phoneNumber}}
{{#if device.phoneNumber}}
<tr role="row" class="odd">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">Phone Number</td>
<td style="padding:10px 15px;">{{deviceView.phoneNumber}}</td>
<td style="padding:10px 15px;">{{device.phoneNumber}}</td>
</tr>
{{/if}}
{{#if deviceView.lastUpdatedTime}}
{{#if device.lastUpdatedTime}}
<tr role="row" class="even">
<td class="sorting_1" style="padding:10px 15px; width: 15%;">Last Update</td>
<td style="padding:10px 15px;">{{deviceView.lastUpdatedTime}}</td>
<td style="padding:10px 15px;">{{device.lastUpdatedTime}}</td>
</tr>
{{/if}}
</tbody>
@ -110,47 +111,30 @@
</div>
{{/zone}}
{{#zone "device-opetations"}}
{{#if deviceView.isNotRemoved}}
{{#if device.isNotRemoved}}
<div style="background: #11375B; color: #fff; padding: 10px; margin-bottom: 5px">
Operations
</div>
<div class="add-margin-top-4x">
{{unit "cdmf.unit.device.operation-bar" device=deviceView backendApiUri=backendApiUri autoCompleteParams=autoCompleteParams}}
{{unit "cdmf.unit.device.type.android.operation-bar" device=device
backendApiUri=backendApiUri autoCompleteParams=autoCompleteParams}}
</div>
{{/if}}
{{/zone}}
</div>
{{#zone "device-detail-properties"}}
<div class="media tab-responsive">
<div class="media-left col-xs-1 col-sm-1 col-md-2 col-lg-2 hidden-xs">
<ul class="list-group nav nav-pills nav-stacked" role="tablist">
{{#if deviceView.isNotWindows}}
<li role="presentation" class="list-group-item active">
<a href="#device_details_tab" role="tab" data-toggle="tab"
aria-controls="device_details_tab">
<i class="icon fw fw-mobile"></i><span class="hidden-sm">Device Details</span>
</a>
</li>
{{/if}}
{{#if deviceView.isNotWindows}}
<li role="presentation" class="list-group-item">
{{else}}
<li role="presentation" class="list-group-item active">
{{/if}}
{{#zone "device-details-tab-injected"}}
<li role="presentation" class="list-group-item">
<a href="#policy_compliance_tab" role="tab" data-toggle="tab"
aria-controls="policy_compliance_tab">
<i class="icon fw fw-policy"></i><span class="hidden-sm">Policy Compliance</span>
</a>
</li>
{{#if deviceView.isNotWindows}}
<li role="presentation" class="list-group-item">
<a href="#device_location_tab" role="tab" data-toggle="tab"
data-lat="{{deviceView.location.latitude}}"
data-long="{{deviceView.location.longitude}}"
data-lat="{{device.location.latitude}}"
data-long="{{device.location.longitude}}"
aria-controls="device_location_tab">
<i class="icon fw fw-map-location"></i><span
class="hidden-sm">Device Location</span>
@ -162,22 +146,9 @@
<i class="icon fw fw-application"></i><span class="hidden-sm">Installed Applications</span>
</a>
</li>
{{/if}}
{{#if deviceView.isNotRemoved}}
<li role="presentation" class="list-group-item">
<a href="#event_log_tab" role="tab" data-toggle="tab" aria-controls="event_log_tab">
<i class="icon fw fw-text"></i><span class="hidden-sm">Operations Log</span>
</a>
</li>
{{/if}}
</ul>
</div>
<div class="media-body add-padding-left-5x remove-padding-xs">
<div class="panel-group tab-content remove-padding" id="tabs" role="tablist"
data-status="{{deviceView.isNotRemoved}}" aria-multiselectable="true">
<div class="arrow-left hidden-xs"></div>
{{/zone}}
{{#if deviceView.isNotWindows}}
{{#zone "device-details-tab-contents"}}
<div class="panel panel-default" role="tabpanel" id="device_details_tab">
<div class="panel-heading visible-xs collapsed" id="device_details">
<h4 class="panel-title">
@ -195,14 +166,8 @@
<div class="panel-body ">
<div class="device-detail-body">
<!-- device summary -->
{{#equal deviceView.type "windows"}}
<div class="message message-info">
<h4 class="remove-margin"><i class="icon fw fw-info"></i>Not
available yet</h4>
</div>
{{/equal}}
{{#if deviceView.deviceInfoAvailable}}
{{#if deviceView.BatteryLevel}}
{{#if device.deviceInfoAvailable}}
{{#if device.BatteryLevel}}
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6">
<div class="col-md-12">
<div class="wr-stats-board-tile">
@ -211,14 +176,15 @@
<div class="tile-icon"><i
class="fw fw-battery"></i></div>
<div class="tile-stats">
{{deviceView.BatteryLevel.value}} %
{{device.BatteryLevel
.value}} %
</div>
</div>
</div>
</div>
</div>
{{/if}}
<!--{{#if deviceView.cpuUsage}}-->
<!--{{#if device.cpuUsage}}-->
<!--<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6">-->
<!--<div class="col-md-12">-->
<!--<div class="wr-stats-board-tile">-->
@ -226,14 +192,14 @@
<!--<div>-->
<!--<div class="tile-icon"><i class="fw fw-dashboard"></i></div>-->
<!--<div class="tile-stats">-->
<!--{{deviceView.cpuUsage.value}} %-->
<!--{{device.cpuUsage.value}} %-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--{{/if}}-->
{{#if deviceView.ramUsage}}
{{#if device.ramUsage}}
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6">
<div class="col-md-12">
<div class="wr-stats-board-tile">
@ -242,14 +208,15 @@
<div class="tile-icon"><i
class="fw fw-hardware"></i></div>
<div class="tile-stats">
{{deviceView.ramUsage.value}} %
{{device.ramUsage
.value}} %
</div>
</div>
</div>
</div>
</div>
{{/if}}
{{#if deviceView.internalMemory}}
{{#if device.internalMemory}}
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6">
<div class="col-md-12">
<div class="wr-stats-board-tile">
@ -259,9 +226,11 @@
class="fw fw-hdd"></i>
</div>
<div class="tile-stats">
{{deviceView.internalMemory.usage}} %
{{device.internalMemory
.usage}} %
<span class="tile-stats-free">
TOTAL OF {{deviceView.internalMemory.total}} GB
TOTAL OF {{device.internalMemory
.total}} GB
</span>
</div>
</div>
@ -269,7 +238,7 @@
</div>
</div>
{{/if}}
{{#if deviceView.externalMemory}}
{{#if device.externalMemory}}
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6">
<div class="col-md-12">
<div class="wr-stats-board-tile">
@ -278,9 +247,11 @@
<div class="tile-icon"><i
class="fw fw-usb-drive"></i></div>
<div class="tile-stats">
{{deviceView.externalMemory.usage}} %
{{device.externalMemory
.usage}} %
<span class="tile-stats-free">
TOTAL OF {{deviceView.externalMemory.total}} GB
TOTAL OF {{device.externalMemory
.total}} GB
</span>
</div>
</div>
@ -301,8 +272,9 @@
</div>
</div>
</div>
{{/if}}
{{/zone}}
{{#zone "device-view-tab-injected-conents"}}
<div class="panel panel-default visible-xs-block" role="tabpanel"
id="policy_compliance_tab">
<div class="panel-heading visible-xs collapsed" id="policy_compliance">
@ -343,6 +315,7 @@
</div>
</div>
</div>
<div class="panel panel-default visible-xs-block" role="tabpanel"
id="device_location_tab">
<div class="panel-heading visible-xs collapsed" id="device_location">
@ -359,10 +332,10 @@
<div id="collapseThree" class="panel-collapse collapse in" role="tabpanel"
aria-labelledby="device_location">
<div class="panel-body">
{{#if deviceView.location}}
{{#if device.location}}
<div id="device-location"
data-lat="{{deviceView.location.latitude}}"
data-long="{{deviceView.location.longitude}}">
data-lat="{{device.location.latitude}}"
data-long="{{device.location.longitude}}">
</div>
{{else}}
<div id="map-error" class="message message-warning">
@ -378,6 +351,7 @@
</div>
</div>
</div>
<div class="panel panel-default visible-xs-block" role="tabpanel"
id="installed_applications_tab">
<div class="panel-heading visible-xs collapsed" id="installed_applications">
@ -423,63 +397,10 @@
</div>
</div>
</div>
<div class="panel panel-default visible-xs-block" role="tabpanel" id="event_log_tab">
<div class="panel-heading visible-xs collapsed" id="event_log">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#tabs"
href="#collapseFive" aria-expanded="true" aria-controls="collapseFive">
<i class="fw fw-text fw-2x"></i>
Operations Log
<i class="caret-updown fw fw-down"></i>
</a>
</h4>
</div>
<div class="panel-heading display-none-xs">
Operations Log
<span>
<a href="javascript:void(0);" id="refresh-operations">
<i class="fw fw-refresh"></i>
</a>
</span>
</div>
<div id="collapseFive" class="panel-collapse collapse in" role="tabpanel"
aria-labelledby="event_log">
<div class="panel-body">
<span class="visible-xs add-padding-2x text-right">
<a href="javascript:void(0);" id="refresh-operations">
<i class="fw fw-refresh"></i>
</a>
</span>
<div id="operations-spinner" class="wr-advance-operations-init hidden">
<i class="fw fw-settings fw-spin fw-2x"></i> Loading Operations Log...
</div>
<div id="operations-log-container">
<div class="message message-info">
<h4 class="remove-margin">
<i class="icon fw fw-info"></i>
There are no operations, performed yet on this device.
</h4>
</div>
</div>
<table class="table table-striped table-hover table-bordered display data-table"
id="operations-log-table">
<thead>
<tr class="sort-row">
<th>Operation Code</th>
<th>Status</th>
<th>Request created at</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
{{/zone}}
</div>
</div>
{{else}}
<h1 class="page-sub-title">
@ -500,19 +421,13 @@
{{#if isAuthorized}}
<!--suppress HtmlUnknownTarget -->
<script id="policy-view" src="{{@unit.publicUri}}/templates/policy-compliance.hbs"
data-device-id="{{deviceView.deviceIdentifier}}" data-device-type="{{deviceView
.type}}"
data-device-id="{{device.deviceIdentifier}}" data-device-type="{{device.type}}"
type="text/x-handlebars-template"></script>
<!--suppress HtmlUnknownTarget -->
<script id="applications-list" src="{{@unit.publicUri}}/templates/applications-list.hbs"
data-device-id="{{deviceView.deviceIdentifier}}" data-device-type="{{deviceView
.type}}"
data-device-id="{{device.deviceIdentifier}}" data-device-type="{{device.type}}"
type="text/x-handlebars-template"></script>
<!--suppress HtmlUnknownTarget -->
<script id="operations-log" src="{{@unit.publicUri}}/templates/operations-log.hbs"
data-device-id="{{deviceView.deviceIdentifier}}" data-device-type="{{deviceView
.type}}"
type="text/x-handlebars-template"></script>
{{js "js/device-detail.js"}}
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true"></script>
{{js "js/load-map.js"}}

@ -36,9 +36,6 @@ function onRequest(context) {
if (filteredDeviceData["type"]) {
viewModel["type"] = filteredDeviceData["type"];
viewModel.isNotWindows = true;
if (viewModel["deviceType"] == "windows") {
viewModel.isNotWindows = false;
}
}
if (filteredDeviceData["deviceIdentifier"]) {
viewModel["deviceIdentifier"] = filteredDeviceData["deviceIdentifier"];
@ -197,7 +194,7 @@ function onRequest(context) {
viewModel["deviceInfoAvailable"] = false;
}
deviceViewData["deviceView"] = viewModel;
deviceViewData["device"] = viewModel;
} else if (response["status"] == "unauthorized") {
deviceViewData["deviceFound"] = true;
deviceViewData["isAuthorized"] = false;
@ -207,5 +204,11 @@ function onRequest(context) {
} else {
deviceViewData["deviceFound"] = false;
}
var autoCompleteParams = [
{"name" : "deviceId", "value" : deviceId}
];
deviceViewData["autoCompleteParams"] = autoCompleteParams;
return deviceViewData;
}

@ -111,9 +111,6 @@ var InitiateViewOption = null;
$(document).ready(function() {
$(".device-detail-body").removeClass("hidden");
$("#loading-content").remove();
console.log("##### something went wrong here!" + deviceType)
loadOperationBar(deviceType);
loadOperationsLog(false);
loadApplicationsList();
loadPolicyCompliance();
@ -126,93 +123,8 @@ var InitiateViewOption = null;
$("#apps-spinner").removeClass("hidden");
loadApplicationsList();
});
$("#refresh-operations").click(function () {
$("#operations-spinner").removeClass("hidden");
loadOperationsLog(true);
});
});
function loadOperationsLog(update) {
var operationsLogTable = "#operations-log-table";
if (update) {
operationTable = $(operationsLogTable).DataTable();
operationTable.ajax.reload(false);
return;
}
operationTable = $(operationsLogTable).datatables_extended({
serverSide: true,
processing: false,
searching: false,
ordering: false,
pageLength : 10,
order: [],
ajax: {
url: "/emm/api/operation/paginate",
data: {deviceId : deviceIdentifier, deviceType: deviceType},
dataSrc: function (json) {
$("#operations-spinner").addClass("hidden");
$("#operations-log-container").empty();
return json.data;
}
},
columnDefs: [
{targets: 0, data: "code" },
{targets: 1, data: "status", render:
function (status) {
var html;
switch (status) {
case "COMPLETED" :
html = "<span><i class='fw fw-ok icon-success'></i> Completed</span>";
break;
case "PENDING" :
html = "<span><i class='fw fw-warning icon-warning'></i> Pending</span>";
break;
case "ERROR" :
html = "<span><i class='fw fw-error icon-danger'></i> Error</span>";
break;
case "IN_PROGRESS" :
html = "<span><i class='fw fw-ok icon-warning'></i> In Progress</span>";
break;
case "REPEATED" :
html = "<span><i class='fw fw-ok icon-warning'></i> Repeated</span>";
break;
}
return html;
}
},
{targets: 2, data: "createdTimeStamp", render:
function (date) {
var value = String(date);
return value.slice(0, 16);
}
}
],
"createdRow": function(row, data) {
$(row).attr("data-type", "selectable");
$(row).attr("data-id", data["id"]);
$.each($("td", row),
function(colIndex) {
switch(colIndex) {
case 1:
$(this).attr("data-grid-label", "Code");
$(this).attr("data-display", data["code"]);
break;
case 2:
$(this).attr("data-grid-label", "Status");
$(this).attr("data-display", data["status"]);
break;
case 3:
$(this).attr("data-grid-label", "Created Timestamp");
$(this).attr("data-display", data["createdTimeStamp"]);
break;
}
}
);
}
});
}
function loadApplicationsList() {
var applicationsList = $("#applications-list");
var applicationListingTemplate = applicationsList.attr("src");

@ -1,24 +0,0 @@
<table class="table table-striped table-hover table-bordered display data-table" id="operations-log-table">
<thead>
<tr class="sort-row">
<th>Operation Code</th>
<th>Status</th>
<th>Request created at</th>
</tr>
</thead>
<tbody>
{{#each operations}}
<tr data-type="selectable" data-id="{{id}}">
<td data-display="{{code}}" data-grid-label="Code">{{code}}</td>
<td data-display="{{status}}" data-grid-label="Status">
{{#equal status "COMPLETED"}}<span><i class="fw fw-ok icon-success"></i> Completed</span>{{/equal}}
{{#equal status "PENDING"}}<span><i class="fw fw-warning icon-warning"></i> Pending</span>{{/equal}}
{{#equal status "ERROR"}}<span><i class="fw fw-error icon-danger"></i> Error</span>{{/equal}}
{{#equal status "IN_PROGRESS"}}<span><i class="fw fw-ok icon-warning"></i> In Progress</span>{{/equal}}
</td>
<td data-display="{{createdTimeStamp}}" data-grid-label="Created Timestamp">{{createdTimeStamp}}</td>
</tr>
{{/each}}
<br class="c-both" />
</tbody>
</table>

@ -45,7 +45,8 @@
</div>
<div class="col-lg-3">
<span class="list-group-item-actions">
<a href="/emm/policies/view?id={{policy.id}}" class="cu-btn-inner policy-view-link" data-id="{{id}}">
<a href="/devicemgt/policies/view?id={{policy.id}}" class="cu-btn-inner
policy-view-link" data-id="{{id}}">
<span class="fw-stack">
<i class="fw fw-ring fw-stack-2x"></i>
<i class="fw fw-view fw-stack-1x"></i>

@ -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.
}}
{{unit "cdmf.unit.device.type.android.date-range-picker"}}
{{#zone "content"}}
<div id="operations-mod" data-permissions="{{permissions}}" data-device-type="{{deviceType}}" data-ownership="{{ownership}}">
{{unit "cdmf.unit.device.type.android.operation-mod"}}
</div>
{{/zone}}
{{#zone "bottomJs"}}
<!--suppress HtmlUnknownTarget -->
<script id="operations-bar" src="{{@unit.publicUri}}/templates/operations.hbs"
type="text/x-handlebars-template"></script>
{{js "js/operation-bar.js"}}
{{/zone}}

@ -0,0 +1,106 @@
/*
* 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 log = new Log("cdmf.unit.device.type.android.operation-bar");
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
var viewModel = {};
var permissions = {};
// adding android operations related permission checks
permissions["android"] = [];
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/ring")) {
permissions["android"].push("DEVICE_RING");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/lock")) {
permissions["android"].push("DEVICE_LOCK");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/unlock")) {
permissions["android"].push("DEVICE_UNLOCK");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/location")) {
permissions["android"].push("DEVICE_LOCATION");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/clear-password")) {
permissions["android"].push("CLEAR_PASSWORD");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/reboot")) {
permissions["android"].push("DEVICE_REBOOT");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/upgrade-firmware")) {
permissions["android"].push("UPGRADE_FIRMWARE");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/mute")) {
permissions["android"].push("DEVICE_MUTE");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/send-notification")) {
permissions["android"].push("NOTIFICATION");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/change-lock-code")) {
permissions["android"].push("CHANGE_LOCK_CODE");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/enterprise-wipe")) {
permissions["android"].push("ENTERPRISE_WIPE");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/wipe")) {
permissions["android"].push("WIPE_DATA");
}
// adding ios operations related permission checks
permissions["ios"] = [];
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/ios/lock")) {
permissions["ios"].push("DEVICE_LOCK");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/ios/location")) {
permissions["ios"].push("LOCATION");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/ios/enterprise-wipe")) {
permissions["ios"].push("ENTERPRISE_WIPE");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/ios/notification")) {
permissions["ios"].push("NOTIFICATION");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/ios/ring")) {
permissions["ios"].push("RING");
}
// adding windows operations related permission checks
permissions["windows"] = [];
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/windows/lock")) {
permissions["windows"].push("DEVICE_LOCK");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/disenroll/windows")) {
permissions["windows"].push("DISENROLL");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/windows/wipe")) {
permissions["windows"].push("WIPE_DATA");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/windows/ring")) {
permissions["windows"].push("DEVICE_RING");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/windows/lock-reset")) {
permissions["windows"].push("LOCK_RESET");
}
viewModel["permissions"] = stringify(permissions);
viewModel["deviceType"] = context.unit.params.deviceType;
viewModel["ownership"] = context.unit.params.ownership;
return viewModel;
}

@ -0,0 +1,248 @@
/*
* 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.
*/
/*
* Setting-up global variables.
*/
var operations = '.wr-operations',
modalPopup = '.modal',
modalPopupContent = modalPopup + ' .modal-content',
navHeight = $('#nav').height(),
headerHeight = $('header').height(),
offset = (headerHeight + navHeight),
deviceSelection = '.device-select',
platformTypeConstants = {
"ANDROID": "android",
"IOS": "ios",
"WINDOWS": "windows"
},
ownershipTypeConstants = {
"BYOD": "BYOD",
"COPE": "COPE"
},
operationBarModeConstants = {
"BULK": "BULK_OPERATION_MODE",
"SINGLE": "SINGLE_OPERATION_MODE"
};
/*
* Function to get selected devices ID's
*/
function getSelectedDeviceIds() {
var deviceIdentifierList = [];
$(deviceSelection).each(function (index) {
var device = $(this);
var deviceId = device.data('deviceid');
var deviceType = device.data('type');
deviceIdentifierList.push({
"id": deviceId,
"type": deviceType
});
});
if (deviceIdentifierList.length == 0) {
var thisTable = $(".DTTT_selected").closest('.dataTables_wrapper').find('.dataTable').dataTable();
thisTable.api().rows().every(function () {
if ($(this.node()).hasClass('DTTT_selected')) {
var deviceId = $(thisTable.api().row(this).node()).data('deviceid');
var deviceType = $(thisTable.api().row(this).node()).data('devicetype');
deviceIdentifierList.push({
"id": deviceId,
"type": deviceType
});
}
});
}
return deviceIdentifierList;
}
/*
* On operation click function.
* @param selection: Selected operation
*/
function operationSelect(selection) {
var deviceIdList = getSelectedDeviceIds();
if (deviceIdList == 0) {
$(modalPopupContent).html($("#errorOperations").html());
} else {
$(modalPopupContent).addClass("operation-data");
$(modalPopupContent).html($(operations + " .operation[data-operation-code=" + selection + "]").html());
$(modalPopupContent).data("operation-code", selection);
}
showPopup();
}
function getDevicesByTypes(deviceList) {
var deviceTypes = {};
$.each(deviceList, function (index, item) {
if (!deviceTypes[item.type]) {
deviceTypes[item.type] = [];
}
if (item.type == platformTypeConstants.ANDROID ||
item.type == platformTypeConstants.IOS || item.type == platformTypeConstants.WINDOWS) {
deviceTypes[item.type].push(item.id);
}
});
return deviceTypes;
}
//function unloadOperationBar() {
// $("#showOperationsBtn").addClass("hidden");
// $(".wr-operations").html("");
//}
function loadOperationBar(deviceType, ownership, mode) {
var operationBar = $("#operations-bar");
var operationBarSrc = operationBar.attr("src");
$.template("operations-bar", operationBarSrc, function (template) {
var serviceURL = "/api/device-mgt/v1.0/devices/" + deviceType + "/*/features";
invokerUtil.get(
serviceURL,
// success callback
function (data) {
var permittedOperations = [];
var i;
var permissionList = $("#operations-mod").data("permissions");
var totalFeatures = JSON.parse(data);
for (i = 0; i < permissionList[deviceType].length; i++) {
var j;
for (j = 0; j < totalFeatures.length; j++) {
if (permissionList[deviceType][i] == totalFeatures[j]["code"]) {
if (deviceType == platformTypeConstants.ANDROID) {
if (totalFeatures[j]["code"] == "DEVICE_UNLOCK") {
if (ownership == ownershipTypeConstants.COPE) {
permittedOperations.push(totalFeatures[j]);
}
} else if (totalFeatures[j]["code"] == "WIPE_DATA") {
if (mode == operationBarModeConstants.BULK) {
if (ownership == ownershipTypeConstants.COPE) {
permittedOperations.push(totalFeatures[j]);
}
} else {
permittedOperations.push(totalFeatures[j]);
}
} else {
permittedOperations.push(totalFeatures[j]);
}
} else {
permittedOperations.push(totalFeatures[j]);
}
}
}
}
var viewModel = {};
permittedOperations = permittedOperations.filter(function (current) {
var iconName;
switch (deviceType) {
case platformTypeConstants.ANDROID:
iconName = operationModule.getAndroidIconForFeature(current.code);
break;
case platformTypeConstants.WINDOWS:
iconName = operationModule.getWindowsIconForFeature(current.code);
break;
case platformTypeConstants.IOS:
iconName = operationModule.getIOSIconForFeature(current.code);
break;
}
/* adding ownership in addition to device-type
as it's vital in cases where UI for the same feature should change
according to ownership
*/
if (ownership) {
current.ownership = ownership;
}
if (iconName) {
current.icon = iconName;
}
return current;
});
viewModel.features = permittedOperations;
var content = template(viewModel);
$(".wr-operations").html(content);
},
// error callback
function (message) {
$(".wr-operations").html(message);
});
});
}
function runOperation(operationName) {
var deviceIdList = getSelectedDeviceIds();
var list = getDevicesByTypes(deviceIdList);
var successCallback = function (data) {
if (operationName == "NOTIFICATION") {
$(modalPopupContent).html($("#messageSuccess").html());
} else {
$(modalPopupContent).html($("#operationSuccess").html());
}
showPopup();
};
var errorCallback = function (data) {
$(modalPopupContent).html($("#errorOperationUnexpected").html());
showPopup();
};
var payload, serviceEndPoint;
if (list[platformTypeConstants.IOS]) {
payload =
operationModule.generatePayload(platformTypeConstants.IOS, operationName, list[platformTypeConstants.IOS]);
serviceEndPoint = operationModule.getIOSServiceEndpoint(operationName);
} else if (list[platformTypeConstants.ANDROID]) {
payload = operationModule
.generatePayload(platformTypeConstants.ANDROID, operationName, list[platformTypeConstants.ANDROID]);
serviceEndPoint = operationModule.getAndroidServiceEndpoint(operationName);
} else if (list[platformTypeConstants.WINDOWS]) {
payload = operationModule.generatePayload(platformTypeConstants.WINDOWS, operationName,
list[platformTypeConstants.WINDOWS]);
serviceEndPoint = operationModule.getWindowsServiceEndpoint(operationName);
}
if (operationName == "NOTIFICATION") {
var errorMsgWrapper = "#notification-error-msg";
var errorMsg = "#notification-error-msg span";
var messageTitle = $("#messageTitle").val();
var messageText = $("#messageText").val();
if (!(messageTitle && messageText)) {
$(errorMsg).text("Enter a message. It cannot be empty.");
$(errorMsgWrapper).removeClass("hidden");
} else {
invokerUtil.post(serviceEndPoint, payload, successCallback, errorCallback);
$(modalPopupContent).removeData();
hidePopup();
}
} else {
invokerUtil.post(serviceEndPoint, payload, successCallback, errorCallback);
$(modalPopupContent).removeData();
hidePopup();
}
}
/*
* DOM ready functions.
*/
$(document).ready(function () {
$(operations).show();
});

@ -15,18 +15,113 @@
specific language governing permissions and limitations
under the License.
}}
{{#if control_operations}}
<div class="wr-operations" style="height: 87px; display: block;">
<style>
::-webkit-input-placeholder {
color: #B8B8B8;
}
{{unit "cdmf.unit.device.type.android.date-range-picker"}}
::-moz-placeholder {
color: #B8B8B8;
}
{{#zone "content"}}
<div id="operations-mod" data-permissions="{{permissions}}" data-device-type="{{deviceType}}" data-ownership="{{ownership}}">
{{unit "cdmf.unit.device.type.android.operation-mod"}}
:-ms-input-placeholder {
color: #B8B8B8;
}
input:-moz-placeholder {
color: #B8B8B8;
}
</style>
{{#each control_operations}}
<a href="javascript:operationSelect('{{operation}}')">
{{#if iconFont}}
<i class="fw {{iconFont}}"></i>
{{else}}
{{#if icon}}
<img src="{{@app.context}}/{{icon}}" style="width: 48px;"/>
{{else}}
<i class="fw fw-service"></i>
{{/if}}
{{/if}}
<span>{{name}}</span>
</a>
<div class="operation" data-operation-code="{{operation}}">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>
<span class="fw-stack">
<i class="fw fw-ring fw-stack-2x"></i>
<i class="fw fw-service fw-stack-1x"></i>
</span>
{{name}}
<br>
</h3>
<h4>
{{description}}
<br>
</h4>
<form action="{{params.0.uri}}" method="{{params.0.method}}"
style="padding-bottom: 20px;"
data-payload="{{payload}}" id="form-{{operation}}"
data-device-id="{{../device.deviceIdentifier}}">
{{#each params.0.pathParams}}
<input type="{{type}}" id="{{name}}" placeholder="{{name}}" class="form-control" data-param-type="path" value="{{value}}" />
<br />
{{/each}}
{{#each params.0.formParams}}
<input type="{{type}}" id="{{name}}" name="{{name}}" placeholder="{{name}}" class="form-control" data-param-type="form" value="{{value}}" />
<br />
{{/each}}
{{#each params.0.queryParams}}
<input type="{{type}}" id="{{name}}" placeholder="{{name}}" class="form-control" data-param-type="query" value="{{value}}" />
<br />
{{/each}}
<button id="btnSend" type="button" onclick="submitForm('form-{{operation}}')" class="btn btn-default">&nbsp;&nbsp;&nbsp;&nbsp;Send
to Device&nbsp;&nbsp;&nbsp;&nbsp;</button>
<label id="lblSending" class="wr-input-label hidden"><i
class="fw fw-lifecycle fw-spin fw-2x"></i> Sending..</label>
<label id="lblSent" class="wr-input-label hidden"><i
class="fw fw-check fw-2x"></i> Sent</label>
</form>
</div>
</div>
</div>
</div>
{{/each}}
</div>
{{else}}
<div align="center">
<h4 style="color: #D8000C"><i class="icon fw fw-error" style="color: #D8000C"></i>
Operations Loading Failed!</h4>
</div>
{{/if}}
<div id="operation-response-template" style="display: none">
<div class="content">
<div class="row">
<div class="col-lg-5 col-md-6 col-centered">
<h3>
<span class="fw-stack">
<i class="fw fw-ring fw-stack-2x"></i>
<i id="status-icon" class="fw fw-error fw-stack-1x"></i>
</span>
<br>
</h3>
<h4>
<span id="title"></span>
<br>
</h4>
<span id="description"></span>
</div>
</div>
</div>
</div>
{{/zone}}
{{#zone "bottomJs"}}
<!--suppress HtmlUnknownTarget -->
<script id="operations-bar" src="{{@unit.publicUri}}/templates/operations.hbs"
type="text/x-handlebars-template"></script>
{{js "js/operation-bar.js"}}
{{/zone}}

@ -17,90 +17,51 @@
*/
function onRequest(context) {
var log = new Log("cdmf.unit.device.type.android.operation-bar");
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
var viewModel = {};
var permissions = {};
// adding android operations related permission checks
permissions["android"] = [];
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/ring")) {
permissions["android"].push("DEVICE_RING");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/lock")) {
permissions["android"].push("DEVICE_LOCK");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/unlock")) {
permissions["android"].push("DEVICE_UNLOCK");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/location")) {
permissions["android"].push("DEVICE_LOCATION");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/clear-password")) {
permissions["android"].push("CLEAR_PASSWORD");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/reboot")) {
permissions["android"].push("DEVICE_REBOOT");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/upgrade-firmware")) {
permissions["android"].push("UPGRADE_FIRMWARE");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/mute")) {
permissions["android"].push("DEVICE_MUTE");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/send-notification")) {
permissions["android"].push("NOTIFICATION");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/change-lock-code")) {
permissions["android"].push("CHANGE_LOCK_CODE");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/enterprise-wipe")) {
permissions["android"].push("ENTERPRISE_WIPE");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning-device/operations/android/wipe")) {
permissions["android"].push("WIPE_DATA");
}
// adding ios operations related permission checks
permissions["ios"] = [];
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/ios/lock")) {
permissions["ios"].push("DEVICE_LOCK");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/ios/location")) {
permissions["ios"].push("LOCATION");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/ios/enterprise-wipe")) {
permissions["ios"].push("ENTERPRISE_WIPE");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/ios/notification")) {
permissions["ios"].push("NOTIFICATION");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/ios/ring")) {
permissions["ios"].push("RING");
var log = new Log("operation.js");
var operationModule = require("/app/modules/business-controllers/operation.js")["operationModule"];
var device = context.unit.params.device;
var autoCompleteParams = context.unit.params.autoCompleteParams;
var encodedFeaturePayloads=context.unit.params.encodedFeaturePayloads;
var controlOperations = operationModule.getControlOperations(device.type);
var queryParams = [];
var formParams = [];
var pathParams = [];
for (var i = 0; i < controlOperations.length; i++) {
var currentParamList = controlOperations[i]["params"];
for (var j = 0; j < currentParamList.length; j++) {
var currentParam = currentParamList[j];
currentParamList[j]["formParams"] = processParams(currentParam["formParams"], autoCompleteParams);
currentParamList[j]["queryParams"] = processParams(currentParam["queryParams"], autoCompleteParams);
currentParamList[j]["pathParams"] = processParams(currentParam["pathParams"], autoCompleteParams);
}
controlOperations[i]["params"] = currentParamList;
if (encodedFeaturePayloads) {
controlOperations[i]["payload"] = getPayload(encodedFeaturePayloads, controlOperations[i]["operation"]);
}
}
return {"control_operations": controlOperations, "device": device};
}
// adding windows operations related permission checks
permissions["windows"] = [];
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/windows/lock")) {
permissions["windows"].push("DEVICE_LOCK");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/disenroll/windows")) {
permissions["windows"].push("DISENROLL");
function processParams(paramsList, autoCompleteParams) {
var log = new Log();
log.info("-- Params : "+paramsList.length);
log.info("-- Auto Params : "+autoCompleteParams);
for (var i = 0; i < paramsList.length; i++) {
var paramName = paramsList[i];
var paramValue = "";
var paramType = "text";
for (var k = 0; k < autoCompleteParams.length; k++) {
if (paramName == autoCompleteParams[k].name) {
paramValue = autoCompleteParams[k].value;
paramType = "hidden";
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/windows/wipe")) {
permissions["windows"].push("WIPE_DATA");
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/windows/ring")) {
permissions["windows"].push("DEVICE_RING");
paramsList[i] = {"name": paramName, "value": paramValue, "type": paramType};
}
if (userModule.isAuthorized("/permission/admin/device-mgt/devices/owning/operations/windows/lock-reset")) {
permissions["windows"].push("LOCK_RESET");
return paramsList;
}
viewModel["permissions"] = stringify(permissions);
viewModel["deviceType"] = context.unit.params.deviceType;
viewModel["ownership"] = context.unit.params.ownership;
return viewModel;
function getPayload(featuresPayload, featureCode){
var featuresJSONPayloads = JSON.parse(featuresPayload);
return featuresJSONPayloads[featureCode];
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
* 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
@ -16,233 +16,131 @@
* under the License.
*/
/*
* Setting-up global variables.
*/
var operations = '.wr-operations',
modalPopup = '.modal',
modalPopupContent = modalPopup + ' .modal-content',
navHeight = $('#nav').height(),
headerHeight = $('header').height(),
offset = (headerHeight + navHeight),
deviceSelection = '.device-select',
platformTypeConstants = {
"ANDROID": "android",
"IOS": "ios",
"WINDOWS": "windows"
},
ownershipTypeConstants = {
"BYOD": "BYOD",
"COPE": "COPE"
},
operationBarModeConstants = {
"BULK": "BULK_OPERATION_MODE",
"SINGLE": "SINGLE_OPERATION_MODE"
};
/*
* Function to get selected devices ID's
*/
function getSelectedDeviceIds() {
var deviceIdentifierList = [];
$(deviceSelection).each(function (index) {
var device = $(this);
var deviceId = device.data('deviceid');
var deviceType = device.data('type');
deviceIdentifierList.push({
"id": deviceId,
"type": deviceType
});
});
if (deviceIdentifierList.length == 0) {
var thisTable = $(".DTTT_selected").closest('.dataTables_wrapper').find('.dataTable').dataTable();
thisTable.api().rows().every(function () {
if ($(this.node()).hasClass('DTTT_selected')) {
var deviceId = $(thisTable.api().row(this).node()).data('deviceid');
var deviceType = $(thisTable.api().row(this).node()).data('devicetype');
deviceIdentifierList.push({
"id": deviceId,
"type": deviceType
});
}
});
}
return deviceIdentifierList;
}
/*
* On operation click function.
* @param selection: Selected operation
*/
function operationSelect(selection) {
var deviceIdList = getSelectedDeviceIds();
if (deviceIdList == 0) {
$(modalPopupContent).html($("#errorOperations").html());
} else {
$(modalPopupContent).addClass("operation-data");
$(modalPopupContent).html($(operations + " .operation[data-operation-code=" + selection + "]").html());
$(modalPopupContent).html($(" .operation[data-operation-code=" + selection + "]").html());
$(modalPopupContent).data("operation-code", selection);
}
showPopup();
}
function getDevicesByTypes(deviceList) {
var deviceTypes = {};
$.each(deviceList, function (index, item) {
if (!deviceTypes[item.type]) {
deviceTypes[item.type] = [];
}
if (item.type == platformTypeConstants.ANDROID ||
item.type == platformTypeConstants.IOS || item.type == platformTypeConstants.WINDOWS) {
deviceTypes[item.type].push(item.id);
function submitForm(formId) {
var form = $("#" + formId);
var uri = form.attr("action");
var deviceId = form.data("device-id");
var uriencodedQueryStr = "";
var uriencodedFormStr = "";
var payload = {};
form.find("input").each(function () {
var input = $(this);
if (input.data("param-type") == "path") {
uri = uri.replace("{" + input.attr("id") + "}", input.val());
} else if (input.data("param-type") == "query") {
var prefix = (uriencodedQueryStr == "") ? "?" : "&";
uriencodedQueryStr += prefix + input.attr("id") + "=" + input.val();
} else if (input.data("param-type") == "form") {
var prefix = (uriencodedFormStr == "") ? "" : "&";
uriencodedFormStr += prefix + input.attr("id") + "=" + input.val();
//payload[input.attr("id")] = input.val();
}
});
return deviceTypes;
}
//function unloadOperationBar() {
// $("#showOperationsBtn").addClass("hidden");
// $(".wr-operations").html("");
//}
function loadOperationBar(deviceType, ownership, mode) {
var operationBar = $("#operations-bar");
var operationBarSrc = operationBar.attr("src");
$.template("operations-bar", operationBarSrc, function (template) {
var serviceURL = "/api/device-mgt/v1.0/devices/" + deviceType + "/*/features";
invokerUtil.get(
serviceURL,
// success callback
function (data) {
var permittedOperations = [];
var i;
var permissionList = $("#operations-mod").data("permissions");
var totalFeatures = JSON.parse(data);
for (i = 0; i < permissionList[deviceType].length; i++) {
var j;
for (j = 0; j < totalFeatures.length; j++) {
if (permissionList[deviceType][i] == totalFeatures[j]["code"]) {
if (deviceType == platformTypeConstants.ANDROID) {
if (totalFeatures[j]["code"] == "DEVICE_UNLOCK") {
if (ownership == ownershipTypeConstants.COPE) {
permittedOperations.push(totalFeatures[j]);
}
} else if (totalFeatures[j]["code"] == "WIPE_DATA") {
if (mode == operationBarModeConstants.BULK) {
if (ownership == ownershipTypeConstants.COPE) {
permittedOperations.push(totalFeatures[j]);
}
} else {
permittedOperations.push(totalFeatures[j]);
}
} else {
permittedOperations.push(totalFeatures[j]);
}
uri += uriencodedQueryStr;
var httpMethod = form.attr("method").toUpperCase();
var contentType = form.attr("enctype");
console.log("URL "+uri);
console.log("Method "+httpMethod);
console.log("Content Type "+contentType);
var featurePayload = form.attr("data-payload");
if (featurePayload) {
contentType = "application/json";
payload = JSON.parse(atob(featurePayload));
} else if (contentType == undefined || contentType.isEmpty()) {
contentType = "application/x-www-form-urlencoded";
payload = uriencodedFormStr;
}
//setting responses callbacks
var defaultStatusClasses = "fw fw-stack-1x";
var content = $("#operation-response-template").find(".content");
var title = content.find("#title");
var statusIcon = content.find("#status-icon");
var description = content.find("#description");
description.html("");
var successCallBack = function (response) {
var res = response;
try {
res = JSON.parse(response).messageFromServer;
} catch (err) {
//do nothing
}
title.html("Operation Triggered!");
statusIcon.attr("class", defaultStatusClasses + " fw-check");
description.html(res);
console.log("success!");
$(modalPopupContent).html(content.html());
};
var errorCallBack = function (response) {
console.log(response);
title.html("An Error Occurred!");
statusIcon.attr("class", defaultStatusClasses + " fw-error");
var reason = (response.responseText == "null")?response.statusText:response.responseText;
try {
reason = JSON.parse(reason).message;
} catch (err) {
//do nothing
}
description.html(reason);
console.log("Error!");
$(modalPopupContent).html(content.html());
};
//executing http request
if (httpMethod == "GET") {
invokerUtil.get(uri, successCallBack, errorCallBack, contentType);
} else if (httpMethod == "POST") {
console.log("------ cType "+contentType);
var payloadTest = [deviceId];
invokerUtil.post(uri, payloadTest, successCallBack, errorCallBack, "application/json");
} else if (httpMethod == "PUT") {
invokerUtil.put(uri, payload, successCallBack, errorCallBack, contentType);
} else if (httpMethod == "DELETE") {
invokerUtil.delete(uri, successCallBack, errorCallBack, contentType);
} else {
permittedOperations.push(totalFeatures[j]);
title.html("An Error Occurred!");
statusIcon.attr("class", defaultStatusClasses + " fw-error");
description.html("This operation requires http method: " + httpMethod + " which is not supported yet!");
$(modalPopupContent).html(content.html());
}
}
}
}
var viewModel = {};
permittedOperations = permittedOperations.filter(function (current) {
var iconName;
switch (deviceType) {
case platformTypeConstants.ANDROID:
iconName = operationModule.getAndroidIconForFeature(current.code);
break;
case platformTypeConstants.WINDOWS:
iconName = operationModule.getWindowsIconForFeature(current.code);
break;
case platformTypeConstants.IOS:
iconName = operationModule.getIOSIconForFeature(current.code);
break;
}
/* adding ownership in addition to device-type
as it's vital in cases where UI for the same feature should change
according to ownership
*/
if (ownership) {
current.ownership = ownership;
}
if (iconName) {
current.icon = iconName;
}
return current;
$(document).on('submit', 'form', function (e) {
cosole.log("darn!!");
e.preventDefault();
var postOperationRequest = $.ajax({
url: $(this).attr("action") + '&' + $(this).serialize(),
method: "post"
});
viewModel.features = permittedOperations;
var content = template(viewModel);
$(".wr-operations").html(content);
},
// error callback
function (message) {
$(".wr-operations").html(message);
});
});
}
var btnSubmit = $('#btnSend', this);
btnSubmit.addClass('hidden');
function runOperation(operationName) {
var deviceIdList = getSelectedDeviceIds();
var list = getDevicesByTypes(deviceIdList);
var successCallback = function (data) {
if (operationName == "NOTIFICATION") {
$(modalPopupContent).html($("#messageSuccess").html());
} else {
$(modalPopupContent).html($("#operationSuccess").html());
}
showPopup();
};
var errorCallback = function (data) {
$(modalPopupContent).html($("#errorOperationUnexpected").html());
showPopup();
};
var lblSending = $('#lblSending', this);
lblSending.removeClass('hidden');
var payload, serviceEndPoint;
if (list[platformTypeConstants.IOS]) {
payload =
operationModule.generatePayload(platformTypeConstants.IOS, operationName, list[platformTypeConstants.IOS]);
serviceEndPoint = operationModule.getIOSServiceEndpoint(operationName);
} else if (list[platformTypeConstants.ANDROID]) {
payload = operationModule
.generatePayload(platformTypeConstants.ANDROID, operationName, list[platformTypeConstants.ANDROID]);
serviceEndPoint = operationModule.getAndroidServiceEndpoint(operationName);
} else if (list[platformTypeConstants.WINDOWS]) {
payload = operationModule.generatePayload(platformTypeConstants.WINDOWS, operationName,
list[platformTypeConstants.WINDOWS]);
serviceEndPoint = operationModule.getWindowsServiceEndpoint(operationName);
}
if (operationName == "NOTIFICATION") {
var errorMsgWrapper = "#notification-error-msg";
var errorMsg = "#notification-error-msg span";
var messageTitle = $("#messageTitle").val();
var messageText = $("#messageText").val();
if (!(messageTitle && messageText)) {
$(errorMsg).text("Enter a message. It cannot be empty.");
$(errorMsgWrapper).removeClass("hidden");
} else {
invokerUtil.post(serviceEndPoint, payload, successCallback, errorCallback);
$(modalPopupContent).removeData();
var lblSent = $('#lblSent', this);
postOperationRequest.done(function (data) {
lblSending.addClass('hidden');
lblSent.removeClass('hidden');
setTimeout(function () {
hidePopup();
}
} else {
invokerUtil.post(serviceEndPoint, payload, successCallback, errorCallback);
$(modalPopupContent).removeData();
hidePopup();
}
}
}, 3000);
});
/*
* DOM ready functions.
*/
$(document).ready(function () {
$(operations).show();
postOperationRequest.fail(function (jqXHR, textStatus) {
lblSending.addClass('hidden');
lblSent.addClass('hidden');
});
});

@ -3,6 +3,43 @@
"label": "Android",
"category": "mobile",
"analyticsEnabled": "false",
"groupingEnabled": "false"
"groupingEnabled": "false",
"features": {
"DEVICE_RING": {
"icon" : "fw-dial-up"
},
"DEVICE_LOCK": {
"icon" : "fw-lock"
},
"DEVICE_LOCATION": {
"icon" : "fw-map-location"
},
"CLEAR_PASSWORD": {
"icon" : "fw-clear"
},
"DEVICE_REBOOT": {
"icon" : "fw-refresh"
},
"UPGRADE_FIRMWARE": {
"icon" : "fw-hardware"
},
"DEVICE_MUTE": {
"icon" : "fw-mute"
},
"NOTIFICATION": {
"icon" : "fw-message"
},
"CHANGE_LOCK_CODE": {
"icon" : "fw-security"
},
"ENTERPRISE_WIPE": {
"icon" : "fw-block"
},
"WIPE_DATA": {
"icon" : "fw-delete"
}
}
}
}

@ -58,347 +58,274 @@
</DataSource>
<Features>
<Feature code="DEVICE_RING">
<Name>Ring</Name>
<Description>Ring the device</Description>
<Operation context="/api/device-mgt/android/v1.0/admin/devices/ring" method="POST">
</Operation>
</Feature>
<Feature code="DEVICE_LOCK">
<Name>Device Lock</Name>
<Description>Lock the device</Description>
<Operation context="android/lock_devices" method="POST">
<Operation context="/api/device-mgt/android/v1.0/admin/devices/lock-devices" method="POST" conent-type="application/json">
<FormParameters>
<Parameter id="message" optional="true" type="text">Message to be sent to the device</Parameter>
</FormParameters>
</Operation>
</Feature>
<Feature code="DEVICE_LOCATION">
<Name>Location</Name>
<Description>Request coordinates of device location</Description>
<Operation context="" method="POST">
<Operation context="/api/device-mgt/android/v1.0/admin/devices/location" method="POST" conent-type="application/json">
</Operation>
</Feature>
<Feature code="WIFI">
<Name>Wifi</Name>
<Description>Setting up wifi configuration</Description>
<Operation context="" method="POST">
<Feature code="CLEAR_PASSWORD">
<Name>Clear Password</Name>
<Description>Clear current password</Description>
<Operation context="/api/device-mgt/android/v1.0/admin/devices/clear-password" method="POST" conent-type="application/json">
</Operation>
</Feature>
<Feature code="CAMERA">
<Name>Camera</Name>
<Description>Enable or disable camera</Description>
<Operation context="" method="POST">
<Feature code="DEVICE_REBOOT">
<Name>Reboot</Name>
<Description>Reboot the device</Description>
<Operation context="/api/device-mgt/android/v1.0/admin/devices/reboot" method="POST" conent-type="application/json">
</Operation>
</Feature>
<Feature code="EMAIL">
<Name>Email</Name>
<Description>Configure email settings</Description>
<Operation context="" method="POST">
<Feature code="UPGRADE_FIRMWARE">
<Name>Upgrade Firmware</Name>
<Description>Upgrade Firmware</Description>
<Operation context="/api/device-mgt/android/v1.0/admin/devices/upgrade-firmware" method="POST" conent-type="application/json">
<FormParameters>
<Parameter id="immediate" optional="true" type="check" helper-text="Once enabled, device firmware upgrade process will start instantly.">Instant Upgrade</Parameter>
<Parameter id="schedule" optional="false" type="text">Enter the date and time to schedule firmware upgrade.</Parameter>
<Parameter id="server" optional="true" type="text">Enter firmware upgrade server URL (ie. http://abc.com or http://abc.com/ota) (Optional).</Parameter>
</FormParameters>
</Operation>
</Feature>
<Feature code="DEVICE_MUTE">
<Name>Mute</Name>
<Description>Enable mute in the device</Description>
<Operation context="" method="POST">
<Operation context="/api/device-mgt/android/v1.0/admin/devices/mute" method="POST" conent-type="application/json">
</Operation>
</Feature>
<Feature code="DEVICE_INFO">
<Name>Device info</Name>
<Description>Request device information</Description>
<Operation context="" method="POST">
<Feature code="NOTIFICATION">
<Name>Message</Name>
<Description>Send message</Description>
<Operation context="/api/device-mgt/android/v1.0/admin/devices/send-notification" method="POST" conent-type="application/json">
<FormParameters>
<Parameter id="messageText" optional="false" type="text">Title Here...</Parameter>
<Parameter id="messageTitle" optional="false" type="text-area">Message Here...</Parameter>
</FormParameters>
</Operation>
</Feature>
<Feature code="CHANGE_LOCK_CODE">
<Name>Change Lock-code</Name>
<Description>Change current lock code</Description>
<Operation context="/api/device-mgt/android/v1.0/admin/devices/change-lock-code" method="POST" conent-type="application/json">
<FormParameters>
<Parameter id="lockCode" optional="false" type="text">Lock Code</Parameter>
</FormParameters>
</Operation>
</Feature>
<Feature code="ENTERPRISE_WIPE">
<Name>Enterprise Wipe</Name>
<Description>Remove enterprise applications</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="CLEAR_PASSWORD">
<Name>Clear Password</Name>
<Description>Clear current password</Description>
<Operation context="" method="POST">
<Operation context="/api/device-mgt/android/v1.0/admin/devices/enterprise-wipe" method="POST" conent-type="application/json">
</Operation>
</Feature>
<Feature code="WIPE_DATA">
<Name>Wipe Data</Name>
<Description>Factory reset the device</Description>
<Operation context="" method="POST">
<Operation context="/api/device-mgt/android/v1.0/admin/devices/wipe" method="POST" conent-type="application/json">
<FormParameters>
<!-- Needs to be converted to a unit -->
<Parameter id="pin" optional="false" type="text">Enter PIN code* of the device.</Parameter>
</FormParameters>
</Operation>
</Feature>
<Feature code="WIFI">
<Name>Wifi</Name>
<Description>Setting up wifi configuration</Description>
</Feature>
<Feature code="CAMERA">
<Name>Camera</Name>
<Description>Enable or disable camera</Description>
</Feature>
<Feature code="EMAIL">
<Name>Email</Name>
<Description>Configure email settings</Description>
</Feature>
<Feature code="DEVICE_INFO">
<Name>Device info</Name>
<Description>Request device information</Description>
</Feature>
<Feature code="APPLICATION_LIST">
<Name>Application List</Name>
<Description>Request list of current installed applications</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="CHANGE_LOCK_CODE">
<Name>Change Lock-code</Name>
<Description>Change current lock code</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="INSTALL_APPLICATION">
<Name>Install App</Name>
<Description>Install App</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="UNINSTALL_APPLICATION">
<Name>Uninstall App</Name>
<Description>Uninstall App</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="BLACKLIST_APPLICATIONS">
<Name>Blacklist app</Name>
<Description>Blacklist applications</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="ENCRYPT_STORAGE">
<Name>Encrypt Storage</Name>
<Description>Encrypt storage</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DEVICE_RING">
<Name>Ring</Name>
<Description>Ring the device</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="PASSCODE_POLICY">
<Name>Password Policy</Name>
<Description>Set passcode policy</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="NOTIFICATION">
<Name>Message</Name>
<Description>Send message</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DEVICE_REBOOT">
<Name>Reboot</Name>
<Description>Reboot the device</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="UPGRADE_FIRMWARE">
<Name>Upgrade Firmware</Name>
<Description>Upgrade Firmware</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="VPN">
<Name>Configure VPN</Name>
<Description>Configure VPN settings</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_ADJUST_VOLUME">
<Name>Disallow user to change volume</Name>
<Description>Allow or disallow user to change volume"</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_CONFIG_BLUETOOTH">
<Name>Disallow bluetooth configuration</Name>
<Description>Allow or disallow bluetooth configuration</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_CONFIG_CELL_BROADCASTS">
<Name>Disallow user to change cell broadcast configurations</Name>
<Description>Allow or disallow user to change cell broadcast configurations</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_CONFIG_CREDENTIALS">
<Name>Disallow user to change user credentials</Name>
<Description>Allow or disallow user to change user credentials</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_CONFIG_MOBILE_NETWORKS">
<Name>Disallow user to change mobile networks configurations</Name>
<Description>Allow or disallow user to change mobile networks configurations</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_CONFIG_TETHERING">
<Name>Disallow user to change tethering configurations</Name>
<Description>Allow or disallow user to change tethering configurations</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_CONFIG_VPN">
<Name>Disallow user to change VPN configurations</Name>
<Description>Allow or disallow user to change VPN configurations</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_CONFIG_WIFI">
<Name>Disallow user to change WIFI configurations</Name>
<Description>Allow or disallow user to change WIFI configurations</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_APPS_CONTROL">
<Name>Disallow user to change app control</Name>
<Description>Allow or disallow user to change app control</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_CREATE_WINDOWS">
<Name>Disallow window creation</Name>
<Description>Allow or disallow window creation</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_APPS_CONTROL">
<Name>Disallow user to change app control configurations</Name>
<Description>Allow or disallow user to change app control configurations</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_CROSS_PROFILE_COPY_PASTE">
<Name>Disallow cross profile copy paste</Name>
<Description>Allow or disallow cross profile copy paste</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_DEBUGGING_FEATURES">
<Name>Disallow debugging features</Name>
<Description>Allow or disallow debugging features</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_FACTORY_RESET">
<Name>Disallow factory reset</Name>
<Description>Allow or disallow factory reset</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_ADD_USER">
<Name>Disallow add user</Name>
<Description>Allow or disallow add user</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_INSTALL_APPS">
<Name>Disallow install apps</Name>
<Description>Allow or disallow install apps</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_INSTALL_UNKNOWN_SOURCES">
<Name>Disallow install unknown sources</Name>
<Description>Allow or disallow install unknown sources</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_MODIFY_ACCOUNTS">
<Name>Disallow modify account</Name>
<Description>Allow or disallow modify account</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_MOUNT_PHYSICAL_MEDIA">
<Name>Disallow mount physical media</Name>
<Description>Allow or disallow mount physical media</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_NETWORK_RESET">
<Name>Disallow network reset</Name>
<Description>Allow or disallow network reset</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_OUTGOING_BEAM">
<Name>Disallow outgoing beam</Name>
<Description>Allow or disallow outgoing beam</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_OUTGOING_CALLS">
<Name>Disallow outgoing calls</Name>
<Description>Allow or disallow outgoing calls</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_REMOVE_USER">
<Name>Disallow remove users</Name>
<Description>Allow or disallow remove users</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_SAFE_BOOT">
<Name>Disallow safe boot</Name>
<Description>Allow or disallow safe boot</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_SHARE_LOCATION">
<Name>Disallow share location</Name>
<Description>Allow or disallow share location</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_SMS">
<Name>Disallow sms</Name>
<Description>Allow or disallow sms</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_UNINSTALL_APPS">
<Name>Disallow uninstall app</Name>
<Description>Allow or disallow uninstall app</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_UNMUTE_MICROPHONE">
<Name>Disallow unmute mic</Name>
<Description>Allow or disallow unmute mic</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DISALLOW_USB_FILE_TRANSFER">
<Name>Disallow usb file transfer</Name>
<Description>Allow or disallow usb file transfer</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="ALLOW_PARENT_PROFILE_APP_LINKING">
<Name>Disallow parent profile app linking</Name>
<Description>Allow or disallow parent profile app linking</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="ENSURE_VERIFY_APPS">
<Name>Disallow ensure verify apps</Name>
<Description>Allow or disallow ensure verify apps</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="AUTO_TIME">
<Name>Disallow auto timing</Name>
<Description>Allow or disallow auto timing</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="REMOVE_DEVICE_OWNER">
<Name>Remove device owner</Name>
<Description>Remove device owner</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="LOGCAT">
<Name>Fetch device logcat</Name>
<Description>Fetch device logcat</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
<Feature code="DEVICE_UNLOCK">
<Name>Unlock the device</Name>
<Description>Unlock the device</Description>
<Operation context="" method="POST">
</Operation>
</Feature>
</Features>

Loading…
Cancel
Save