Browse Source

Merge pull request #15606 from rmehta/fix-user-info

fix: load user_info on-demand
version-14
Rushabh Mehta 3 years ago
committed by GitHub
parent
commit
0486cde0a5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 179 additions and 93 deletions
  1. +3
    -0
      frappe/__init__.py
  2. +1
    -2
      frappe/auth.py
  3. +7
    -10
      frappe/boot.py
  4. +1
    -1
      frappe/core/doctype/user/user.py
  5. +80
    -13
      frappe/desk/form/load.py
  6. +10
    -2
      frappe/desk/reportview.py
  7. +1
    -3
      frappe/model/base_document.py
  8. +35
    -14
      frappe/public/js/frappe/form/form_viewers.js
  9. +5
    -0
      frappe/public/js/frappe/list/base_list.js
  10. +21
    -10
      frappe/public/js/frappe/model/sync.js
  11. +0
    -31
      frappe/public/js/frappe/utils/user.js
  12. +13
    -3
      frappe/utils/__init__.py
  13. +0
    -1
      frappe/utils/scheduler.py
  14. +2
    -3
      frappe/utils/user.py

+ 3
- 0
frappe/__init__.py View File

@@ -12,6 +12,8 @@ Read the documentation: https://frappeframework.com/docs
""" """
import os, warnings import os, warnings


STANDARD_USERS = ('Guest', 'Administrator')

_dev_server = os.environ.get('DEV_SERVER', False) _dev_server = os.environ.get('DEV_SERVER', False)


if _dev_server: if _dev_server:
@@ -121,6 +123,7 @@ def set_user_lang(user, user_language=None):
local.lang = get_user_lang(user) local.lang = get_user_lang(user)


# local-globals # local-globals

db = local("db") db = local("db")
qb = local("qb") qb = local("qb")
conf = local("conf") conf = local("conf")


+ 1
- 2
frappe/auth.py View File

@@ -250,8 +250,7 @@ class LoginManager:
if not self.user: if not self.user:
return return


from frappe.core.doctype.user.user import STANDARD_USERS
if self.user in STANDARD_USERS:
if self.user in frappe.STANDARD_USERS:
return False return False


