From 8219f1344865a712b3328f5059335b51edcd15a4 Mon Sep 17 00:00:00 2001 From: Marcelo Schmidt <marcelo.schmidt@gmail.com> Date: Wed, 28 Dec 2016 18:04:38 -0200 Subject: [PATCH] Admin section for managing sounds --- .../admin/adminSounds.html | 6 +- .../admin/adminSounds.js | 19 ++- .../admin/soundEdit.html | 2 +- .../admin/soundEdit.js | 74 ++++++------ .../admin/soundInfo.html | 19 +++ .../admin/soundInfo.js | 111 ++++++++++++++++++ .../assets/stylesheets/customSoundsAdmin.less | 102 ++++++++++++++++ .../client/lib/CustomSounds.js | 56 +++++++++ .../client/lib/custom-sounds.js | 3 - .../client/notifications/deleteCustomSound.js | 3 + .../client/notifications/updateCustomSound.js | 3 + packages/rocketchat-custom-sounds/package.js | 16 ++- .../server/methods/deleteCustomSound.js | 22 ++++ .../server/methods/insertOrUpdateSound.js | 62 ++++++++++ .../server/methods/listCustomSounds.js | 5 + .../server/methods/uploadCustomSound.js | 20 ++++ .../server/models/CustomSounds.js | 9 ++ .../server/publications/customSounds.js | 3 +- .../server/startup/custom-sounds.js | 38 +----- packages/rocketchat-i18n/i18n/en.i18n.json | 17 ++- .../client/views/pushNotificationsFlexTab.js | 19 ++- .../account/accountPreferences.coffee | 2 +- .../rocketchat-ui/lib/notification.coffee | 10 -- packages/rocketchat-ui/package.js | 1 - .../views/app/audioNotification.html | 19 ++- .../views/app/audioNotification.js | 6 - 26 files changed, 517 insertions(+), 130 deletions(-) create mode 100644 packages/rocketchat-custom-sounds/admin/soundInfo.html create mode 100644 packages/rocketchat-custom-sounds/admin/soundInfo.js create mode 100644 packages/rocketchat-custom-sounds/assets/stylesheets/customSoundsAdmin.less create mode 100644 packages/rocketchat-custom-sounds/client/lib/CustomSounds.js delete mode 100644 packages/rocketchat-custom-sounds/client/lib/custom-sounds.js create mode 100644 packages/rocketchat-custom-sounds/client/notifications/deleteCustomSound.js create mode 100644 packages/rocketchat-custom-sounds/client/notifications/updateCustomSound.js create mode 100644 packages/rocketchat-custom-sounds/server/methods/deleteCustomSound.js create mode 100644 packages/rocketchat-custom-sounds/server/methods/insertOrUpdateSound.js create mode 100644 packages/rocketchat-custom-sounds/server/methods/listCustomSounds.js create mode 100644 packages/rocketchat-custom-sounds/server/methods/uploadCustomSound.js delete mode 100644 packages/rocketchat-ui/views/app/audioNotification.js diff --git a/packages/rocketchat-custom-sounds/admin/adminSounds.html b/packages/rocketchat-custom-sounds/admin/adminSounds.html index bcf0f377496..c891ff724e6 100644 --- a/packages/rocketchat-custom-sounds/admin/adminSounds.html +++ b/packages/rocketchat-custom-sounds/admin/adminSounds.html @@ -22,15 +22,13 @@ <table> <thead> <tr> - <th>{{_ "Name"}}</th> - <th>{{_ "Play"}}</th> + <th width="100%">{{_ "Name"}}</th> </tr> </thead> <tbody> {{#each customsounds}} <tr class="sound-info row-link"> - <td>{{name}}</td> - <td><i class="icon-play-circle"></i></td> + <td>{{name}} <i class="icon-play-circled"></i></td> </tr> {{/each}} </tbody> diff --git a/packages/rocketchat-custom-sounds/admin/adminSounds.js b/packages/rocketchat-custom-sounds/admin/adminSounds.js index 67c6921e68b..32d7bb7a118 100644 --- a/packages/rocketchat-custom-sounds/admin/adminSounds.js +++ b/packages/rocketchat-custom-sounds/admin/adminSounds.js @@ -43,7 +43,7 @@ Template.adminSounds.onCreated(function() { id: 'add-sound', i18nTitle: 'Custom_Sound_Add', icon: 'icon-plus', - template: 'adminSoundsEdit', + template: 'adminSoundEdit', openClick(/*e, t*/) { RocketChat.TabBar.setData(); return true; @@ -56,7 +56,7 @@ Template.adminSounds.onCreated(function() { id: 'admin-sound-info', i18nTitle: 'Custom_Sound_Info', icon: 'icon-cog', - template: 'adminSoundsInfo', + template: 'adminSoundInfo', order: 2 }); @@ -73,7 +73,7 @@ Template.adminSounds.onCreated(function() { if (filter) { let filterReg = new RegExp(s.escapeRegExp(filter), 'i'); - query = { $or: [ { name: filterReg }, {aliases: filterReg } ] }; + query = { name: filterReg }; } let limit = (isSetNotNull(() => instance.limit))? instance.limit.get() : 0; @@ -106,8 +106,8 @@ Template.adminSounds.events({ ['click .sound-info'](e) { e.preventDefault(); - RocketChat.TabBar.setTemplate('adminSoundsInfo'); - RocketChat.TabBar.setData(RocketChat.models.CustomSound.findOne({_id: this._id})); + RocketChat.TabBar.setTemplate('adminSoundInfo'); + RocketChat.TabBar.setData(RocketChat.models.CustomSounds.findOne({_id: this._id})); RocketChat.TabBar.openFlex(); RocketChat.TabBar.showGroup('adminSounds-selected'); }, @@ -116,5 +116,14 @@ Template.adminSounds.events({ e.preventDefault(); e.stopPropagation(); t.limit.set(t.limit.get() + 50); + }, + + ['click .icon-play-circled'](e) { + e.preventDefault(); + e.stopPropagation(); + const $audio = $('#' + this._id); + if ($audio && $audio[0] && $audio[0].play) { + $audio[0].play(); + } } }); diff --git a/packages/rocketchat-custom-sounds/admin/soundEdit.html b/packages/rocketchat-custom-sounds/admin/soundEdit.html index b5e96922848..81b24b6fecc 100644 --- a/packages/rocketchat-custom-sounds/admin/soundEdit.html +++ b/packages/rocketchat-custom-sounds/admin/soundEdit.html @@ -12,7 +12,7 @@ <input type="text" id="name" autocomplete="off" value="{{sound.name}}"> </div> <div class="input-line"> - <label for="image">{{_ "Sound_File"}}</label> + <label for="image">{{_ "Sound_File_mp3"}}</label> <input id="image" type="file" /> </div> <nav> diff --git a/packages/rocketchat-custom-sounds/admin/soundEdit.js b/packages/rocketchat-custom-sounds/admin/soundEdit.js index 038f48b1d78..9a7ee93d5fa 100644 --- a/packages/rocketchat-custom-sounds/admin/soundEdit.js +++ b/packages/rocketchat-custom-sounds/admin/soundEdit.js @@ -83,7 +83,7 @@ Template.soundEdit.onCreated(function() { if (!soundData._id) { if (!this.soundFile) { - errors.push('Sound_File'); + errors.push('Sound_File_mp3'); } } @@ -92,7 +92,7 @@ Template.soundEdit.onCreated(function() { } if (this.soundFile) { - if (!/audio\/mpeg/.test(this.soundFile.type)) { + if (!/audio\/mp3/.test(this.soundFile.type)) { errors.push('FileType'); toastr.error(TAPi18n.__('error-invalid-file-type')); } @@ -108,44 +108,42 @@ Template.soundEdit.onCreated(function() { if (this.soundFile) { soundData.newFile = true; soundData.extension = this.soundFile.name.split('.').pop(); + soundData.type = this.soundFile.type; } - console.log('save -> ', soundData); - - // Meteor.call('insertOrUpdateSound', soundData, (error, result) => { - // if (result) { - // if (this.soundFile) { - // toastr.info(TAPi18n.__('Uploading_file')); - - // let reader = new FileReader(); - // reader.readAsBinaryString(this.soundFile); - // reader.onloadend = () => { - // Meteor.call('uploadSoundCustom', reader.result, this.soundFile.type, soundData, (uploadError/*, data*/) => { - // if (uploadError != null) { - // handleError(uploadError); - // console.log(uploadError); - // return; - // } - // } - // ); - // delete this.soundFile; - // toastr.success(TAPi18n.__('File_uploaded')); - // }; - // } - - // if (soundData._id) { - // toastr.success(t('Custom_Sound_Updated_Successfully')); - // } else { - // toastr.success(t('Custom_Sound_Added_Successfully')); - // } - - // this.cancel(form, soundData.name); - // } - - // if (error) { - // handleError(error); - // } - // }); + Meteor.call('insertOrUpdateSound', soundData, (error, result) => { + if (result) { + soundData._id = result; + soundData.random = Math.round(Math.random() * 1000); + + if (this.soundFile) { + toastr.info(TAPi18n.__('Uploading_file')); + + let reader = new FileReader(); + reader.readAsBinaryString(this.soundFile); + reader.onloadend = () => { + Meteor.call('uploadCustomSound', reader.result, this.soundFile.type, soundData, (uploadError/*, data*/) => { + if (uploadError != null) { + handleError(uploadError); + console.log(uploadError); + return; + } + } + ); + delete this.soundFile; + toastr.success(TAPi18n.__('File_uploaded')); + }; + } + + toastr.success(t('Custom_Sound_Saved_Successfully')); + + this.cancel(form, soundData.name); + } + + if (error) { + handleError(error); + } + }); } }; }); diff --git a/packages/rocketchat-custom-sounds/admin/soundInfo.html b/packages/rocketchat-custom-sounds/admin/soundInfo.html new file mode 100644 index 00000000000..09374f00609 --- /dev/null +++ b/packages/rocketchat-custom-sounds/admin/soundInfo.html @@ -0,0 +1,19 @@ +<template name="soundInfo"> + {{#if editingSound}} + {{> soundEdit (soundToEdit)}} + {{else}} + {{#with sound}} + <div class="about clearfix"> + <div class="info"> + <h3 title="{{name}}">{{name}}</h3> + </div> + </div> + {{/with}} + <nav> + {{#if hasPermission 'manage-sounds'}} + <button class='button button-block danger delete'><span><i class='icon-trash'></i> {{_ "Delete"}}</span></button> + <button class='button button-block primary edit-sound'><span><i class='icon-edit'></i> {{_ "Edit"}}</span></button> + {{/if}} + </nav> + {{/if}} +</template> diff --git a/packages/rocketchat-custom-sounds/admin/soundInfo.js b/packages/rocketchat-custom-sounds/admin/soundInfo.js new file mode 100644 index 00000000000..f0f28e42892 --- /dev/null +++ b/packages/rocketchat-custom-sounds/admin/soundInfo.js @@ -0,0 +1,111 @@ +/* globals isSetNotNull */ +Template.soundInfo.helpers({ + name() { + let sound = Template.instance().sound.get(); + return sound.name; + }, + + sound() { + return Template.instance().sound.get(); + }, + + editingSound() { + return Template.instance().editingSound.get(); + }, + + soundToEdit() { + let instance = Template.instance(); + return { + sound: instance.sound.get(), + back(name) { + instance.editingSound.set(); + + if (isSetNotNull(() => name)) { + let sound = instance.sound.get(); + if (isSetNotNull(() => sound.name) && sound.name !== name) { + return instance.loadedName.set(name); + } + } + } + }; + } +}); + +Template.soundInfo.events({ + ['click .delete'](e, instance) { + e.stopPropagation(); + e.preventDefault(); + let sound = instance.sound.get(); + if (isSetNotNull(() => sound)) { + let _id = sound._id; + swal({ + title: t('Are_you_sure'), + text: t('Custom_Sound_Delete_Warning'), + type: 'warning', + showCancelButton: true, + confirmButtonColor: '#DD6B55', + confirmButtonText: t('Yes_delete_it'), + cancelButtonText: t('Cancel'), + closeOnConfirm: false, + html: false + }, function() { + swal.disableButtons(); + + Meteor.call('deleteCustomSound', _id, (error/*, result*/) => { + if (error) { + handleError(error); + swal.enableButtons(); + } else { + swal({ + title: t('Deleted'), + text: t('Custom_Sound_Has_Been_Deleted'), + type: 'success', + timer: 2000, + showConfirmButton: false + }); + + RocketChat.TabBar.showGroup('adminSounds'); + RocketChat.TabBar.closeFlex(); + } + }); + }); + } + }, + + ['click .edit-sound'](e, instance) { + e.stopPropagation(); + e.preventDefault(); + + instance.editingSound.set(instance.sound.get()._id); + } +}); + +Template.soundInfo.onCreated(function() { + this.sound = new ReactiveVar(); + + this.editingSound = new ReactiveVar(); + + this.loadedName = new ReactiveVar(); + + this.autorun(() => { + let data = Template.currentData(); + if (isSetNotNull(() => data.clear)) { + this.clear = data.clear; + } + }); + + this.autorun(() => { + let data = Template.currentData(); + let sound = this.sound.get(); + if (isSetNotNull(() => sound.name)) { + this.loadedName.set(sound.name); + } else if (isSetNotNull(() => data.name)) { + this.loadedName.set(data.name); + } + }); + + this.autorun(() => { + let data = Template.currentData(); + this.sound.set(data); + }); +}); diff --git a/packages/rocketchat-custom-sounds/assets/stylesheets/customSoundsAdmin.less b/packages/rocketchat-custom-sounds/assets/stylesheets/customSoundsAdmin.less new file mode 100644 index 00000000000..4845388adc8 --- /dev/null +++ b/packages/rocketchat-custom-sounds/assets/stylesheets/customSoundsAdmin.less @@ -0,0 +1,102 @@ +.sound-info { + .icon-play-circled { + cursor: pointer; + } +} + +.sound-view { + z-index: 15; + overflow-y: auto; + overflow-x: hidden; + + .thumb { + width: 100%; + height: 350px; + padding: 20px; + } + + nav { + padding: 0 20px; + } + + .info { + white-space: normal; + padding: 0 20px; + + h3 { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + font-size: 24px; + margin: 8px 0; + line-height: 27px; + text-overflow: ellipsis; + width: 100%; + overflow: hidden; + white-space: nowrap; + + i::after { + content: " "; + display: inline-block; + width: 8px; + height: 8px; + border-radius: 4px; + vertical-align: middle; + } + } + + p { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + line-height: 18px; + font-size: 12px; + font-weight: 300; + } + } + + .edit-form { + padding: 20px 20px 0; + white-space: normal; + + h3 { + font-size: 24px; + margin-bottom: 8px; + line-height: 22px; + } + + p { + line-height: 18px; + font-size: 12px; + font-weight: 300; + } + + > .input-line { + margin-top: 20px; + } + + nav { + padding: 0; + + &.buttons { + margin-top: 2em; + } + } + + .form-divisor { + text-align: center; + margin: 2em 0; + height: 9px; + + > span { + padding: 0 1em; + } + } + } + + .room-info-content > div { + margin: 0 0 20px; + } +} diff --git a/packages/rocketchat-custom-sounds/client/lib/CustomSounds.js b/packages/rocketchat-custom-sounds/client/lib/CustomSounds.js new file mode 100644 index 00000000000..4d2afb3ad9a --- /dev/null +++ b/packages/rocketchat-custom-sounds/client/lib/CustomSounds.js @@ -0,0 +1,56 @@ +/* globals isSetNotNull */ +class CustomSounds { + constructor() { + this.list = new ReactiveVar({}); + } + + add(sound) { + sound.src = this.getURL(sound); + const audio = $('<audio />', { id: sound._id, preload: true }).append( + $('<source />', { src: sound.src }) + ); + const list = this.list.get(); + list[sound._id] = sound; + this.list.set(list); + $('body').append(audio); + } + + remove(sound) { + const list = this.list.get(); + delete this.list[sound._id]; + this.list.set(list); + $('#' + sound._id).remove(); + } + + update(sound) { + const audio = $(`#${sound._id}`); + if (audio && audio[0]) { + const list = this.list.get(); + list[sound._id] = sound; + this.list.set(list); + $('source', audio).attr('src', this.getURL(sound)); + audio[0].load(); + } else { + this.add(sound); + } + } + + getURL(sound) { + let path = (Meteor.isCordova) ? Meteor.absoluteUrl().replace(/\/$/, '') : __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || ''; + return `${path}/custom-sounds/${sound._id}.${sound.extension}?_dc=${sound.random || 0}`; + } + + getList() { + return Object.values(this.list.get()); + } +} + +RocketChat.CustomSounds = new CustomSounds; + +Meteor.startup(() => + Meteor.call('listCustomSounds', (error, result) => { + for (let sound of result) { + RocketChat.CustomSounds.add(sound); + } + }) +); diff --git a/packages/rocketchat-custom-sounds/client/lib/custom-sounds.js b/packages/rocketchat-custom-sounds/client/lib/custom-sounds.js deleted file mode 100644 index 2273ffea5b2..00000000000 --- a/packages/rocketchat-custom-sounds/client/lib/custom-sounds.js +++ /dev/null @@ -1,3 +0,0 @@ -RocketChat.sounds = { - list: [] -}; diff --git a/packages/rocketchat-custom-sounds/client/notifications/deleteCustomSound.js b/packages/rocketchat-custom-sounds/client/notifications/deleteCustomSound.js new file mode 100644 index 00000000000..81323dabc76 --- /dev/null +++ b/packages/rocketchat-custom-sounds/client/notifications/deleteCustomSound.js @@ -0,0 +1,3 @@ +Meteor.startup(() => + RocketChat.Notifications.onAll('deleteCustomSound', data => RocketChat.CustomSounds.remove(data.soundData)) +); diff --git a/packages/rocketchat-custom-sounds/client/notifications/updateCustomSound.js b/packages/rocketchat-custom-sounds/client/notifications/updateCustomSound.js new file mode 100644 index 00000000000..73b16ae4aa9 --- /dev/null +++ b/packages/rocketchat-custom-sounds/client/notifications/updateCustomSound.js @@ -0,0 +1,3 @@ +Meteor.startup(() => + RocketChat.Notifications.onAll('updateCustomSound', data => RocketChat.CustomSounds.update(data.soundData)) +); diff --git a/packages/rocketchat-custom-sounds/package.js b/packages/rocketchat-custom-sounds/package.js index b81cf338dce..37fb34df9c8 100644 --- a/packages/rocketchat-custom-sounds/package.js +++ b/packages/rocketchat-custom-sounds/package.js @@ -12,6 +12,7 @@ Package.onUse(function(api) { 'rocketchat:file', 'rocketchat:lib', 'templating', + 'reactive-var', 'underscore', 'webapp' ]); @@ -27,6 +28,15 @@ Package.onUse(function(api) { api.addFiles('server/models/CustomSounds.js', 'server'); api.addFiles('server/publications/customSounds.js', 'server'); + api.addFiles([ + 'server/methods/deleteCustomSound.js', + 'server/methods/insertOrUpdateSound.js', + 'server/methods/listCustomSounds.js', + 'server/methods/uploadCustomSound.js' + ], 'server'); + + api.addFiles('assets/stylesheets/customSoundsAdmin.less', 'client'); + api.addFiles('admin/startup.js', 'client'); api.addFiles('admin/adminSounds.html', 'client'); api.addFiles('admin/adminSounds.js', 'client'); @@ -34,8 +44,12 @@ Package.onUse(function(api) { api.addFiles('admin/adminSoundInfo.html', 'client'); api.addFiles('admin/soundEdit.html', 'client'); api.addFiles('admin/soundEdit.js', 'client'); + api.addFiles('admin/soundInfo.html', 'client'); + api.addFiles('admin/soundInfo.js', 'client'); api.addFiles('admin/route.js', 'client'); - api.addFiles('client/lib/custom-sounds.js', 'client'); + api.addFiles('client/lib/CustomSounds.js', 'client'); api.addFiles('client/models/CustomSounds.js', 'client'); + api.addFiles('client/notifications/updateCustomSound.js', 'client'); + api.addFiles('client/notifications/deleteCustomSound.js', 'client'); }); diff --git a/packages/rocketchat-custom-sounds/server/methods/deleteCustomSound.js b/packages/rocketchat-custom-sounds/server/methods/deleteCustomSound.js new file mode 100644 index 00000000000..b63c7d03857 --- /dev/null +++ b/packages/rocketchat-custom-sounds/server/methods/deleteCustomSound.js @@ -0,0 +1,22 @@ +/* globals isSetNotNull, RocketChatFileCustomSoundsInstance */ +Meteor.methods({ + deleteCustomSound(_id) { + let sound = null; + + if (RocketChat.authz.hasPermission(this.userId, 'manage-sounds')) { + sound = RocketChat.models.CustomSounds.findOneByID(_id); + } else { + throw new Meteor.Error('not_authorized'); + } + + if (!isSetNotNull(() => sound)) { + throw new Meteor.Error('Custom_Sound_Error_Invalid_Sound', 'Invalid sound', { method: 'deleteCustomSound' }); + } + + RocketChatFileCustomSoundsInstance.deleteFile(`${sound._id}.${sound.extension}`); + RocketChat.models.CustomSounds.removeByID(_id); + RocketChat.Notifications.notifyAll('deleteCustomSound', {soundData: sound}); + + return true; + } +}); diff --git a/packages/rocketchat-custom-sounds/server/methods/insertOrUpdateSound.js b/packages/rocketchat-custom-sounds/server/methods/insertOrUpdateSound.js new file mode 100644 index 00000000000..845bda2cec7 --- /dev/null +++ b/packages/rocketchat-custom-sounds/server/methods/insertOrUpdateSound.js @@ -0,0 +1,62 @@ +/* globals RocketChatFileCustomSoundsInstance */ +Meteor.methods({ + insertOrUpdateSound(soundData) { + if (!RocketChat.authz.hasPermission(this.userId, 'manage-sounds')) { + throw new Meteor.Error('not_authorized'); + } + + if (!s.trim(soundData.name)) { + throw new Meteor.Error('error-the-field-is-required', 'The field Name is required', { method: 'insertOrUpdateSound', field: 'Name' }); + } + + //let nameValidation = new RegExp('^[0-9a-zA-Z-_+;.]+$'); + + //allow all characters except colon, whitespace, comma, >, <, &, ", ', /, \, (, ) + //more practical than allowing specific sets of characters; also allows foreign languages + let nameValidation = /[\s,:><&"'\/\\\(\)]/; + + //silently strip colon; this allows for uploading :soundname: as soundname + soundData.name = soundData.name.replace(/:/g, ''); + + if (nameValidation.test(soundData.name)) { + throw new Meteor.Error('error-input-is-not-a-valid-field', `${soundData.name} is not a valid name`, { method: 'insertOrUpdateSound', input: soundData.name, field: 'Name' }); + } + + let matchingResults = []; + + if (soundData._id) { + matchingResults = RocketChat.models.CustomSounds.findByNameExceptID(soundData.name, soundData._id).fetch(); + } else { + matchingResults = RocketChat.models.CustomSounds.findByName(soundData.name).fetch(); + } + + if (matchingResults.length > 0) { + throw new Meteor.Error('Custom_Sound_Error_Name_Already_In_Use', 'The custom sound name is already in use', { method: 'insertOrUpdateSound' }); + } + + if (!soundData._id) { + //insert sound + let createSound = { + name: soundData.name, + extension: soundData.extension + }; + + let _id = RocketChat.models.CustomSounds.create(createSound); + createSound._id = _id; + + return _id; + } else { + //update sound + if (soundData.newFile) { + RocketChatFileCustomSoundsInstance.deleteFile(`${soundData._id}.${soundData.previousExtension}`); + } + + if (soundData.name !== soundData.previousName) { + RocketChat.models.CustomSounds.setName(soundData._id, soundData.name); + RocketChat.Notifications.notifyAll('updateCustomSound', {soundData}); + } + + return soundData._id; + } + } +}); diff --git a/packages/rocketchat-custom-sounds/server/methods/listCustomSounds.js b/packages/rocketchat-custom-sounds/server/methods/listCustomSounds.js new file mode 100644 index 00000000000..e16afd389f9 --- /dev/null +++ b/packages/rocketchat-custom-sounds/server/methods/listCustomSounds.js @@ -0,0 +1,5 @@ +Meteor.methods({ + listCustomSounds() { + return RocketChat.models.CustomSounds.find({}).fetch(); + } +}); diff --git a/packages/rocketchat-custom-sounds/server/methods/uploadCustomSound.js b/packages/rocketchat-custom-sounds/server/methods/uploadCustomSound.js new file mode 100644 index 00000000000..9929871559a --- /dev/null +++ b/packages/rocketchat-custom-sounds/server/methods/uploadCustomSound.js @@ -0,0 +1,20 @@ +/* globals RocketChatFileCustomSoundsInstance */ +Meteor.methods({ + uploadCustomSound(binaryContent, contentType, soundData) { + if (!RocketChat.authz.hasPermission(this.userId, 'manage-sounds')) { + throw new Meteor.Error('not_authorized'); + } + + let file = new Buffer(binaryContent, 'binary'); + + let rs = RocketChatFile.bufferToStream(file); + RocketChatFileCustomSoundsInstance.deleteFile(`${soundData._id}.${soundData.extension}`); + let ws = RocketChatFileCustomSoundsInstance.createWriteStream(`${soundData._id}.${soundData.extension}`, contentType); + ws.on('end', Meteor.bindEnvironment(() => + Meteor.setTimeout(() => RocketChat.Notifications.notifyAll('updateCustomSound', {soundData}) + , 500) + )); + + rs.pipe(ws); + } +}); diff --git a/packages/rocketchat-custom-sounds/server/models/CustomSounds.js b/packages/rocketchat-custom-sounds/server/models/CustomSounds.js index e205072abca..45f742ce5ac 100644 --- a/packages/rocketchat-custom-sounds/server/models/CustomSounds.js +++ b/packages/rocketchat-custom-sounds/server/models/CustomSounds.js @@ -19,6 +19,15 @@ class CustomSounds extends RocketChat.models._Base { return this.find(query, options); } + findByNameExceptID(name, except, options) { + let query = { + _id: { $nin: [ except ] }, + name + }; + + return this.find(query, options); + } + //update setName(_id, name) { let update = { diff --git a/packages/rocketchat-custom-sounds/server/publications/customSounds.js b/packages/rocketchat-custom-sounds/server/publications/customSounds.js index 8b105fe1227..03032018d73 100644 --- a/packages/rocketchat-custom-sounds/server/publications/customSounds.js +++ b/packages/rocketchat-custom-sounds/server/publications/customSounds.js @@ -4,7 +4,8 @@ Meteor.publish('customSounds', function(filter, limit) { } let fields = { - name: 1 + name: 1, + extension: 1 }; filter = s.trim(filter); diff --git a/packages/rocketchat-custom-sounds/server/startup/custom-sounds.js b/packages/rocketchat-custom-sounds/server/startup/custom-sounds.js index 502f8bc8712..0f8c10a5240 100644 --- a/packages/rocketchat-custom-sounds/server/startup/custom-sounds.js +++ b/packages/rocketchat-custom-sounds/server/startup/custom-sounds.js @@ -1,4 +1,4 @@ -/* globals isSetNotNull */ +/* globals isSetNotNull, RocketChatFileCustomSoundsInstance */ Meteor.startup(function() { let storeType = 'GridFS'; @@ -39,41 +39,13 @@ Meteor.startup(function() { return; } - let file = self.RocketChatFileCustomSoundsInstance.getFileWithReadStream(encodeURIComponent(params.sound)); - - res.setHeader('Content-Disposition', 'inline'); - - if (!isSetNotNull(() => file)) { - //use code from username initials renderer until file upload is complete - res.setHeader('Content-Type', 'image/svg+xml'); - res.setHeader('Cache-Control', 'public, max-age=0'); - res.setHeader('Expires', '-1'); - res.setHeader('Last-Modified', 'Thu, 01 Jan 2015 00:00:00 GMT'); - - let reqModifiedHeader = req.headers['if-modified-since']; - if (reqModifiedHeader != null) { - if (reqModifiedHeader === 'Thu, 01 Jan 2015 00:00:00 GMT') { - res.writeHead(304); - res.end(); - return; - } - } - - let color = '#000'; - let initials = '?'; - - let svg = `<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg xmlns="http://www.w3.org/2000/svg" pointer-events="none" width="50" height="50" style="width: 50px; height: 50px; background-color: ${color};"> - <text text-anchor="middle" y="50%" x="50%" dy="0.36em" pointer-events="auto" fill="#ffffff" font-family="Helvetica, Arial, Lucida Grande, sans-serif" style="font-weight: 400; font-size: 28px;"> - ${initials} - </text> -</svg>`; - - res.write(svg); - res.end(); + let file = RocketChatFileCustomSoundsInstance.getFileWithReadStream(params.sound); + if (!file) { return; } + res.setHeader('Content-Disposition', 'inline'); + let fileUploadDate = undefined; if (isSetNotNull(() => file.uploadDate)) { fileUploadDate = file.uploadDate.toUTCString(); diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 0ecf988416a..9f849d18d9e 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -329,14 +329,13 @@ "Custom_Script_Logged_In": "Custom Script for logged in users", "Custom_Script_Logged_Out": "Custom Script for logged out users", "Custom_Sounds": "Custom Sounds", - "Custom_Sounds_Add": "Add Custom Sound", - "Custom_Sounds_Added_Successfully": "Custom sound added successfully", - "Custom_Sounds_Delete_Warning": "Deleting a sound cannot be undone.", - "Custom_Sounds_Error_Invalid_Sound": "Invalid sound", - "Custom_Sounds_Error_Name_Already_In_Use": "The custom sound name is already in use.", - "Custom_Sounds_Has_Been_Deleted": "The custom sound has been deleted.", - "Custom_Sounds_Info": "Custom Sound Info", - "Custom_Sounds_Updated_Successfully": "Custom sound updated successfully", + "Custom_Sound_Add": "Add Custom Sound", + "Custom_Sound_Delete_Warning": "Deleting a sound cannot be undone.", + "Custom_Sound_Error_Invalid_Sound": "Invalid sound", + "Custom_Sound_Error_Name_Already_In_Use": "The custom sound name is already in use.", + "Custom_Sound_Has_Been_Deleted": "The custom sound has been deleted.", + "Custom_Sound_Info": "Custom Sound Info", + "Custom_Sound_Saved_Successfully": "Custom sound saved successfully", "Custom_Translations": "Custom Translations", "Custom_Translations_Description": "Should be a valid JSON where keys are languages containing a dictionary of key and translations. Example:</br><code>{\n\t\"en\": {\n\t\t\"key\": \"translation\"\n\t},\n\t\"pt\": {\n\t\t\"key\": \"tradução\"\n\t}\n}</code> ", "CustomSoundsFilesystem": "Custom Sounds Filesystem", @@ -1245,7 +1244,7 @@ "Snippet_Messages": "Snippet Messages", "Snippeted_a_message": "Created a snippet __snippetLink__", "Sound": "Sound", - "Sound_File": "Sound File", + "Sound_File_mp3": "Sound File (mp3)", "SSL": "SSL", "Star_Message": "Star Message", "Starred_Messages": "Starred Messages", diff --git a/packages/rocketchat-push-notifications/client/views/pushNotificationsFlexTab.js b/packages/rocketchat-push-notifications/client/views/pushNotificationsFlexTab.js index 0a5df266957..c553c3a8eaa 100644 --- a/packages/rocketchat-push-notifications/client/views/pushNotificationsFlexTab.js +++ b/packages/rocketchat-push-notifications/client/views/pushNotificationsFlexTab.js @@ -1,9 +1,9 @@ import toastr from 'toastr'; -/* globals ChatSubscription, KonchatNotification */ +/* globals ChatSubscription */ Template.pushNotificationsFlexTab.helpers({ audioAssets() { - return KonchatNotification.audioAssets; + return RocketChat.CustomSounds && RocketChat.CustomSounds.getList && RocketChat.CustomSounds.getList() || []; }, audioNotifications() { const sub = ChatSubscription.findOne({ @@ -13,7 +13,7 @@ Template.pushNotificationsFlexTab.helpers({ audioNotifications: 1 } }); - return sub ? sub.audioNotifications : ''; + return sub ? sub.audioNotifications || '' : ''; }, desktopNotifications() { var sub = ChatSubscription.findOne({ @@ -91,7 +91,7 @@ Template.pushNotificationsFlexTab.helpers({ audioNotifications: 1 } }); - const audio = sub ? sub.audioNotifications : ''; + const audio = sub ? sub.audioNotifications || '': ''; if (audio === 'none') { return t('None'); } else if (audio === '') { @@ -99,7 +99,8 @@ Template.pushNotificationsFlexTab.helpers({ } else if (audio === 'chime') { return 'Chime'; } else { - const asset = _.findWhere(KonchatNotification.audioAssets, { _id: audio }); + const audioAssets = RocketChat.CustomSounds && RocketChat.CustomSounds.getList && RocketChat.CustomSounds.getList() || []; + const asset = _.findWhere(audioAssets, { _id: audio }); return asset && asset.name; } }, @@ -234,6 +235,14 @@ Template.pushNotificationsFlexTab.events({ if ($audio && $audio[0] && $audio[0].play) { $audio[0].play(); } + } else { + audio = Meteor.user() && Meteor.user().settings && Meteor.user().settings.preferences && Meteor.user().settings.preferences.audioNotifications || 'chime'; + if (audio && audio !== 'none') { + let $audio = $('#' + audio); + if ($audio && $audio[0] && $audio[0].play) { + $audio[0].play(); + } + } } }, diff --git a/packages/rocketchat-ui-account/account/accountPreferences.coffee b/packages/rocketchat-ui-account/account/accountPreferences.coffee index 128f88fa672..ba5a08d1445 100644 --- a/packages/rocketchat-ui-account/account/accountPreferences.coffee +++ b/packages/rocketchat-ui-account/account/accountPreferences.coffee @@ -1,7 +1,7 @@ import toastr from 'toastr' Template.accountPreferences.helpers audioAssets: -> - return KonchatNotification.audioAssets + return RocketChat.CustomSounds && RocketChat.CustomSounds.getList && RocketChat.CustomSounds.getList() || []; audioNotifications: -> return Meteor.user()?.settings?.preferences?.audioNotifications || 'chime' diff --git a/packages/rocketchat-ui/lib/notification.coffee b/packages/rocketchat-ui/lib/notification.coffee index 75c4d1dd921..e7b72b46237 100644 --- a/packages/rocketchat-ui/lib/notification.coffee +++ b/packages/rocketchat-ui/lib/notification.coffee @@ -2,16 +2,6 @@ @KonchatNotification = notificationStatus: new ReactiveVar - audioAssets: [ - { '_id': 'beep', 'name': 'Beep', 'sources': [ { 'src': 'sounds/beep.mp3', 'type': 'audio/mpeg' } ] } - { '_id': 'ding', 'name': 'Ding', 'sources': [ { 'src': 'sounds/ding.mp3', 'type': 'audio/mpeg' } ] } - { '_id': 'seasons', 'name': 'Seasons', 'sources': [ { 'src': 'sounds/seasons.mp3', 'type': 'audio/mpeg' } ] } - { '_id': 'chelle', 'name': 'Chelle', 'sources': [ { 'src': 'sounds/chelle.mp3', 'type': 'audio/mpeg' } ] } - { '_id': 'highbell', 'name': 'High Bell', 'sources': [ { 'src': 'sounds/highbell.mp3', 'type': 'audio/mpeg' } ] } - { '_id': 'droplet', 'name': 'Droplet', 'sources': [ { 'src': 'sounds/droplet.mp3', 'type': 'audio/mpeg' } ] } - { '_id': 'verbal', 'name': 'Verbal', 'sources': [ { 'src': 'sounds/verbal.mp3', 'type': 'audio/mpeg' } ] } - ] - # notificacoes HTML5 getDesktopPermission: -> if window.Notification && Notification.permission != "granted" && !Meteor.settings.public.sandstorm diff --git a/packages/rocketchat-ui/package.js b/packages/rocketchat-ui/package.js index 90231eaa764..72f8da9b1a6 100644 --- a/packages/rocketchat-ui/package.js +++ b/packages/rocketchat-ui/package.js @@ -78,7 +78,6 @@ Package.onUse(function(api) { api.addFiles('views/404/roomNotFound.html', 'client'); api.addFiles('views/404/invalidSecretURL.html', 'client'); api.addFiles('views/app/audioNotification.html', 'client'); - api.addFiles('views/app/audioNotification.js', 'client'); api.addFiles('views/app/burger.html', 'client'); api.addFiles('views/app/home.html', 'client'); api.addFiles('views/app/notAuthorized.html', 'client'); diff --git a/packages/rocketchat-ui/views/app/audioNotification.html b/packages/rocketchat-ui/views/app/audioNotification.html index a9f36904cca..ec4106f7756 100644 --- a/packages/rocketchat-ui/views/app/audioNotification.html +++ b/packages/rocketchat-ui/views/app/audioNotification.html @@ -1,15 +1,10 @@ <template name="audioNotification"> - <audio id="chime" preload> - <source src="sounds/chime.mp3" type="audio/mpeg" /> - </audio> - <audio id="chatNewRoomNotification" preload> - <source src="sounds/door.mp3" type="audio/mpeg" /> - </audio> - {{#each audioAssets}} - <audio id="{{_id}}" preload> - {{#each sources}} - <source src="{{src}}" type="{{type}}" /> - {{/each}} + <div id="audioFilesPreload"> + <audio id="chime" preload> + <source src="sounds/chime.mp3" type="audio/mpeg" /> </audio> - {{/each}} + <audio id="chatNewRoomNotification" preload> + <source src="sounds/door.mp3" type="audio/mpeg" /> + </audio> + </div> </template> diff --git a/packages/rocketchat-ui/views/app/audioNotification.js b/packages/rocketchat-ui/views/app/audioNotification.js deleted file mode 100644 index c0f1ccdd8b9..00000000000 --- a/packages/rocketchat-ui/views/app/audioNotification.js +++ /dev/null @@ -1,6 +0,0 @@ -/* globals KonchatNotification */ -Template.audioNotification.helpers({ - audioAssets() { - return KonchatNotification.audioAssets; - } -}) -- GitLab