Merge branch 'develop' of https://github.com/frappe/frappe into rebrand-ui
This commit is contained in:
commit
ce379bc200
32 changed files with 593 additions and 358 deletions
|
|
@ -104,11 +104,11 @@ install:
|
|||
|
||||
- cd ./frappe-bench
|
||||
|
||||
- sed -i 's/watch:/# watch:/g' Procfile
|
||||
- sed -i 's/schedule:/# schedule:/g' Procfile
|
||||
- sed -i 's/^watch:/# watch:/g' Procfile
|
||||
- sed -i 's/^schedule:/# schedule:/g' Procfile
|
||||
|
||||
- if [ $TYPE == "server" ]; then sed -i 's/socketio:/# socketio:/g' Procfile; fi
|
||||
- if [ $TYPE == "server" ]; then sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile; fi
|
||||
- if [ $TYPE == "server" ]; then sed -i 's/^socketio:/# socketio:/g' Procfile; fi
|
||||
- if [ $TYPE == "server" ]; then sed -i 's/^redis_socketio:/# redis_socketio:/g' Procfile; fi
|
||||
|
||||
- if [ $TYPE == "ui" ]; then bench setup requirements --node; fi
|
||||
|
||||
|
|
|
|||
0
frappe/core/doctype/module_profile/__init__.py
Normal file
0
frappe/core/doctype/module_profile/__init__.py
Normal file
19
frappe/core/doctype/module_profile/module_profile.js
Normal file
19
frappe/core/doctype/module_profile/module_profile.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2020, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Module Profile', {
|
||||
refresh: function(frm) {
|
||||
if (has_common(frappe.user_roles, ["Administrator", "System Manager"])) {
|
||||
if (!frm.module_editor && frm.doc.__onload && frm.doc.__onload.all_modules) {
|
||||
let module_area = $('<div style="min-height: 300px">')
|
||||
.appendTo(frm.fields_dict.module_html.wrapper);
|
||||
|
||||
frm.module_editor = new frappe.ModuleEditor(frm, module_area);
|
||||
}
|
||||
}
|
||||
|
||||
if (frm.module_editor) {
|
||||
frm.module_editor.refresh();
|
||||
}
|
||||
}
|
||||
});
|
||||
60
frappe/core/doctype/module_profile/module_profile.json
Normal file
60
frappe/core/doctype/module_profile/module_profile.json
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"actions": [],
|
||||
"autoname": "field:module_profile_name",
|
||||
"creation": "2020-12-22 22:00:30.614475",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"module_profile_name",
|
||||
"module_html",
|
||||
"block_modules"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "module_profile_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Module Profile Name",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "module_html",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Module HTML"
|
||||
},
|
||||
{
|
||||
"fieldname": "block_modules",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 1,
|
||||
"label": "Block Modules",
|
||||
"options": "Block Module",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-01-03 15:36:52.622696",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Module Profile",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 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
|
||||
}
|
||||
12
frappe/core/doctype/module_profile/module_profile.py
Normal file
12
frappe/core/doctype/module_profile/module_profile.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
|
||||
class ModuleProfile(Document):
|
||||
def onload(self):
|
||||
from frappe.config import get_modules_from_all_apps
|
||||
self.set_onload('all_modules',
|
||||
[m.get("module_name") for m in get_modules_from_all_apps()])
|
||||
32
frappe/core/doctype/module_profile/test_module_profile.py
Normal file
32
frappe/core/doctype/module_profile/test_module_profile.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
class TestModuleProfile(unittest.TestCase):
|
||||
def test_make_new_module_profile(self):
|
||||
if not frappe.db.get_value('Module Profile', '_Test Module Profile'):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Module Profile',
|
||||
'module_profile_name': '_Test Module Profile',
|
||||
'block_modules': [
|
||||
{'module': 'Accounts'}
|
||||
]
|
||||
}).insert()
|
||||
|
||||
# add to user and check
|
||||
if not frappe.db.get_value('User', 'test-for-module_profile@example.com'):
|
||||
new_user = frappe.get_doc({
|
||||
'doctype': 'User',
|
||||
'email':'test-for-module_profile@example.com',
|
||||
'first_name':'Test User'
|
||||
}).insert()
|
||||
else:
|
||||
new_user = frappe.get_doc('User', 'test-for-module_profile@example.com')
|
||||
|
||||
new_user.module_profile = '_Test Module Profile'
|
||||
new_user.save()
|
||||
|
||||
self.assertEqual(new_user.block_modules[0].module, 'Accounts')
|
||||
|
|
@ -37,6 +37,25 @@ frappe.ui.form.on('User', {
|
|||
}
|
||||
},
|
||||
|
||||
module_profile: function(frm) {
|
||||
if (frm.doc.module_profile) {
|
||||
frappe.call({
|
||||
"method": "frappe.core.doctype.user.user.get_module_profile",
|
||||
args: {
|
||||
module_profile: frm.doc.module_profile
|
||||
},
|
||||
callback: function(data) {
|
||||
frm.set_value("block_modules", []);
|
||||
$.each(data.message || [], function(i, v) {
|
||||
let d = frm.add_child("block_modules");
|
||||
d.module = v.module;
|
||||
});
|
||||
frm.module_editor && frm.module_editor.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
frm.can_edit_roles = has_access_to_edit_user();
|
||||
|
||||
|
|
@ -255,43 +274,3 @@ function get_roles_for_editing_user() {
|
|||
.filter(perm => perm.permlevel >= 1 && perm.write)
|
||||
.map(perm => perm.role) || ['System Manager'];
|
||||
}
|
||||
|
||||
frappe.ModuleEditor = Class.extend({
|
||||
init: function(frm, wrapper) {
|
||||
this.wrapper = $('<div class="row module-block-list"></div>').appendTo(wrapper);
|
||||
this.frm = frm;
|
||||
this.make();
|
||||
},
|
||||
make: function() {
|
||||
var me = this;
|
||||
this.frm.doc.__onload.all_modules.forEach(function(m) {
|
||||
$(repl('<div class="col-sm-4"><div class="checkbox">\
|
||||
<label><input type="checkbox" class="block-module-check" data-module="%(module)s">\
|
||||
%(module)s</label></div></div>', {module: m})).appendTo(me.wrapper);
|
||||
});
|
||||
this.bind();
|
||||
},
|
||||
refresh: function() {
|
||||
var me = this;
|
||||
this.wrapper.find(".block-module-check").prop("checked", true);
|
||||
$.each(this.frm.doc.block_modules, function(i, d) {
|
||||
me.wrapper.find(".block-module-check[data-module='"+ d.module +"']").prop("checked", false);
|
||||
});
|
||||
},
|
||||
bind: function() {
|
||||
var me = this;
|
||||
this.wrapper.on("change", ".block-module-check", function() {
|
||||
var module = $(this).attr('data-module');
|
||||
if($(this).prop("checked")) {
|
||||
// remove from block_modules
|
||||
me.frm.doc.block_modules = $.map(me.frm.doc.block_modules || [], function(d) {
|
||||
if (d.module != module) {
|
||||
return d;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
me.frm.add_child("block_modules", {"module": module});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
"allowed_in_mentions",
|
||||
"user_emails",
|
||||
"sb_allow_modules",
|
||||
"module_profile",
|
||||
"modules_html",
|
||||
"block_modules",
|
||||
"home_settings",
|
||||
|
|
@ -594,6 +595,12 @@
|
|||
"fieldtype": "Select",
|
||||
"label": "Desk Theme",
|
||||
"options": "Light\nDark"
|
||||
},
|
||||
{
|
||||
"fieldname": "module_profile",
|
||||
"fieldtype": "Link",
|
||||
"label": "Module Profile",
|
||||
"options": "Module Profile"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-user",
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ class User(Document):
|
|||
self.validate_user_email_inbox()
|
||||
ask_pass_update()
|
||||
self.validate_roles()
|
||||
self.validate_allowed_modules()
|
||||
self.validate_user_image()
|
||||
|
||||
if self.language == "Loading...":
|
||||
|
|
@ -89,6 +90,15 @@ class User(Document):
|
|||
self.set('roles', [])
|
||||
self.append_roles(*[role.role for role in role_profile.roles])
|
||||
|
||||
def validate_allowed_modules(self):
|
||||
if self.module_profile:
|
||||
module_profile = frappe.get_doc('Module Profile', self.module_profile)
|
||||
self.set('block_modules', [])
|
||||
for d in module_profile.get('block_modules'):
|
||||
self.append('block_modules', {
|
||||
'module': d.module
|
||||
})
|
||||
|
||||
def validate_user_image(self):
|
||||
if self.user_image and len(self.user_image) > 2000:
|
||||
frappe.throw(_("Not a valid User Image."))
|
||||
|
|
@ -1044,6 +1054,11 @@ def get_role_profile(role_profile):
|
|||
roles = frappe.get_doc('Role Profile', {'role_profile': role_profile})
|
||||
return roles.roles
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_module_profile(module_profile):
|
||||
module_profile = frappe.get_doc('Module Profile', {'module_profile_name': module_profile})
|
||||
return module_profile.get('block_modules')
|
||||
|
||||
def update_roles(role_profile):
|
||||
users = frappe.get_all('User', filters={'role_profile_name': role_profile})
|
||||
role_profile = frappe.get_doc('Role Profile', role_profile)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
from frappe.core.doctype.user_permission.user_permission import add_user_permissions
|
||||
from frappe.permissions import has_user_permission
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
|
@ -10,7 +11,12 @@ import unittest
|
|||
class TestUserPermission(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.db.sql("""DELETE FROM `tabUser Permission`
|
||||
WHERE `user` in ('test_bulk_creation_update@example.com', 'test_user_perm1@example.com')""")
|
||||
WHERE `user` in (
|
||||
'test_bulk_creation_update@example.com',
|
||||
'test_user_perm1@example.com',
|
||||
'nested_doc_user@example.com')""")
|
||||
frappe.delete_doc_if_exists("DocType", "Person")
|
||||
frappe.db.sql_ddl("DROP TABLE IF EXISTS `tabPerson`")
|
||||
|
||||
def test_default_user_permission_validation(self):
|
||||
user = create_user('test_default_permission@example.com')
|
||||
|
|
@ -108,6 +114,45 @@ class TestUserPermission(unittest.TestCase):
|
|||
self.assertIsNone(removed_applicable_second)
|
||||
self.assertEquals(is_created, 1)
|
||||
|
||||
def test_user_perm_for_nested_doctype(self):
|
||||
"""Test if descendants' visibility is controlled for a nested DocType."""
|
||||
from frappe.core.doctype.doctype.test_doctype import new_doctype
|
||||
|
||||
user = create_user("nested_doc_user@example.com", "Blogger")
|
||||
if not frappe.db.exists("DocType", "Person"):
|
||||
doc = new_doctype("Person",
|
||||
fields=[
|
||||
{
|
||||
"label": "Person Name",
|
||||
"fieldname": "person_name",
|
||||
"fieldtype": "Data"
|
||||
}
|
||||
], unique=0)
|
||||
doc.is_tree = 1
|
||||
doc.insert()
|
||||
|
||||
parent_record = frappe.get_doc(
|
||||
{"doctype": "Person", "person_name": "Parent", "is_group": 1}
|
||||
).insert()
|
||||
|
||||
child_record = frappe.get_doc(
|
||||
{"doctype": "Person", "person_name": "Child", "is_group": 0, "parent_person": parent_record.name}
|
||||
).insert()
|
||||
|
||||
add_user_permissions(get_params(user, "Person", parent_record.name))
|
||||
|
||||
# check if adding perm on a group record, makes child record visible
|
||||
self.assertTrue(has_user_permission(frappe.get_doc("Person", parent_record.name), user.name))
|
||||
self.assertTrue(has_user_permission(frappe.get_doc("Person", child_record.name), user.name))
|
||||
|
||||
frappe.db.set_value("User Permission", {"allow": "Person", "for_value": parent_record.name}, "hide_descendants", 1)
|
||||
frappe.cache().delete_value("user_permissions")
|
||||
|
||||
# check if adding perm on a group record with hide_descendants enabled,
|
||||
# hides child records
|
||||
self.assertTrue(has_user_permission(frappe.get_doc("Person", parent_record.name), user.name))
|
||||
self.assertFalse(has_user_permission(frappe.get_doc("Person", child_record.name), user.name))
|
||||
|
||||
def create_user(email, role="System Manager"):
|
||||
''' create user with role system manager '''
|
||||
if frappe.db.exists('User', email):
|
||||
|
|
@ -119,7 +164,7 @@ def create_user(email, role="System Manager"):
|
|||
user.add_roles(role)
|
||||
return user
|
||||
|
||||
def get_params(user, doctype, docname, is_default=0, applicable=None):
|
||||
def get_params(user, doctype, docname, is_default=0, hide_descendants=0, applicable=None):
|
||||
''' Return param to insert '''
|
||||
param = {
|
||||
"user": user.name,
|
||||
|
|
@ -127,7 +172,8 @@ def get_params(user, doctype, docname, is_default=0, applicable=None):
|
|||
"docname":docname,
|
||||
"is_default": is_default,
|
||||
"apply_to_all_doctypes": 1,
|
||||
"applicable_doctypes": []
|
||||
"applicable_doctypes": [],
|
||||
"hide_descendants": hide_descendants
|
||||
}
|
||||
if applicable:
|
||||
param.update({"apply_to_all_doctypes": 0})
|
||||
|
|
|
|||
|
|
@ -26,11 +26,15 @@ frappe.ui.form.on('User Permission', {
|
|||
() => frappe.set_route('query-report', 'Permitted Documents For User',
|
||||
{ user: frm.doc.user }));
|
||||
frm.trigger('set_applicable_for_constraint');
|
||||
frm.trigger('toggle_hide_descendants');
|
||||
},
|
||||
|
||||
allow: frm => {
|
||||
if(frm.doc.for_value) {
|
||||
frm.set_value('for_value', null);
|
||||
if (frm.doc.allow) {
|
||||
if (frm.doc.for_value) {
|
||||
frm.set_value('for_value', null);
|
||||
}
|
||||
frm.trigger('toggle_hide_descendants');
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -43,6 +47,11 @@ frappe.ui.form.on('User Permission', {
|
|||
if (frm.doc.apply_to_all_doctypes) {
|
||||
frm.set_value('applicable_for', null);
|
||||
}
|
||||
},
|
||||
|
||||
toggle_hide_descendants: frm => {
|
||||
let show = frappe.boot.nested_set_doctypes.includes(frm.doc.allow);
|
||||
frm.toggle_display('hide_descendants', show);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,330 +1,116 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2017-07-17 14:25:27.881871",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"user",
|
||||
"allow",
|
||||
"column_break_3",
|
||||
"for_value",
|
||||
"is_default",
|
||||
"advanced_control_section",
|
||||
"apply_to_all_doctypes",
|
||||
"applicable_for",
|
||||
"column_break_9",
|
||||
"hide_descendants"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "User",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "User",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "allow",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Allow",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "for_value",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 1,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "For Value",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "allow",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"default": "0",
|
||||
"fieldname": "is_default",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Is Default",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Is Default"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "advanced_control_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Advanced Control",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Advanced Control"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "apply_to_all_doctypes",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Apply To All Document Types",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Apply To All Document Types"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:!doc.apply_to_all_doctypes",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "applicable_for",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Applicable For",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Hide descendant records of <b>For Value</b>.",
|
||||
"fieldname": "hide_descendants",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Hide Descendants"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-04-16 19:17:23.644724",
|
||||
"links": [],
|
||||
"modified": "2021-01-21 18:14:10.839381",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "User Permission",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "user",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -49,7 +49,8 @@ class UserPermission(Document):
|
|||
'name': ['!=', self.name]
|
||||
}, or_filters={
|
||||
'applicable_for': cstr(self.applicable_for),
|
||||
'apply_to_all_doctypes': 1
|
||||
'apply_to_all_doctypes': 1,
|
||||
'hide_descendants': cstr(self.hide_descendants)
|
||||
}, limit=1)
|
||||
if overlap_exists:
|
||||
ref_link = frappe.get_desk_link(self.doctype, overlap_exists[0].name)
|
||||
|
|
@ -91,13 +92,13 @@ def get_user_permissions(user=None):
|
|||
|
||||
try:
|
||||
for perm in frappe.get_all('User Permission',
|
||||
fields=['allow', 'for_value', 'applicable_for', 'is_default'],
|
||||
fields=['allow', 'for_value', 'applicable_for', 'is_default', 'hide_descendants'],
|
||||
filters=dict(user=user)):
|
||||
|
||||
meta = frappe.get_meta(perm.allow)
|
||||
add_doc_to_perm(perm, perm.for_value, perm.is_default)
|
||||
|
||||
if meta.is_nested_set():
|
||||
if meta.is_nested_set() and not perm.hide_descendants:
|
||||
decendants = frappe.db.get_descendants(perm.allow, perm.for_value)
|
||||
for doc in decendants:
|
||||
add_doc_to_perm(perm, doc, False)
|
||||
|
|
@ -172,8 +173,8 @@ def check_applicable_doc_perm(user, doctype, docname):
|
|||
"allow": doctype,
|
||||
"for_value":docname,
|
||||
})
|
||||
for d in data:
|
||||
applicable.append(d.applicable_for)
|
||||
for permission in data:
|
||||
applicable.append(permission.applicable_for)
|
||||
return applicable
|
||||
|
||||
|
||||
|
|
@ -194,7 +195,8 @@ def add_user_permissions(data):
|
|||
data = json.loads(data)
|
||||
data = frappe._dict(data)
|
||||
|
||||
d = check_applicable_doc_perm(data.user, data.doctype, data.docname)
|
||||
# get all doctypes on whom this permission is applied
|
||||
perm_applied_docs = check_applicable_doc_perm(data.user, data.doctype, data.docname)
|
||||
exists = frappe.db.exists("User Permission", {
|
||||
"user": data.user,
|
||||
"allow": data.doctype,
|
||||
|
|
@ -202,26 +204,27 @@ def add_user_permissions(data):
|
|||
"apply_to_all_doctypes": 1
|
||||
})
|
||||
if data.apply_to_all_doctypes == 1 and not exists:
|
||||
remove_applicable(d, data.user, data.doctype, data.docname)
|
||||
insert_user_perm(data.user, data.doctype, data.docname, data.is_default, apply_to_all = 1)
|
||||
remove_applicable(perm_applied_docs, data.user, data.doctype, data.docname)
|
||||
insert_user_perm(data.user, data.doctype, data.docname, data.is_default, data.hide_descendants, apply_to_all=1)
|
||||
return 1
|
||||
elif len(data.applicable_doctypes) > 0 and data.apply_to_all_doctypes != 1:
|
||||
remove_apply_to_all(data.user, data.doctype, data.docname)
|
||||
update_applicable(d, data.applicable_doctypes, data.user, data.doctype, data.docname)
|
||||
update_applicable(perm_applied_docs, data.applicable_doctypes, data.user, data.doctype, data.docname)
|
||||
for applicable in data.applicable_doctypes :
|
||||
if applicable not in d:
|
||||
insert_user_perm(data.user, data.doctype, data.docname, data.is_default, applicable = applicable)
|
||||
if applicable not in perm_applied_docs:
|
||||
insert_user_perm(data.user, data.doctype, data.docname, data.is_default, data.hide_descendants, applicable=applicable)
|
||||
elif exists:
|
||||
insert_user_perm(data.user, data.doctype, data.docname, data.is_default, applicable = applicable)
|
||||
insert_user_perm(data.user, data.doctype, data.docname, data.is_default, data.hide_descendants, applicable=applicable)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def insert_user_perm(user, doctype, docname, is_default=0, apply_to_all=None, applicable=None):
|
||||
def insert_user_perm(user, doctype, docname, is_default=0, hide_descendants=0, apply_to_all=None, applicable=None):
|
||||
user_perm = frappe.new_doc("User Permission")
|
||||
user_perm.user = user
|
||||
user_perm.allow = doctype
|
||||
user_perm.for_value = docname
|
||||
user_perm.is_default = is_default
|
||||
user_perm.hide_descendants = hide_descendants
|
||||
if applicable:
|
||||
user_perm.applicable_for = applicable
|
||||
user_perm.apply_to_all_doctypes = 0
|
||||
|
|
@ -229,8 +232,8 @@ def insert_user_perm(user, doctype, docname, is_default=0, apply_to_all=None, ap
|
|||
user_perm.apply_to_all_doctypes = 1
|
||||
user_perm.insert()
|
||||
|
||||
def remove_applicable(d, user, doctype, docname):
|
||||
for applicable_for in d:
|
||||
def remove_applicable(perm_applied_docs, user, doctype, docname):
|
||||
for applicable_for in perm_applied_docs:
|
||||
frappe.db.sql("""DELETE FROM `tabUser Permission`
|
||||
WHERE `user`=%s
|
||||
AND `applicable_for`=%s
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ frappe.listview_settings['User Permission'] = {
|
|||
dialog.set_df_property("is_default", "hidden", 1);
|
||||
dialog.set_df_property("apply_to_all_doctypes", "hidden", 1);
|
||||
dialog.set_df_property("applicable_doctypes", "hidden", 1);
|
||||
dialog.set_df_property("hide_descendants", "hidden", 1);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -54,6 +55,10 @@ frappe.listview_settings['User Permission'] = {
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldtype: "Section Break",
|
||||
hide_border: 1
|
||||
},
|
||||
{
|
||||
fieldname: 'is_default',
|
||||
label: __('Is Default'),
|
||||
|
|
@ -74,6 +79,19 @@ frappe.listview_settings['User Permission'] = {
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldtype: "Column Break"
|
||||
},
|
||||
{
|
||||
fieldname: 'hide_descendants',
|
||||
label: __('Hide Descendants'),
|
||||
fieldtype: 'Check',
|
||||
hidden: 1
|
||||
},
|
||||
{
|
||||
fieldtype: "Section Break",
|
||||
hide_border: 1
|
||||
},
|
||||
{
|
||||
label: __("Applicable Document Types"),
|
||||
fieldname: "applicable_doctypes",
|
||||
|
|
@ -214,6 +232,9 @@ frappe.listview_settings['User Permission'] = {
|
|||
dialog.set_df_property("is_default", "hidden", 0);
|
||||
dialog.set_df_property("apply_to_all_doctypes", "hidden", 0);
|
||||
dialog.set_value("apply_to_all_doctypes", "checked", 1);
|
||||
let show = frappe.boot.nested_set_doctypes.includes(dialog.get_value("doctype"));
|
||||
dialog.set_df_property("hide_descendants", "hidden", !show);
|
||||
dialog.refresh();
|
||||
},
|
||||
|
||||
on_docname_change: function(dialog, options, applicable) {
|
||||
|
|
@ -233,6 +254,7 @@ frappe.listview_settings['User Permission'] = {
|
|||
dialog.set_df_property("applicable_doctypes", "options", options);
|
||||
dialog.set_df_property("applicable_doctypes", "hidden", 1);
|
||||
}
|
||||
dialog.refresh();
|
||||
},
|
||||
|
||||
on_apply_to_all_doctypes_change: function(dialog, options) {
|
||||
|
|
@ -243,5 +265,6 @@ frappe.listview_settings['User Permission'] = {
|
|||
dialog.set_df_property("applicable_doctypes", "options", options);
|
||||
dialog.set_df_property("applicable_doctypes", "hidden", 1);
|
||||
}
|
||||
dialog.refresh_sections();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -455,11 +455,15 @@ class CustomizeForm(Document):
|
|||
self.fetch_to_customize()
|
||||
|
||||
def reset_customization(doctype):
|
||||
frappe.db.sql("""
|
||||
DELETE FROM `tabProperty Setter` WHERE doc_type=%s
|
||||
and `field_name`!='naming_series'
|
||||
and `property`!='options'
|
||||
""", doctype)
|
||||
setters = frappe.get_all("Property Setter", filters={
|
||||
'doc_type': doctype,
|
||||
'field_name': ['!=', 'naming_series'],
|
||||
'property': ['!=', 'options']
|
||||
}, pluck='name')
|
||||
|
||||
for setter in setters:
|
||||
frappe.delete_doc("Property Setter", setter)
|
||||
|
||||
frappe.clear_cache(doctype=doctype)
|
||||
|
||||
doctype_properties = {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ def get_event_conditions(doctype, filters=None):
|
|||
def get_events(doctype, start, end, field_map, filters=None, fields=None):
|
||||
|
||||
field_map = frappe._dict(json.loads(field_map))
|
||||
fields = frappe.parse_json(fields)
|
||||
|
||||
doc_meta = frappe.get_meta(doctype)
|
||||
for d in doc_meta.fields:
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ def has_permission(doc, ptype, user):
|
|||
if doc.report_name in allowed_reports:
|
||||
return True
|
||||
else:
|
||||
allowed_doctypes = [frappe.permissions.get_doctypes_with_read()]
|
||||
allowed_doctypes = frappe.permissions.get_doctypes_with_read()
|
||||
if doc.document_type in allowed_doctypes:
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ def install():
|
|||
|
||||
@frappe.whitelist()
|
||||
def update_genders():
|
||||
default_genders = [_("Male"), _("Female"), _("Other"),_("Transgender"), _("Genderqueer"), _("Non-Conforming"),_("Prefer not to say")]
|
||||
default_genders = ["Male", "Female", "Other","Transgender", "Genderqueer", "Non-Conforming","Prefer not to say"]
|
||||
records = [{'doctype': 'Gender', 'gender': d} for d in default_genders]
|
||||
for record in records:
|
||||
frappe.get_doc(record).insert(ignore_permissions=True, ignore_if_duplicate=True)
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_salutations():
|
||||
default_salutations = [_("Mr"), _("Ms"), _('Mx'), _("Dr"), _("Mrs"), _("Madam"), _("Miss"), _("Master"), _("Prof")]
|
||||
default_salutations = ["Mr", "Ms", 'Mx', "Dr", "Mrs", "Madam", "Miss", "Master", "Prof"]
|
||||
records = [{'doctype': 'Salutation', 'salutation': d} for d in default_salutations]
|
||||
for record in records:
|
||||
doc = frappe.new_doc(record.get("doctype"))
|
||||
|
|
|
|||
|
|
@ -54,6 +54,12 @@ def get_form_params():
|
|||
|
||||
fields = data["fields"]
|
||||
|
||||
if ((isinstance(fields, string_types) and fields == "*")
|
||||
or (isinstance(fields, (list, tuple)) and len(fields) == 1 and fields[0] == "*")):
|
||||
parenttype = data.doctype
|
||||
data["fields"] = frappe.db.get_table_columns(parenttype)
|
||||
fields = data["fields"]
|
||||
|
||||
for field in fields:
|
||||
key = field.split(" as ")[0]
|
||||
|
||||
|
|
@ -61,21 +67,24 @@ def get_form_params():
|
|||
if key.startswith('sum('): continue
|
||||
if key.startswith('avg('): continue
|
||||
|
||||
if "." in key:
|
||||
parenttype, fieldname = key.split(".")[0][4:-1], key.split(".")[1].strip("`")
|
||||
else:
|
||||
parenttype = data.doctype
|
||||
fieldname = field.strip("`")
|
||||
parenttype, fieldname = get_parent_dt_and_field(key, data)
|
||||
|
||||
df = frappe.get_meta(parenttype).get_field(fieldname)
|
||||
if fieldname == "*":
|
||||
# * inside list is not allowed with other fields
|
||||
fields.remove(field)
|
||||
|
||||
meta = frappe.get_meta(parenttype)
|
||||
df = meta.get_field(fieldname)
|
||||
|
||||
fieldname = df.fieldname if df else None
|
||||
report_hide = df.report_hide if df else None
|
||||
|
||||
# remove the field from the query if the report hide flag is set and current view is Report
|
||||
if report_hide and is_report:
|
||||
fields.remove(field)
|
||||
|
||||
if df and fieldname in [df.fieldname for df in meta.get_high_permlevel_fields()]:
|
||||
if df.get('permlevel') not in meta.get_permlevel_access(parenttype=data.doctype) and field in fields:
|
||||
fields.remove(field)
|
||||
|
||||
# queries must always be server side
|
||||
data.query = None
|
||||
|
|
@ -83,6 +92,16 @@ def get_form_params():
|
|||
|
||||
return data
|
||||
|
||||
def get_parent_dt_and_field(field, data):
|
||||
if "." in field:
|
||||
parenttype, fieldname = field.split(".")[0][4:-1], field.split(".")[1].strip("`")
|
||||
else:
|
||||
parenttype = data.doctype
|
||||
fieldname = field.strip("`")
|
||||
|
||||
return parenttype, fieldname
|
||||
|
||||
|
||||
def compress(data, args = {}):
|
||||
"""separate keys and values"""
|
||||
from frappe.desk.query_report import add_total_row
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ class AutoEmailReport(Document):
|
|||
|
||||
if self.format == 'HTML':
|
||||
columns, data = make_links(columns, data)
|
||||
|
||||
columns = update_field_types(columns)
|
||||
return self.get_html_table(columns, data)
|
||||
|
||||
elif self.format == 'XLSX':
|
||||
|
|
@ -252,5 +252,14 @@ def make_links(columns, data):
|
|||
elif col.fieldtype == "Dynamic Link":
|
||||
if col.options and row.get(col.fieldname) and row.get(col.options):
|
||||
row[col.fieldname] = get_link_to_form(row[col.options], row[col.fieldname])
|
||||
elif col.fieldtype == "Currency":
|
||||
row[col.fieldname] = frappe.format_value(row[col.fieldname], col)
|
||||
|
||||
return columns, data
|
||||
|
||||
def update_field_types(columns):
|
||||
for col in columns:
|
||||
if col.fieldtype in ("Link", "Dynamic Link", "Currency") and col.options != "Currency":
|
||||
col.fieldtype = "Data"
|
||||
col.options = ""
|
||||
return columns
|
||||
|
|
@ -2,34 +2,56 @@
|
|||
# License: GNU General Public License v3. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe, unittest
|
||||
from frappe.utils import getdate, add_days
|
||||
import unittest
|
||||
from random import choice
|
||||
|
||||
from frappe.email.doctype.newsletter.newsletter import confirmed_unsubscribe, send_scheduled_email
|
||||
from six.moves.urllib.parse import unquote
|
||||
import frappe
|
||||
from frappe.email.doctype.newsletter.newsletter import (
|
||||
confirmed_unsubscribe,
|
||||
send_scheduled_email,
|
||||
)
|
||||
from frappe.email.doctype.newsletter.newsletter import get_newsletter_list
|
||||
from frappe.email.queue import flush
|
||||
from frappe.utils import add_days, getdate
|
||||
|
||||
test_dependencies = ["Email Group"]
|
||||
emails = [
|
||||
"test_subscriber1@example.com",
|
||||
"test_subscriber2@example.com",
|
||||
"test_subscriber3@example.com",
|
||||
"test1@example.com",
|
||||
]
|
||||
|
||||
emails = ["test_subscriber1@example.com", "test_subscriber2@example.com",
|
||||
"test_subscriber3@example.com", "test1@example.com"]
|
||||
|
||||
class TestNewsletter(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.make_email_group()
|
||||
frappe.set_user("Administrator")
|
||||
frappe.db.sql("delete from `tabEmail Group Member`")
|
||||
|
||||
if not frappe.db.exists("Email Group", "_Test Email Group"):
|
||||
frappe.get_doc({"doctype": "Email Group", "title": "_Test Email Group"}).insert()
|
||||
|
||||
for email in emails:
|
||||
frappe.get_doc({
|
||||
"doctype": "Email Group Member",
|
||||
"email": email,
|
||||
"email_group": "_Test Email Group"
|
||||
}).insert()
|
||||
|
||||
def test_send(self):
|
||||
name = self.send_newsletter()
|
||||
self.send_newsletter()
|
||||
|
||||
email_queue_list = [frappe.get_doc('Email Queue', e.name) for e in frappe.get_all("Email Queue")]
|
||||
email_queue_list = [frappe.get_doc("Email Queue", e.name) for e in frappe.get_all("Email Queue")]
|
||||
self.assertEqual(len(email_queue_list), 4)
|
||||
recipients = [e.recipients[0].recipient for e in email_queue_list]
|
||||
for email in emails:
|
||||
self.assertTrue(email in recipients)
|
||||
|
||||
recipients = set([e.recipients[0].recipient for e in email_queue_list])
|
||||
self.assertTrue(set(emails).issubset(recipients))
|
||||
|
||||
def test_unsubscribe(self):
|
||||
# test unsubscribe
|
||||
name = self.send_newsletter()
|
||||
from frappe.email.queue import flush
|
||||
to_unsubscribe = choice(emails)
|
||||
group = frappe.get_all("Newsletter Email Group", filters={"parent": name}, fields=["email_group"])
|
||||
|
||||
flush(from_test=True)
|
||||
to_unsubscribe = unquote(frappe.local.flags.signed_query_string.split("email=")[1].split("&")[0])
|
||||
|
||||
|
|
@ -37,10 +59,12 @@ class TestNewsletter(unittest.TestCase):
|
|||
confirmed_unsubscribe(to_unsubscribe, email_group)
|
||||
|
||||
name = self.send_newsletter()
|
||||
|
||||
email_queue_list = [frappe.get_doc('Email Queue', e.name) for e in frappe.get_all("Email Queue")]
|
||||
email_queue_list = [
|
||||
frappe.get_doc("Email Queue", e.name) for e in frappe.get_all("Email Queue")
|
||||
]
|
||||
self.assertEqual(len(email_queue_list), 3)
|
||||
recipients = [e.recipients[0].recipient for e in email_queue_list]
|
||||
|
||||
for email in emails:
|
||||
if email != to_unsubscribe:
|
||||
self.assertTrue(email in recipients)
|
||||
|
|
@ -104,7 +128,7 @@ class TestNewsletter(unittest.TestCase):
|
|||
email_group = "_Test Email Group"
|
||||
if not frappe.db.exists("Email Group", email_group):
|
||||
frappe.get_doc("Email Group", email_group).insert()
|
||||
|
||||
|
||||
for email in emails:
|
||||
if not frappe.db.exists('Email Group Member', dict(email=email, email_group = email_group)):
|
||||
frappe.get_doc({
|
||||
|
|
@ -112,3 +136,27 @@ class TestNewsletter(unittest.TestCase):
|
|||
"email": email,
|
||||
"email_group": email_group
|
||||
}).insert()
|
||||
|
||||
def test_portal(self):
|
||||
self.send_newsletter(1)
|
||||
frappe.set_user("test1@example.com")
|
||||
newsletters = get_newsletter_list("Newsletter", None, None, 0)
|
||||
self.assertEqual(len(newsletters), 1)
|
||||
|
||||
def test_newsletter_context(self):
|
||||
context = frappe._dict()
|
||||
newsletter_name = self.send_newsletter(1)
|
||||
frappe.set_user("test2@example.com")
|
||||
doc = frappe.get_doc("Newsletter", newsletter_name)
|
||||
doc.get_context(context)
|
||||
self.assertEqual(context.no_cache, 1)
|
||||
self.assertTrue("attachments" not in list(context))
|
||||
|
||||
def test_schedule_send(self):
|
||||
self.send_newsletter(schedule_send=add_days(getdate(), -1))
|
||||
|
||||
email_queue_list = [frappe.get_doc('Email Queue', e.name) for e in frappe.get_all("Email Queue")]
|
||||
self.assertEqual(len(email_queue_list), 4)
|
||||
recipients = [e.recipients[0].recipient for e in email_queue_list]
|
||||
for email in emails:
|
||||
self.assertTrue(email in recipients)
|
||||
|
|
|
|||
|
|
@ -450,6 +450,25 @@ class Meta(Document):
|
|||
|
||||
return self.high_permlevel_fields
|
||||
|
||||
def get_permlevel_access(self, permission_type='read', parenttype=None):
|
||||
has_access_to = []
|
||||
roles = frappe.get_roles()
|
||||
for perm in self.get_permissions(parenttype):
|
||||
if perm.role in roles and perm.permlevel > 0 and perm.get(permission_type):
|
||||
if perm.permlevel not in has_access_to:
|
||||
has_access_to.append(perm.permlevel)
|
||||
|
||||
return has_access_to
|
||||
|
||||
def get_permissions(self, parenttype=None):
|
||||
if self.istable and parenttype:
|
||||
# use parent permissions
|
||||
permissions = frappe.get_meta(parenttype).permissions
|
||||
else:
|
||||
permissions = self.get('permissions', [])
|
||||
|
||||
return permissions
|
||||
|
||||
def get_dashboard_data(self):
|
||||
'''Returns dashboard setup related to this doctype.
|
||||
|
||||
|
|
|
|||
|
|
@ -398,7 +398,8 @@ def set_user_permission_if_allowed(doctype, name, user, with_message=False):
|
|||
if get_role_permissions(frappe.get_meta(doctype), user).set_user_permissions!=1:
|
||||
add_user_permission(doctype, name, user)
|
||||
|
||||
def add_user_permission(doctype, name, user, ignore_permissions=False, applicable_for=None, is_default=0):
|
||||
def add_user_permission(doctype, name, user, ignore_permissions=False, applicable_for=None,
|
||||
is_default=0, hide_descendants=0):
|
||||
'''Add user permission'''
|
||||
from frappe.core.doctype.user_permission.user_permission import user_permission_exists
|
||||
|
||||
|
|
@ -413,6 +414,7 @@ def add_user_permission(doctype, name, user, ignore_permissions=False, applicabl
|
|||
for_value=name,
|
||||
is_default=is_default,
|
||||
applicable_for=applicable_for,
|
||||
hide_descendants=hide_descendants,
|
||||
)).insert(ignore_permissions=ignore_permissions)
|
||||
|
||||
def remove_user_permission(doctype, name, user):
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@
|
|||
"public/js/frappe/router_history.js",
|
||||
"public/js/frappe/defaults.js",
|
||||
"public/js/frappe/roles_editor.js",
|
||||
"public/js/frappe/module_editor.js",
|
||||
"public/js/frappe/microtemplate.js",
|
||||
|
||||
"public/js/frappe/ui/page.html",
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ frappe.ui.form.ControlGeolocation = frappe.ui.form.ControlData.extend({
|
|||
this.map_area.prependTo($input_wrapper);
|
||||
this.$wrapper.find('.control-input').addClass("hidden");
|
||||
|
||||
if ($input_wrapper.is(':visible')) {
|
||||
if (this.frm) {
|
||||
this.make_map();
|
||||
} else {
|
||||
$(document).on('frappe.ui.Dialog:shown', () => {
|
||||
|
|
|
|||
39
frappe/public/js/frappe/module_editor.js
Normal file
39
frappe/public/js/frappe/module_editor.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
frappe.ModuleEditor = Class.extend({
|
||||
init: function(frm, wrapper) {
|
||||
this.wrapper = $('<div class="row module-block-list"></div>').appendTo(wrapper);
|
||||
this.frm = frm;
|
||||
this.make();
|
||||
},
|
||||
make: function() {
|
||||
var me = this;
|
||||
this.frm.doc.__onload.all_modules.forEach(function(m) {
|
||||
$(repl('<div class="col-sm-6"><div class="checkbox">\
|
||||
<label><input type="checkbox" class="block-module-check" data-module="%(module)s">\
|
||||
%(module)s</label></div></div>', {module: m})).appendTo(me.wrapper);
|
||||
});
|
||||
this.bind();
|
||||
},
|
||||
refresh: function() {
|
||||
var me = this;
|
||||
this.wrapper.find(".block-module-check").prop("checked", true);
|
||||
$.each(this.frm.doc.block_modules, function(i, d) {
|
||||
me.wrapper.find(".block-module-check[data-module='"+ d.module +"']").prop("checked", false);
|
||||
});
|
||||
},
|
||||
bind: function() {
|
||||
var me = this;
|
||||
this.wrapper.on("change", ".block-module-check", function() {
|
||||
var module = $(this).attr('data-module');
|
||||
if ($(this).prop("checked")) {
|
||||
// remove from block_modules
|
||||
me.frm.doc.block_modules = $.map(me.frm.doc.block_modules || [], function(d) {
|
||||
if (d.module != module) {
|
||||
return d;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
me.frm.add_child("block_modules", {"module": module});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -708,10 +708,19 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
}
|
||||
},
|
||||
|
||||
setup_earlier_reply: function() {
|
||||
get_default_outgoing_email_account_signature: function() {
|
||||
return frappe.db.get_value('Email Account', { 'default_outgoing': 1, 'add_signature': 1 }, 'signature');
|
||||
},
|
||||
|
||||
setup_earlier_reply: async function() {
|
||||
let fields = this.dialog.fields_dict;
|
||||
let signature = frappe.boot.user.email_signature || "";
|
||||
|
||||
if (!signature) {
|
||||
const res = await this.get_default_outgoing_email_account_signature();
|
||||
signature = res.message.signature;
|
||||
}
|
||||
|
||||
if(!frappe.utils.is_html(signature)) {
|
||||
signature = signature.replace(/\n/g, "<br>");
|
||||
}
|
||||
|
|
@ -792,4 +801,3 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
return text.replace(/\n{3,}/g, '\n\n');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,14 @@ import frappe, unittest
|
|||
from frappe.model.db_query import DatabaseQuery
|
||||
from frappe.desk.reportview import get_filters_cond
|
||||
|
||||
from frappe.core.page.permission_manager.permission_manager import update, reset, add
|
||||
from frappe.permissions import add_user_permission, clear_user_permissions_for_doctype
|
||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||
from frappe.handler import execute_cmd
|
||||
|
||||
test_dependencies = ['User', 'Blog Post']
|
||||
from frappe.utils.testutils import add_custom_field, clear_custom_fields
|
||||
|
||||
test_dependencies = ['User', 'Blog Post', 'Blog Category', 'Blogger']
|
||||
|
||||
class TestReportview(unittest.TestCase):
|
||||
def test_basic(self):
|
||||
|
|
@ -355,6 +360,79 @@ class TestReportview(unittest.TestCase):
|
|||
owners = DatabaseQuery("DocType").execute(filters={"name": "DocType"}, pluck="owner")
|
||||
self.assertEqual(owners, ["Administrator"])
|
||||
|
||||
def test_reportview_get(self):
|
||||
user = frappe.get_doc("User", "test@example.com")
|
||||
add_child_table_to_blog_post()
|
||||
|
||||
user_roles = frappe.get_roles()
|
||||
user.remove_roles(*user_roles)
|
||||
user.add_roles("Blogger")
|
||||
|
||||
make_property_setter("Blog Post", "published", "permlevel", 1, "Int")
|
||||
reset("Blog Post")
|
||||
add("Blog Post", "Website Manager", 1)
|
||||
update("Blog Post", "Website Manager", 1, "write", 1)
|
||||
|
||||
frappe.set_user(user.name)
|
||||
|
||||
frappe.local.request = frappe._dict()
|
||||
frappe.local.request.method = "POST"
|
||||
|
||||
frappe.local.form_dict = frappe._dict({
|
||||
"doctype": "Blog Post",
|
||||
"fields": ["published", "title", "`tabTest Child`.`test_field`"],
|
||||
})
|
||||
|
||||
# even if * is passed, fields which are not accessible should be filtered out
|
||||
response = execute_cmd("frappe.desk.reportview.get")
|
||||
self.assertListEqual(response["keys"], ["title"])
|
||||
frappe.local.form_dict = frappe._dict({
|
||||
"doctype": "Blog Post",
|
||||
"fields": ["*"],
|
||||
})
|
||||
|
||||
response = execute_cmd("frappe.desk.reportview.get")
|
||||
self.assertNotIn("published", response["keys"])
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
user.add_roles("Website Manager")
|
||||
frappe.set_user(user.name)
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
# Admin should be able to see access all fields
|
||||
frappe.local.form_dict = frappe._dict({
|
||||
"doctype": "Blog Post",
|
||||
"fields": ["published", "title", "`tabTest Child`.`test_field`"],
|
||||
})
|
||||
|
||||
response = execute_cmd("frappe.desk.reportview.get")
|
||||
self.assertListEqual(response["keys"], ['published', 'title', 'test_field'])
|
||||
|
||||
# reset user roles
|
||||
user.remove_roles("Blogger", "Website Manager")
|
||||
user.add_roles(*user_roles)
|
||||
|
||||
|
||||
def add_child_table_to_blog_post():
|
||||
child_table = frappe.get_doc({
|
||||
'doctype': 'DocType',
|
||||
'istable': 1,
|
||||
'custom': 1,
|
||||
'name': 'Test Child',
|
||||
'module': 'Custom',
|
||||
'autoname': 'Prompt',
|
||||
'fields': [{
|
||||
'fieldname': 'test_field',
|
||||
'fieldtype': 'Data',
|
||||
'permlevel': 1
|
||||
}],
|
||||
})
|
||||
|
||||
child_table.insert(ignore_permissions=True, ignore_if_duplicate=True)
|
||||
clear_custom_fields('Blog Post')
|
||||
add_custom_field('Blog Post', 'child_table', 'Table', child_table.name)
|
||||
|
||||
def create_event(subject="_Test Event", starts_on=None):
|
||||
""" create a test event """
|
||||
|
||||
|
|
|
|||
|
|
@ -155,14 +155,22 @@ def get_time_zone():
|
|||
|
||||
return frappe.cache().get_value("time_zone", _get_time_zone)
|
||||
|
||||
def convert_utc_to_user_timezone(utc_timestamp):
|
||||
def convert_utc_to_timezone(utc_timestamp, time_zone):
|
||||
from pytz import timezone, UnknownTimeZoneError
|
||||
utcnow = timezone('UTC').localize(utc_timestamp)
|
||||
try:
|
||||
return utcnow.astimezone(timezone(get_time_zone()))
|
||||
return utcnow.astimezone(timezone(time_zone))
|
||||
except UnknownTimeZoneError:
|
||||
return utcnow
|
||||
|
||||
def get_datetime_in_timezone(time_zone):
|
||||
utc_timestamp = datetime.datetime.utcnow()
|
||||
return convert_utc_to_timezone(utc_timestamp, time_zone)
|
||||
|
||||
def convert_utc_to_user_timezone(utc_timestamp):
|
||||
time_zone = get_time_zone()
|
||||
return convert_utc_to_timezone(utc_timestamp, time_zone)
|
||||
|
||||
def now():
|
||||
"""return current datetime as yyyy-mm-dd hh:mm:ss"""
|
||||
if frappe.flags.current_date:
|
||||
|
|
|
|||
|
|
@ -230,12 +230,19 @@ def update_oauth_user(user, data, provider):
|
|||
|
||||
save = True
|
||||
user = frappe.new_doc("User")
|
||||
|
||||
gender = (data.get("gender") or "").title()
|
||||
|
||||
if not frappe.db.exists("Gender", gender):
|
||||
doc = frappe.new_doc("Gender", {"gender": gender})
|
||||
doc.insert(ignore_permissions=True)
|
||||
|
||||
user.update({
|
||||
"doctype":"User",
|
||||
"first_name": get_first_name(data),
|
||||
"last_name": get_last_name(data),
|
||||
"email": get_email(data),
|
||||
"gender": (data.get("gender") or "").title(),
|
||||
"gender": gender,
|
||||
"enabled": 1,
|
||||
"new_password": frappe.generate_hash(get_email(data)),
|
||||
"location": data.get("location"),
|
||||
|
|
|
|||
|
|
@ -223,6 +223,7 @@ VALID_UTILS = (
|
|||
"get_last_day_of_week",
|
||||
"get_last_day",
|
||||
"get_time",
|
||||
"get_datetime_in_timezone",
|
||||
"get_datetime_str",
|
||||
"get_date_str",
|
||||
"get_time_str",
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ pypng==0.0.20
|
|||
PyQRCode==1.2.1
|
||||
python-dateutil==2.8.1
|
||||
pytz==2019.3
|
||||
PyYAML==5.3.1
|
||||
PyYAML==5.4
|
||||
rauth==0.7.3
|
||||
redis==3.5.3
|
||||
requests-oauthlib==1.3.0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue