Merge branch 'hotfix'

This commit is contained in:
Nabin Hait 2017-03-15 11:26:00 +05:30
commit ca1cd65fa4
10 changed files with 79 additions and 229 deletions

View file

@ -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()

View file

@ -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

View file

@ -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()

View file

@ -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)

View file

@ -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)

View file

@ -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",

View file

@ -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

View file

@ -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);
})
}
})
}

View file

@ -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;
}));

View file

@ -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",