@@ -13,6 +13,17 @@ | |||||
frappe.ui.form.on('DocType', { | frappe.ui.form.on('DocType', { | ||||
refresh: function(frm) { | refresh: function(frm) { | ||||
frm.set_query('role', 'permissions', function(doc) { | |||||
if (doc.custom && frappe.session.user != 'Administrator') { | |||||
return { | |||||
query: "frappe.core.doctype.role.role.role_query", | |||||
filters: { | |||||
name: ['not in', ['All']] | |||||
} | |||||
}; | |||||
} | |||||
}); | |||||
if(frappe.session.user !== "Administrator" || !frappe.boot.developer_mode) { | if(frappe.session.user !== "Administrator" || !frappe.boot.developer_mode) { | ||||
if(frm.is_new()) { | if(frm.is_new()) { | ||||
frm.set_value("custom", 1); | frm.set_value("custom", 1); | ||||
@@ -1112,6 +1112,20 @@ def validate_permissions(doctype, for_remove=False, alert=False): | |||||
if d.get("import") and not isimportable: | if d.get("import") and not isimportable: | ||||
frappe.throw(_("{0}: Cannot set import as {1} is not importable").format(get_txt(d), doctype)) | frappe.throw(_("{0}: Cannot set import as {1} is not importable").format(get_txt(d), doctype)) | ||||
def validate_permission_for_all_role(d): | |||||
if frappe.session.user == 'Administrator': return | |||||
if doctype.custom: | |||||
if d.role == 'All': | |||||
frappe.throw(_('Row # {0}: Non administrator user can not set the role {1} to the custom doctype') | |||||
.format(d.idx, frappe.bold(_('All'))), title=_('Permissions Error')) | |||||
roles = [row.name for row in frappe.get_all('Role', filters={'is_custom': 1})] | |||||
if d.role in roles: | |||||
frappe.throw(_('Row # {0}: Non administrator user can not set the role {1} to the custom doctype') | |||||
.format(d.idx, frappe.bold(_(d.role))), title=_('Permissions Error')) | |||||
for d in permissions: | for d in permissions: | ||||
if not d.permlevel: | if not d.permlevel: | ||||
d.permlevel=0 | d.permlevel=0 | ||||
@@ -1123,6 +1137,7 @@ def validate_permissions(doctype, for_remove=False, alert=False): | |||||
check_if_importable(d) | check_if_importable(d) | ||||
check_level_zero_is_set(d) | check_level_zero_is_set(d) | ||||
remove_rights_for_single(d) | remove_rights_for_single(d) | ||||
validate_permission_for_all_role(d) | |||||
def make_module_and_roles(doc, perm_fieldname="permissions"): | def make_module_and_roles(doc, perm_fieldname="permissions"): | ||||
"""Make `Module Def` and `Role` records if already not made. Called while installing.""" | """Make `Module Def` and `Role` records if already not made. Called while installing.""" | ||||
@@ -3,6 +3,9 @@ | |||||
frappe.ui.form.on('Role', { | frappe.ui.form.on('Role', { | ||||
refresh: function(frm) { | refresh: function(frm) { | ||||
frm.set_df_property('is_custom', 'read_only', | |||||
frappe.session.user == 'Administrator' ? false : true); | |||||
frm.add_custom_button("Role Permissions Manager", function() { | frm.add_custom_button("Role Permissions Manager", function() { | ||||
frappe.route_options = {"role": frm.doc.name}; | frappe.route_options = {"role": frm.doc.name}; | ||||
frappe.set_route("permission-manager"); | frappe.set_route("permission-manager"); | ||||
@@ -25,7 +25,8 @@ | |||||
"form_settings_section", | "form_settings_section", | ||||
"form_sidebar", | "form_sidebar", | ||||
"timeline", | "timeline", | ||||
"dashboard" | |||||
"dashboard", | |||||
"is_custom" | |||||
], | ], | ||||
"fields": [ | "fields": [ | ||||
{ | { | ||||
@@ -141,13 +142,20 @@ | |||||
"fieldname": "notifications", | "fieldname": "notifications", | ||||
"fieldtype": "Check", | "fieldtype": "Check", | ||||
"label": "Notifications" | "label": "Notifications" | ||||
}, | |||||
{ | |||||
"default": "0", | |||||
"fieldname": "is_custom", | |||||
"fieldtype": "Check", | |||||
"in_list_view": 1, | |||||
"label": "Is Custom" | |||||
} | } | ||||
], | ], | ||||
"icon": "fa fa-bookmark", | "icon": "fa fa-bookmark", | ||||
"idx": 1, | "idx": 1, | ||||
"index_web_pages_for_search": 1, | "index_web_pages_for_search": 1, | ||||
"links": [], | "links": [], | ||||
"modified": "2020-12-03 14:08:38.181035", | |||||
"modified": "2021-01-27 10:35:37.638350", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "Role", | "name": "Role", | ||||
@@ -53,7 +53,6 @@ class Role(Document): | |||||
if user_type != user.user_type: | if user_type != user.user_type: | ||||
user.save() | user.save() | ||||
def get_info_based_on_role(role, field='email'): | def get_info_based_on_role(role, field='email'): | ||||
''' Get information of all users that have been assigned this role ''' | ''' Get information of all users that have been assigned this role ''' | ||||
users = frappe.get_list("Has Role", filters={"role": role, "parenttype": "User"}, | users = frappe.get_list("Has Role", filters={"role": role, "parenttype": "User"}, | ||||
@@ -73,3 +72,15 @@ def get_user_info(users, field='email'): | |||||
def get_users(role): | def get_users(role): | ||||
return [d.parent for d in frappe.get_all("Has Role", filters={"role": role, "parenttype": "User"}, | return [d.parent for d in frappe.get_all("Has Role", filters={"role": role, "parenttype": "User"}, | ||||
fields=["parent"])] | fields=["parent"])] | ||||
# searches for active employees | |||||
@frappe.whitelist() | |||||
@frappe.validate_and_sanitize_search_inputs | |||||
def role_query(doctype, txt, searchfield, start, page_len, filters): | |||||
filters.update({ | |||||
'is_custom': 0, 'name': ('like', '%{0}%'.format(txt)) | |||||
}) | |||||
return frappe.get_all('Role', limit_start=start, limit_page_length=page_len, | |||||
filters=filters, as_list=1) |
@@ -59,10 +59,11 @@ frappe.ui.form.on('User', { | |||||
onload: function(frm) { | onload: function(frm) { | ||||
frm.can_edit_roles = has_access_to_edit_user(); | frm.can_edit_roles = has_access_to_edit_user(); | ||||
if (frm.can_edit_roles && !frm.is_new()) { | |||||
if (!frm.roles_editor) { | |||||
if (frm.can_edit_roles && !frm.is_new() && frm.doc.user_type == 'System User') { | |||||
if(!frm.roles_editor) { | |||||
const role_area = $('<div class="role-editor">') | const role_area = $('<div class="role-editor">') | ||||
.appendTo(frm.fields_dict.roles_html.wrapper); | .appendTo(frm.fields_dict.roles_html.wrapper); | ||||
frm.roles_editor = new frappe.RoleEditor(role_area, frm, frm.doc.role_profile_name ? 1 : 0); | frm.roles_editor = new frappe.RoleEditor(role_area, frm, frm.doc.role_profile_name ? 1 : 0); | ||||
var module_area = $('<div>') | var module_area = $('<div>') | ||||
@@ -75,7 +76,7 @@ frappe.ui.form.on('User', { | |||||
}, | }, | ||||
refresh: function(frm) { | refresh: function(frm) { | ||||
var doc = frm.doc; | var doc = frm.doc; | ||||
if(!frm.is_new() && !frm.roles_editor && frm.can_edit_roles) { | |||||
if (frm.doc.user_type == 'System User' && !frm.is_new() && !frm.roles_editor && frm.can_edit_roles) { | |||||
frm.reload_doc(); | frm.reload_doc(); | ||||
return; | return; | ||||
} | } | ||||
@@ -191,7 +191,7 @@ | |||||
"print_hide": 1 | "print_hide": 1 | ||||
}, | }, | ||||
{ | { | ||||
"depends_on": "enabled", | |||||
"depends_on": "eval:doc.user_type == 'System User' && doc.enabled == 1", | |||||
"fieldname": "sb1", | "fieldname": "sb1", | ||||
"fieldtype": "Section Break", | "fieldtype": "Section Break", | ||||
"label": "Roles", | "label": "Roles", | ||||
@@ -391,6 +391,7 @@ | |||||
}, | }, | ||||
{ | { | ||||
"collapsible": 1, | "collapsible": 1, | ||||
"depends_on": "eval:doc.user_type == 'System User'", | |||||
"fieldname": "sb_allow_modules", | "fieldname": "sb_allow_modules", | ||||
"fieldtype": "Section Break", | "fieldtype": "Section Break", | ||||
"label": "Allow Modules", | "label": "Allow Modules", | ||||
@@ -453,18 +454,18 @@ | |||||
"label": "Simultaneous Sessions" | "label": "Simultaneous Sessions" | ||||
}, | }, | ||||
{ | { | ||||
"bold": 1, | |||||
"default": "System User", | "default": "System User", | ||||
"description": "If the user has any role checked, then the user becomes a \"System User\". \"System User\" has access to the desktop", | "description": "If the user has any role checked, then the user becomes a \"System User\". \"System User\" has access to the desktop", | ||||
"fieldname": "user_type", | "fieldname": "user_type", | ||||
"fieldtype": "Select", | |||||
"fieldtype": "Link", | |||||
"in_list_view": 1, | "in_list_view": 1, | ||||
"in_standard_filter": 1, | "in_standard_filter": 1, | ||||
"label": "User Type", | "label": "User Type", | ||||
"oldfieldname": "user_type", | "oldfieldname": "user_type", | ||||
"oldfieldtype": "Select", | "oldfieldtype": "Select", | ||||
"options": "System User\nWebsite User", | |||||
"permlevel": 1, | |||||
"read_only": 1 | |||||
"options": "User Type", | |||||
"permlevel": 1 | |||||
}, | }, | ||||
{ | { | ||||
"description": "Allow user to login only after this hour (0-24)", | "description": "Allow user to login only after this hour (0-24)", | ||||
@@ -669,7 +670,7 @@ | |||||
} | } | ||||
], | ], | ||||
"max_attachments": 5, | "max_attachments": 5, | ||||
"modified": "2021-02-01 16:11:06.037543", | |||||
"modified": "2021-02-02 16:11:06.037543", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "User", | "name": "User", | ||||
@@ -10,7 +10,8 @@ import frappe.share | |||||
import frappe.defaults | import frappe.defaults | ||||
import frappe.permissions | import frappe.permissions | ||||
from frappe.model.document import Document | from frappe.model.document import Document | ||||
from frappe.utils import cint, flt, has_gravatar, escape_html, format_datetime, now_datetime, get_formatted_email, today | |||||
from frappe.utils import (cint, flt, has_gravatar, escape_html, format_datetime, | |||||
now_datetime, get_formatted_email, today) | |||||
from frappe import throw, msgprint, _ | from frappe import throw, msgprint, _ | ||||
from frappe.utils.password import update_password as _update_password, check_password, get_password_reset_limit | from frappe.utils.password import update_password as _update_password, check_password, get_password_reset_limit | ||||
from frappe.desk.notifications import clear_notifications | from frappe.desk.notifications import clear_notifications | ||||
@@ -19,6 +20,7 @@ from frappe.utils.user import get_system_managers | |||||
from frappe.website.utils import is_signup_enabled | from frappe.website.utils import is_signup_enabled | ||||
from frappe.rate_limiter import rate_limit | from frappe.rate_limiter import rate_limit | ||||
from frappe.utils.background_jobs import enqueue | from frappe.utils.background_jobs import enqueue | ||||
from frappe.core.doctype.user_type.user_type import user_linked_with_permission_on_doctype | |||||
STANDARD_USERS = ("Guest", "Administrator") | STANDARD_USERS = ("Guest", "Administrator") | ||||
@@ -186,11 +188,36 @@ class User(Document): | |||||
_update_password(user=self.name, pwd=new_password, logout_all_sessions=self.logout_all_sessions) | _update_password(user=self.name, pwd=new_password, logout_all_sessions=self.logout_all_sessions) | ||||
def set_system_user(self): | def set_system_user(self): | ||||
'''Set as System User if any of the given roles has desk_access''' | |||||
if self.has_desk_access() or self.name == 'Administrator': | |||||
self.user_type = 'System User' | |||||
'''For the standard users like admin and guest, the user type is fixed.''' | |||||
user_type_mapper = { | |||||
'Administrator': 'System User', | |||||
'Guest': 'Website User' | |||||
} | |||||
if self.user_type and not frappe.get_cached_value('User Type', self.user_type, 'is_standard'): | |||||
if user_type_mapper.get(self.name): | |||||
self.user_type = user_type_mapper.get(self.name) | |||||
else: | |||||
self.set_roles_and_modules_based_on_user_type() | |||||
else: | else: | ||||
self.user_type = 'Website User' | |||||
'''Set as System User if any of the given roles has desk_access''' | |||||
self.user_type = 'System User' if self.has_desk_access() else 'Website User' | |||||
def set_roles_and_modules_based_on_user_type(self): | |||||
user_type_doc = frappe.get_cached_doc('User Type', self.user_type) | |||||
if user_type_doc.role: | |||||
self.roles = [] | |||||
# Check whether User has linked with the 'Apply User Permission On' doctype or not | |||||
if user_linked_with_permission_on_doctype(user_type_doc, self.name): | |||||
self.append('roles', { | |||||
'role': user_type_doc.role | |||||
}) | |||||
frappe.msgprint(_('Role has been set as per the user type {0}') | |||||
.format(self.user_type), alert=True) | |||||
user_type_doc.update_modules_in_user(self) | |||||
def has_desk_access(self): | def has_desk_access(self): | ||||
'''Return true if any of the set roles has desk access''' | '''Return true if any of the set roles has desk access''' | ||||
@@ -877,7 +904,8 @@ def reset_password(user): | |||||
def user_query(doctype, txt, searchfield, start, page_len, filters): | def user_query(doctype, txt, searchfield, start, page_len, filters): | ||||
from frappe.desk.reportview import get_match_cond, get_filters_cond | from frappe.desk.reportview import get_match_cond, get_filters_cond | ||||
conditions=[] | conditions=[] | ||||
user_type_condition = "and user_type = 'System User'" | |||||
user_type_condition = "and user_type != 'Website User'" | |||||
if filters and filters.get('ignore_user_type'): | if filters and filters.get('ignore_user_type'): | ||||
user_type_condition = '' | user_type_condition = '' | ||||
filters.pop('ignore_user_type') | filters.pop('ignore_user_type') | ||||
@@ -0,0 +1,76 @@ | |||||
{ | |||||
"actions": [], | |||||
"creation": "2021-01-13 01:51:40.158521", | |||||
"doctype": "DocType", | |||||
"editable_grid": 1, | |||||
"engine": "InnoDB", | |||||
"field_order": [ | |||||
"document_type", | |||||
"column_break_2", | |||||
"is_custom", | |||||
"permissions_section", | |||||
"read", | |||||
"write", | |||||
"create" | |||||
], | |||||
"fields": [ | |||||
{ | |||||
"fieldname": "document_type", | |||||
"fieldtype": "Link", | |||||
"in_list_view": 1, | |||||
"label": "Document Type", | |||||
"options": "DocType", | |||||
"reqd": 1 | |||||
}, | |||||
{ | |||||
"fieldname": "permissions_section", | |||||
"fieldtype": "Section Break", | |||||
"label": "Role Permissions" | |||||
}, | |||||
{ | |||||
"default": "1", | |||||
"fieldname": "read", | |||||
"fieldtype": "Check", | |||||
"in_list_view": 1, | |||||
"label": "Read" | |||||
}, | |||||
{ | |||||
"default": "0", | |||||
"fieldname": "write", | |||||
"fieldtype": "Check", | |||||
"in_list_view": 1, | |||||
"label": "Write" | |||||
}, | |||||
{ | |||||
"default": "0", | |||||
"fieldname": "create", | |||||
"fieldtype": "Check", | |||||
"in_list_view": 1, | |||||
"label": "Create" | |||||
}, | |||||
{ | |||||
"fieldname": "column_break_2", | |||||
"fieldtype": "Column Break" | |||||
}, | |||||
{ | |||||
"default": "0", | |||||
"fetch_from": "document_type.custom", | |||||
"fieldname": "is_custom", | |||||
"fieldtype": "Check", | |||||
"label": "Is Custom", | |||||
"read_only": 1 | |||||
} | |||||
], | |||||
"index_web_pages_for_search": 1, | |||||
"istable": 1, | |||||
"links": [], | |||||
"modified": "2021-01-19 01:25:34.773430", | |||||
"modified_by": "Administrator", | |||||
"module": "Core", | |||||
"name": "User Document Type", | |||||
"owner": "Administrator", | |||||
"permissions": [], | |||||
"sort_field": "modified", | |||||
"sort_order": "DESC", | |||||
"track_changes": 1 | |||||
} |
@@ -0,0 +1,10 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2021, Frappe Technologies and contributors | |||||
# For license information, please see license.txt | |||||
from __future__ import unicode_literals | |||||
# import frappe | |||||
from frappe.model.document import Document | |||||
class UserDocumentType(Document): | |||||
pass |
@@ -0,0 +1,33 @@ | |||||
{ | |||||
"actions": [], | |||||
"creation": "2021-01-17 18:28:14.208576", | |||||
"doctype": "DocType", | |||||
"editable_grid": 1, | |||||
"engine": "InnoDB", | |||||
"field_order": [ | |||||
"document_type" | |||||
], | |||||
"fields": [ | |||||
{ | |||||
"fieldname": "document_type", | |||||
"fieldtype": "Link", | |||||
"in_list_view": 1, | |||||
"label": "Document Type", | |||||
"options": "DocType", | |||||
"reqd": 1 | |||||
} | |||||
], | |||||
"index_web_pages_for_search": 1, | |||||
"istable": 1, | |||||
"links": [], | |||||
"modified": "2021-01-17 18:45:44.993190", | |||||
"modified_by": "Administrator", | |||||
"module": "Core", | |||||
"name": "User Select Document Type", | |||||
"owner": "Administrator", | |||||
"permissions": [], | |||||
"quick_entry": 1, | |||||
"sort_field": "modified", | |||||
"sort_order": "DESC", | |||||
"track_changes": 1 | |||||
} |
@@ -0,0 +1,10 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2021, Frappe Technologies and contributors | |||||
# For license information, please see license.txt | |||||
from __future__ import unicode_literals | |||||
# import frappe | |||||
from frappe.model.document import Document | |||||
class UserSelectDocumentType(Document): | |||||
pass |
@@ -0,0 +1,10 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2021, Frappe Technologies and Contributors | |||||
# See license.txt | |||||
from __future__ import unicode_literals | |||||
# import frappe | |||||
import unittest | |||||
class TestUserType(unittest.TestCase): | |||||
pass |
@@ -0,0 +1,70 @@ | |||||
// Copyright (c) 2021, Frappe Technologies and contributors | |||||
// For license information, please see license.txt | |||||
frappe.ui.form.on('User Type', { | |||||
refresh: function(frm) { | |||||
frm.toggle_display('is_standard', frappe.boot.developer_mode ? true : false); | |||||
frm.set_df_property('is_standard', 'read_only', frappe.boot.developer_mode ? false : true); | |||||
const fields = ['role', 'apply_user_permission_on', 'user_id_field', | |||||
'user_doctypes', 'select_doctypes', 'user_type_modules']; | |||||
frm.toggle_display(fields, frm.doc.is_standard ? false : true); | |||||
frm.set_query('document_type', 'user_doctypes', function() { | |||||
return { | |||||
filters: { | |||||
istable: 0 | |||||
} | |||||
}; | |||||
}); | |||||
frm.set_query('document_type', 'select_doctypes', function() { | |||||
return { | |||||
filters: { | |||||
istable: 0, | |||||
is_submittable: 0 | |||||
} | |||||
}; | |||||
}); | |||||
frm.set_query('role', function() { | |||||
return { | |||||
filters: { | |||||
is_custom: 1, | |||||
disabled: 0, | |||||
desk_access: 1 | |||||
} | |||||
}; | |||||
}); | |||||
frm.set_query('apply_user_permission_on', function() { | |||||
return { | |||||
query: "frappe.core.doctype.user_type.user_type.get_user_linked_doctypes" | |||||
}; | |||||
}); | |||||
}, | |||||
onload: function(frm) { | |||||
frm.trigger('get_user_id_fields'); | |||||
}, | |||||
apply_user_permission_on: function(frm) { | |||||
frm.set_value('user_id_field', ''); | |||||
frm.trigger('get_user_id_fields'); | |||||
}, | |||||
get_user_id_fields: function(frm) { | |||||
if (frm.doc.apply_user_permission_on) { | |||||
frappe.call({ | |||||
method: 'frappe.core.doctype.user_type.user_type.get_user_id', | |||||
args: { | |||||
parent: frm.doc.apply_user_permission_on | |||||
}, | |||||
callback: function(r) { | |||||
set_field_options('user_id_field', [""].concat(r.message)); | |||||
} | |||||
}); | |||||
} | |||||
} | |||||
}); |
@@ -0,0 +1,128 @@ | |||||
{ | |||||
"actions": [], | |||||
"autoname": "Prompt", | |||||
"creation": "2021-01-13 01:48:02.378548", | |||||
"doctype": "DocType", | |||||
"editable_grid": 1, | |||||
"engine": "InnoDB", | |||||
"field_order": [ | |||||
"is_standard", | |||||
"section_break_2", | |||||
"role", | |||||
"column_break_4", | |||||
"apply_user_permission_on", | |||||
"user_id_field", | |||||
"section_break_6", | |||||
"user_doctypes", | |||||
"select_doctypes", | |||||
"allowed_modules_section", | |||||
"user_type_modules" | |||||
], | |||||
"fields": [ | |||||
{ | |||||
"default": "0", | |||||
"fieldname": "is_standard", | |||||
"fieldtype": "Check", | |||||
"label": "Is Standard" | |||||
}, | |||||
{ | |||||
"depends_on": "eval: !doc.is_standard", | |||||
"fieldname": "section_break_2", | |||||
"fieldtype": "Section Break", | |||||
"label": "Document Types and Permissions" | |||||
}, | |||||
{ | |||||
"fieldname": "user_doctypes", | |||||
"fieldtype": "Table", | |||||
"label": "Document Types", | |||||
"mandatory_depends_on": "eval: !doc.is_standard", | |||||
"options": "User Document Type" | |||||
}, | |||||
{ | |||||
"fieldname": "role", | |||||
"fieldtype": "Link", | |||||
"in_list_view": 1, | |||||
"label": "Role", | |||||
"mandatory_depends_on": "eval: !doc.is_standard", | |||||
"options": "Role" | |||||
}, | |||||
{ | |||||
"fieldname": "select_doctypes", | |||||
"fieldtype": "Table", | |||||
"label": "Document Types (Select Permissions Only)", | |||||
"options": "User Select Document Type" | |||||
}, | |||||
{ | |||||
"fieldname": "column_break_4", | |||||
"fieldtype": "Column Break" | |||||
}, | |||||
{ | |||||
"description": "Can only list down the document types which has been linked to the User document type.", | |||||
"fieldname": "apply_user_permission_on", | |||||
"fieldtype": "Link", | |||||
"label": "Apply User Permission On", | |||||
"mandatory_depends_on": "eval: !doc.is_standard", | |||||
"options": "DocType" | |||||
}, | |||||
{ | |||||
"depends_on": "eval: !doc.is_standard", | |||||
"fieldname": "section_break_6", | |||||
"fieldtype": "Section Break", | |||||
"hide_border": 1 | |||||
}, | |||||
{ | |||||
"depends_on": "apply_user_permission_on", | |||||
"fieldname": "user_id_field", | |||||
"fieldtype": "Select", | |||||
"label": "User Id Field", | |||||
"mandatory_depends_on": "eval: !doc.is_standard" | |||||
}, | |||||
{ | |||||
"depends_on": "eval: !doc.is_standard", | |||||
"fieldname": "allowed_modules_section", | |||||
"fieldtype": "Section Break", | |||||
"label": "Allowed Modules" | |||||
}, | |||||
{ | |||||
"fieldname": "user_type_modules", | |||||
"fieldtype": "Table", | |||||
"no_copy": 1, | |||||
"options": "User Type Module", | |||||
"print_hide": 1, | |||||
"read_only": 1 | |||||
} | |||||
], | |||||
"index_web_pages_for_search": 1, | |||||
"links": [], | |||||
"modified": "2021-01-24 04:47:08.243320", | |||||
"modified_by": "Administrator", | |||||
"module": "Core", | |||||
"name": "User Type", | |||||
"owner": "Administrator", | |||||
"permissions": [ | |||||
{ | |||||
"create": 1, | |||||
"email": 1, | |||||
"export": 1, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "Administrator", | |||||
"share": 1, | |||||
"write": 1 | |||||
}, | |||||
{ | |||||
"email": 1, | |||||
"export": 1, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "System Manager", | |||||
"share": 1, | |||||
"write": 1 | |||||
} | |||||
], | |||||
"sort_field": "modified", | |||||
"sort_order": "DESC", | |||||
"track_changes": 1 | |||||
} |
@@ -0,0 +1,209 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2021, Frappe Technologies and contributors | |||||
# For license information, please see license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
from frappe import _ | |||||
from six import iteritems | |||||
from frappe.utils import get_link_to_form | |||||
from frappe.config import get_modules_from_app | |||||
from frappe.permissions import add_permission, add_user_permission | |||||
from frappe.model.document import Document | |||||
class UserType(Document): | |||||
def validate(self): | |||||
self.set_modules() | |||||
def on_update(self): | |||||
if self.is_standard: return | |||||
self.validate_document_type_limit() | |||||
self.validate_role() | |||||
self.add_role_permissions_for_user_doctypes() | |||||
self.add_role_permissions_for_select_doctypes() | |||||
self.update_users() | |||||
get_non_standard_user_type_details() | |||||
self.remove_permission_for_deleted_doctypes() | |||||
def on_trash(self): | |||||
if self.is_standard: | |||||
frappe.throw(_('Standard user type {0} can not be deleted.') | |||||
.format(frappe.bold(self.name))) | |||||
def set_modules(self): | |||||
if not self.user_doctypes: return | |||||
modules = frappe.get_all('DocType', fields=['distinct module as module'], | |||||
filters={'name': ('in', [d.document_type for d in self.user_doctypes])}) | |||||
self.set('user_type_modules', []) | |||||
for row in modules: | |||||
self.append('user_type_modules', { | |||||
'module': row.module | |||||
}) | |||||
def validate_document_type_limit(self): | |||||
limit = frappe.conf.get('user_type_doctype_limit').get(frappe.scrub(self.name)) or 10 | |||||
if self.user_doctypes and len(self.user_doctypes) > limit: | |||||
frappe.throw(_('The total number of user document types limit has been crossed.'), | |||||
title=_('User Document Types Limit Exceeded')) | |||||
custom_doctypes = [row.document_type for row in self.user_doctypes if row.is_custom] | |||||
if custom_doctypes and len(custom_doctypes) > 3: | |||||
frappe.throw(_('You can only set the 3 custom doctypes in the Document Types table.'), | |||||
title=_('Custom Document Types Limit Exceeded')) | |||||
def validate_role(self): | |||||
if not self.role: | |||||
frappe.throw(_("The field {0} is mandatory") | |||||
.format(frappe.bold(_('Role')))) | |||||
if not frappe.db.get_value('Role', self.role, 'is_custom'): | |||||
frappe.throw(_("The role {0} should be a custom role.") | |||||
.format(frappe.bold(get_link_to_form('Role', self.role)))) | |||||
def update_users(self): | |||||
for row in frappe.get_all('User', filters = {'user_type': self.name}): | |||||
user = frappe.get_cached_doc('User', row.name) | |||||
self.update_roles_in_user(user) | |||||
self.update_modules_in_user(user) | |||||
user.update_children() | |||||
def update_roles_in_user(self, user): | |||||
user.set('roles', []) | |||||
user.append('roles', { | |||||
'role': self.role | |||||
}) | |||||
def update_modules_in_user(self, user): | |||||
block_modules = frappe.get_all('Module Def', fields = ['name as module'], | |||||
filters={'name': ['not in', [d.module for d in self.user_type_modules]]}) | |||||
if block_modules: | |||||
user.set('block_modules', block_modules) | |||||
def add_role_permissions_for_user_doctypes(self): | |||||
perms = ['read', 'write', 'create'] | |||||
for row in self.user_doctypes: | |||||
docperm = add_role_permissions(row.document_type, self.role) | |||||
values = {perm:row.get(perm) for perm in perms} | |||||
for perm in ['print', 'email', 'share']: | |||||
values[perm] = 1 | |||||
frappe.db.set_value('Custom DocPerm', docperm, values) | |||||
def add_role_permissions_for_select_doctypes(self): | |||||
for row in self.select_doctypes: | |||||
docperm = add_role_permissions(row.document_type, self.role) | |||||
frappe.db.set_value('Custom DocPerm', docperm, | |||||
{'select': 1, 'read': 0, 'create': 0, 'write': 0}) | |||||
def remove_permission_for_deleted_doctypes(self): | |||||
doctypes = [d.document_type for d in self.user_doctypes] | |||||
for dt in self.select_doctypes: | |||||
doctypes.append(dt.document_type) | |||||
for perm in frappe.get_all('Custom DocPerm', | |||||
filters = {'role': self.role, 'parent': ['not in', doctypes]}): | |||||
frappe.delete_doc('Custom DocPerm', perm.name) | |||||
def add_role_permissions(doctype, role): | |||||
name = frappe.get_value('Custom DocPerm', dict(parent=doctype, | |||||
role=role, permlevel=0)) | |||||
if not name: | |||||
name = add_permission(doctype, role, 0) | |||||
return name | |||||
def get_non_standard_user_type_details(): | |||||
user_types = frappe.get_all('User Type', | |||||
fields=['apply_user_permission_on', 'name', 'user_id_field'], | |||||
filters={'is_standard': 0}) | |||||
if user_types: | |||||
user_type_details = {d.name: [d.apply_user_permission_on, d.user_id_field] for d in user_types} | |||||
frappe.cache().set_value('non_standard_user_types', user_type_details) | |||||
return user_type_details | |||||
@frappe.whitelist() | |||||
@frappe.validate_and_sanitize_search_inputs | |||||
def get_user_linked_doctypes(doctype, txt, searchfield, start, page_len, filters): | |||||
modules = [d.get('module_name') for d in get_modules_from_app('frappe')] | |||||
filters = [['DocField', 'options', '=', 'User'], ['DocType', 'is_submittable', '=', 0], | |||||
['DocType', 'issingle', '=', 0], ['DocType', 'module', 'not in', modules], | |||||
['DocType', 'read_only', '=', 0], ['DocType', 'name', 'like', '%{0}%'.format(txt)]] | |||||
doctypes = frappe.get_all('DocType', fields = ['`tabDocType`.`name`'], filters=filters, | |||||
order_by = '`tabDocType`.`idx` desc', limit_start=start, limit_page_length=page_len, as_list=1, debug=1) | |||||
custom_dt_filters = [['Custom Field', 'dt', 'like', '%{0}%'.format(txt)], | |||||
['Custom Field', 'options', '=', 'User'], ['Custom Field', 'fieldtype', '=', 'Link']] | |||||
custom_doctypes = frappe.get_all('Custom Field', fields = ['dt as name'], | |||||
filters= custom_dt_filters, as_list=1) | |||||
return doctypes + custom_doctypes | |||||
@frappe.whitelist() | |||||
def get_user_id(parent): | |||||
data = frappe.get_all('DocField', fields = ['label', 'fieldname as value'], | |||||
filters= {'options': 'User', 'fieldtype': 'Link', 'parent': parent}) or [] | |||||
data.extend(frappe.get_all('Custom Field', fields = ['label', 'fieldname as value'], | |||||
filters= {'options': 'User', 'fieldtype': 'Link', 'dt': parent})) | |||||
return data | |||||
def user_linked_with_permission_on_doctype(doc, user): | |||||
if not doc.apply_user_permission_on: return True | |||||
if not doc.user_id_field: | |||||
frappe.throw(_('User Id Field is mandatory in the user type {0}') | |||||
.format(frappe.bold(doc.name))) | |||||
if frappe.db.get_value(doc.apply_user_permission_on, | |||||
{doc.user_id_field: user}, 'name'): | |||||
return True | |||||
else: | |||||
label = frappe.get_meta(doc.apply_user_permission_on).get_field(doc.user_id_field).label | |||||
frappe.msgprint(_("To set the role {0} in the user {1}, kindly set the {2} field as {3} in one of the {4} record.") | |||||
.format(frappe.bold(doc.role), frappe.bold(user), frappe.bold(label), | |||||
frappe.bold(user), frappe.bold(doc.apply_user_permission_on))) | |||||
return False | |||||
def apply_permissions_for_non_standard_user_type(doc, method=None): | |||||
'''Create user permission for the non standard user type''' | |||||
user_types = frappe.cache().get_value('non_standard_user_types') | |||||
if not user_types: | |||||
user_types = get_non_standard_user_type_details() | |||||
for user_type, data in iteritems(user_types): | |||||
if doc.doctype != data[0]: continue | |||||
if frappe.get_cached_value('User', doc.get(data[1]), 'user_type') != user_type: | |||||
return | |||||
if (doc.get(data[1]) and (doc.get(data[1]) != doc._doc_before_save.get(data[1]) | |||||
or not frappe.db.get_value('User Permission', | |||||
{'user': doc.get(data[1]), 'allow': data[0], 'for_value': doc.name}, 'name'))): | |||||
perm_data = frappe.db.get_value('User Permission', | |||||
{'allow': doc.doctype, 'for_value': doc.name}, ['name', 'user']) | |||||
if not perm_data: | |||||
user_doc = frappe.get_cached_doc('User', doc.get(data[1])) | |||||
user_doc.set_roles_and_modules_based_on_user_type() | |||||
user_doc.update_children() | |||||
add_user_permission(doc.doctype, doc.name, doc.get(data[1])) | |||||
else: | |||||
frappe.db.set_value('User Permission', perm_data[0], 'user', doc.get(data[1])) |
@@ -0,0 +1,13 @@ | |||||
from __future__ import unicode_literals | |||||
from frappe import _ | |||||
def get_data(): | |||||
return { | |||||
'fieldname': 'user_type', | |||||
'transactions': [ | |||||
{ | |||||
'label': _('Reference'), | |||||
'items': ['User'] | |||||
} | |||||
] | |||||
} |
@@ -0,0 +1,10 @@ | |||||
frappe.listview_settings['User Type'] = { | |||||
add_fields: ["is_standard"], | |||||
get_indicator: function (doc) { | |||||
if (doc.is_standard) { | |||||
return [__("Standard"), "green", "is_standard,=,1"]; | |||||
} else { | |||||
return [__("Custom"), "blue", "is_standard,=,0"]; | |||||
} | |||||
} | |||||
}; |
@@ -0,0 +1,33 @@ | |||||
{ | |||||
"actions": [], | |||||
"creation": "2021-01-24 03:05:24.634719", | |||||
"doctype": "DocType", | |||||
"editable_grid": 1, | |||||
"engine": "InnoDB", | |||||
"field_order": [ | |||||
"module" | |||||
], | |||||
"fields": [ | |||||
{ | |||||
"fieldname": "module", | |||||
"fieldtype": "Link", | |||||
"in_list_view": 1, | |||||
"label": "Module", | |||||
"options": "Module Def", | |||||
"reqd": 1 | |||||
} | |||||
], | |||||
"index_web_pages_for_search": 1, | |||||
"istable": 1, | |||||
"links": [], | |||||
"modified": "2021-01-24 03:07:43.602927", | |||||
"modified_by": "Administrator", | |||||
"module": "Core", | |||||
"name": "User Type Module", | |||||
"owner": "Administrator", | |||||
"permissions": [], | |||||
"quick_entry": 1, | |||||
"sort_field": "modified", | |||||
"sort_order": "DESC", | |||||
"track_changes": 1 | |||||
} |
@@ -0,0 +1,10 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2021, Frappe Technologies and contributors | |||||
# For license information, please see license.txt | |||||
from __future__ import unicode_literals | |||||
# import frappe | |||||
from frappe.model.document import Document | |||||
class UserTypeModule(Document): | |||||
pass |
@@ -30,8 +30,16 @@ def get_roles_and_doctypes(): | |||||
"restrict_to_domain": ("in", active_domains) | "restrict_to_domain": ("in", active_domains) | ||||
}, fields=["name"]) | }, fields=["name"]) | ||||
restricted_roles = ['Administrator'] | |||||
if frappe.session.user != 'Administrator': | |||||
custom_user_type_roles = frappe.get_all('User Type', filters = {'is_standard': 0}, fields=['role']) | |||||
for row in custom_user_type_roles: | |||||
restricted_roles.append(row.role) | |||||
restricted_roles.append('All') | |||||
roles = frappe.get_all("Role", filters={ | roles = frappe.get_all("Role", filters={ | ||||
"name": ("not in", "Administrator"), | |||||
"name": ("not in", restricted_roles), | |||||
"disabled": 0, | "disabled": 0, | ||||
}, or_filters={ | }, or_filters={ | ||||
"ifnull(restrict_to_domain, '')": "", | "ifnull(restrict_to_domain, '')": "", | ||||
@@ -54,9 +62,14 @@ def get_permissions(doctype=None, role=None): | |||||
if doctype: | if doctype: | ||||
out = [p for p in out if p.parent == doctype] | out = [p for p in out if p.parent == doctype] | ||||
else: | else: | ||||
out = frappe.get_all('Custom DocPerm', fields='*', filters=dict(parent = doctype), order_by="permlevel") | |||||
filters=dict(parent = doctype) | |||||
if frappe.session.user != 'Administrator': | |||||
custom_roles = frappe.get_all('Role', filters={'is_custom': 1}) | |||||
filters['role'] = ['not in', [row.name for row in custom_roles]] | |||||
out = frappe.get_all('Custom DocPerm', fields='*', filters=filters, order_by="permlevel") | |||||
if not out: | if not out: | ||||
out = frappe.get_all('DocPerm', fields='*', filters=dict(parent = doctype), order_by="permlevel") | |||||
out = frappe.get_all('DocPerm', fields='*', filters=filters, order_by="permlevel") | |||||
linked_doctypes = {} | linked_doctypes = {} | ||||
for d in out: | for d in out: | ||||
@@ -78,14 +91,14 @@ def add(parent, role, permlevel): | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def update(doctype, role, permlevel, ptype, value=None): | def update(doctype, role, permlevel, ptype, value=None): | ||||
"""Update role permission params | """Update role permission params | ||||
Args: | Args: | ||||
doctype (str): Name of the DocType to update params for | doctype (str): Name of the DocType to update params for | ||||
role (str): Role to be updated for, eg "Website Manager". | role (str): Role to be updated for, eg "Website Manager". | ||||
permlevel (int): perm level the provided rule applies to | permlevel (int): perm level the provided rule applies to | ||||
ptype (str): permission type, example "read", "delete", etc. | ptype (str): permission type, example "read", "delete", etc. | ||||
value (None, optional): value for ptype, None indicates False | value (None, optional): value for ptype, None indicates False | ||||
Returns: | Returns: | ||||
str: Refresh flag is permission is updated successfully | str: Refresh flag is permission is updated successfully | ||||
""" | """ | ||||
@@ -147,6 +147,7 @@ doc_events = { | |||||
"frappe.core.doctype.file.file.attach_files_to_document", | "frappe.core.doctype.file.file.attach_files_to_document", | ||||
"frappe.event_streaming.doctype.event_update_log.event_update_log.notify_consumers", | "frappe.event_streaming.doctype.event_update_log.event_update_log.notify_consumers", | ||||
"frappe.automation.doctype.assignment_rule.assignment_rule.update_due_date", | "frappe.automation.doctype.assignment_rule.assignment_rule.update_due_date", | ||||
"frappe.core.doctype.user_type.user_type.apply_permissions_for_non_standard_user_type" | |||||
], | ], | ||||
"after_rename": "frappe.desk.notifications.clear_doctype_notifications", | "after_rename": "frappe.desk.notifications.clear_doctype_notifications", | ||||
"on_cancel": [ | "on_cancel": [ | ||||
@@ -333,3 +333,4 @@ frappe.patches.v13_0.delete_package_publish_tool | |||||
frappe.patches.v13_0.rename_list_view_setting_to_list_view_settings | frappe.patches.v13_0.rename_list_view_setting_to_list_view_settings | ||||
frappe.patches.v13_0.remove_twilio_settings | frappe.patches.v13_0.remove_twilio_settings | ||||
frappe.patches.v12_0.rename_uploaded_files_with_proper_name | frappe.patches.v12_0.rename_uploaded_files_with_proper_name | ||||
frappe.patches.v13_0.make_user_type |
@@ -0,0 +1,10 @@ | |||||
import frappe | |||||
from frappe.utils.install import create_user_type | |||||
def execute(): | |||||
frappe.reload_doc('core', 'doctype', 'role') | |||||
frappe.reload_doc('core', 'doctype', 'user_document_type') | |||||
frappe.reload_doc('core', 'doctype', 'user_select_document_type') | |||||
frappe.reload_doc('core', 'doctype', 'user_type') | |||||
create_user_type() |
@@ -471,7 +471,7 @@ def setup_custom_perms(parent): | |||||
copy_perms(parent) | copy_perms(parent) | ||||
return True | return True | ||||
def add_permission(doctype, role, permlevel=0): | |||||
def add_permission(doctype, role, permlevel=0, ptype=None): | |||||
'''Add a new permission rule to the given doctype | '''Add a new permission rule to the given doctype | ||||
for the given Role and Permission Level''' | for the given Role and Permission Level''' | ||||
from frappe.core.doctype.doctype.doctype import validate_permissions_for_doctype | from frappe.core.doctype.doctype.doctype import validate_permissions_for_doctype | ||||
@@ -481,6 +481,9 @@ def add_permission(doctype, role, permlevel=0): | |||||
permlevel=permlevel, if_owner=0)): | permlevel=permlevel, if_owner=0)): | ||||
return | return | ||||
if not ptype: | |||||
ptype = 'read' | |||||
custom_docperm = frappe.get_doc({ | custom_docperm = frappe.get_doc({ | ||||
"doctype":"Custom DocPerm", | "doctype":"Custom DocPerm", | ||||
"__islocal": 1, | "__islocal": 1, | ||||
@@ -488,13 +491,14 @@ def add_permission(doctype, role, permlevel=0): | |||||
"parenttype": "DocType", | "parenttype": "DocType", | ||||
"parentfield": "permissions", | "parentfield": "permissions", | ||||
"role": role, | "role": role, | ||||
'read': 1, | |||||
"permlevel": permlevel, | "permlevel": permlevel, | ||||
ptype: 1, | |||||
}) | }) | ||||
custom_docperm.save() | custom_docperm.save() | ||||
validate_permissions_for_doctype(doctype) | validate_permissions_for_doctype(doctype) | ||||
return custom_docperm.name | |||||
def copy_perms(parent): | def copy_perms(parent): | ||||
'''Copy all DocPerm in to Custom DocPerm for the given document''' | '''Copy all DocPerm in to Custom DocPerm for the given document''' | ||||
@@ -18,6 +18,7 @@ def after_install(): | |||||
# reset installed apps for re-install | # reset installed apps for re-install | ||||
frappe.db.set_global("installed_apps", '["frappe"]') | frappe.db.set_global("installed_apps", '["frappe"]') | ||||
create_user_type() | |||||
install_basic_docs() | install_basic_docs() | ||||
from frappe.core.doctype.file.file import make_home_folder | from frappe.core.doctype.file.file import make_home_folder | ||||
@@ -49,6 +50,15 @@ def after_install(): | |||||
frappe.db.commit() | frappe.db.commit() | ||||
def create_user_type(): | |||||
for user_type in ['System User', 'Website User']: | |||||
if not frappe.db.exists('User Type', user_type): | |||||
frappe.get_doc({ | |||||
'doctype': 'User Type', | |||||
'name': user_type, | |||||
'is_standard': 1 | |||||
}).insert(ignore_permissions=True) | |||||
def install_basic_docs(): | def install_basic_docs(): | ||||
# core users / roles | # core users / roles | ||||
install_docs = [ | install_docs = [ | ||||