Merge branch 'hotfix'
This commit is contained in:
commit
ca1cd65fa4
10 changed files with 79 additions and 229 deletions
|
|
@ -13,7 +13,7 @@ import os, sys, importlib, inspect, json
|
|||
from .exceptions import *
|
||||
from .utils.jinja import get_jenv, get_template, render_template
|
||||
|
||||
__version__ = '7.2.29'
|
||||
__version__ = '7.2.30'
|
||||
__title__ = "Frappe Framework"
|
||||
|
||||
local = Local()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ def get_notifications():
|
|||
cache = frappe.cache()
|
||||
|
||||
notification_count = {}
|
||||
|
||||
for name in groups:
|
||||
count = cache.hget("notification_count:" + name, frappe.session.user)
|
||||
if count is not None:
|
||||
|
|
@ -108,12 +109,19 @@ def get_notifications_for_doctypes(config, notification_count):
|
|||
|
||||
return open_count_doctype
|
||||
|
||||
def clear_notifications(user="*"):
|
||||
if user=="*":
|
||||
frappe.cache().delete_keys("notification_count:")
|
||||
else:
|
||||
# delete count for user
|
||||
frappe.cache().hdel_keys("notification_count:", user)
|
||||
def clear_notifications(user=None):
|
||||
if frappe.flags.in_install:
|
||||
return
|
||||
|
||||
config = get_notification_config()
|
||||
groups = config.get("for_doctype").keys() + config.get("for_module").keys()
|
||||
cache = frappe.cache()
|
||||
|
||||
for name in groups:
|
||||
if user:
|
||||
cache.hdel("notification_count:" + name, user)
|
||||
else:
|
||||
cache.delete_key("notification_count:" + name)
|
||||
|
||||
def delete_notification_count_for(doctype):
|
||||
frappe.cache().delete_key("notification_count:" + doctype)
|
||||
|
|
@ -126,9 +134,6 @@ def clear_doctype_notifications(doc, method=None, *args, **kwargs):
|
|||
delete_notification_count_for(doctype)
|
||||
return
|
||||
|
||||
if doctype in config.for_module_doctypes:
|
||||
delete_notification_count_for(config.for_module_doctypes[doctype])
|
||||
|
||||
def get_notification_info_for_boot():
|
||||
out = get_notifications()
|
||||
config = get_notification_config()
|
||||
|
|
@ -156,7 +161,7 @@ def get_notification_config():
|
|||
config = frappe._dict()
|
||||
for notification_config in frappe.get_hooks().notification_config:
|
||||
nc = frappe.get_attr(notification_config)()
|
||||
for key in ("for_doctype", "for_module", "for_module_doctypes", "for_other"):
|
||||
for key in ("for_doctype", "for_module", "for_other"):
|
||||
config.setdefault(key, {})
|
||||
config[key].update(nc.get(key, {}))
|
||||
return config
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ def get_root_connection(root_login='root', root_password=None):
|
|||
return frappe.local.flags.root_connection
|
||||
|
||||
def install_app(name, verbose=False, set_as_patched=True):
|
||||
frappe.flags.in_install = name
|
||||
frappe.clear_cache()
|
||||
app_hooks = frappe.get_hooks(app_name=name)
|
||||
installed_apps = frappe.get_installed_apps()
|
||||
|
|
|
|||
|
|
@ -381,6 +381,9 @@ class BaseDocument(object):
|
|||
self.set("modified", now())
|
||||
self.set("modified_by", frappe.session.user)
|
||||
|
||||
# to trigger email alert on value change
|
||||
self.run_method('before_change')
|
||||
|
||||
frappe.db.set_value(self.doctype, self.name, fieldname, value,
|
||||
self.modified, self.modified_by, update_modified=update_modified)
|
||||
|
||||
|
|
|
|||
|
|
@ -681,6 +681,7 @@ class Document(BaseDocument):
|
|||
def _evaluate_alert(alert):
|
||||
if not alert.name in self.flags.email_alerts_executed:
|
||||
evaluate_alert(self, alert.name, alert.event)
|
||||
self.flags.email_alerts_executed.append(alert.name)
|
||||
|
||||
event_map = {
|
||||
"on_update": "Save",
|
||||
|
|
@ -692,6 +693,7 @@ class Document(BaseDocument):
|
|||
if not self.flags.in_insert:
|
||||
# value change is not applicable in insert
|
||||
event_map['validate'] = 'Value Change'
|
||||
event_map['before_change'] = 'Value Change'
|
||||
|
||||
for alert in self.flags.email_alerts:
|
||||
event = event_map.get(method, None)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@
|
|||
"public/js/lib/jquery/jquery-ui.min.js",
|
||||
"public/js/lib/Sortable.min.js",
|
||||
"public/js/lib/tag-it.min.js",
|
||||
"public/js/lib/notify.js",
|
||||
"public/js/lib/bootstrap.min.js",
|
||||
"public/js/lib/moment/moment-with-locales.min.js",
|
||||
"public/js/lib/moment/moment-timezone-with-data.min.js",
|
||||
|
|
|
|||
|
|
@ -62,9 +62,6 @@ frappe.Application = Class.extend({
|
|||
this.show_notes();
|
||||
}
|
||||
|
||||
// ask to allow notifications
|
||||
frappe.utils.if_notify_permitted();
|
||||
|
||||
// listen to csrf_update
|
||||
frappe.realtime.on("csrf_generated", function(data) {
|
||||
// handles the case when a user logs in again from another tab
|
||||
|
|
|
|||
|
|
@ -517,27 +517,8 @@ frappe.utils = {
|
|||
frappe.msgprint("Note: Changing the Page Name will break previous URL to this page.");
|
||||
},
|
||||
|
||||
if_notify_permitted: function(callback) {
|
||||
if (Notify.needsPermission) {
|
||||
Notify.requestPermission(callback);
|
||||
} else {
|
||||
callback && callback();
|
||||
}
|
||||
},
|
||||
|
||||
notify: function(subject, body, route, onclick) {
|
||||
if(!route) route = "messages";
|
||||
if(!onclick) onclick = function() {
|
||||
frappe.set_route(route);
|
||||
}
|
||||
|
||||
frappe.utils.if_notify_permitted(function() {
|
||||
var notify = new Notify(subject, {
|
||||
body: body.replace(/<[^>]*>/g, ""),
|
||||
notifyClick: onclick
|
||||
});
|
||||
notify.show();
|
||||
});
|
||||
console.log('push notifications are evil and deprecated');
|
||||
},
|
||||
|
||||
set_title: function(title) {
|
||||
|
|
@ -593,3 +574,56 @@ frappe.utils = {
|
|||
return email_list;
|
||||
}
|
||||
};
|
||||
|
||||
// String.prototype.includes polyfill
|
||||
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/includes
|
||||
if (!String.prototype.includes) {
|
||||
String.prototype.includes = function(search, start) {
|
||||
'use strict';
|
||||
if (typeof start !== 'number') {
|
||||
start = 0;
|
||||
}
|
||||
if (start + search.length > this.length) {
|
||||
return false;
|
||||
} else {
|
||||
return this.indexOf(search, start) !== -1;
|
||||
}
|
||||
};
|
||||
}
|
||||
// Array.prototype.includes polyfill
|
||||
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
|
||||
if (!Array.prototype.includes) {
|
||||
Object.defineProperty(Array.prototype, 'includes', {
|
||||
value: function(searchElement, fromIndex) {
|
||||
if (this == null) {
|
||||
throw new TypeError('"this" is null or not defined');
|
||||
}
|
||||
var o = Object(this);
|
||||
var len = o.length >>> 0;
|
||||
if (len === 0) {
|
||||
return false;
|
||||
}
|
||||
var n = fromIndex | 0;
|
||||
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
|
||||
while (k < len) {
|
||||
if (o[k] === searchElement) {
|
||||
return true;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
// Array de duplicate
|
||||
if (!Array.prototype.uniqBy) {
|
||||
Object.defineProperty(Array.prototype, 'uniqBy', {
|
||||
value: function (key) {
|
||||
var seen = {};
|
||||
return this.filter(function (item) {
|
||||
var k = key(item);
|
||||
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
/*
|
||||
* Author: Alex Gibson
|
||||
* https://github.com/alexgibson/notify.js
|
||||
* License: MIT license
|
||||
*/
|
||||
|
||||
(function(global, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD environment
|
||||
define(function() {
|
||||
return factory(global, global.document);
|
||||
});
|
||||
} else if (typeof module !== 'undefined' && module.exports) {
|
||||
// CommonJS environment
|
||||
module.exports = factory(global, global.document);
|
||||
} else {
|
||||
// Browser environment
|
||||
global.Notify = factory(global, global.document);
|
||||
}
|
||||
} (typeof window !== 'undefined' ? window : this, function (w, d) {
|
||||
|
||||
'use strict';
|
||||
|
||||
function isFunction (item) {
|
||||
return typeof item === 'function';
|
||||
}
|
||||
|
||||
function Notify(title, options) {
|
||||
|
||||
if (typeof title !== 'string') {
|
||||
throw new Error('Notify(): first arg (title) must be a string.');
|
||||
}
|
||||
|
||||
this.title = title;
|
||||
|
||||
this.options = {
|
||||
icon: '',
|
||||
body: '',
|
||||
tag: '',
|
||||
notifyShow: null,
|
||||
notifyClose: null,
|
||||
notifyClick: null,
|
||||
notifyError: null,
|
||||
timeout: null
|
||||
};
|
||||
|
||||
this.permission = null;
|
||||
|
||||
if (!Notify.isSupported) {
|
||||
return;
|
||||
}
|
||||
|
||||
//User defined options for notification content
|
||||
if (typeof options === 'object') {
|
||||
|
||||
for (var i in options) {
|
||||
if (options.hasOwnProperty(i)) {
|
||||
this.options[i] = options[i];
|
||||
}
|
||||
}
|
||||
|
||||
//callback when notification is displayed
|
||||
if (isFunction(this.options.notifyShow)) {
|
||||
this.onShowCallback = this.options.notifyShow;
|
||||
}
|
||||
|
||||
//callback when notification is closed
|
||||
if (isFunction(this.options.notifyClose)) {
|
||||
this.onCloseCallback = this.options.notifyClose;
|
||||
}
|
||||
|
||||
//callback when notification is clicked
|
||||
if (isFunction(this.options.notifyClick)) {
|
||||
this.onClickCallback = this.options.notifyClick;
|
||||
}
|
||||
|
||||
//callback when notification throws error
|
||||
if (isFunction(this.options.notifyError)) {
|
||||
this.onErrorCallback = this.options.notifyError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// true if the browser supports HTML5 Notification
|
||||
Notify.isSupported = 'Notification' in w;
|
||||
|
||||
// true if the permission is not granted
|
||||
Notify.needsPermission = !(Notify.isSupported && Notification.permission === 'granted');
|
||||
|
||||
// returns current permission level ('granted', 'default', 'denied' or null)
|
||||
Notify.permissionLevel = (Notify.isSupported ? Notification.permission : null);
|
||||
|
||||
// asks the user for permission to display notifications. Then calls the callback functions is supplied.
|
||||
Notify.requestPermission = function (onPermissionGrantedCallback, onPermissionDeniedCallback) {
|
||||
if (!Notify.isSupported) {
|
||||
return;
|
||||
}
|
||||
w.Notification.requestPermission(function (perm) {
|
||||
switch (perm) {
|
||||
case 'granted':
|
||||
Notify.needsPermission = false;
|
||||
if (isFunction(onPermissionGrantedCallback)) {
|
||||
onPermissionGrantedCallback();
|
||||
}
|
||||
break;
|
||||
case 'denied':
|
||||
if (isFunction(onPermissionDeniedCallback)) {
|
||||
onPermissionDeniedCallback();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Notify.prototype.show = function () {
|
||||
|
||||
if (!Notify.isSupported) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.myNotify = new Notification(this.title, {
|
||||
'body': this.options.body,
|
||||
'tag' : this.options.tag,
|
||||
'icon' : this.options.icon
|
||||
});
|
||||
|
||||
if (this.options.timeout && !isNaN(this.options.timeout)) {
|
||||
setTimeout(this.close.bind(this), this.options.timeout * 1000);
|
||||
}
|
||||
|
||||
this.myNotify.addEventListener('show', this, false);
|
||||
this.myNotify.addEventListener('error', this, false);
|
||||
this.myNotify.addEventListener('close', this, false);
|
||||
this.myNotify.addEventListener('click', this, false);
|
||||
};
|
||||
|
||||
Notify.prototype.onShowNotification = function (e) {
|
||||
if (this.onShowCallback) {
|
||||
this.onShowCallback(e);
|
||||
}
|
||||
};
|
||||
|
||||
Notify.prototype.onCloseNotification = function (e) {
|
||||
if (this.onCloseCallback) {
|
||||
this.onCloseCallback(e);
|
||||
}
|
||||
this.destroy();
|
||||
};
|
||||
|
||||
Notify.prototype.onClickNotification = function (e) {
|
||||
if (this.onClickCallback) {
|
||||
this.onClickCallback(e);
|
||||
}
|
||||
};
|
||||
|
||||
Notify.prototype.onErrorNotification = function (e) {
|
||||
if (this.onErrorCallback) {
|
||||
this.onErrorCallback(e);
|
||||
}
|
||||
this.destroy();
|
||||
};
|
||||
|
||||
Notify.prototype.destroy = function () {
|
||||
this.myNotify.removeEventListener('show', this, false);
|
||||
this.myNotify.removeEventListener('error', this, false);
|
||||
this.myNotify.removeEventListener('close', this, false);
|
||||
this.myNotify.removeEventListener('click', this, false);
|
||||
};
|
||||
|
||||
Notify.prototype.close = function () {
|
||||
this.myNotify.close();
|
||||
};
|
||||
|
||||
Notify.prototype.handleEvent = function (e) {
|
||||
switch (e.type) {
|
||||
case 'show':
|
||||
this.onShowNotification(e);
|
||||
break;
|
||||
case 'close':
|
||||
this.onCloseNotification(e);
|
||||
break;
|
||||
case 'click':
|
||||
this.onClickNotification(e);
|
||||
break;
|
||||
case 'error':
|
||||
this.onErrorNotification(e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return Notify;
|
||||
|
||||
}));
|
||||
|
|
@ -18,6 +18,7 @@ import frappe.translate
|
|||
from frappe.utils.change_log import get_change_log
|
||||
import redis
|
||||
from urllib import unquote
|
||||
from frappe.desk.notifications import clear_notifications
|
||||
|
||||
@frappe.whitelist()
|
||||
def clear(user=None):
|
||||
|
|
@ -45,6 +46,8 @@ def clear_cache(user=None):
|
|||
clear_global_cache()
|
||||
frappe.defaults.clear_cache()
|
||||
|
||||
clear_notifications(user)
|
||||
|
||||
def clear_global_cache():
|
||||
frappe.model.meta.clear_cache()
|
||||
frappe.cache().delete_value(["app_hooks", "installed_apps",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue