diff --git a/README.md b/README.md index 338ffb5..77bf601 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ Clone the repository to your Monster UI apps directory (often /var/www/html/mons ```bash cd /var/www/html/monster-ui/apps -git clone https://github.com/ruhnet/monster-ui-ruhnet-provisioner provisioner +git clone https://github.com/ruhnet/monster-ui-ruhnet-provisioner ruhnet-provisioner -sup crossbar_maintenance init_app '/var/www/html/monster-ui/apps/provisioner' \ -'http://mycrossbarapi.tld:8000/v2' +sup crossbar_maintenance init_app '/var/www/html/monster-ui/apps/ruhnet-provisioner' \ +'https://mycrossbarapi.tld:8443/v2' ``` ![Provisioner Screen](https://github.com/ruhnet/monster-ui-ruhnet-provisioner/raw/master/metadata/screenshots/Provisioner.png) diff --git a/app.js b/app.js index 97b4676..c3e17da 100644 --- a/app.js +++ b/app.js @@ -19,6 +19,46 @@ define(function (require) { 'url': 'api/{accountId}/initialtoken', 'verb': 'GET' }, + 'provisioner.phone_configfile.list': { + 'apiRoot': monster.config.api.provisioner, + 'url': 'api/{accountId}/files/customconfigs', + 'verb': 'GET' + }, + 'provisioner.phone_configfile.get': { + 'apiRoot': monster.config.api.provisioner, + 'url': 'api/{accountId}/files/customconfigs', + 'verb': 'GET' + }, + 'provisioner.phone_configfile.add': { + 'apiRoot': monster.config.api.provisioner, + 'url': 'api/{accountId}/files/customconfigs', + 'verb': 'PUT' + }, + 'provisioner.phone_configfile.remove': { + 'apiRoot': monster.config.api.provisioner, + 'url': 'api/{accountId}/files/customconfigs', + 'verb': 'DELETE' + }, + 'provisioner.account_configfile.list': { + 'apiRoot': monster.config.api.provisioner, + 'url': 'api/{accountId}/files/accountconfigs', + 'verb': 'GET' + }, + 'provisioner.account_configfile.get': { + 'apiRoot': monster.config.api.provisioner, + 'url': 'api/{accountId}/files/accountconfigs', + 'verb': 'GET' + }, + 'provisioner.account_configfile.add': { + 'apiRoot': monster.config.api.provisioner, + 'url': 'api/{accountId}/files/accountconfigs', + 'verb': 'PUT' + }, + 'provisioner.account_configfile.remove': { + 'apiRoot': monster.config.api.provisioner, + 'url': 'api/{accountId}/files/accountconfigs', + 'verb': 'DELETE' + }, 'provisioner.acls.list': { 'apiRoot': monster.config.api.provisioner, 'url': 'api/{accountId}/acl', @@ -33,6 +73,21 @@ define(function (require) { 'apiRoot': monster.config.api.provisioner, 'url': 'api/{accountId}/acl', 'verb': 'DELETE' + }, + 'provisioner.devicepassword.get': { + 'apiRoot': monster.config.api.provisioner, + 'url': 'api/{accountId}/devicepassword', + 'verb': 'GET' + }, + 'provisioner.devicepassword.set': { + 'apiRoot': monster.config.api.provisioner, + 'url': 'api/{accountId}/devicepassword', + 'verb': 'POST' + }, + 'provisioner.devicepassword.delete': { + 'apiRoot': monster.config.api.provisioner, + 'url': 'api/{accountId}/devicepassword', + 'verb': 'DELETE' } }, @@ -92,6 +147,7 @@ define(function (require) { $(document).ready(function (e) { loadProvUrl(); loadAcls(); + loadDevicePassword(); //setInterval(loadParkingLot, 30000); }); @@ -99,6 +155,7 @@ define(function (require) { $template.find("#refresh").on("click", function (e) { loadProvUrl(); loadAcls(); + loadDevicePassword(); }); @@ -152,7 +209,6 @@ define(function (require) { }); } //end function loadProvUrl(); - function loadAcls() { self.getAcls(function (aclinfo) { var $aclinfo = $( @@ -183,8 +239,134 @@ define(function (require) { }); } //end function loadAcls(); + function loadDevicePassword() { + self.getDevicePassword(function (devicepassword) { + var $devicepassword = $( + self.getTemplate({ + name: "devicepassword", + data: { + devicepassword: devicepassword, + }, + }) + ); + $template.find(".devicepassword").empty().append($devicepassword); + + ///Delete device password binding + $template.find(".devicepassword-delete").on("click", function (e) { + self.deleteDevicePassword("#devicepassword"); + }); + + //Add device password binding + $template.find("#devicepassword-set-button").on("click", function (e) { + let dp = $template.find("#devicepassword-set-input")[0].value; + if (dp.length >= 3) { //min length 3 chars + self.setDevicePassword(dp); + } else { + monster.ui.alert("Password too short: must be at least 3 characters."); + } + }); + }); + } //end function loadDevicePassword(); + }, //bindEvents + updateDevicePasswordTemplate: function (devicepassword) { + var self = this; + var $aclinfo = $( + app.getTemplate({ + name: "devicepassword", + data: { + devicepassword: devicepassword, + }, + }) + ); + }, + + getDevicePassword: function (callback) { + var self = this; + monster.request({ + resource: "provisioner.devicepassword.get", + data: { + accountId: self.accountId, + userId: monster.apps.auth.currentUser.id, + }, + success: function(res) { + //console.log(data); + callback(res.data); + }, + error: function(res) { + if (res.status == 404) { + callback([]); //Populate results with nothing + } else if (res.status == 401) { + monster.util.logoutAndReload(); + } else { + monster.ui.alert("ERROR: Failed to get account device password: " + parsedError); + callback([]); //Populate results with nothing + } + } + }); + }, //end getDevicePassword + + setDevicePassword: function(devicepassword) { + var self = this; + + monster.request({ + resource: "provisioner.devicepassword.set", + data: { + accountId: self.accountId, + userId: monster.apps.auth.currentUser.id, + data: { + password: devicepassword + } + }, + success: function(res) { + //console.log(data); + let newpassword = `
${devicepassword}  
`; + $("#devicepassword-list").append(newpassword); + + $('#refresh').click(); //?? + + }, + error: function(res) { + if (res.status == 401) { + //monster.util.logoutAndReload(); + } else { + monster.ui.alert("ERROR: Failed to add account device password: " + parsedError); + } + } + }); + }, + + deleteDevicePassword: function(devicepassword) { + var self = this; + monster.request({ + resource: "provisioner.devicepassword.delete", + data: { + accountId: self.accountId, + userId: monster.apps.auth.currentUser.id, + data: { + devicepassword: devicepassword + } + }, + success: function(res) { + //console.log(data); + //self.getAcls(self.updateAclsTemplate); + //"select[title='" + acl + "']").remove(); + $('#refresh').click(); + }, + error: function(res) { + if (res.status == 401) { + monster.util.logoutAndReload(); + } else { + monster.ui.alert("ERROR: Failed to delete account device password: " + parsedError); + } + } + }); + }, + + + + updateAclsTemplate: function (aclinfo) { var self = this; var $aclinfo = $( @@ -251,7 +433,7 @@ define(function (require) { success: function(res) { //console.log(data); //acl = res.data.acls[res.data.acls.length - 1]; - let newacl = `
${acl}  
`; + let newacl = `
${acl}  
`; $("#acl-list").append(newacl); $('#refresh').click(); }, diff --git a/i18n/en-US.json b/i18n/en-US.json index ceda3c9..2f08793 100644 --- a/i18n/en-US.json +++ b/i18n/en-US.json @@ -8,17 +8,25 @@ "helptext": "Retrieve a call by clicking the red panel, the slot number, or the caller name/number. Call the person who parked the call by clicking their name or number. The parking lot will auto-refresh every 30 seconds." }, "provurl": { - "description":"Provisioning URL", - "subtitle":"(Does not require IP ACL)", - "tooltip":"For clients at an IP ACL address/network, you may use a shortened generic form of the URL, by replacing the account ID and secret token with an 'x'. E.g. 'https://provisioner.mydomain.com/p/x/x'" + "description": "Provisioning URL", + "subtitle": "(Does not require IP ACL)", + "tooltip": "For clients at an IP ACL address/network, you may use a shortened generic form of the URL, by replacing the account ID and secret token with an 'x'. E.g. 'https://provisioner.mydomain.com/p/x/x'" }, "acls": { "description": "IP ACLs", "noAcls": "No IP ACLs defined on this account.", "addAcl": { - "tooltip":"IP network (v4 or v6) or single address in CIDR format. E.g. 198.51.100.128/32 or 2001:1a:4c9::18/112", + "tooltip": "IP network (v4 or v6) or single address in CIDR format. E.g. 198.51.100.128/32 or 2001:1a:4c9::18/112", "aclAddButton": "Add ACL" } + }, + "devicePassword": { + "description": "Device Password", + "notset": "No device password is set on this account. Devices will have their admin password set to their own MAC address (all lowercase with no colons e.g.: 'a2fe805b735d').", + "setDevicePassword": { + "tooltip": "Set the password used for devices in this account. (The phone web interface password.)", + "passwordSetButton": "Set Password" + } } } } diff --git a/metadata/app.json b/metadata/app.json index a388612..0b9823f 100644 --- a/metadata/app.json +++ b/metadata/app.json @@ -1,11 +1,12 @@ { - "name": "provisioner", + "name": "ruhnet-provisioner", "i18n": { "en-US": { "label": "RuhNet Provisioner", "description": "Control provisioner settings.", "features": [ "Add Phone Models", + "Set Device Password", "Control IP ACLs" ] } @@ -13,15 +14,14 @@ "tags": [ "developer" ], - "icon": "Provisioner_app.png", + "icon": "provisioner_icon.png", "api_url": "", "author": "RuhNet", - "version": "1.0", + "version": "1.1", "license": "GPLv3", "price": 0, "screenshots": [ - "Provisioner1.png", - "Provisioner2.png" + "provisioner_screenshot.png" ], "urls": { "documentation": "{documentation_url}", diff --git a/metadata/icon/Provisioner_icon.png b/metadata/icon/provisioner_icon.png similarity index 100% rename from metadata/icon/Provisioner_icon.png rename to metadata/icon/provisioner_icon.png diff --git a/metadata/screenshots/provisioner_screenshot.png b/metadata/screenshots/provisioner_screenshot.png new file mode 100644 index 0000000..b1d98bc Binary files /dev/null and b/metadata/screenshots/provisioner_screenshot.png differ diff --git a/style/app.css b/style/app.css index a0c7d35..7fc6acc 100644 --- a/style/app.css +++ b/style/app.css @@ -7,19 +7,7 @@ div.account-settings { flex-direction: column; } -div.provurlinfo-wrapper { - /* width: 100%; */ - display:flex; - flex-wrap: wrap; - flex-direction: column; - margin: 10px; - padding: 10px; - border: 1px solid lightgrey; - border-radius: 10px; - background: #f8f8f8; -} - -div.aclinfo-wrapper { +div.section-wrapper { /* width: 100%; */ display:flex; flex-wrap: wrap; @@ -35,6 +23,11 @@ div.aclinfo-wrapper { cursor: pointer; } +.devicepassword-entry { + padding: 5px; + font-size: +1.2em; +} + .acl-entry { padding: 5px; font-size: +1.2em; diff --git a/views/aclinfo.html b/views/aclinfo.html index d42e10b..1d65efe 100644 --- a/views/aclinfo.html +++ b/views/aclinfo.html @@ -1,4 +1,4 @@ -
+

{{ i18n.provisioner.acls.description }}

{{#each acls}} @@ -11,7 +11,7 @@

-
+
diff --git a/views/devicepassword.html b/views/devicepassword.html new file mode 100644 index 0000000..0ba20e6 --- /dev/null +++ b/views/devicepassword.html @@ -0,0 +1,16 @@ +
+

{{ i18n.provisioner.devicePassword.description }}

+
+ {{#if devicepassword}} +
{{devicepassword}}  
+ {{else}} + +
{{ @root.i18n.provisioner.devicePassword.notset }}
+ {{/if}} +
+
+
+ +
+
+ diff --git a/views/layout.html b/views/layout.html index d04b22e..35e1373 100644 --- a/views/layout.html +++ b/views/layout.html @@ -7,11 +7,13 @@

Account Settings

-
+

{{ i18n.provisioner.provurl.description }}

{{provurl}}
+
+