Browse Source

Merge branch 'master' of github.com:2600hz/monster-ui-voip

4.3
Maxime Roux 11 years ago
parent
commit
048979c07c
9 changed files with 294 additions and 4 deletions
  1. +30
    -0
      i18n/en-US.json
  2. +28
    -0
      i18n/fr-FR.json
  3. +3
    -1
      submodules/callLogs/callLogs.js
  4. +14
    -0
      submodules/devices/devices.css
  5. +147
    -3
      submodules/devices/devices.js
  6. +2
    -0
      submodules/groups/groups.js
  7. +5
    -0
      submodules/users/users.js
  8. +2
    -0
      submodules/vmboxes/vmboxes.js
  9. +63
    -0
      views/devices-sip_device.html

+ 30
- 0
i18n/en-US.json View File

@ -194,6 +194,36 @@
"H263": "H263",
"H261": "H261"
}
},
"__comment": "UI-1351: Add ability to configure feature keys for SIP devices in SmartPBX",
"__version": "v3.20_s4",
"featureKeys": {
"menuTitle": "Feature Keys",
"description": "Feature key",
"labels": {
"user": "User",
"parkingSpot": "Parking Spot",
"value": "Value"
},
"info": {
"link": {
"showInfo": "Show Info",
"hideInfo": "Hide Info"
},
"types": {
"presence": "Indicate when a user has an incoming call or is on the phone. The indicator can be used to pick up ringing calls or speed dial that user.",
"parking": "Indicate when a call is placed on a parking slot available to the entire company. The indicator can be used to transfer a call to the parking slot or retrieve a parked call.",
"personal_parking": "Indicate when a call is placed on a parking slot dedicated to the selected user. The indicator can be used to transfer a call to the parking slot or retrieve a parked call.",
"speed_dial": "Does not indicate for any calls event but can be used to rapidly call the configured number."
}
},
"types": {
"none": "None",
"presence": "Presence",
"parking": "Parking",
"personal_parking": "Personal Parking",
"speed_dial": "Speed Dial"
}
}
},
"buy": "Buy Device",


+ 28
- 0
i18n/fr-FR.json View File

@ -166,6 +166,34 @@
"sectionTitle": "Réglages vidéo",
"selectedCodecs": "Codecs sélectionnés",
"unselectedCodecs": "Codecs disponibles"
},
"featureKeys": {
"menuTitle": "Feature Keys",
"description": "Feature key",
"labels": {
"user": "Utilisateur",
"parkingSpot": "Place",
"value": "Valeur"
},
"info": {
"link": {
"showInfo": "Show Info",
"hideInfo": "Hide Info"
},
"types": {
"presence": "Indique lorsqu'un utilisateur à un appel entrant ou est en train d'appeler. L'indicateur peut être utilisé pour répondre à un appel ou appeler l'utilisateur.",
"parking": "Indique lorsqu'un appel est placé sur une place de parking accessible par l'intégralité de l'entreprise. L'indicateur peut être utilisé pour transférer un appel vers la place de parking ou récupérer un appel en attente.",
"personal_parking": "Indique lorsqu'un appel est placé sur une place de parking dédié à l'utilisateur sélectionné. L'indicateur peut être utilisé pour transférer un appel vers la place de parking ou récupérer un appel en attente.",
"speed_dial": "N'indique pas l'état d'un appel mais peut être utilisé pour appeller rapidement le numéro configuré."
}
},
"types": {
"none": "Aucun",
"presence": "Présence",
"parking": "Parking",
"personal_parking": "Personal Parking",
"speed_dial": "Speed Dial"
}
}
},
"buy": "Acheter téléphone",


+ 3
- 1
submodules/callLogs/callLogs.js View File

