diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index c7dcf918aa..59ca2849a3 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -435,6 +435,28 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "mute_sounds", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Mute Sounds", + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -1228,7 +1250,7 @@ "issingle": 0, "istable": 0, "max_attachments": 5, - "modified": "2015-10-29 07:52:54.705196", + "modified": "2015-11-03 09:57:27.930248", "modified_by": "Administrator", "module": "Core", "name": "User", diff --git a/frappe/hooks.py b/frappe/hooks.py index 7d543e711f..74a7c0f319 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -165,3 +165,14 @@ default_background = "/assets/frappe/images/ui/into-the-dawn.jpg" get_translated_dict = { ("doctype", "System Settings"): "frappe.geo.country_info.get_translated_dict" } + +sounds = [ + {"name": "email", "src": "/assets/frappe/sounds/email.mp3"}, + {"name": "submit", "src": "/assets/frappe/sounds/submit.mp3"}, + {"name": "cancel", "src": "/assets/frappe/sounds/cancel.mp3"}, + {"name": "delete", "src": "/assets/frappe/sounds/delete.mp3"}, + {"name": "click", "src": "/assets/frappe/sounds/click.mp3"}, + {"name": "error", "src": "/assets/frappe/sounds/error.mp3"}, + # {"name": "alert", "src": "/assets/frappe/sounds/alert.mp3"}, + # {"name": "chime", "src": "/assets/frappe/sounds/chime.mp3"}, +] diff --git a/frappe/public/js/frappe/form/footer/timeline.js b/frappe/public/js/frappe/form/footer/timeline.js index 250b88c9b2..dc70a410da 100644 --- a/frappe/public/js/frappe/form/footer/timeline.js +++ b/frappe/public/js/frappe/form/footer/timeline.js @@ -230,6 +230,8 @@ frappe.ui.form.Comments = Class.extend({ if(!r.exc) { me.input.val(""); + frappe.utils.play_sound("click"); + var comment = r.message; var comments = me.get_comments(); var comment_exists = false; @@ -261,6 +263,8 @@ frappe.ui.form.Comments = Class.extend({ }, callback: function(r) { if(!r.exc) { + frappe.utils.play_sound("delete"); + me.frm.get_docinfo().comments = $.map(me.frm.get_docinfo().comments, function(v) { diff --git a/frappe/public/js/frappe/list/doclistview.js b/frappe/public/js/frappe/list/doclistview.js index bdec7e3e78..b17b0aa363 100644 --- a/frappe/public/js/frappe/list/doclistview.js +++ b/frappe/public/js/frappe/list/doclistview.js @@ -421,16 +421,16 @@ frappe.views.DocListView = frappe.ui.Listing.extend({ }) }, true); } - + //bulk assignment me.page.add_menu_item(__("Assign To"), function(){ - + docname = []; - + $.each(me.get_checked_items(), function(i, doc){ docname.push(doc.name); }) - + if(docname.length >= 1){ me.dialog = frappe.ui.to_do_dialog({ obj: me, @@ -439,7 +439,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({ docname: docname, bulk_assign: true, re_assign: true, - callback: function(){ + callback: function(){ me.dirty = true; me.refresh(); } @@ -544,6 +544,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({ doctype: me.doctype }, callback: function() { + frappe.utils.play_sound("delete"); me.set_working(false); me.dirty = true; me.refresh(); diff --git a/frappe/public/js/frappe/misc/utils.js b/frappe/public/js/frappe/misc/utils.js index 9fad0213be..17db281cf5 100644 --- a/frappe/public/js/frappe/misc/utils.js +++ b/frappe/public/js/frappe/misc/utils.js @@ -493,5 +493,22 @@ frappe.utils = { is_image_file: function(filename) { return (/\.(gif|jpg|jpeg|tiff|png|svg)$/i).test(filename); + }, + + play_sound: function(name) { + try { + if (frappe.boot.user.mute_sounds) { + return; + } + + var audio = $("#sound-" + name)[0]; + audio.volume = audio.getAttribute("volume"); + audio.play(); + + } catch(e) { + console.log("Cannot play sound", name, e); + // pass + } + } }; diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js index 67e0d87c9f..e32439df20 100644 --- a/frappe/public/js/frappe/model/model.js +++ b/frappe/public/js/frappe/model/model.js @@ -472,6 +472,7 @@ $.extend(frappe.model, { }, callback: function(r, rt) { if(!r.exc) { + frappe.utils.play_sound("delete"); frappe.model.clear_doc(doctype, docname); if(callback) callback(r,rt); } diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 59a1a52666..5cf0a0a583 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -84,14 +84,16 @@ frappe.request.call = function(opts) { } } + frappe.utils.play_sound("error"); msgprint(__("Not permitted")); }, 508: function(xhr) { + frappe.utils.play_sound("error"); msgprint(__("Another transaction is blocking this one. Please try again in a few seconds.")); }, 413: function(data, xhr) { msgprint(__("File size exceeded the maximum allowed size of {0} MB", - [(frappe.boot.max_file_size || 5242880) / 1048576])) + [(frappe.boot.max_file_size || 5242880) / 1048576])); }, 417: function(xhr) { var r = xhr.responseJSON; @@ -102,6 +104,7 @@ frappe.request.call = function(opts) { r = xhr.responseText; } } + opts.error_callback && opts.error_callback(r); }, 501: function(data, xhr) { @@ -109,6 +112,7 @@ frappe.request.call = function(opts) { opts.error_callback && opts.error_callback(data, xhr.responseText); }, 500: function(xhr) { + frappe.utils.play_sound("error"); msgprint(__("Server Error: Please check your server logs or contact tech support.")) opts.error_callback && opts.error_callback(); frappe.request.report_error(xhr, opts); diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index 4167fdbdac..33a43818bb 100644 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -434,6 +434,8 @@ frappe.views.CommunicationComposer = Class.extend({ btn: btn, callback: function(r) { if(!r.exc) { + frappe.utils.play_sound("email"); + if(form_values.send_email && r.message["emails_not_sent_to"]) { msgprint( __("Email not sent to {0} (unsubscribed / disabled)", [ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ]) ); diff --git a/frappe/public/js/legacy/form.js b/frappe/public/js/legacy/form.js index db3ab56feb..86a0ee8f86 100644 --- a/frappe/public/js/legacy/form.js +++ b/frappe/public/js/legacy/form.js @@ -635,6 +635,10 @@ _f.Frm.prototype._save = function(save_action, callback, btn, on_error) { var after_save = function(r) { if(!r.exc) { + if (["Save", "Update", "Amend"].indexOf(save_action)!==-1) { + frappe.utils.play_sound("click"); + } + me.script_manager.trigger("after_save"); me.refresh(); } else { @@ -691,6 +695,7 @@ _f.Frm.prototype.savesubmit = function(btn, callback, on_error) { me.save('Submit', function(r) { if(!r.exc) { + frappe.utils.play_sound("submit"); callback && callback(); me.script_manager.trigger("on_submit"); } @@ -713,6 +718,7 @@ _f.Frm.prototype.savecancel = function(btn, callback, on_error) { var after_cancel = function(r) { if(!r.exc) { + frappe.utils.play_sound("cancel"); me.refresh(); callback && callback(); me.script_manager.trigger("after_cancel"); @@ -746,6 +752,7 @@ _f.Frm.prototype.amend_doc = function() { newdoc.amendment_date = dateutil.obj_to_str(new Date()); } this.copy_doc(fn, 1); + frappe.utils.play_sound("click"); } _f.Frm.prototype.disable_save = function() { diff --git a/frappe/public/sounds/alert.mp3 b/frappe/public/sounds/alert.mp3 new file mode 100644 index 0000000000..7a676a1a8a Binary files /dev/null and b/frappe/public/sounds/alert.mp3 differ diff --git a/frappe/public/sounds/cancel.mp3 b/frappe/public/sounds/cancel.mp3 new file mode 100644 index 0000000000..3a4fb36544 Binary files /dev/null and b/frappe/public/sounds/cancel.mp3 differ diff --git a/frappe/public/sounds/chime.mp3 b/frappe/public/sounds/chime.mp3 new file mode 100644 index 0000000000..396883ecb4 Binary files /dev/null and b/frappe/public/sounds/chime.mp3 differ diff --git a/frappe/public/sounds/click.mp3 b/frappe/public/sounds/click.mp3 new file mode 100644 index 0000000000..1c8480beec Binary files /dev/null and b/frappe/public/sounds/click.mp3 differ diff --git a/frappe/public/sounds/delete.mp3 b/frappe/public/sounds/delete.mp3 new file mode 100644 index 0000000000..6dd17ca812 Binary files /dev/null and b/frappe/public/sounds/delete.mp3 differ diff --git a/frappe/public/sounds/email.mp3 b/frappe/public/sounds/email.mp3 new file mode 100644 index 0000000000..18e51631da Binary files /dev/null and b/frappe/public/sounds/email.mp3 differ diff --git a/frappe/public/sounds/error.mp3 b/frappe/public/sounds/error.mp3 new file mode 100644 index 0000000000..dca7767bfe Binary files /dev/null and b/frappe/public/sounds/error.mp3 differ diff --git a/frappe/public/sounds/submit.mp3 b/frappe/public/sounds/submit.mp3 new file mode 100644 index 0000000000..d3bc463ba6 Binary files /dev/null and b/frappe/public/sounds/submit.mp3 differ diff --git a/frappe/templates/pages/desk.html b/frappe/templates/pages/desk.html index 33d9c9161c..c954bc4c7b 100644 --- a/frappe/templates/pages/desk.html +++ b/frappe/templates/pages/desk.html @@ -61,4 +61,10 @@ {% endfor %} {% include "templates/includes/google_analytics.html" %} + + {% for sound in (sounds or []) %} + + {% endfor %} diff --git a/frappe/templates/pages/desk.py b/frappe/templates/pages/desk.py index 1ad4134fc3..05c58cfce2 100644 --- a/frappe/templates/pages/desk.py +++ b/frappe/templates/pages/desk.py @@ -34,6 +34,7 @@ def get_context(context): "build_version": get_build_version(), "include_js": hooks["app_include_js"], "include_css": hooks["app_include_css"], + "sounds": hooks["sounds"], "boot": boot if context.get("for_mobile") else boot_json, "csrf_token": csrf_token, "background_image": boot.user.background_image or boot.default_background_image, diff --git a/frappe/utils/user.py b/frappe/utils/user.py index 5f4dad3513..26e29db787 100644 --- a/frappe/utils/user.py +++ b/frappe/utils/user.py @@ -189,7 +189,7 @@ class User: def load_user(self): d = frappe.db.sql("""select email, first_name, last_name, - email_signature, user_type, language, background_image, background_style + email_signature, user_type, language, background_image, background_style, mute_sounds from tabUser where name = %s""", (self.name,), as_dict=1)[0] if not self.can_read: