TT#35501 PBXConfig: As a Customer, I want see all buttons and assigned seats of a device item

Change-Id: I86d48c11bcb3fdda2193df2d00de61d24123467a
changes/98/21398/6
Hans-Peter Herzog 7 years ago
parent 803128ea22
commit 6071b43b49

2
.gitignore vendored

@ -15,3 +15,5 @@ t/TESTS*
*.iml
csc/
src/config.js

1
debian/rules vendored

@ -8,5 +8,6 @@
override_dh_auto_install:
npm install
cp src/config.template.js src/config.js
npm run build
mv dist csc

@ -0,0 +1,15 @@
#!/bin/bash
case "$1" in
*)
cp ./dev-config/ngcp-panel_csc.customtt.tt2 /etc/ngcp-config/templates/etc/nginx/sites-available/
cp ./dev-config/ngcp-panel_params.customtt.tt2 /etc/ngcp-config/templates/etc/nginx/
ngcpcfg set /etc/ngcp-config/config.yml www_admin.http_csc.csc_js_enable=yes
ngcpcfg set /etc/ngcp-config/config.yml rtcengine.enable=yes
ngcpcfg set /etc/ngcp-config/config.yml pbx.enable=yes
ngcpcfg set /etc/ngcp-config/config.yml www_admin.privileges.subscriberadmin.subscribers.0=write
ngcpcfg apply 'csc, pbx, rtcengine'
;;
esac

@ -0,0 +1,229 @@
[%
PROCESS '/usr/lib/ngcp-ngcpcfg/get_hostname';
hostname = out;
argv.host=hostname; argv.type='sip_ext';
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_shared_ips_for_host';
sip_ext_ips = out;
IF !sip_ext_ips.size;
argv.type='sip_ext';
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_ips_for_host';
sip_ext_ips = out;
END;
ext_ip = sip_ext_ips.0;
argv.type='web_ext';
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_shared_ips_for_host';
web_ext_ips = out;
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_shared_v6ips_for_host';
web_ext_v6ips = out;
argv.type='web_ext';
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_ips_for_host';
web_ext_ips = out.merge(web_ext_ips);
argv.type='web_ext';
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_v6ips_for_host';
web_ext_v6ips = out.merge(web_ext_v6ips);
argv.role='mgmt';
PROCESS '/usr/lib/ngcp-ngcpcfg/has_role';
is_mgmt = out;
argv.role='proxy'; argv.type='sip_int';
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_shared_ips';
sip_int_ips = out;
IF !sip_int_ips.size;
argv.type='sip_int';
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_ips_for_host';
sip_int_ips = out;
END;
-%]
[% IF www_admin.enable == 'yes' && is_mgmt -%]
[% IF www_admin.http_admin.port != 80 && www_admin.http_csc.port != 80 -%]
server {
listen [::]:80 ipv6only=off;
location /handbook {
return 301 http://$host:[% www_admin.http_admin.port %]$request_uri;
}
location /login/subscriber {
return 301 https://$host:[% www_admin.http_csc.port %]$request_uri;
}
location /login/admin {
return 301 https://$host:[% www_admin.http_admin.port %]$request_uri;
}
location / {
return 301 https://$host:[% www_admin.http_csc.port %]$request_uri;
}
}
[% END -%]
[% IF rtcengine.enable == 'yes' -%]
upstream rtc_ws {
[% FOREACH ip IN sip_int_ips -%]
[% IF ip -%]
server [% ip %]:[% rtcengine.port %];
[% END -%]
[% END -%]
}
[% END -%]
server {
[% FOREACH ip IN web_ext_ips -%]
[% IF ip -%]
listen [% ip %]:[% www_admin.http_csc.port %];
[% END -%]
[% END -%]
[% FOREACH ip IN web_ext_v6ips -%]
[% IF ip -%]
listen [[% ip %]]:[% www_admin.http_csc.port %];
[% END -%]
[% END -%]
server_name [% www_admin.http_csc.servername.remove('\"') %];
ssl_certificate [% www_admin.http_csc.sslcertfile %];
ssl_certificate_key [% www_admin.http_csc.sslcertkeyfile %];
include /etc/nginx/ssl_params;
client_max_body_size [% www_admin.filesize_limit ? www_admin.filesize_limit : "128M"%];
location ~* /login(/)?$ {
return 301 /login/subscriber;
}
location /login/admin {
return 301 https://$host:[% www_admin.http_admin.port %]$request_uri;
}
location /favicon.ico {
alias /usr/share/ngcp-panel/static/favicon.ico;
}
location /static {
root /usr/share/ngcp-panel;
}
[% IF rtcengine.enable == 'yes' -%]
location ~* /rtc/api(/)?$ {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
add_header 'Access-Control-Max-Age' '1728000' always;
add_header 'Content-Type' 'text/plain charset=UTF-8' always;
add_header 'Content-Length' '0' always;
return 204;
}
### Set proxy ####
proxy_max_temp_file_size 0;
proxy_connect_timeout 43200000;
proxy_send_timeout 43200000;
proxy_read_timeout 43200000;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
### Set headers ####
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Accept-Encoding "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Server-IP $server_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header Front-End-Https on;
proxy_pass http://rtc_ws;
proxy_http_version 1.1;
}
location /rtc/files {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
add_header 'Access-Control-Max-Age' '1728000' always;
add_header 'Content-Type' 'text/plain charset=UTF-8' always;
add_header 'Content-Length' '0' always;
return 204;
}
rewrite /rtc/files/(.*) /$1 break;
index index.html;
root /usr/share/ngcp-rtcengine/public/;
}
[% END -%]
[% IF rtcengine.expose_provisioning_api == 'yes' %]
location /rtc/prov/ {
rewrite /rtc/prov/(.*) /$1 break;
proxy_pass http://rtc_ws;
proxy_http_version 1.1;
}
[% END %]
location /api {
include /etc/nginx/ngcp-panel_params;
# fastcgi_param SSL_CLIENT_CERT $ssl_client_raw_cert;
# fastcgi_param SSL_CLIENT_M_SERIAL $ssl_client_serial;
# fastcgi_param SSL_CLIENT_M_DN $ssl_client_s_dn;
fastcgi_param NGCP_API_REALM "subscriber";
proxy_buffers 8 1024k;
proxy_buffer_size 1024k;
proxy_busy_buffers_size 1024k;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
}
location / {
include /etc/nginx/ngcp-panel_params;
}
location ~ ^/wss/xmpp/(.*)$ {
proxy_pass https://127.0.0.1:5281/xmpp-websocket/$1;
proxy_set_header Host $host;
proxy_buffering off;
tcp_nodelay on;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location ~ ^/wss/sip/(.*)$ {
proxy_pass https://[% ext_ip %]:[% kamailio.lb.tls.port %]/ws/$1;
proxy_set_header Host $host;
proxy_buffering off;
tcp_nodelay on;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
[% IF www_admin.http_csc.csc_js_enable == "yes" -%]
location /csc {
root /usr/share/ngcp-csc;
}
[% END -%]
}
[% END -%]

@ -0,0 +1,21 @@
include /etc/nginx/fastcgi_params;
# Catalyst requires setting PATH_INFO (instead of SCRIPT_NAME) to $fastcgi_script_name
fastcgi_param SCRIPT_NAME '';
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_pass unix:/var/run/fastcgi/ngcp-panel.sock;
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
add_header 'Access-Control-Max-Age' '1728000' always;
add_header 'Content-Type' 'text/plain charset=UTF-8' always;
add_header 'Content-Length' '0' always;
return 204;
}