@ -88,6 +88,8 @@ define(function(require){
toDate = params.toDate,
startKey = params.nextStartKey;
setTimeout(function() { template.find('.search-query').focus() });
template.find('.apply-filter').on('click', function(e) {
var fromDate = template.find('input.filter-from').datepicker("getDate"),
toDate = template.find('input.filter-to').datepicker("getDate");
@ -332,7 +334,7 @@ define(function(require){
formatCdr = function(cdr) {
var date = monster.util.gregorianToDate(cdr.timestamp),
shortDate = monster.util.toFriendlyDate(date, 'shortDate'),
time = monster.util.toFriendlyDate(date, 'shortTime'),
time = monster.util.toFriendlyDate(date, 'time'),
durationMin = parseInt(cdr.duration_seconds/60).toString(),
durationSec = (cdr.duration_seconds % 60 < 10 ? "0" : "") + (cdr.duration_seconds % 60),
hangupI18n = self.i18n.active().hangupCauses,


+ 14
- 0
submodules/devices/devices.css View File

@ -350,6 +350,20 @@
color: #aaa;
}
.edit-device .feature-key-value {
display: none;
margin-left: 20px;
}
.edit-device .feature-key-value.active {
display: inline-block;
}
.edit-device .feature-key-value label {
display: inline-block;
margin-right: 10px;
}
/* Codecs selector */
.edit-device .content .codec-selector .box-selector {
float: left;


+ 147
- 3
submodules/devices/devices.js View File

@ -7,7 +7,14 @@ define(function(require){
var app = {
requests: {},
requests: {
'provisioner.ui.getModel': {
'apiRoot': monster.config.api.provisioner,
'url': 'ui/{brand}/{family}/{model}',
'verb': 'GET',
generateError: false
}
},
subscribe: {
'voip.devices.render': 'devicesRender',
@ -59,6 +66,8 @@ define(function(require){
devicesBindEvents: function(template, parent, data) {
var self = this;
setTimeout(function() { template.find('.search-query').focus(); });
template.find('.devices-header .search-query').on('keyup', function() {
var searchString = $(this).val().toLowerCase(),
rows = template.find('.devices-rows .grid-row:not(.title)'),
@ -148,7 +157,69 @@ define(function(require){
};
self.devicesGetEditData(data, function(dataDevice) {
self.devicesRenderDevice(dataDevice, callbackSave, callbackDelete);
if (dataDevice.hasOwnProperty('provision')) {
self.devicesGetIterator(dataDevice.provision, function(template) {
if (template.hasOwnProperty('feature_keys')) {
if (!dataDevice.provision.hasOwnProperty('feature_keys')) {
dataDevice.provision.feature_keys = {};
}
for (var i = 0, len = template.feature_keys.iterate - 1; i < len; i++) {
if (!dataDevice.provision.feature_keys.hasOwnProperty(i)) {
dataDevice.provision.feature_keys[i] = { type: 'none' };
}
}
self.callApi({
resource: 'user.list',
data: {
accountId: self.accountId
},
success: function(data, status) {
var keyTypes = [ 'none', 'presence', 'parking', 'personal_parking', 'speed_dial' ],
parkingSpots = [],
extra;
data.data.sort(function(a, b) {
return a.last_name.toLowerCase() > b.last_name.toLowerCase() ? 1 : -1;
});
for (var i = 0; i < 10; i++) {
parkingSpots[i] = i + 1;
}
keyTypes.forEach(function(val, idx, arr) {
arr[idx] = { id: val, text: self.i18n.active().devices.popupSettings.featureKeys.types[val] };
if (val !== 'none') {
arr[idx].info = self.i18n.active().devices.popupSettings.featureKeys.info.types[val];
}
});
extra = {
users: data.data,
featureKeys:{
parkingSpots: parkingSpots,
types: keyTypes
}
};
dataDevice.extra = dataDevice.hasOwnProperty(extra) ? $.extend(true, {}, dataDevice.extra, extra) : extra;
self.devicesRenderDevice(dataDevice, callbackSave, callbackDelete);
}
});
}
else {
self.devicesRenderDevice(dataDevice, callbackSave, callbackDelete);
}
}, function() {
self.devicesRenderDevice(dataDevice, callbackSave, callbackDelete);
});
}
else {
self.devicesRenderDevice(dataDevice, callbackSave, callbackDelete);
}
});
},
@ -191,13 +262,32 @@ define(function(require){
},
devicesRenderDevice: function(data, callbackSave, callbackDelete) {
var self = this
var self = this,
mode = data.id ? 'edit' : 'add',
type = data.device_type,
popupTitle = mode === 'edit' ? monster.template(self, '!' + self.i18n.active().devices[type].editTitle, { name: data.name }) : self.i18n.active().devices[type].addTitle;
templateDevice = $(monster.template(self, 'devices-'+type, data)),
deviceForm = templateDevice.find('#form_device');
if (data.hasOwnProperty('provision') && data.provision.hasOwnProperty('feature_keys')) {
var section = '.tabs-section[data-section="featureKeys"] ';
_.each(data.provision.feature_keys, function(val, key){
var group = '.control-group[data-id="' + key + '"] ',
value = '.feature-key-value[data-type="' + val.type + '"]';
templateDevice
.find(section.concat(group, value))
.addClass('active')
.find('[name="provision.feature_keys[' + key + '].value"]')
.val(val.value);
});
$.each(templateDevice.find('.feature-key-index'), function(idx, val) {
$(val).text(parseInt($(val).text(), 10) + 2);
});
}
if ( data.extra.hasE911Numbers ) {
if(data.caller_id && data.caller_id.emergency && data.caller_id.emergency.number) {
self.devicesGetE911NumberAddress(data.caller_id.emergency.number, function(address) {
@ -259,6 +349,8 @@ define(function(require){
templateDevice.find('.actions .save').on('click', function() {
if(monster.ui.valid(deviceForm)) {
templateDevice.find('.feature-key-value:not(.active)').remove();
var dataToSave = self.devicesMergeData(data, templateDevice, audioCodecs, videoCodecs);
self.devicesSaveDevice(dataToSave, function(data) {
@ -364,6 +456,23 @@ define(function(require){
}
});
templateDevice.find('.feature-key-type').on('change', function() {
var type = $(this).val();
$(this).siblings('.feature-key-value.active').removeClass('active');
$(this).siblings('.feature-key-value[data-type="' + type + '"]').addClass('active');
});
templateDevice.find('.tabs-section[data-section="featureKeys"] .type-info a').on('click', function() {
var $this = $(this);
setTimeout(function() {
var action = ($this.hasClass('collapsed') ? 'show' : 'hide').concat('Info');
$this.find('.text').text(self.i18n.active().devices.popupSettings.featureKeys.info.link[action]);
});
});
var popup = monster.ui.dialog(templateDevice, {
position: ['center', 20],
title: popupTitle
@ -450,6 +559,22 @@ define(function(require){
delete mergedData.media.fax.option;
}
if (mergedData.hasOwnProperty('provision') && mergedData.provision.hasOwnProperty('feature_keys')) {
var tmp = mergedData.provision.feature_keys;
mergedData.provision.feature_keys = {};
for (var i = 0, len = tmp.length; i < len; i++) {
if (tmp[i].type !== 'none') {
mergedData.provision.feature_keys[i] = tmp[i];
}
}
if (_.isEmpty(mergedData.provision.feature_keys)) {
delete mergedData.provision.feature_keys;
}
}
/* Migration clean-up */
delete mergedData.media.secure_rtp;
delete mergedData.extra;
@ -945,6 +1070,25 @@ define(function(require){
}
}
});
},
devicesGetIterator: function(args, callbackSuccess, callbackError) {
var self = this;
monster.request({
resource: 'provisioner.ui.getModel',
data: {
brand: args.endpoint_brand,
family: args.endpoint_family,
model: args.endpoint_model
},
success: function(data, status) {
callbackSuccess && callbackSuccess(data.data.template);
},
error: function(data, status) {
callbackError && callbackError();
}
});
}
};


+ 2
- 0
submodules/groups/groups.js View File

@ -158,6 +158,8 @@ define(function(require){
groupsBindEvents: function(template, parent) {
var self = this;
setTimeout(function() { template.find('.search-query').focus(); });
template.find('.grid-row:not(.title) .grid-cell').on('click', function() {
var cell = $(this),
type = cell.data('type'),


+ 5
- 0
submodules/users/users.js View File

@ -370,6 +370,8 @@ define(function(require){
);
};
setTimeout(function() { template.find('.search-query').focus() });
template.find('.grid-row:not(.title) .grid-cell').on('click', function() {
var cell = $(this),
type = cell.data('type'),
@ -454,6 +456,9 @@ define(function(require){
else if(type === 'features') {
currentUser = data;
}
else if (type === 'devices') {
setTimeout(function() { template.find('.search-query').focus(); });
}
row.find('.edit-user').append(template).slideDown(400, function() {
$('body').animate({ scrollTop: row.offset().top - (window.innerHeight - row.height() - 10) });


+ 2
- 0
submodules/vmboxes/vmboxes.js View File

@ -60,6 +60,8 @@ define(function(require){
self.vmboxesRender({ voicemailId: vmbox.id });
};
setTimeout(function() { template.find('.search-query').focus(); });
template.find('.settings').on('click', function() {
self.vmboxesRenderEdit($(this).parents('.grid-row').data('id'), callbackSave);
});


+ 63
- 0
views/devices-sip_device.html View File

@ -34,6 +34,9 @@
<li><a class="tabs-selector change-section" data-section="restrictions" href="javascript:void(0)"><i class="icon-ban-circle"></i>{{ i18n.devices.popupSettings.restrictions.menuTitle }}</a></li>
<li><a class="tabs-selector change-section" data-section="callerId" href="javascript:void(0)"><i class="icon-ambulance"></i>{{ i18n.devices.popupSettings.callerId.menuTitle }}</a></li>
<li><a class="tabs-selector change-section" data-section="miscellaneous" href="javascript:void(0)"><i class="icon-cogs"></i>{{ i18n.devices.popupSettings.miscellaneous.menuTitle }}</a></li>
{{#if provision.feature_keys}}
<li><a class="tabs-selector change-section" data-section="featureKeys" href="javascript:void(0);"><i class="icon-lightbulb"></i>{{ i18n.devices.popupSettings.featureKeys.menuTitle }}</a></li>
{{/if}}
</ul>
</li>
</ul>
@ -242,6 +245,66 @@
</div>
</div>
</div>
{{#if provision.feature_keys}}
<div class="tabs-section clearfix" data-section="featureKeys">
<div class="title">
{{ i18n.devices.popupSettings.featureKeys.menuTitle }}
</div>
<div class="type-info helper">
<a href="javascript:void(0);" data-toggle="collapse" data-target="#info_content"><i class="icon-question-sign"></i><span class="text">{{ i18n.devices.popupSettings.featureKeys.info.link.showInfo }}</span></a>
<div id="info_content" class="collapse">
{{#each extra.featureKeys.types}}
<p>{{#if info}}<strong>{{text}}</strong>: {{info}}{{/if}}</p>
{{/each}}
</div>
</div>
{{#each provision.feature_keys}}
<div class="control-group" data-id="{{@key}}">
<label for="provision.feature_keys[{{@key}}].type" class="control-label">
{{ ../i18n.devices.popupSettings.featureKeys.description }} <span class="feature-key-index">{{@key}}</span>
</label>
<div class="controls">
<select name="provision.feature_keys[{{@key}}].type" class="feature-key-type span2">
{{#select type}}
{{#each ../../extra.featureKeys.types}}
<option value="{{id}}">{{text}}</option>
{{/each}}
{{/select}}
</select>
<div class="feature-key-value" data-type="presence">
<label for="provision.feature_keys[{{@key}}].value">{{ ../i18n.devices.popupSettings.featureKeys.labels.user }}</label>
<select name="provision.feature_keys[{{@key}}].value">
{{#each ../../extra.users}}
<option value="{{id}}">{{first_name}} {{last_name}}</option>
{{/each}}
</select>
</div>
<div class="feature-key-value" data-type="parking">
<label for="provision.feature_keys[{{@key}}].value">{{ ../i18n.devices.popupSettings.featureKeys.labels.parkingSpot }}</label>
<select class="span1" name="provision.feature_keys[{{@key}}].value">
{{#each ../../extra.featureKeys.parkingSpots}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</div>
<div class="feature-key-value" data-type="personal_parking">
<label for="provision.feature_keys[{{@key}}].value">{{ ../i18n.devices.popupSettings.featureKeys.labels.user }}</label>
<select name="provision.feature_keys[{{@key}}].value">
{{#each ../../extra.users}}
<option value="{{id}}">{{first_name}} {{last_name}}</option>
{{/each}}
</select>
</div>
<div class="feature-key-value" data-type="speed_dial">
<label for="provision.feature_keys[{{@key}}].value">{{ ../i18n.devices.popupSettings.featureKeys.labels.value }}</label>
<input type="text" value="" name="provision.feature_keys[{{@key}}].value">
</div>
</div>
</div>
{{/each}}
</div>
{{/if}}
</form>
</div>


Loading…
Cancel
Save