소스 검색

[feature] domainification for frappe (#3227)

* [feature] domainify doctype, pages and roles

* [minor] added get active domain method to __init__

* [fixes] minor fixes in domainification and added test cases

* [minor] added checkbox-editor class to render the domains

* [minor] added checkbox_selector option

* [minor] added or_filters to filter restrict to domain doctype and roles

* [minor] fixed codacy errors
version-14
Makarand Bauskar 8 년 전
committed by Rushabh Mehta
부모
커밋
17a09e4b70
26개의 변경된 파일969개의 추가작업 그리고 20개의 파일을 삭제
  1. +15
    -1
      frappe/__init__.py
  2. +2
    -0
      frappe/boot.py
  3. +90
    -3
      frappe/core/doctype/doctype/doctype.json
  4. +1
    -1
      frappe/core/doctype/doctype/test_doctype.py
  5. +0
    -0
      frappe/core/doctype/domain/__init__.py
  6. +8
    -0
      frappe/core/doctype/domain/domain.js
  7. +95
    -0
      frappe/core/doctype/domain/domain.json
  8. +10
    -0
      frappe/core/doctype/domain/domain.py
  9. +10
    -0
      frappe/core/doctype/domain/test_domain.py
  10. +0
    -0
      frappe/core/doctype/domain_settings/__init__.py
  11. +57
    -0
      frappe/core/doctype/domain_settings/domain_settings.js
  12. +153
    -0
      frappe/core/doctype/domain_settings/domain_settings.json
  13. +12
    -0
      frappe/core/doctype/domain_settings/domain_settings.py
  14. +0
    -0
      frappe/core/doctype/has_domain/__init__.py
  15. +72
    -0
      frappe/core/doctype/has_domain/has_domain.json
  16. +10
    -0
      frappe/core/doctype/has_domain/has_domain.py
  17. +42
    -1
      frappe/core/doctype/page/page.json
  18. +36
    -1
      frappe/core/doctype/role/role.json
  19. +11
    -2
      frappe/core/doctype/user/user.py
  20. +21
    -4
      frappe/core/page/permission_manager/permission_manager.py
  21. +2
    -1
      frappe/custom/doctype/customize_form/customize_form.js
  22. +14
    -1
      frappe/desk/doctype/desktop_icon/desktop_icon.py
  23. +31
    -5
      frappe/desk/moduleview.py
  24. +1
    -0
      frappe/public/build.json
  25. +140
    -0
      frappe/public/js/frappe/checkbox_editor.js
  26. +136
    -0
      frappe/tests/test_domainification.py

+ 15
- 1
frappe/__init__.py 파일 보기

@@ -1340,4 +1340,18 @@ def safe_eval(code, eval_globals=None, eval_locals=None):

eval_globals.update(whitelisted_globals)

return eval(code, eval_globals, eval_locals)
return eval(code, eval_globals, eval_locals)

def get_active_domains():
""" get the domains set in the Domain Settings as active domain """

active_domains = cache().hget("domains", "active_domains") or None
if active_domains is None:
domains = get_all("Has Domain", filters={ "parent": "Domain Settings" },
fields=["domain"], distinct=True)

active_domains = [row.get("domain") for row in domains]
active_domains.append("")
cache().hset("domains", "active_domains", active_domains)

return active_domains

+ 2
- 0
frappe/boot.py 파일 보기

@@ -37,6 +37,8 @@ def get_bootinfo():
bootinfo.module_list = []
load_desktop_icons(bootinfo)
bootinfo.letter_heads = get_letter_heads()
bootinfo.active_domains = frappe.get_active_domains()
bootinfo.all_domains = [d.get("name") for d in frappe.get_all("Domain")]

bootinfo.module_app = frappe.local.module_app
bootinfo.single_types = frappe.db.sql_list("""select name from tabDocType


+ 90
- 3
frappe/core/doctype/doctype/doctype.json 파일 보기

@@ -15,6 +15,7 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -44,6 +45,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -75,6 +77,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -106,6 +109,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -138,6 +142,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -169,6 +174,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -196,6 +202,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -227,6 +234,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -258,6 +266,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -286,6 +295,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -315,6 +325,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -346,6 +357,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -374,6 +386,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -403,6 +416,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -434,6 +448,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -462,6 +477,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -493,6 +509,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -524,6 +541,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -552,6 +570,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -582,6 +601,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -611,6 +631,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -641,6 +662,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -672,6 +694,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -702,6 +725,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -733,6 +757,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -761,6 +786,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -793,6 +819,38 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "restrict_to_domain",
"fieldtype": "Link",
"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": "Restrict To Domain",
"length": 0,
"no_copy": 0,
"options": "Domain",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -821,6 +879,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -850,6 +909,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -880,6 +940,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -910,6 +971,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -940,6 +1002,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -970,6 +1033,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -999,6 +1063,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1028,6 +1093,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1060,6 +1126,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1088,6 +1155,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1116,6 +1184,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1144,6 +1213,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1173,6 +1243,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1203,6 +1274,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1231,6 +1303,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1261,6 +1334,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1289,6 +1363,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1318,6 +1393,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1349,6 +1425,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1380,6 +1457,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1408,6 +1486,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1438,6 +1517,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1468,6 +1548,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1497,6 +1578,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1527,6 +1609,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1558,6 +1641,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1586,6 +1670,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1616,6 +1701,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1647,6 +1733,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -1676,6 +1763,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1715,13 +1803,12 @@
"idx": 6,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-03-03 15:55:30.792653",
"modified_by": "Administrator",
"modified": "2017-05-03 16:15:40.198072",
"modified_by": "makarand@erpnext.com",
"module": "Core",
"name": "DocType",
"owner": "Administrator",


+ 1
- 1
frappe/core/doctype/doctype/test_doctype.py 파일 보기

@@ -28,4 +28,4 @@ class TestDocType(unittest.TestCase):
frappe.delete_doc("DocType", name)

doc = self.new_doctype(name).insert()
doc.delete()
doc.delete()

+ 0
- 0
frappe/core/doctype/domain/__init__.py 파일 보기


+ 8
- 0
frappe/core/doctype/domain/domain.js 파일 보기

@@ -0,0 +1,8 @@
// Copyright (c) 2017, Frappe Technologies and contributors
// For license information, please see license.txt

frappe.ui.form.on('Domain', {
refresh: function(frm) {

}
});

+ 95
- 0
frappe/core/doctype/domain/domain.json 파일 보기

@@ -0,0 +1,95 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:domain",
"beta": 0,
"creation": "2017-05-03 15:07:39.752820",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "domain",
"fieldtype": "Data",
"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": "Domain",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-05-09 11:39:03.877720",
"modified_by": "Administrator",
"module": "Core",
"name": "Domain",
"name_case": "",
"owner": "makarand@erpnext.com",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 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": 1,
"read_only": 1,
"read_only_onload": 0,
"search_fields": "domain",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "domain",
"track_changes": 0,
"track_seen": 0
}

+ 10
- 0
frappe/core/doctype/domain/domain.py 파일 보기

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, 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 Domain(Document):
pass

+ 10
- 0
frappe/core/doctype/domain/test_domain.py 파일 보기

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals

import frappe
import unittest

class TestDomain(unittest.TestCase):
pass

+ 0
- 0
frappe/core/doctype/domain_settings/__init__.py 파일 보기


+ 57
- 0
frappe/core/doctype/domain_settings/domain_settings.js 파일 보기

@@ -0,0 +1,57 @@
// Copyright (c) 2017, Frappe Technologies and contributors
// For license information, please see license.txt

frappe.ui.form.on('Domain Settings', {
onload: function(frm) {
let domains = $('<div class="domain-editor">')
.appendTo(frm.fields_dict.domains_html.wrapper);

if(!frm.domain_editor) {
frm.domain_editor = new frappe.DomainsEditor(domains, frm);
}

frm.domain_editor.show();
},

validate: function(frm) {
if(frm.domain_editor) {
frm.domain_editor.set_items_in_table();
}
},
});

frappe.DomainsEditor = frappe.CheckboxEditor.extend({
init: function(wrapper, frm) {
var opts = {};
$.extend(opts, {
wrapper: wrapper,
frm: frm,
field_mapper: {
child_table_field: "active_domains",
item_field: "domain",
cdt: "Has Domain"
},
attribute: 'data-domain',
checkbox_selector: false,
get_items: this.get_all_domains,
editor_template: this.get_template()
});

this._super(opts);
},

get_template: function() {
return `
<div class="user-role" data-domain="{{item}}">
<input type="checkbox" style="margin-top:0px;">
{{__(item)}}
</div>
`;
},

get_all_domains: function() {
// return all the domains available in the system
this.items = frappe.boot.all_domains;
this.render_items();
},
});

+ 153
- 0
frappe/core/doctype/domain_settings/domain_settings.json 파일 보기

@@ -0,0 +1,153 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-05-03 16:28:11.295095",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "domains",
"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": "Domains",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "domains_html",
"fieldtype": "HTML",
"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": "Domains",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "active_domains",
"fieldtype": "Table",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Active Domains",
"length": 0,
"no_copy": 0,
"options": "Has Domain",
"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,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-05-12 17:01:18.615000",
"modified_by": "Administrator",
"module": "Core",
"name": "Domain Settings",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

+ 12
- 0
frappe/core/doctype/domain_settings/domain_settings.py 파일 보기

@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, 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 DomainSettings(Document):
def on_update(self):
cache = frappe.cache()
cache.delete_key("domains", "active_domains")

+ 0
- 0
frappe/core/doctype/has_domain/__init__.py 파일 보기


+ 72
- 0
frappe/core/doctype/has_domain/has_domain.json 파일 보기

@@ -0,0 +1,72 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-05-03 15:20:22.326623",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "domain",
"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": "Domain",
"length": 0,
"no_copy": 0,
"options": "Domain",
"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,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-05-04 11:05:54.750351",
"modified_by": "Administrator",
"module": "Core",
"name": "Has Domain",
"name_case": "",
"owner": "makarand@erpnext.com",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

+ 10
- 0
frappe/core/doctype/has_domain/has_domain.py 파일 보기

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, 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 HasDomain(Document):
pass

+ 42
- 1
frappe/core/doctype/page/page.json 파일 보기

@@ -14,6 +14,7 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -43,6 +44,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -72,6 +74,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -102,6 +105,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -130,6 +134,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -158,6 +163,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -185,6 +191,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -216,6 +223,38 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "restrict_to_domain",
"fieldtype": "Link",
"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": "Restrict To Domain",
"length": 0,
"no_copy": 0,
"options": "Domain",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -247,6 +286,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -274,6 +314,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -317,7 +358,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-04-12 16:39:15.179130",
"modified": "2017-05-03 17:24:10.162110",
"modified_by": "Administrator",
"module": "Core",
"name": "Page",


+ 36
- 1
frappe/core/doctype/role/role.json 파일 보기

@@ -9,9 +9,11 @@
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -42,6 +44,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -72,6 +75,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -100,6 +104,37 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "restrict_to_domain",
"fieldtype": "Link",
"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": "Restrict To Domain",
"length": 0,
"no_copy": 0,
"options": "Domain",
"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,
"unique": 0
}
],
"has_web_view": 0,
@@ -113,7 +148,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-04-18 01:50:32.995937",
"modified": "2017-05-04 11:03:41.533058",
"modified_by": "Administrator",
"module": "Core",
"name": "Role",


+ 11
- 2
frappe/core/doctype/user/user.py 파일 보기

@@ -490,8 +490,17 @@ def get_timezones():
@frappe.whitelist()
def get_all_roles(arg=None):
"""return all roles"""
return [r[0] for r in frappe.db.sql("""select name from tabRole
where name not in ('Administrator', 'Guest', 'All') and not disabled order by name""")]
active_domains = frappe.get_active_domains()

roles = frappe.get_all("Role", filters={
"name": ("not in", "Administrator,Guest,All"),
"disabled": 0
}, or_filters={
"ifnull(restrict_to_domain, '')": "",
"restrict_to_domain": ("in", active_domains)
}, order_by="name")

return [ role.get("name") for role in roles ]

@frappe.whitelist()
def get_roles(arg=None):


+ 21
- 4
frappe/core/page/permission_manager/permission_manager.py 파일 보기

@@ -14,11 +14,28 @@ from frappe import _
def get_roles_and_doctypes():
frappe.only_for("System Manager")
send_translations(frappe.get_lang_dict("doctype", "DocPerm"))

active_domains = frappe.get_active_domains()

doctypes = frappe.get_all("DocType", filters={
"istable": 0,
"name": ("not in", "DocType"),
}, or_filters={
"ifnull(restrict_to_domain, '')": "",
"restrict_to_domain": ("in", active_domains)
}, fields=["name"])

roles = frappe.get_all("Role", filters={
"name": ("not in", "Administrator"),
"disabled": 0,
}, or_filters={
"ifnull(restrict_to_domain, '')": "",
"restrict_to_domain": ("in", active_domains)
}, fields=["name"])

return {
"doctypes": [d[0] for d in frappe.db.sql("""select name from `tabDocType` dt where
istable=0 and name not in ('DocType')""")],
"roles": [d[0] for d in frappe.db.sql("""select name from tabRole where
name != 'Administrator' and disabled=0""")]
"doctypes": [d.get("name") for d in doctypes],
"roles": [d.get("name") for d in roles]
}

@frappe.whitelist()


+ 2
- 1
frappe/custom/doctype/customize_form/customize_form.js 파일 보기

@@ -15,7 +15,8 @@ frappe.ui.form.on("Customize Form", {
['DocType', 'custom', '=', 0],
['DocType', 'name', 'not in', 'DocType, DocField, DocPerm, User, Role, Has Role, \
Page, Has Role, Module Def, Print Format, Report, Customize Form, \
Customize Form Field']
Customize Form Field'],
['DocType', 'restrict_to_domain', 'in', frappe.boot.active_domains]
]
};
});


+ 14
- 1
frappe/desk/doctype/desktop_icon/desktop_icon.py 파일 보기

@@ -32,27 +32,40 @@ def get_desktop_icons(user=None):
fields = ['module_name', 'hidden', 'label', 'link', 'type', 'icon', 'color',
'_doctype', '_report', 'idx', 'force_show', 'reverse', 'custom', 'standard', 'blocked']

active_domains = frappe.get_active_domains()

blocked_doctypes = frappe.get_all("DocType", filters={
"ifnull(restrict_to_domain, '')": ("not in", ",".join(active_domains))
}, fields=["name"])

blocked_doctypes = [ d.get("name") for d in blocked_doctypes ]

standard_icons = frappe.db.get_all('Desktop Icon',
fields=fields, filters={'standard': 1})

standard_map = {}
for icon in standard_icons:
if icon._doctype in blocked_doctypes:
icon.blocked = 1
standard_map[icon.module_name] = icon

user_icons = frappe.db.get_all('Desktop Icon', fields=fields,
filters={'standard': 0, 'owner': user})


# update hidden property
for icon in user_icons:
standard_icon = standard_map.get(icon.module_name, None)

if icon._doctype in blocked_doctypes:
icon.blocked = 1

# override properties from standard icon
if standard_icon:
for key in ('route', 'label', 'color', 'icon', 'link'):
if standard_icon.get(key):
icon[key] = standard_icon.get(key)


if standard_icon.blocked:
icon.hidden = 1



+ 31
- 5
frappe/desk/moduleview.py 파일 보기

@@ -55,6 +55,27 @@ def build_config_from_file(module):
except ImportError:
pass

return filter_by_restrict_to_domain(data)

def filter_by_restrict_to_domain(data):
""" filter Pages and DocType depending on the Active Module(s) """
mapper = {
"page": "Page",
"doctype": "DocType"
}
active_domains = frappe.get_active_domains()

for d in data:
_items = []
for item in d.get("items", []):
doctype = mapper.get(item.get("type"))

doctype_domain = frappe.db.get_value(doctype, item.get("name"), "restrict_to_domain") or ''
if not doctype_domain or (doctype_domain in active_domains):
_items.append(item)

d.update({ "items": _items })

return data

def build_standard_config(module, doctype_info):
@@ -95,11 +116,16 @@ def add_custom_doctypes(data, doctype_info):

def get_doctype_info(module):
"""Returns list of non child DocTypes for given module."""
doctype_info = frappe.db.sql("""select "doctype" as type, name, description,
ifnull(document_type, "") as document_type, custom as custom,
issingle as issingle
from `tabDocType` where module=%s and istable=0
order by custom asc, document_type desc, name asc""", module, as_dict=True)
active_domains = frappe.get_active_domains()

doctype_info = frappe.get_all("DocType", filters={
"module": module,
"istable": 0
}, or_filters={
"ifnull(restrict_to_domain, '')": "",
"restrict_to_domain": ("in", active_domains)
}, fields=["'doctype' as type", "name", "description", "ifnull(document_type, '') as document_type",
"custom", "issingle"], order_by="custom asc, document_type desc, name asc")

for d in doctype_info:
d.description = _(d.description or "")


+ 1
- 0
frappe/public/build.json 파일 보기

@@ -90,6 +90,7 @@
"public/js/frappe/socketio_client.js",
"public/js/frappe/router.js",
"public/js/frappe/defaults.js",
"public/js/frappe/checkbox_editor.js",
"public/js/frappe/roles_editor.js",
"public/js/lib/microtemplate.js",



+ 140
- 0
frappe/public/js/frappe/checkbox_editor.js 파일 보기

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

// opts:
// frm
// wrapper
// get_items
// add_btn_label
// remove_btn_label
// field_mapper:
// cdt
// child_table_field
// item_field
// attribute

frappe.CheckboxEditor = Class.extend({
init: function(opts) {
$.extend(this, opts);

this.doctype = this.field_mapper.cdt;
this.fieldname = this.field_mapper.child_table_field;
this.item_fieldname = this.field_mapper.item_field;
$(this.wrapper).html('<div class="help">' + __("Loading") + '...</div>');

if(this.get_items) {
this.get_items();
}
},
render_items: function(callback) {
let me = this;
$(this.wrapper).empty();

if(this.checkbox_selector) {
let toolbar = $('<p><button class="btn btn-default btn-add btn-sm" style="margin-right: 5px;"></button>\
<button class="btn btn-sm btn-default btn-remove"></button></p>').appendTo($(this.wrapper));

toolbar.find(".btn-add")
.html(__(this.add_btn_label))
.on("click", function() {
$(me.wrapper).find('input[type="checkbox"]').each(function(i, check) {
if(!$(check).is(":checked")) {
check.checked = true;
}
});
});

toolbar.find(".btn-remove")
.html(__(this.remove_btn_label))
.on("click", function() {
$(me.wrapper).find('input[type="checkbox"]').each(function(i, check) {
if($(check).is(":checked")) {
check.checked = false;
}
});
});
}

$.each(this.items, function(i, item) {
$(me.wrapper).append(frappe.render(me.editor_template, {'item': item}));
});

$(this.wrapper).find('input[type="checkbox"]').change(function() {
if(me.fieldname && me.doctype && me.item_field) {
me.set_items_in_table();
me.frm.dirty();
}
});

callback && callback()
},
show: function() {
let me = this;

// uncheck all items
$(this.wrapper).find('input[type="checkbox"]')
.each(function(i, checkbox) { checkbox.checked = false; });

// set user items as checked
$.each((me.frm.doc[this.fieldname] || []), function(i, row) {
let selector = repl('[%(attribute)s="%(value)s"] input[type="checkbox"]', {
attribute: me.attribute,
value: row[me.item_fieldname]
});

let checkbox = $(me.wrapper)
.find(selector).get(0);
if(checkbox) checkbox.checked = true;
});
},

get_selected_unselected_items: function() {
let checked_items = [];
let unchecked_items = [];
let selector = repl('[%(attribute)s]', { attribute: this.attribute });
let me = this;

$(this.wrapper).find(selector).each(function() {
if($(this).find('input[type="checkbox"]:checked').length) {
checked_items.push($(this).attr(me.attribute));
} else {
unchecked_items.push($(this).attr(me.attribute));
}
});

return {
checked_items: checked_items,
unchecked_items: unchecked_items
}
},

set_items_in_table: function() {
let opts = this.get_selected_unselected_items();
let existing_items_map = {};
let existing_items_list = [];
let me = this;

$.each(me.frm.doc[this.fieldname] || [], function(i, row) {
existing_items_map[row[me.item_fieldname]] = row.name;
existing_items_list.push(row[me.item_fieldname]);
});

// remove unchecked items
$.each(opts.unchecked_items, function(i, item) {
if(existing_items_list.indexOf(item)!=-1) {
frappe.model.clear_doc(me.doctype, existing_items_map[item]);
}
});

// add new items that are checked
$.each(opts.checked_items, function(i, item) {
if(existing_items_list.indexOf(item)==-1) {
let row = frappe.model.add_child(me.frm.doc, me.doctype, me.fieldname);
row[me.item_fieldname] = item;
}
});

refresh_field(this.fieldname);
}
});

+ 136
- 0
frappe/tests/test_domainification.py 파일 보기

@@ -0,0 +1,136 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals

import unittest, frappe
from frappe.core.page.permission_manager.permission_manager import get_roles_and_doctypes
from frappe.desk.doctype.desktop_icon.desktop_icon import (get_desktop_icons, add_user_icon,
clear_desktop_icons_cache)

class TestDomainification(unittest.TestCase):
def setUp(self):
# create test domain
self.new_domain("_Test Domain 1")
self.new_domain("_Test Domain 2")

self.remove_from_active_domains(remove_all=True)
self.add_active_domain("_Test Domain 1")

def tearDown(self):
frappe.db.sql("delete from tabRole where name='_Test Role'")
frappe.db.sql("delete from tabDomain where name in ('_Test Domain 1', '_Test Domain 2')")
frappe.delete_doc('DocType', 'Test Domainification')

def add_active_domain(self, domain):
""" add domain in active domain """

if not domain:
return

domain_settings = frappe.get_doc("Domain Settings", "Domain Settings")
domain_settings.append("active_domains", { "domain": domain })
domain_settings.save()

def remove_from_active_domains(self, domain=None, remove_all=False):
""" remove domain from domain settings """
if not domain:
return

domain_settings = frappe.get_doc("Domain Settings", "Domain Settings")

if remove_all:
domain_settings.set("active_domains", [])
else:
to_remove = []
[ to_remove.append(row) for row in domain_settings.active_domains if row.domain == domain ]
[ domain_settings.remove(row) for row in to_remove ]

domain_settings.save()

def new_domain(self, domain):
# create new domain
frappe.get_doc({
"doctype": "Domain",
"domain": domain
}).insert()

def new_doctype(self, name):
return frappe.get_doc({
"doctype": "DocType",
"module": "Core",
"custom": 1,
"fields": [{"label": "Some Field", "fieldname": "some_fieldname", "fieldtype": "Data"}],
"permissions": [{"role": "System Manager", "read": 1}],
"name": name
})

def test_active_domains(self):
self.assertTrue("_Test Domain 1" in frappe.get_active_domains())
self.assertFalse("_Test Domain 2" in frappe.get_active_domains())

self.add_active_domain("_Test Domain 2")
self.assertTrue("_Test Domain 2" in frappe.get_active_domains())

self.remove_from_active_domains("_Test Domain 1")
self.assertTrue("_Test Domain 1" not in frappe.get_active_domains())

def test_doctype_and_role_domainification(self):
"""
test if doctype is hidden if the doctype's restrict to domain is not included
in active domains
"""

test_doctype = self.new_doctype("Test Domainification")
test_doctype.insert()

test_role = frappe.get_doc({
"doctype": "Role",
"role_name": "_Test Role"
}).insert()

# doctype should be hidden in desktop icon, role permissions
results = get_roles_and_doctypes()
self.assertTrue("Test Domainification" in results.get("doctypes"))
self.assertTrue("_Test Role" in results.get("roles"))

self.add_active_domain("_Test Domain 2")
test_doctype.restrict_to_domain = "_Test Domain 2"
test_doctype.save()

test_role.restrict_to_domain = "_Test Domain 2"
test_role.save()

results = get_roles_and_doctypes()
self.assertTrue("Test Domainification" in results.get("doctypes"))
self.assertTrue("_Test Role" in results.get("roles"))

self.remove_from_active_domains("_Test Domain 2")
results = get_roles_and_doctypes()

self.assertTrue("Test Domainification" not in results.get("doctypes"))
self.assertTrue("_Test Role" not in results.get("roles"))

def test_desktop_icon_for_domainification(self):
""" desktop icon should be hidden if doctype's restrict to domain is not in active domains """
test_doctype = self.new_doctype("Test Domainification")
test_doctype.restrict_to_domain = "_Test Domain 2"
test_doctype.insert()

self.add_active_domain("_Test Domain 2")
add_user_icon('Test Domainification')

icons = get_desktop_icons()

doctypes = [icon.get("_doctype") for icon in icons if icon.get("_doctype") == "Test Domainification" \
and icon.get("blocked") == 0]
self.assertTrue("Test Domainification" in doctypes)

# doctype should be hidden from the desk
self.remove_from_active_domains("_Test Domain 2")
clear_desktop_icons_cache() # clear cache to fetch the desktop icon according to new active domains
icons = get_desktop_icons()

doctypes = [icon.get("_doctype") for icon in icons if icon.get("_doctype") == "Test Domainification" \
and icon.get("blocked") == 0]
self.assertFalse("Test Domainification" in doctypes)

불러오는 중...
취소
저장