@ -4958,9 +4958,9 @@
"resolved": "https://npm-registry.sipwise.com/quasar-extras/-/quasar-extras-0.0.8.tgz"
},
"quasar-framework": {
"version": "0.14.6",
"from": "quasar-framework@>=0.14.4 <0.15.0",
"resolved": "https://npm-registry.sipwise.com/quasar-framework/-/quasar-framework-0.14.6.tgz"
"version": "0.14.9",
"from": "quasar-framework@0.14.9",
"resolved": "https://registry.npmjs.org/quasar-framework/-/quasar-framework-0.14.9.tgz"
},
"query-string": {
"version": "4.3.4",

@ -14,7 +14,7 @@
"dependencies": {
"babel-runtime": "^6.25.0",
"quasar-extras": "0.x",
"quasar-framework": "^0.14.4",
"quasar-framework": "0.14.9",
"uuid": "3.2.1",
"vue": "^2.5.0",
"vue-i18n": "7.3.2",

@ -8,7 +8,7 @@ import { LIST_ALL_ROWS } from './common';
export function getMappings(id) {
return new Promise((resolve, reject) => {
Vue.http.get('/api/cfmappings/' + id).then(result => {
Vue.http.get('api/cfmappings/' + id).then(result => {
let jsonBody = getJsonBody(result.body);
delete jsonBody._links;
delete jsonBody.cfs;
@ -22,12 +22,12 @@ export function getMappings(id) {
export function getSourcesets(id) {
return new Promise((resolve, reject) => {
Promise.resolve().then(() => {
return Vue.http.get('/api/cfsourcesets/',
return Vue.http.get('api/cfsourcesets/',
{ params: { subscriber_id: id, page: 1, rows: LIST_ALL_ROWS } })
}).then(result => {
let totalCount = getJsonBody(result.body).total_count;
if (totalCount > LIST_ALL_ROWS) {
return Vue.http.get('/api/cfsourcesets/',
return Vue.http.get('api/cfsourcesets/',
{ params: { subscriber_id: id, page: 1,
rows: totalCount } })
}
@ -49,12 +49,12 @@ export function getSourcesets(id) {
export function getTimesets(id) {
return new Promise((resolve, reject) => {
Promise.resolve().then(() => {
return Vue.http.get('/api/cftimesets/',
return Vue.http.get('api/cftimesets/',
{ params: { subscriber_id: id, page: 1, rows: LIST_ALL_ROWS } })
}).then(result => {
let totalCount = getJsonBody(result.body).total_count;
if (totalCount > LIST_ALL_ROWS) {
return Vue.http.get('/api/cftimesets/',
return Vue.http.get('api/cftimesets/',
{ params: { subscriber_id: id, page: 1,
rows: totalCount } })
}
@ -74,12 +74,12 @@ export function getTimesets(id) {
export function getDestinationsets(id) {
return new Promise((resolve, reject) => {
Promise.resolve().then(() => {
return Vue.http.get('/api/cfdestinationsets/',
return Vue.http.get('api/cfdestinationsets/',
{ params: { subscriber_id: id, page: 1, rows: LIST_ALL_ROWS } })
}).then(result => {
let totalCount = getJsonBody(result.body).total_count;
if (totalCount > LIST_ALL_ROWS) {
return Vue.http.get('/api/cfdestinationsets/',
return Vue.http.get('api/cfdestinationsets/',
{ params: { subscriber_id: id, page: 1,
rows: totalCount } })
}
@ -214,7 +214,7 @@ export function addNameIdAndTerminating(options) {
export function getDestinationsetById(id) {
return new Promise((resolve, reject)=>{
Vue.http.get('/api/cfdestinationsets/' + id).then((res)=>{
Vue.http.get('api/cfdestinationsets/' + id).then((res)=>{
let destinationset = getJsonBody(res.body);
delete destinationset['_links'];
destinationset.destinations.sort((a, b) => {
@ -232,7 +232,7 @@ export function deleteDestinationFromDestinationset(options) {
'Content-Type': 'application/json-patch+json'
};
return new Promise((resolve, reject) => {
Vue.http.patch('/api/cfdestinationsets/' + options.id, [{
Vue.http.patch('api/cfdestinationsets/' + options.id, [{
op: 'replace',
path: '/destinations',
value: options.data
@ -255,7 +255,7 @@ export function deleteDestinationFromDestinationset(options) {
export function deleteDestinationsetById(id) {
return new Promise((resolve, reject) => {
Vue.http.delete('/api/cfdestinationsets/' + id).then(result => {
Vue.http.delete('api/cfdestinationsets/' + id).then(result => {
resolve(result);
}).catch(err => {
reject(err);
@ -268,7 +268,7 @@ export function addDestinationToDestinationset(options) {
'Content-Type': 'application/json-patch+json'
};
return new Promise((resolve, reject) => {
Vue.http.patch('/api/cfdestinationsets/' + options.id, [{
Vue.http.patch('api/cfdestinationsets/' + options.id, [{
op: 'replace',
path: '/destinations',
value: options.data
@ -283,7 +283,7 @@ export function addDestinationToDestinationset(options) {
export function addNewDestinationset() {
let destinationsetName = `csc-${Date.now()}`;
return new Promise((resolve, reject) => {
Vue.http.post('/api/cfdestinationsets/', { name: destinationsetName })
Vue.http.post('api/cfdestinationsets/', { name: destinationsetName })
.then(response => {
resolve(_.last(_.split(response.headers.get('Location'), '/')));
}).catch(err => {
@ -345,7 +345,7 @@ export function addDestinationToEmptyGroup(options) {
export function addNewMapping(options) {
let headers = { 'Content-Type': 'application/json-patch+json' };
return new Promise((resolve, reject) => {
Vue.http.patch('/api/cfmappings/' + options.subscriberId, [{
Vue.http.patch('api/cfmappings/' + options.subscriberId, [{
op: 'replace',
path: '/' + options.group,
value: options.mappings
@ -362,7 +362,7 @@ export function changePositionOfDestination(options) {
'Content-Type': 'application/json-patch+json'
};
return new Promise((resolve, reject) => {
Vue.http.patch('/api/cfdestinationsets/' + options.id, [{
Vue.http.patch('api/cfdestinationsets/' + options.id, [{
op: 'replace',
path: '/destinations',
value: options.destinations
@ -580,7 +580,7 @@ export function deleteTimeFromTimeset(options) {
'Content-Type': 'application/json-patch+json'
};
return new Promise((resolve, reject) => {
Vue.http.patch('/api/cftimesets/' + options.timesetId, [{
Vue.http.patch('api/cftimesets/' + options.timesetId, [{
op: 'replace',
path: '/times',
value: options.times
@ -594,7 +594,7 @@ export function deleteTimeFromTimeset(options) {
export function deleteTimesetById(id) {
return new Promise((resolve, reject) => {
Vue.http.delete('/api/cftimesets/' + id).then(() => {
Vue.http.delete('api/cftimesets/' + id).then(() => {
resolve();
}).catch(err => {
reject(err);
@ -625,7 +625,7 @@ export function addTimeToTimeset(options) {
let headers = {
'Content-Type': 'application/json-patch+json'
};
Vue.http.patch('/api/cftimesets/' + options.id, [{
Vue.http.patch('api/cftimesets/' + options.id, [{
op: 'replace',
path: '/times',
value: options.times
@ -639,7 +639,7 @@ export function addTimeToTimeset(options) {
export function addNewTimeset(timesetName) {
return new Promise((resolve, reject) => {
Vue.http.post('/api/cftimesets/', { name: timesetName })
Vue.http.post('api/cftimesets/', { name: timesetName })
.then(response => {
resolve(_.last(_.split(response.headers.get('Location'), '/')));
}).catch(err => {
@ -721,7 +721,7 @@ export function createTimesetWithTime(options) {
export function getTimesByTimesetId(id) {
return new Promise((resolve, reject)=>{
Vue.http.get('/api/cftimesets/' + id).then((res)=>{
Vue.http.get('api/cftimesets/' + id).then((res)=>{
let timeset = getJsonBody(res.body);
delete timeset['_links'];
resolve(timeset.times);
@ -749,7 +749,7 @@ export function appendTimeToTimeset(options) {
export function getSourcesBySourcesetId(id) {
return new Promise((resolve, reject)=>{
Vue.http.get('/api/cfsourcesets/' + id).then((res)=>{
Vue.http.get('api/cfsourcesets/' + id).then((res)=>{
let sourceset = getJsonBody(res.body);
resolve(sourceset.sources);
}).catch((err)=>{
@ -763,7 +763,7 @@ export function addSourceToSourceset(options) {
let headers = {
'Content-Type': 'application/json-patch+json'
};
Vue.http.patch('/api/cfsourcesets/' + options.id, [{
Vue.http.patch('api/cfsourcesets/' + options.id, [{
op: 'replace',
path: '/sources',
value: options.sources
@ -792,7 +792,7 @@ export function appendSourceToSourceset(options) {
export function createSourcesetWithSource(options) {
return new Promise((resolve, reject) => {
Vue.http.post('/api/cfsourcesets/', {
Vue.http.post('api/cfsourcesets/', {
name: options.sourcesetName,
subscriber_id: options.subscriberId,
mode: options.mode,
@ -809,7 +809,7 @@ export function createSourcesetWithSource(options) {
export function deleteSourcesetById(id) {
return new Promise((resolve, reject) => {
Vue.http.delete('/api/cfsourcesets/' + id).then(() => {
Vue.http.delete('api/cfsourcesets/' + id).then(() => {
resolve();
}).catch(err => {
reject(err);
@ -832,7 +832,7 @@ export function deleteSourceFromSourcesetByIndex(options) {
let headers = {
'Content-Type': 'application/json-patch+json'
};
Vue.http.patch('/api/cfsourcesets/' + options.sourceset.sourcesetId, [{
Vue.http.patch('api/cfsourcesets/' + options.sourceset.sourcesetId, [{
op: 'replace',
path: '/sources',
value: sources

@ -15,6 +15,9 @@ export function getList(options) {
params: {
page: LIST_DEFAULT_PAGE,
rows: LIST_DEFAULT_ROWS
},
headers: {
'Accept': 'application/json'
}
}, options);
Promise.resolve().then(()=>{
@ -22,7 +25,8 @@ export function getList(options) {
options.params.rows = LIST_ALL_ROWS;
}
return Vue.http.get(options.path, {
params: options.params
params: options.params,
headers: options.headers
});
}).then((res)=>{
let body = getJsonBody(res.body);
@ -30,7 +34,8 @@ export function getList(options) {
return Vue.http.get(options.path, {
params: _.merge(options.params, {
rows: body.total_count
})
}),
headers: options.headers
});
}
else {
@ -54,7 +59,11 @@ export function getList(options) {
export function get(options) {
return new Promise((resolve, reject)=>{
return Vue.http.get(options.path).then((result)=>{
return Vue.http.get(options.path, {
headers: {
'Accept': 'application/json'
}
}).then((result)=>{
resolve(getJsonBody(result.body));
}).catch((err)=>{
if(err.status && err.status >= 400) {

@ -8,7 +8,7 @@ export function createFax(options, subscriberId) {
};
let mergedParams = Object.assign(options, subscriberId);
let payload = JSON.stringify(mergedParams);
Vue.http.post('/api/faxes/', payload, { headers: headers }).then(() => {
Vue.http.post('api/faxes/', payload, { headers: headers }).then(() => {
resolve();
}).catch((err)=>{
reject(err);

@ -9,7 +9,7 @@ export function getConversations(id, page, rows) {
let params = { subscriber_id: id, page: page, rows: rows,
order_by: 'timestamp', order_by_direction: 'desc' };
return new Promise((resolve, reject) => {
Vue.http.get('/api/conversations/', { params: params })
Vue.http.get('api/conversations/', { params: params })
.then(result => {
let jsonBody = getJsonBody(result.body);
if (_.has(jsonBody, "_embedded.ngcp:conversations")) {
@ -38,7 +38,7 @@ export function getConversations(id, page, rows) {
export function downloadVoiceMail(id) {
return new Promise((resolve, reject)=>{
Vue.http.get('/api/voicemailrecordings/' + id, { responseType: 'blob' })
Vue.http.get('api/voicemailrecordings/' + id, { responseType: 'blob' })
.then(res => {
return res.blob();
}).then(voicemail => {
@ -52,7 +52,7 @@ export function downloadVoiceMail(id) {
export function downloadFax(id) {
return new Promise((resolve, reject)=>{
Vue.http.get('/api/faxrecordings/' + id, { responseType: 'blob' })
Vue.http.get('api/faxrecordings/' + id, { responseType: 'blob' })
.then(res => {
return res.blob();
}).then(fax => {

@ -4,7 +4,7 @@ import Vue from 'vue';
import { getNumbers, assignNumbers } from './user';
import { createSubscriber, deleteSubscriber, setDisplayName,
setPbxExtension, setPbxHuntPolicy, setPbxHuntTimeout,
setPbxGroupMemberIds, setPbxGroupIds, getSubscribers } from './subscriber';
setPbxGroupMemberIds, setPbxGroupIds, getSubscribers, getSubscriber } from './subscriber';
import uuid from 'uuid';
import { getList, get } from './common'
@ -72,7 +72,7 @@ export function getDevices(options) {
return new Promise((resolve, reject)=>{
options = options || {};
options = _.merge(options, {
path: '/api/pbxdevices/',
path: 'api/pbxdevices/',
root: '_embedded.ngcp:pbxdevices'
});
getList(options).then((list)=>{
@ -87,7 +87,7 @@ export function getProfiles(options) {
return new Promise((resolve, reject)=>{
options = options || {};
options = _.merge(options, {
path: '/api/pbxdeviceprofiles/',
path: 'api/pbxdeviceprofiles/',
root: '_embedded.ngcp:pbxdeviceprofiles'
});
getList(options).then((list)=>{
@ -102,7 +102,7 @@ export function getModels(options) {
return new Promise((resolve, reject)=>{
options = options || {};
options = _.merge(options, {
path: '/api/pbxdevicemodels/',
path: 'api/pbxdevicemodels/',
root: '_embedded.ngcp:pbxdevicemodels'
});
getList(options).then((list)=>{
@ -278,18 +278,31 @@ export function getDevice(id, join) {
let device;
Promise.resolve().then(()=>{
return get({
path: '/api/pbxdevices/' + id
path: 'api/pbxdevices/' + id
});
}).then(($device)=> {
device = $device;
if(join === true) {
return getProfile(device.profile_id, join);
let requests = [
getProfile(device.profile_id, join)
];
if(_.isArray(device.lines) && device.lines.length > 0) {
device.lines.forEach((line)=>{
requests.push(getSubscriber(line.subscriber_id));
});
}
return Promise.all(requests);
}
else {
resolve(device);
}
}).then((profile)=>{
device.profile = profile;
}).then((results)=>{
device.profile = results[0];
if(results.length > 1) {
for(let i = 1; i < results.length; i++) {
device.lines[i - 1].subscriber = results[i];
}
}
resolve(device);
}).catch((err)=>{
reject(err);
@ -302,13 +315,13 @@ export function getProfile(id, join) {
let profile;
Promise.resolve().then(()=>{
return get({
path: '/api/pbxdeviceprofiles/' + id
path: 'api/pbxdeviceprofiles/' + id
});
}).then(($profile)=> {
profile = $profile;
if(join === true) {
return Promise.all([
// getModel(profile.device_id),
getModel(profile.device_id),
getModelFrontImage(profile.device_id)
]);
}
@ -316,8 +329,8 @@ export function getProfile(id, join) {
resolve(profile);
}
}).then((res)=>{
// profile.model = res[0];
profile.modelFrontImage = res[0];
profile.model = res[0];
profile.modelFrontImage = res[1];
profile.modelFrontImageUrl = URL.createObjectURL(profile.modelFrontImage);
resolve(profile);
}).catch((err)=>{
@ -330,7 +343,7 @@ export function getModel(id) {
return new Promise((resolve, reject)=>{
Promise.resolve().then(()=>{
return get({
path: '/api/pbxdevicemodels/' + id
path: 'api/pbxdevicemodels/' + id
});
}).then((model)=> {
resolve(model);
@ -342,7 +355,7 @@ export function getModel(id) {
export function getModelFrontImage(id) {
return new Promise((resolve, reject)=>{
Vue.http.get('/api/pbxdevicemodelimages/' + id, {
Vue.http.get('api/pbxdevicemodelimages/' + id, {
responseType: 'blob',
params: {
type: 'front'

@ -8,7 +8,7 @@ export function createReminder(id) {
recur: 'never',
active: false
};
Vue.http.post('/api/reminders/', data).then((result) => {
Vue.http.post('api/reminders/', data).then((result) => {
var reminderID = result.headers.get('Location').split('/')[3];
resolve(reminderID);
}).catch((err) => {
@ -19,7 +19,7 @@ export function createReminder(id) {
export function getReminder(id) {
return new Promise((resolve, reject) => {
Vue.http.get('/api/reminders/', {
Vue.http.get('api/reminders/', {
params: {
supplier_id: id
}
@ -59,7 +59,7 @@ function patchReminder(id, field, value) {
'Content-Type': 'application/json-patch+json'
}
};
Vue.http.patch('/api/reminders/' + id, data, patchHeaders).then(() => {
Vue.http.patch('api/reminders/' + id, data, patchHeaders).then(() => {
resolve();
}).catch((err) => {
reject(err);

@ -1,10 +1,11 @@
import config from '../config'
import Vue from 'vue';
import { getJsonBody } from './utils';
export function create() {
return new Promise((resolve, reject)=>{
Vue.http.post('/api/rtcsessions/').then((res)=>{
Vue.http.post('api/rtcsessions/').then((res)=>{
resolve(res);
}).catch((err)=>{
reject(err);
@ -27,7 +28,7 @@ export function createSession() {
Promise.resolve().then(()=>{
return create();
}).then((res)=>{
return getByUrl(res.headers.get('Location'));
return getByUrl(config.baseHttpUrl + res.headers.get('Location'));
}).then((res)=>{
resolve(res);
}).catch((err)=>{

@ -2,11 +2,11 @@
import _ from 'lodash';
import Vue from 'vue';
import { getJsonBody } from './utils'
import { getList } from './common'
import { getList, get } from './common'
export function getPreferences(id) {
return new Promise((resolve, reject)=>{
Vue.http.get('/api/subscriberpreferences/' + id).then((result)=>{
Vue.http.get('api/subscriberpreferences/' + id).then((result)=>{
resolve(getJsonBody(result.body));
}).catch((err)=>{
reject(err);
@ -19,7 +19,7 @@ export function setPreference(id, field, value) {
var headers = {};
headers['Content-Type'] = 'application/json-patch+json';
Promise.resolve().then(()=>{
return Vue.http.patch('/api/subscriberpreferences/' + id, [{
return Vue.http.patch('api/subscriberpreferences/' + id, [{
op: 'replace',
path: '/'+ field,
value: value
@ -28,7 +28,7 @@ export function setPreference(id, field, value) {
resolve();
}).catch((err)=>{
if(err.status === 422) {
Vue.http.patch('/api/subscriberpreferences/' + id, [{
Vue.http.patch('api/subscriberpreferences/' + id, [{
op: 'add',
path: '/'+ field,
value: value
@ -54,7 +54,7 @@ export function prependItemToArrayPreference(id, field, value) {
delete prefs._links;
prefs[field] = _.get(prefs, field, []);
prefs[field] = [value].concat(prefs[field]);
return Vue.http.put('/api/subscriberpreferences/' + id, prefs);
return Vue.http.put('api/subscriberpreferences/' + id, prefs);
}).then(()=>{
resolve();
}).catch((err)=>{
@ -72,7 +72,7 @@ export function appendItemToArrayPreference(id, field, value) {
delete prefs._links;
prefs[field] = _.get(prefs, field, []);
prefs[field].push(value);
return Vue.http.put('/api/subscriberpreferences/' + id, prefs);
return Vue.http.put('api/subscriberpreferences/' + id, prefs);
}).then(()=>{
resolve();
}).catch((err)=>{
@ -90,7 +90,7 @@ export function editItemInArrayPreference(id, field, itemIndex, value) {
delete prefs._links;
if(_.isArray(prefs[field]) && itemIndex < prefs[field].length) {
prefs[field][itemIndex] = value;
return Vue.http.put('/api/subscriberpreferences/' + id, prefs);
return Vue.http.put('api/subscriberpreferences/' + id, prefs);
}
else {
return Promise.reject(new Error('Array index does not exists'));
@ -114,7 +114,7 @@ export function removeItemFromArrayPreference(id, field, itemIndex) {
_.remove(prefs[field], (value, index)=>{
return index === itemIndex;
});
return Vue.http.put('/api/subscriberpreferences/' + id, prefs);
return Vue.http.put('api/subscriberpreferences/' + id, prefs);
}).then(()=>{
resolve();
}).catch((err)=>{
@ -185,7 +185,7 @@ export function disablePrivacy(id) {
export function createSubscriber(subscriber) {
return new Promise((resolve, reject)=>{
Vue.http.post('/api/subscribers/', subscriber, {
Vue.http.post('api/subscribers/', subscriber, {
params: {
customer_id: subscriber.customer_id
}
@ -204,7 +204,7 @@ export function createSubscriber(subscriber) {
export function deleteSubscriber(id) {
return new Promise((resolve, reject)=>{
Vue.http.delete('/api/subscribers/' + id).then(()=>{
Vue.http.delete('api/subscribers/' + id).then(()=>{
resolve();
}).catch((err)=>{
if(err.status >= 400) {
@ -219,7 +219,7 @@ export function deleteSubscriber(id) {
export function setField(id, field, value) {
return new Promise((resolve, reject)=>{
Vue.http.patch('/api/subscribers/' + id, [{
Vue.http.patch('api/subscribers/' + id, [{
op: 'replace',
path: '/'+ field,
value: value
@ -269,7 +269,7 @@ export function getSubscribers(options) {
return new Promise((resolve, reject)=>{
options = options || {};
options = _.merge(options, {
path: '/api/subscribers/',
path: 'api/subscribers/',
root: '_embedded.ngcp:subscribers'
});
getList(options).then((list)=>{
@ -279,3 +279,15 @@ export function getSubscribers(options) {
});
});
}
export function getSubscriber(id) {
return new Promise((resolve, reject)=>{
get({
path: 'api/subscribers/' + id
}).then((subscriber)=>{
resolve(subscriber);
}).catch((err)=>{
reject(err);
});
});
}

@ -7,7 +7,7 @@ export function login(username, password) {
return new Promise((resolve, reject)=>{
var jwt = null;
var subscriberId = null;
Vue.http.post('/login_jwt', {
Vue.http.post('login_jwt', {
username: username,
password: password
}).then((result)=>{
@ -46,7 +46,7 @@ export function getUserData(id) {
export function getSubscriberById(id) {
return new Promise((resolve, reject)=>{
Vue.http.get('/api/subscribers/' + id).then((result)=>{
Vue.http.get('api/subscribers/' + id).then((result)=>{
var body = JSON.parse(result.body);
resolve(body);
}).catch((err)=>{
@ -57,7 +57,7 @@ export function getSubscriberById(id) {
export function getCapabilities() {
return new Promise((resolve, reject)=>{
Vue.http.get('/api/capabilities/').then((result)=>{
Vue.http.get('api/capabilities/').then((result)=>{
var capabilities = {};
var body = JSON.parse(result.body);
if(_.isArray(body["_embedded"]["ngcp:capabilities"])) {
@ -77,7 +77,7 @@ export function assignNumber(numberId, subscriberId) {
var headers = {};
headers['Content-Type'] = 'application/json-patch+json';
Promise.resolve().then(() => {
return Vue.http.patch('/api/numbers/' + numberId, [{
return Vue.http.patch('api/numbers/' + numberId, [{
op: 'replace',
path: '/subscriber_id',
value: subscriberId
@ -114,7 +114,7 @@ export function assignNumbers(numberIds, subscriberId) {
export function getNumbers() {
return new Promise((resolve, reject)=>{
let params = {};
let path = '/api/numbers';
let path = 'api/numbers';
Promise.resolve().then(()=>{
return Vue.http.get(path, {
params: _.assign(params, {

@ -37,7 +37,7 @@
</div>
</div>
<div>
<q-card class="blocked-number" :key="index" v-for="(number, index) in numbers">
<q-card class="blocked-number" v-for="(number, index) in numbers" :key="index">
<q-card-title>
<q-icon v-if="!(editing && editingIndex == index) && enabled == 'blacklist'" name="fa-ban" color="negative" size="22px"/>
<q-icon v-if="!(editing && editingIndex == index) && enabled == 'whitelist'" name="check" color="primary" size="22px"/>

@ -57,8 +57,7 @@
highlight
separator
v-for="(source, index) in sourcesetSources(sourceset.sourcesetId)"
class="source-item"
>
class="source-item" :key="index">
<q-item-main>
{{ source.source }}
</q-item-main>

@ -1,36 +1,46 @@
<template>
<q-card class="csc-entity csc-pbx-device shadow-1">
<q-card-title>
<q-icon name="fa-fax" color="secondary" size="24px"/>
<span class="csc-entity-title-text">{{ device.station_name }}</span>
<q-btn :icon="titleIcon" :small="isMobile" color="primary" slot="right" flat @click="toggleMain()" />
</q-card-title>
<q-card-main v-if="expanded">
<q-field :label="$t('pbxConfig.deviceStationName')">
<q-input v-model="device.station_name" readonly />
</q-field>
<q-field :label="$t('pbxConfig.deviceIdentifier')">
<q-input v-model="device.identifier" readonly />
</q-field>
<q-field :label="$t('pbxConfig.deviceModel')">
<!--<q-select v-model="device.model.id" :options="modelOptions" clearable disable />-->
<p>{{ name }}</p>
<div class="csc-pbx-device-image">
<img v-show="hasFrontImage" ref="modelImage" :src="frontImageUrl" />
</div>
</q-field>
</q-card-main>
<q-item class="csc-entity csc-pbx-device">
<q-item-side v-if="!expanded">
<q-item-tile avatar>
<img :src="frontImageUrl" />
</q-item-tile>
</q-item-side>
<q-item-main>
<q-item-tile v-if="!expanded" label>{{ device.station_name }}</q-item-tile>
<q-item-tile v-if="!expanded" sublabel><span class="gt-sm">Model: </span>{{ name }}</q-item-tile>
<q-item-tile v-if="!expanded" sublabel><span class="gt-sm">MAC address: </span>{{ device.identifier }}</q-item-tile>
<q-item-tile v-if="expanded" class="csc-pbx-device-content">
<q-field :label="$t('pbxConfig.deviceStationName')">
<q-input v-model="device.station_name" readonly />
</q-field>
<q-field :label="$t('pbxConfig.deviceIdentifier')">
<q-input v-model="device.identifier" readonly />
</q-field>
<q-field :label="$t('pbxConfig.deviceModel')">
<p>{{ name }}</p>
</q-field>
<csc-pbx-device-config :device="device" />
</q-item-tile>
</q-item-main>
<q-item-side right>
<q-item-tile>
<q-btn :icon="titleIcon" :big="isMobile" color="primary" slot="right" flat @click="toggleMain()" />
</q-item-tile>
</q-item-side>
<q-inner-loading :visible="loading">
<q-spinner-mat size="60px" color="primary"></q-spinner-mat>
</q-inner-loading>
</q-card>
</q-item>
</template>
<script>
import _ from 'lodash'
import { QCard, QCardTitle, QCardMain, QCollapsible,
QIcon, QField, QInput, QSelect, QBtn, QInnerLoading, QSpinnerMat } from 'quasar-framework'
QIcon, QField, QInput, QSelect, QBtn, QInnerLoading, QSpinnerMat, QCardMedia,
QItem, QItemMain, QItemSide, QItemTile, Platform } from 'quasar-framework'
import CscPbxDeviceConfig from './CscPbxDeviceConfig'
export default {
name: 'csc-pbx-device',
props: [
@ -39,15 +49,20 @@
'modelOptions'
],
components: {
QCard, QCardTitle, QCardMain, QCollapsible,
QIcon, QField, QInput, QSelect, QBtn, QInnerLoading, QSpinnerMat
CscPbxDeviceConfig, QCard, QCardTitle, QCardMain, QCollapsible,
QIcon, QField, QInput, QSelect, QBtn, QInnerLoading, QSpinnerMat, QCardMedia,
QItem, QItemMain, QItemSide, QItemTile, Platform
},
data () {
return {
expanded: false
expanded: false,
modalOpened: false
}
},
computed: {
isMobile() {
return Platform.is.mobile;
},
titleIcon() {
if(!this.expanded) {
return 'keyboard arrow down';
@ -80,8 +95,22 @@
<style lang="stylus" rel="stylesheet/stylus">
@import '../../../themes/app.common'
.csc-pbx-device-image
width 100%
img
width 100%
.csc-pbx-device
.csc-pbx-device-content
padding-top: 32px;
.q-item-side.q-item-side-right
position absolute
right 10px
.q-item-avatar
img
border-radius 0
.q-item-label
font-size 18px
font-weight 400
letter-spacing normal
line-height 1.8rem
</style>

@ -0,0 +1,271 @@
<template>
<div ref="config" class="csc-pbx-device-config justify-center row">
<div class="csc-pbx-device-canvas" :style="canvasStyles">
<div ref="imageWrapper" class="csc-pbx-device-image" :style="imageWrapperStyles">
<img ref="image" :src="imageUrl" @load="imageLoaded" :style="imageStyles" />
</div>
<div :class="spotClasses(index)" v-for="(key,index) in keys"
:key="index" :style="spotPosition(key)">
<span>{{ index + 1 }}</span>
<q-popover ref="keyPopover">
<div class="csc-pbx-key-popover">
<q-field v-if="getLineByKey(index) !== null" :icon="getIconByKey(index)">
<q-input :value="getLineByKey(index).subscriber.display_name" readonly :float-label="'Key ' + (index + 1)" />
</q-field>
<q-field v-else >
<q-input value="Empty" readonly :float-label="'Key ' + (index + 1)" />
</q-field>
<q-btn icon="clear" :small="!isMobile" class="absolute-top-right"
@click="$refs.keyPopover[index].close()" flat round color="primary" />
</div>
</q-popover>
</div>
</div>
<q-window-resize-observable @resize="windowResize" />
</div>
</template>
<script>
import _ from 'lodash'
import { QList, QItem, QItemMain, QItemTile, QTabs, QTab, Platform,
QTabPane, QChip, QWindowResizeObservable, QModal, QBtn, QPopover, QIcon, QField, QInput } from 'quasar-framework'
import { BoundingBox2D } from '../../../helpers/graphics'
export default {
name: 'csc-pbx-device-config',
props: [
'device',
'loading',
],
components: {
QList, QItem, QItemMain, QItemTile, QTabs, QTab, Platform,
QTabPane, QChip, QWindowResizeObservable, QModal, QBtn, QPopover, QIcon, QField, QInput
},
data () {
return {
configWidth: 0,
imageDeltaX: 0,
imageScaleFactor: 1,
imageWidth: 0,
boundingBox: null,
scaledBoundingBox: null,
modalOpened: false,
selectedKey: null,
selectedKeyIndex: null,
selectedLine: null
}
},
computed: {
isMobile() {
return Platform.is.mobile;
},
imageUrl() {
return _.get(this.device, 'profile.modelFrontImageUrl');
},
keys() {
return _.get(this.device, 'profile.model.linerange[0].keys', []);
},
canvasStyles() {
return {
width: this.configWidth + "px"
}
},
imageWrapperStyles() {
return {
width: this.configWidth + "px"
}
},
imageStyles() {
return {
left: this.imageDeltaX + "px",
width: (this.imageWidth * this.imageScaleFactor) + "px"
}
}
},
mounted() {
this.boundingBox = BoundingBox2D.createFromPoints(this.keys);
this.boundingBox.addMargin(40);
},
methods: {
getLineByKey(key) {
let line = null;
this.device.lines.forEach(($line)=>{
if($line.key_num === key) {
line = $line;
}
});
return line;
},
openModal(key, index) {
this.$refs.modal.open();
this.selectedKey = key;
this.selectedKeyIndex = index + 1;
this.selectedLine = this.getLineByKey(index);
},
closeModal() {
this.$refs.modal.close();
this.selectedKey = null;
this.selectedKeyIndex = null;
},
windowResize() {
this.resize();
this.placeImage();
},
imageLoaded() {
this.resize();
this.placeImage();
},
resize() {
this.imageWidth = this.$refs.image.naturalWidth;
this.configWidth = this.$refs.config.clientWidth;
if(this.boundingBox !== null ) {
if (this.boundingBox.getWidth() > this.configWidth) {
this.imageScaleFactor = this.configWidth / this.boundingBox.getWidth();
}
else {
this.imageScaleFactor = 1;
}
this.scaledBoundingBox = this.boundingBox.clone();
this.scaledBoundingBox.scale(this.imageScaleFactor);
}
},
placeImage() {
let configCenterX = this.configWidth / 2;
if (this.scaledBoundingBox !== null) {
this.imageDeltaX = -1 * this.scaledBoundingBox.getCenterX() + configCenterX;
}
},
getScaleFactorX() {
if (_.isObject(this.$refs.image)) {
return this.$refs.image.width / this.$refs.image.naturalWidth;
}
return 1;
},
getScaleFactorY() {
if (_.isObject(this.$refs.image)) {
return this.$refs.image.height / this.$refs.image.naturalHeight;
}
return 1;
},
keyPointing(key) {
let pointing = {
left: 'right',
right: 'left',
top: 'down',
bottom: 'up'
};
return pointing[key.labelpos];
},
spotPosition(key) {
let deltaX = 0;
if(this.scaledBoundingBox !== null) {
deltaX = -1 * this.scaledBoundingBox.getCenterX() + this.configWidth / 2;
}
let width = 25 * this.imageScaleFactor;
let height = 25 * this.imageScaleFactor;
let x = (key.x * this.imageScaleFactor) + deltaX;
let y = (key.y * this.imageScaleFactor);
switch(key.labelpos){
case 'left':
y = y - height / 2;
x = x - width;
break;
case 'right':
y = y - height / 2;
break;
case 'top':
x = x - width / 2;
y = y - height;
break;
case 'bottom':
x = x - width / 2;
break;
}
return {
top: y + "px",
left: x + "px",
width: width + "px",
height: height + "px",
position: 'absolute',
lineHeight: width + "px",
zIndex: 10
};
},
spotClasses(key) {
let classes = ['csc-pbx-device-button-spot', 'shadow-3'];
if(this.getLineByKey(key) !== null) {
classes.push('csc-pbx-device-button-active');
}
return classes;
},
getIconByKey(key) {
let line = this.getLineByKey(key);
if(line !== null && line.subscriber.is_pbx_group === true) {
return 'group';
}
else if(line !== null && line.subscriber.is_pbx_group === false) {
return 'person';
}
else {
return ''
}
}
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import '../../../themes/app.common'
$spotSize = 25px
.csc-pbx-device-key-details
padding 50px
position relative
.csc-pbx-device-config
position relative
.spot-modal-content
position relative
.csc-pbx-device-image
position relative
overflow hidden
img
display block
position relative
.csc-pbx-device-canvas
position: relative
.csc-pbx-key-popover-title
font-size 18px
font-weight 400
letter-spacing normal
line-height 1.8rem
.csc-pbx-device-button-spot
border-radius: 50%;
width $spotSize
height $spotSize
background-color white
line-height $spotSize
color $primary
text-align center
cursor pointer
font-weight bold
.csc-pbx-device-button
background-color $primary
.csc-pbx-device-button-active
background-color $primary
color white
.csc-pbx-key-popover
min-height 40px
padding 16px
padding-right 40px
</style>

@ -6,8 +6,10 @@
<div v-if="devices.length > 0 && !isListRequesting && listLastPage > 1" class="row justify-center">
<q-pagination :value="listCurrentPage" :max="listLastPage" @change="changePage" />
</div>
<csc-pbx-device v-for="device in devices" :key="device.id" :device="device"
:modelOptions="modelOptions" :loading="isDeviceLoading(device.id)" />
<q-list no-border separator sparse multiline>
<csc-pbx-device v-for="device in devices" :key="device.id" :device="device"
:modelOptions="modelOptions" :loading="isDeviceLoading(device.id)" />
</q-list>
<div v-if="devices.length === 0 && !isListRequesting" class="row justify-center csc-no-entities">
{{ $t('pbxConfig.noDevices') }}
</div>
@ -18,7 +20,7 @@
import { mapGetters } from 'vuex'
import CscPage from '../../CscPage'
import CscPbxDevice from './CscPbxDevice'
import { QSpinnerDots, QPagination } from 'quasar-framework'
import { QSpinnerDots, QPagination, QList } from 'quasar-framework'
export default {
data () {
return {
@ -33,7 +35,8 @@
CscPage,
CscPbxDevice,
QSpinnerDots,
QPagination
QPagination,
QList
},
computed: {
...mapGetters('pbxConfig', [

@ -0,0 +1,5 @@
export default {
baseHttpUrl: 'https://' + window.location.host,
baseWsUrl: 'wss://' + window.location.host
}

@ -1,15 +1,15 @@
import config from '../config'
import loadScript from 'load-script'
var scriptId = 'cdk';
var scriptPath = '/rtc/files/dist/cdk-prod.js';
var webSocketPath = '/rtc/api';
var webSocketUrl = 'wss://' + window.location.host + webSocketPath;
var scriptUrl = config.baseHttpUrl + '/rtc/files/dist/cdk-prod.js';
var webSocketUrl = config.baseWsUrl + '/rtc/api';
export function loadCdkLib() {
return new Promise((resolve, reject)=>{
if(!document.getElementById(scriptId)) {
loadScript(scriptPath, {
loadScript(scriptUrl, {
attrs: {
id: scriptId
}

@ -0,0 +1,93 @@
'use strict';
import _ from 'lodash'
export class BoundingBox2D {
constructor(points) {
this.points = points;
this.topLeftX = null;
this.topLeftY = null;
this.bottomRightX = null;
this.bottomRightY = null;
this.init();
}
init() {
if(_.isArray(this.points)) {
for(let i = 0; i < this.points.length; i++) {
if(this.points[i].x < this.topLeftX || this.topLeftX === null) {
this.topLeftX = this.points[i].x;
}
if(this.points[i].x > this.bottomRightX || this.bottomRightX === null) {
this.bottomRightX = this.points[i].x;
}
if(this.points[i].y < this.topLeftY || this.topLeftY === null) {
this.topLeftY = this.points[i].y;
}
if(this.points[i].y > this.bottomRightY || this.bottomRightY === null) {
this.bottomRightY = this.points[i].y;
}
}
}
}
getTopLeftX() {
return this.topLeftX;
}
getTopLeftY() {
return this.topLeftY;
}
getBottomRightX() {
return this.bottomRightX;
}
getBottomRightY() {
return this.bottomRightY;
}
getCenterX() {
return this.getTopLeftX() + this.getWidth() / 2;
}
getCenterY() {
return this.getTopLeftY() + this.getHeight() / 2;
}
getWidth() {
return this.getBottomRightX() - this.getTopLeftX();
}
getHeight() {
return this.getBottomRightY() - this.getTopLeftY();
}
addMargin(margin) {
this.topLeftX = this.topLeftX - margin;
this.topLeftY = this.topLeftY - margin;
this.bottomRightX = this.bottomRightX + margin;
this.bottomRightY = this.bottomRightY + margin;
}
scale(factor) {
this.topLeftX = this.topLeftX * factor;
this.topLeftY = this.topLeftY * factor;
this.bottomRightX = this.bottomRightX * factor;
this.bottomRightY = this.bottomRightY * factor;
}
clone() {
let boundingBox = new BoundingBox2D();
boundingBox.topLeftX = this.getTopLeftX();
boundingBox.topLeftY = this.getTopLeftY();
boundingBox.bottomRightX = this.getBottomRightX();
boundingBox.bottomRightY = this.getBottomRightY();
return boundingBox;
}
static createFromPoints(points) {
return new BoundingBox2D(points);
}
}

@ -17,6 +17,8 @@ import { RtcEngineCall } from './plugins/call'
import App from './App.vue'
import './filters'
import config from './config'
Vue.config.productionTip = false;
Vue.use(Quasar);
Vue.use(VueResource);
@ -24,6 +26,7 @@ Vue.use(RtcEngineCall);
sync(store, router);
Vue.http.options.root = config.baseHttpUrl;
Vue.http.interceptors.push(function(request, next) {
var jwt = localStorage.getItem('jwt');
if(!_.isEmpty(jwt)) {

@ -147,12 +147,13 @@ export default {
state.listLoadingSilently = _.get(options, 'silent', false);
state.listState = RequestState.requesting;
state.listError = null;
state.devices = {};
state.devicesOrdered = [];
},
deviceListSucceeded(state, data) {
state.listState = RequestState.succeeded;
state.listError = null;
state.listLastPage = data.lastPage;
state.devices = {};
state.devicesOrdered = data.items;
state.devicesOrdered.forEach((device)=>{
state.devices[device.id + ""] = device;
@ -171,7 +172,14 @@ export default {
let deviceLoadingStates = _.clone(state.deviceLoadingStates);
deviceLoadingStates[device.id + ""] = false;
state.deviceLoadingStates = deviceLoadingStates;
state.devices[device.id + ""].profile = device.profile;
for(let i = 0; i <= state.devicesOrdered.length; i++) {
if(state.devicesOrdered[i].id === device.id) {
state.devicesOrdered[i] = device;
}
}
delete state.devices[device.id + ""];
state.devices[device.id + ""] = device;
},
deviceFailed(state, deviceId, errorMessage) {
let deviceLoadingStates = _.clone(state.deviceLoadingStates);

@ -5,7 +5,7 @@ FROM docker.mgm.sipwise.com/sipwise-stretch:latest
# is updated with the current date. It will force refresh of all
# of the base images and things like `apt-get update` won't be using
# old cached versions when the Dockerfile is built.
ENV REFRESHED_AT 2018-04-26
ENV REFRESHED_AT 2018-05-24
ENV DEBIAN_FRONTEND noninteractive
ENV DISPLAY=:0

@ -31,7 +31,7 @@ describe('CallBlocking', function(){
describe('Incoming', function(){
it('should enable call blocking for incoming calls', function(done) {
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
assert.equal(request.body[0].op, 'replace');
assert.equal(request.body[0].path, '/block_in_mode');
assert.equal(request.body[0].value, true);
@ -48,7 +48,7 @@ describe('CallBlocking', function(){
it('should disable call blocking for incoming calls', function(done) {
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
assert.equal(request.body[0].op, 'replace');
assert.equal(request.body[0].path, '/block_in_mode');
assert.equal(request.body[0].value, false);
@ -69,7 +69,7 @@ describe('CallBlocking', function(){
"0987654321"
];
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
next(request.respondWith(JSON.stringify({
"block_in_list" : list,
"block_in_mode" : true
@ -92,7 +92,7 @@ describe('CallBlocking', function(){
"0123456789"
];
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
if(request.method === 'GET') {
next(request.respondWith(JSON.stringify({
"block_in_list" : list
@ -119,7 +119,7 @@ describe('CallBlocking', function(){
"0123456789"
];
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
if(request.method === 'GET') {
next(request.respondWith(JSON.stringify({
"block_in_list" : list
@ -146,7 +146,7 @@ describe('CallBlocking', function(){
"0123456789"
];
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
if(request.method === 'GET') {
next(request.respondWith(JSON.stringify({
"block_in_list" : [].concat([number]).concat(list)
@ -171,7 +171,7 @@ describe('CallBlocking', function(){
describe('Outgoing', function(){
it('should enable call blocking for outgoing calls', function(done) {
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
assert.equal(request.body[0].op, 'replace');
assert.equal(request.body[0].path, '/block_out_mode');
assert.equal(request.body[0].value, true);
@ -188,7 +188,7 @@ describe('CallBlocking', function(){
it('should disable call blocking for outgoing calls', function(done) {
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
assert.equal(request.body[0].op, 'replace');
assert.equal(request.body[0].path, '/block_out_mode');
assert.equal(request.body[0].value, false);
@ -209,7 +209,7 @@ describe('CallBlocking', function(){
"0987654321"
];
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
next(request.respondWith(JSON.stringify({
"block_out_list" : list,
"block_out_mode" : true
@ -232,7 +232,7 @@ describe('CallBlocking', function(){
"0123456789"
];
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
if(request.method === 'GET') {
next(request.respondWith(JSON.stringify({
"block_out_list" : list
@ -259,7 +259,7 @@ describe('CallBlocking', function(){
"0123456789"
];
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
if(request.method === 'GET') {
next(request.respondWith(JSON.stringify({
"block_out_list" : list
@ -286,7 +286,7 @@ describe('CallBlocking', function(){
"0123456789"
];
Vue.http.interceptors.unshift((request, next)=>{
assert.equal(request.url, '/api/subscriberpreferences/' + subscriberId);
assert.equal(request.url, 'api/subscriberpreferences/' + subscriberId);
if(request.method === 'GET') {
next(request.respondWith(JSON.stringify({
"block_out_list" : [].concat([number]).concat(list)

@ -28,7 +28,7 @@ describe('Reminder', function() {
it('should return 201 when creating a new reminder', function(done) {
Vue.http.interceptors.unshift((request, next) => {
assert.equal(request.url, '/api/reminders/');
assert.equal(request.url, 'api/reminders/');
assert.equal(request.body.subscriber_id, subscriberId);
assert.equal(request.body.time, '00:00');
assert.equal(request.body.recur, 'never');
@ -37,7 +37,7 @@ describe('Reminder', function() {
request.respondWith('', {
status: 201,
headers: {
location: '/api/reminders/' + reminderID
location: 'api/reminders/' + reminderID
}
})
)
@ -51,7 +51,7 @@ describe('Reminder', function() {
it('should get the existing reminder', function(done) {
Vue.http.interceptors.unshift((request, next) => {
assert.equal(request.url, '/api/reminders/');
assert.equal(request.url, 'api/reminders/');
assert.equal(request.params.supplier_id, subscriberId);
next(request.respondWith(JSON.stringify({
"_embedded": {
@ -88,7 +88,7 @@ describe('Reminder', function() {
it('should activate a reminder', function(done) {
Vue.http.interceptors.unshift((request, next) => {
assert.equal(request.url, '/api/reminders/' + reminderID);
assert.equal(request.url, 'api/reminders/' + reminderID);
assert.equal(request.body[0].op, 'replace');
assert.equal(request.body[0].path, '/active');
assert.equal(request.body[0].value, true);
@ -105,7 +105,7 @@ describe('Reminder', function() {
it('should deactivate a reminder', function(done) {
Vue.http.interceptors.unshift((request, next) => {
assert.equal(request.url, '/api/reminders/' + reminderID);
assert.equal(request.url, 'api/reminders/' + reminderID);
assert.equal(request.body[0].op, 'replace');
assert.equal(request.body[0].path, '/active');
assert.equal(request.body[0].value, false);
@ -122,7 +122,7 @@ describe('Reminder', function() {
it('should set reminder time', function(done) {
Vue.http.interceptors.unshift((request, next) => {
assert.equal(request.url, '/api/reminders/' + reminderID);
assert.equal(request.url, 'api/reminders/' + reminderID);
assert.equal(request.body[0].op, 'replace');
assert.equal(request.body[0].path, '/time');
assert.equal(request.body[0].value, '22:22:00');
@ -139,7 +139,7 @@ describe('Reminder', function() {
it('should set reminder recurrence', function(done) {
Vue.http.interceptors.unshift((request, next) => {
assert.equal(request.url, '/api/reminders/' + reminderID);
assert.equal(request.url, 'api/reminders/' + reminderID);
assert.equal(request.body[0].op, 'replace');
assert.equal(request.body[0].path, '/recur');
assert.equal(request.body[0].value, 'always');
@ -153,5 +153,5 @@ describe('Reminder', function() {
done(err);
});
});
});

Loading…
Cancel
Save