reset_pwd_after_days = cint(frappe.db.get_single_value("System Settings", reset_pwd_after_days = cint(frappe.db.get_single_value("System Settings",


+ 7
- 10
frappe/boot.py View File

@@ -17,7 +17,7 @@ from frappe.social.doctype.energy_point_log.energy_point_log import get_energy_p
from frappe.model.base_document import get_controller from frappe.model.base_document import get_controller
from frappe.social.doctype.post.post import frequently_visited_links from frappe.social.doctype.post.post import frequently_visited_links
from frappe.core.doctype.navbar_settings.navbar_settings import get_navbar_settings, get_app_logo from frappe.core.doctype.navbar_settings.navbar_settings import get_navbar_settings, get_app_logo
from frappe.utils import get_time_zone
from frappe.utils import get_time_zone, add_user_info


def get_bootinfo(): def get_bootinfo():
"""build and return boot info""" """build and return boot info"""
@@ -222,17 +222,14 @@ def load_translations(bootinfo):
bootinfo["__messages"] = messages bootinfo["__messages"] = messages


def get_user_info(): def get_user_info():
user_info = frappe.db.get_all('User', fields=['`name`', 'full_name as fullname', 'user_image as image', 'gender',
'email', 'username', 'bio', 'location', 'interest', 'banner_image', 'allowed_in_mentions', 'user_type', 'time_zone'],
filters=dict(enabled=1))
# get info for current user
user_info = frappe._dict()
add_user_info(frappe.session.user, user_info)


user_info_map = {d.name: d for d in user_info}
if frappe.session.user == 'Administrator' and user_info.Administrator.email:
user_info[user_info.Administrator.email] = user_info.Administrator


admin_data = user_info_map.get('Administrator')
if admin_data:
user_info_map[admin_data.email] = admin_data

return user_info_map
return user_info


def get_user(bootinfo): def get_user(bootinfo):
"""get user info""" """get user info"""


+ 1
- 1
frappe/core/doctype/user/user.py View File

@@ -19,7 +19,7 @@ from frappe.core.doctype.user_type.user_type import user_linked_with_permission_
from frappe.query_builder import DocType from frappe.query_builder import DocType




STANDARD_USERS = ("Guest", "Administrator")
STANDARD_USERS = frappe.STANDARD_USERS


class User(Document): class User(Document):
__new_password = None __new_password = None


+ 80
- 13
frappe/desk/form/load.py View File

@@ -94,30 +94,78 @@ def get_docinfo(doc=None, doctype=None, name=None):
automated_messages = filter(lambda x: x['communication_type'] == 'Automated Message', all_communications) automated_messages = filter(lambda x: x['communication_type'] == 'Automated Message', all_communications)
communications_except_auto_messages = filter(lambda x: x['communication_type'] != 'Automated Message', all_communications) communications_except_auto_messages = filter(lambda x: x['communication_type'] != 'Automated Message', all_communications)


frappe.response["docinfo"] = {
docinfo = frappe._dict(user_info = {})

add_comments(doc, docinfo)

docinfo.update({
"attachments": get_attachments(doc.doctype, doc.name), "attachments": get_attachments(doc.doctype, doc.name),
"attachment_logs": get_comments(doc.doctype, doc.name, 'attachment'),
"communications": communications_except_auto_messages, "communications": communications_except_auto_messages,
"automated_messages": automated_messages, "automated_messages": automated_messages,
'comments': get_comments(doc.doctype, doc.name),
'total_comments': len(json.loads(doc.get('_comments') or '[]')), 'total_comments': len(json.loads(doc.get('_comments') or '[]')),
'versions': get_versions(doc), 'versions': get_versions(doc),
"assignments": get_assignments(doc.doctype, doc.name), "assignments": get_assignments(doc.doctype, doc.name),
"assignment_logs": get_comments(doc.doctype, doc.name, 'assignment'),
"permissions": get_doc_permissions(doc), "permissions": get_doc_permissions(doc),
"shared": frappe.share.get_users(doc.doctype, doc.name), "shared": frappe.share.get_users(doc.doctype, doc.name),
"info_logs": get_comments(doc.doctype, doc.name, comment_type=['Info', 'Edit', 'Label']),
"share_logs": get_comments(doc.doctype, doc.name, 'share'),
"like_logs": get_comments(doc.doctype, doc.name, 'Like'),
"workflow_logs": get_comments(doc.doctype, doc.name, comment_type="Workflow"),
"views": get_view_logs(doc.doctype, doc.name), "views": get_view_logs(doc.doctype, doc.name),
"energy_point_logs": get_point_logs(doc.doctype, doc.name), "energy_point_logs": get_point_logs(doc.doctype, doc.name),
"additional_timeline_content": get_additional_timeline_content(doc.doctype, doc.name), "additional_timeline_content": get_additional_timeline_content(doc.doctype, doc.name),
"milestones": get_milestones(doc.doctype, doc.name), "milestones": get_milestones(doc.doctype, doc.name),
"is_document_followed": is_document_followed(doc.doctype, doc.name, frappe.session.user), "is_document_followed": is_document_followed(doc.doctype, doc.name, frappe.session.user),
"tags": get_tags(doc.doctype, doc.name), "tags": get_tags(doc.doctype, doc.name),
"document_email": get_document_email(doc.doctype, doc.name)
}
"document_email": get_document_email(doc.doctype, doc.name),
})

update_user_info(docinfo)

frappe.response["docinfo"] = docinfo

def add_comments(doc, docinfo):
# divide comments into separate lists
docinfo.comments = []
docinfo.shared = []
docinfo.assignment_logs = []
docinfo.attachment_logs = []
docinfo.info_logs = []
docinfo.like_logs = []
docinfo.workflow_logs = []

comments = frappe.get_all("Comment",
fields=["name", "creation", "content", "owner", "comment_type"],
filters={
"reference_doctype": doc.doctype,
"reference_name": doc.name
}
)

for c in comments:
if c.comment_type == "Comment":
c.content = frappe.utils.markdown(c.content)
docinfo.comments.append(c)

elif c.comment_type in ('Shared', 'Unshared'):
docinfo.shared.append(c)

elif c.comment_type in ('Assignment Completed', 'Assigned'):
docinfo.assignment_logs.append(c)

elif c.comment_type in ('Attachment', 'Attachment Removed'):
docinfo.attachment_logs.append(c)

elif c.comment_type in ('Info', 'Edit', 'Label'):
docinfo.info_logs.append(c)

elif c.comment_type == "Like":
docinfo.like_logs.append(c)

elif c.comment_type == "Workflow":
docinfo.workflow_logs.append(c)

frappe.utils.add_user_info(c.owner, docinfo.user_info)


return comments



def get_milestones(doctype, name): def get_milestones(doctype, name):
return frappe.db.get_all('Milestone', fields = ['creation', 'owner', 'track_field', 'value'], return frappe.db.get_all('Milestone', fields = ['creation', 'owner', 'track_field', 'value'],
@@ -252,7 +300,7 @@ def get_communication_data(doctype, name, start=0, limit=20, after=None, fields=
return communications return communications


def get_assignments(dt, dn): def get_assignments(dt, dn):
cl = frappe.get_all("ToDo",
return frappe.get_all("ToDo",
fields=['name', 'allocated_to as owner', 'description', 'status'], fields=['name', 'allocated_to as owner', 'description', 'status'],
filters={ filters={
'reference_type': dt, 'reference_type': dt,
@@ -260,8 +308,6 @@ def get_assignments(dt, dn):
'status': ('!=', 'Cancelled'), 'status': ('!=', 'Cancelled'),
}) })


return cl

@frappe.whitelist() @frappe.whitelist()
def get_badge_info(doctypes, filters): def get_badge_info(doctypes, filters):
filters = json.loads(filters) filters = json.loads(filters)
@@ -319,3 +365,24 @@ def get_additional_timeline_content(doctype, docname):
contents.extend(frappe.get_attr(method)(doctype, docname) or []) contents.extend(frappe.get_attr(method)(doctype, docname) or [])


return contents return contents

def update_user_info(docinfo):
for d in docinfo.communications:
frappe.utils.add_user_info(d.sender, docinfo.user_info)

for d in docinfo.shared:
frappe.utils.add_user_info(d.user, docinfo.user_info)

for d in docinfo.assignments:
frappe.utils.add_user_info(d.owner, docinfo.user_info)

for d in docinfo.views:
frappe.utils.add_user_info(d.owner, docinfo.user_info)

@frappe.whitelist()
def get_user_info_for_viewers(users):
user_info = {}
for user in json.loads(users):
frappe.utils.add_user_info(user, user_info)

return user_info

+ 10
- 2
frappe/desk/reportview.py View File

@@ -12,7 +12,7 @@ from io import StringIO
from frappe.core.doctype.access_log.access_log import make_access_log from frappe.core.doctype.access_log.access_log import make_access_log
from frappe.utils import cstr, format_duration from frappe.utils import cstr, format_duration
from frappe.model.base_document import get_controller from frappe.model.base_document import get_controller
from frappe.utils import add_user_info


@frappe.whitelist() @frappe.whitelist()
@frappe.read_only() @frappe.read_only()
@@ -219,6 +219,8 @@ def compress(data, args=None):
"""separate keys and values""" """separate keys and values"""
from frappe.desk.query_report import add_total_row from frappe.desk.query_report import add_total_row


user_info = {}

if not data: return data if not data: return data
if args is None: if args is None:
args = {} args = {}
@@ -230,13 +232,19 @@ def compress(data, args=None):
new_row.append(row.get(key)) new_row.append(row.get(key))
values.append(new_row) values.append(new_row)


# add user info for assignments (avatar)
if row._assign:
for user in json.loads(row._assign):
add_user_info(user, user_info)

if args.get("add_total_row"): if args.get("add_total_row"):
meta = frappe.get_meta(args.doctype) meta = frappe.get_meta(args.doctype)
values = add_total_row(values, keys, meta) values = add_total_row(values, keys, meta)


return { return {
"keys": keys, "keys": keys,
"values": values
"values": values,
"user_info": user_info
} }


@frappe.whitelist() @frappe.whitelist()


+ 1
- 3
frappe/model/base_document.py View File

@@ -646,8 +646,6 @@ class BaseDocument(object):
value, comma_options)) value, comma_options))


def _validate_data_fields(self): def _validate_data_fields(self):
from frappe.core.doctype.user.user import STANDARD_USERS

# data_field options defined in frappe.model.data_field_options # data_field options defined in frappe.model.data_field_options
for data_field in self.meta.get_data_fields(): for data_field in self.meta.get_data_fields():
data = self.get(data_field.fieldname) data = self.get(data_field.fieldname)
@@ -658,7 +656,7 @@ class BaseDocument(object):
continue continue


if data_field_options == "Email": if data_field_options == "Email":
if (self.owner in STANDARD_USERS) and (data in STANDARD_USERS):
if (self.owner in frappe.STANDARD_USERS) and (data in frappe.STANDARD_USERS):
continue continue
for email_address in frappe.utils.split_emails(data): for email_address in frappe.utils.split_emails(data):
frappe.utils.validate_email_address(email_address, throw=True) frappe.utils.validate_email_address(email_address, throw=True)


+ 35
- 14
frappe/public/js/frappe/form/form_viewers.js View File

@@ -27,19 +27,40 @@ frappe.ui.form.FormViewers.set_users = function(data, type) {
const users = data.users || []; const users = data.users || [];
const new_users = users.filter(user => !past_users.includes(user)); const new_users = users.filter(user => !past_users.includes(user));


frappe.model.set_docinfo(doctype, docname, type, {
past: past_users.concat(new_users),
new: new_users,
current: users
});

if (
cur_frm &&
cur_frm.doc &&
cur_frm.doc.doctype === doctype &&
cur_frm.doc.name == docname &&
cur_frm.viewers
) {
cur_frm.viewers.refresh(true, type);
if (new_users.length===0) return;

const set_and_refresh = () => {
const info = {
past: past_users.concat(new_users),
new: new_users,
current: users
};

frappe.model.set_docinfo(doctype, docname, type, info);

if (
cur_frm &&
cur_frm.doc &&
cur_frm.doc.doctype === doctype &&
cur_frm.doc.name == docname &&
cur_frm.viewers
) {
cur_frm.viewers.refresh(true, type);
}
};

let unknown_users = [];
for (let user of users) {
if (!frappe.boot.user_info[user]) unknown_users.push(user);
}

if (unknown_users.length===0) {
set_and_refresh();
} else {
// load additional user info
frappe.xcall('frappe.desk.form.load.get_user_info_for_viewers', {users: unknown_users}).then((data) => {
Object.assign(frappe.boot.user_info, data);
set_and_refresh();
});
} }
}; };

+ 5
- 0
frappe/public/js/frappe/list/base_list.js View File

@@ -484,6 +484,11 @@ frappe.views.BaseList = class BaseList {


prepare_data(r) { prepare_data(r) {
let data = r.message || {}; let data = r.message || {};

// extract user_info for assignments
Object.assign(frappe.boot.user_info, data.user_info);
delete data.user_info;

data = !Array.isArray(data) data = !Array.isArray(data)
? frappe.utils.dict(data.keys, data.values) ? frappe.utils.dict(data.keys, data.values)
: data; : data;


+ 21
- 10
frappe/public/js/frappe/model/sync.js View File

@@ -1,7 +1,7 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt // MIT License. See license.txt


$.extend(frappe.model, {
Object.assign(frappe.model, {
docinfo: {}, docinfo: {},
sync: function(r) { sync: function(r) {
/* docs: /* docs:
@@ -33,22 +33,28 @@ $.extend(frappe.model, {
} }


if(d.localname) { if(d.localname) {
frappe.model.new_names[d.localname] = d.name;
$(document).trigger('rename', [d.doctype, d.localname, d.name]);
delete locals[d.doctype][d.localname];

// update docinfo to new dict keys
if(i===0) {
frappe.model.docinfo[d.doctype][d.name] = frappe.model.docinfo[d.doctype][d.localname];
frappe.model.docinfo[d.doctype][d.localname] = undefined;
}
frappe.model.rename_after_save(d, i);
} }
} }
}


frappe.model.sync_docinfo(r);

},


rename_after_save: (d, i) => {
frappe.model.new_names[d.localname] = d.name;
$(document).trigger('rename', [d.doctype, d.localname, d.name]);
delete locals[d.doctype][d.localname];


// update docinfo to new dict keys
if(i===0) {
frappe.model.docinfo[d.doctype][d.name] = frappe.model.docinfo[d.doctype][d.localname];
frappe.model.docinfo[d.doctype][d.localname] = undefined;
} }
},


sync_docinfo: (r) => {
// set docinfo (comments, assign, attachments) // set docinfo (comments, assign, attachments)
if(r.docinfo) { if(r.docinfo) {
var doc; var doc;
@@ -62,10 +68,14 @@ $.extend(frappe.model, {
frappe.model.docinfo[doc.doctype] = {}; frappe.model.docinfo[doc.doctype] = {};
frappe.model.docinfo[doc.doctype][doc.name] = r.docinfo; frappe.model.docinfo[doc.doctype][doc.name] = r.docinfo;
} }

// copy values to frappe.boot.user_info
Object.assign(frappe.boot.user_info, r.docinfo.user_info);
} }


return r.docs; return r.docs;
}, },

add_to_locals: function(doc) { add_to_locals: function(doc) {
if(!locals[doc.doctype]) if(!locals[doc.doctype])
locals[doc.doctype] = {}; locals[doc.doctype] = {};
@@ -100,6 +110,7 @@ $.extend(frappe.model, {
} }
} }
}, },

update_in_locals: function(doc) { update_in_locals: function(doc) {
// update values in the existing local doc instead of replacing // update values in the existing local doc instead of replacing
let local_doc = locals[doc.doctype][doc.name]; let local_doc = locals[doc.doctype][doc.name];


+ 0
- 31
frappe/public/js/frappe/utils/user.js View File

@@ -2,14 +2,6 @@ frappe.user_info = function(uid) {
if(!uid) if(!uid)
uid = frappe.session.user; uid = frappe.session.user;


if(uid.toLowerCase()==="bot") {
return {
fullname: __("Bot"),
image: "/assets/frappe/images/ui/bot.png",
abbr: "B"
};
}

if(!(frappe.boot.user_info && frappe.boot.user_info[uid])) { if(!(frappe.boot.user_info && frappe.boot.user_info[uid])) {
var user_info = {fullname: uid || "Unknown"}; var user_info = {fullname: uid || "Unknown"};
} else { } else {
@@ -22,29 +14,6 @@ frappe.user_info = function(uid) {
return user_info; return user_info;
}; };


frappe.ui.set_user_background = function(src, selector, style) {
if(!selector) selector = "#page-desktop";
if(!style) style = "Fill Screen";
if(src) {
if (window.cordova && src.indexOf("http") === -1) {
src = frappe.base_url + src;
}
var background = repl('background: url("%(src)s") center center;', {src: src});
} else {
var background = "background-color: #4B4C9D;";
}

frappe.dom.set_style(repl('%(selector)s { \
%(background)s \
background-attachment: fixed; \
%(style)s \
}', {
selector:selector,
background:background,
style: style==="Fill Screen" ? "background-size: cover;" : ""
}));
};

frappe.provide('frappe.user'); frappe.provide('frappe.user');


$.extend(frappe.user, { $.extend(frappe.user, {


+ 13
- 3
frappe/utils/__init__.py View File

@@ -56,7 +56,7 @@ def get_email_address(user=None):
def get_formatted_email(user, mail=None): def get_formatted_email(user, mail=None):
"""get Email Address of user formatted as: `John Doe <johndoe@example.com>`""" """get Email Address of user formatted as: `John Doe <johndoe@example.com>`"""
fullname = get_fullname(user) fullname = get_fullname(user)
method = get_hook_method('get_sender_details') method = get_hook_method('get_sender_details')
if method: if method:
sender_name, mail = method() sender_name, mail = method()
@@ -623,12 +623,11 @@ def get_installed_apps_info():
return out return out


def get_site_info(): def get_site_info():
from frappe.core.doctype.user.user import STANDARD_USERS
from frappe.email.queue import get_emails_sent_this_month from frappe.email.queue import get_emails_sent_this_month
from frappe.utils.user import get_system_managers from frappe.utils.user import get_system_managers


# only get system users # only get system users
users = frappe.get_all('User', filters={'user_type': 'System User', 'name': ('not in', STANDARD_USERS)},
users = frappe.get_all('User', filters={'user_type': 'System User', 'name': ('not in', frappe.STANDARD_USERS)},
fields=['name', 'enabled', 'last_login', 'last_active', 'language', 'time_zone']) fields=['name', 'enabled', 'last_login', 'last_active', 'language', 'time_zone'])
system_managers = get_system_managers(only_name=True) system_managers = get_system_managers(only_name=True)
for u in users: for u in users:
@@ -898,3 +897,14 @@ def dictify(arg):
arg = frappe._dict(arg) arg = frappe._dict(arg)


return arg return arg

def add_user_info(user, user_info):
if user not in user_info:
info = frappe.db.get_value("User",
user, ["full_name", "user_image", "name", 'email'], as_dict=True) or frappe._dict()
user_info[user] = frappe._dict(
fullname = info.full_name or user,
image = info.user_image,
name = user,
email = info.email
)

+ 0
- 1
frappe/utils/scheduler.py View File

@@ -17,7 +17,6 @@ import schedule


# imports - module imports # imports - module imports
import frappe import frappe
from frappe.core.doctype.user.user import STANDARD_USERS
from frappe.installer import update_site_config from frappe.installer import update_site_config
from frappe.utils import get_sites, now_datetime from frappe.utils import get_sites, now_datetime
from frappe.utils.background_jobs import get_jobs from frappe.utils.background_jobs import get_jobs


+ 2
- 3
frappe/utils/user.py View File

@@ -230,7 +230,6 @@ def get_fullname_and_avatar(user):
def get_system_managers(only_name=False): def get_system_managers(only_name=False):
"""returns all system manager's user details""" """returns all system manager's user details"""
import email.utils import email.utils
from frappe.core.doctype.user.user import STANDARD_USERS
system_managers = frappe.db.sql("""SELECT DISTINCT `name`, `creation`, system_managers = frappe.db.sql("""SELECT DISTINCT `name`, `creation`,
CONCAT_WS(' ', CONCAT_WS(' ',
CASE WHEN `first_name`= '' THEN NULL ELSE `first_name` END, CASE WHEN `first_name`= '' THEN NULL ELSE `first_name` END,
@@ -245,8 +244,8 @@ def get_system_managers(only_name=False):
FROM `tabHas Role` AS ur FROM `tabHas Role` AS ur
WHERE ur.parent = p.name WHERE ur.parent = p.name
AND ur.role='System Manager') AND ur.role='System Manager')
ORDER BY `creation` DESC""".format(", ".join(["%s"]*len(STANDARD_USERS))),
STANDARD_USERS, as_dict=True)
ORDER BY `creation` DESC""".format(", ".join(["%s"]*len(frappe.STANDARD_USERS))),
frappe.STANDARD_USERS, as_dict=True)


if only_name: if only_name:
return [p.name for p in system_managers] return [p.name for p in system_managers]


Loading…
Cancel
Save