瀏覽代碼

FormMeta, Form Load, refactored client side Model and Meta - #478

version-14
Rushabh Mehta 11 年之前
committed by Anand Doshi
父節點
當前提交
d5d9c8c563
共有 41 個檔案被更改,包括 548 行新增513 行删除
  1. +10
    -11
      frappe/__init__.py
  2. +6
    -9
      frappe/boot.py
  3. +45
    -49
      frappe/core/doctype/doctype/doctype.py
  4. +29
    -27
      frappe/core/doctype/page/page.py
  5. +2
    -4
      frappe/core/doctype/user/user.js
  6. +3
    -5
      frappe/core/doctype/workflow/workflow.js
  7. +6
    -8
      frappe/core/page/permission_manager/permission_manager.js
  8. +1
    -1
      frappe/handler.py
  9. +10
    -0
      frappe/model/base_document.py
  10. +1
    -0
      frappe/model/code.py
  11. +2
    -0
      frappe/model/doctype.py
  12. +43
    -23
      frappe/model/document.py
  13. +2
    -0
      frappe/model/meta.py
  14. +1
    -1
      frappe/public/build.json
  15. +3
    -2
      frappe/public/js/frappe/form/control.js
  16. +1
    -1
      frappe/public/js/frappe/form/formatters.js
  17. +1
    -5
      frappe/public/js/frappe/form/grid.js
  18. +1
    -3
      frappe/public/js/frappe/form/linked_with.js
  19. +37
    -39
      frappe/public/js/frappe/form/save.js
  20. +1
    -1
      frappe/public/js/frappe/form/script_manager.js
  21. +2
    -2
      frappe/public/js/frappe/form/workflow.js
  22. +22
    -6
      frappe/public/js/frappe/model/create_new.js
  23. +9
    -2
      frappe/public/js/frappe/model/meta.js
  24. +31
    -58
      frappe/public/js/frappe/model/model.js
  25. +2
    -10
      frappe/public/js/frappe/model/perm.js
  26. +12
    -86
      frappe/public/js/frappe/model/sync.js
  27. +8
    -26
      frappe/public/js/frappe/model/workflow.js
  28. +1
    -2
      frappe/public/js/frappe/request.js
  29. +6
    -6
      frappe/public/js/frappe/views/reportview.js
  30. +2
    -9
      frappe/public/js/legacy/clientscriptAPI.js
  31. +20
    -57
      frappe/public/js/legacy/form.js
  32. +10
    -9
      frappe/public/js/legacy/handler.js
  33. +1
    -0
      frappe/tests/test_document.py
  34. +16
    -0
      frappe/tests/test_form_load.py
  35. +10
    -8
      frappe/utils/response.py
  36. +1
    -1
      frappe/website/doctype/blog_post/blog_post.js
  37. +19
    -19
      frappe/widgets/form/load.py
  38. +157
    -0
      frappe/widgets/form/meta.py
  39. +0
    -2
      frappe/widgets/form/run_method.py
  40. +0
    -2
      frappe/widgets/form/utils.py
  41. +14
    -19
      frappe/widgets/page.py

+ 10
- 11
frappe/__init__.py 查看文件

@@ -96,9 +96,9 @@ def init(site, sites_path=None):
local.site_path = os.path.join(sites_path, site)
local.message_log = []
local.debug_log = []
local.response = _dict({})
local.lang = "en"
local.request_method = request.method if request else None
local.response = _dict({"docs":[]})
local.conf = _dict(get_site_config())
local.initialised = True
local.flags = _dict({})
@@ -115,6 +115,15 @@ def init(site, sites_path=None):

setup_module_map()

def connect(site=None, db_name=None):
from database import Database
if site:
init(site)
local.db = Database(user=db_name or local.conf.db_name)
local.form_dict = _dict()
local.session = _dict()
set_user("Administrator")

def get_site_config(sites_path=None, site_path=None):
config = {}
@@ -200,16 +209,6 @@ def throw(msg, exc=ValidationError):
def create_folder(path):
if not os.path.exists(path): os.makedirs(path)
def connect(site=None, db_name=None):
from database import Database
if site:
init(site)
local.db = Database(user=db_name or local.conf.db_name)
local.response = _dict()
local.form_dict = _dict()
local.session = _dict()
set_user("Administrator")

def set_user(username):
from frappe.utils.user import User
local.session["user"] = username


+ 6
- 9
frappe/boot.py 查看文件

@@ -65,9 +65,6 @@ def get_bootinfo():
for method in hooks.boot_session or []:
frappe.get_attr(method)(bootinfo)
from frappe.model.utils import compress
bootinfo['docs'] = compress(bootinfo['docs'])

if bootinfo.lang:
bootinfo.lang = unicode(bootinfo.lang)
@@ -138,10 +135,10 @@ def add_home_page(bootinfo, doclist):
home_page = frappe.get_application_home_page(frappe.session.user)

try:
page_doclist = frappe.widgets.page.get(home_page)
page = frappe.widgets.page.get(home_page)
except (frappe.DoesNotExistError, frappe.PermissionError), e:
page_doclist = frappe.widgets.page.get('desktop')
bootinfo['home_page_html'] = page_doclist[0].content
bootinfo['home_page'] = page_doclist[0].name
doclist += page_doclist
frappe.message_log.pop()
page = frappe.widgets.page.get('desktop')
bootinfo['home_page'] = page.name
doclist.append(page)

+ 45
- 49
frappe/core/doctype/doctype/doctype.py 查看文件

@@ -9,23 +9,20 @@ import os

from frappe.utils import now, cint
from frappe.model import no_value_fields
from frappe.model.document import Document

class DocType:
def __init__(self, doc=None, doclist=[]):
self.doc = doc
self.doclist = doclist

class DocType(Document):
def validate(self):
if not frappe.conf.get("developer_mode"):
frappe.throw("Not in Developer Mode! Set in site_config.json")
for c in [".", "/", "#", "&", "=", ":", "'", '"']:
if c in self.doc.name:
if c in self.name:
frappe.msgprint(c + " not allowed in name", raise_exception=1)
self.validate_series()
self.scrub_field_names()
self.validate_title_field()
validate_fields(self.doclist.get({"doctype":"DocField"}))
validate_permissions(self.doclist.get({"doctype":"DocPerm"}))
validate_fields(self.get("fields"))
validate_permissions(self.get("permissions"))
self.make_amendable()
self.check_link_replacement_error()

@@ -33,14 +30,14 @@ class DocType:
if frappe.flags.in_import:
return
parent_list = frappe.db.sql("""SELECT parent
from tabDocField where fieldtype="Table" and options=%s""", self.doc.name)
from tabDocField where fieldtype="Table" and options=%s""", self.name)
for p in parent_list:
frappe.db.sql('UPDATE tabDocType SET modified=%s WHERE `name`=%s', (now(), p[0]))

def scrub_field_names(self):
restricted = ('name','parent','idx','owner','creation','modified','modified_by',
'parentfield','parenttype',"file_list")
for d in self.doclist:
for d in self.get("fields"):
if d.parent and d.fieldtype:
if (not d.fieldname):
if d.label:
@@ -52,16 +49,16 @@ class DocType:
def validate_title_field(self):
if self.doc.title_field and \
self.doc.title_field not in [d.fieldname for d in self.doclist.get({"doctype":"DocField"})]:
if self.title_field and \
self.title_field not in [d.fieldname for d in self.get("fields")]:
frappe.throw(_("Title field must be a valid fieldname"))
def validate_series(self, autoname=None, name=None):
if not autoname: autoname = self.doc.autoname
if not name: name = self.doc.name
if not autoname: autoname = self.autoname
if not name: name = self.name
if not autoname and self.doclist.get({"fieldname":"naming_series"}):
self.doc.autoname = "naming_series:"
if not autoname and self.get("fields", {"fieldname":"naming_series"}):
self.autoname = "naming_series:"
if autoname and (not autoname.startswith('field:')) and (not autoname.startswith('eval:')) \
and (not autoname=='Prompt') and (not autoname.startswith('naming_series:')):
@@ -72,10 +69,10 @@ class DocType:

def on_update(self):
from frappe.model.db_schema import updatedb
updatedb(self.doc.name)
updatedb(self.name)

self.change_modified_of_parent()
make_module_and_roles(self.doclist)
make_module_and_roles(self)
from frappe import conf
if (not frappe.flags.in_import) and conf.get('developer_mode') or 0:
@@ -83,53 +80,53 @@ class DocType:
self.make_controller_template()
# update index
if not self.doc.custom:
if not self.custom:
from frappe.model.code import load_doctype_module
module = load_doctype_module( self.doc.name, self.doc.module)
module = load_doctype_module( self.name, self.module)
if hasattr(module, "on_doctype_update"):
module.on_doctype_update()
frappe.clear_cache(doctype=self.doc.name)
frappe.clear_cache(doctype=self.name)

def check_link_replacement_error(self):
for d in self.doclist.get({"doctype":"DocField", "fieldtype":"Select"}):
for d in self.get("fields", {"fieldtype":"Select"}):
if (frappe.db.get_value("DocField", d.name, "options") or "").startswith("link:") \
and not d.options.startswith("link:"):
frappe.msgprint("link: type Select fields are getting replaced. Please check for %s" % d.label,
raise_exception=True)

def on_trash(self):
frappe.db.sql("delete from `tabCustom Field` where dt = %s", self.doc.name)
frappe.db.sql("delete from `tabCustom Script` where dt = %s", self.doc.name)
frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", self.doc.name)
frappe.db.sql("delete from `tabReport` where ref_doctype=%s", self.doc.name)
frappe.db.sql("delete from `tabCustom Field` where dt = %s", self.name)
frappe.db.sql("delete from `tabCustom Script` where dt = %s", self.name)
frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", self.name)
frappe.db.sql("delete from `tabReport` where ref_doctype=%s", self.name)
def before_rename(self, old, new, merge=False):
if merge:
frappe.throw(_("DocType can not be merged"))
def after_rename(self, old, new, merge=False):
if self.doc.issingle:
if self.issingle:
frappe.db.sql("""update tabSingles set doctype=%s where doctype=%s""", (new, old))
else:
frappe.db.sql("rename table `tab%s` to `tab%s`" % (old, new))
def export_doc(self):
from frappe.modules.export_file import export_to_files
export_to_files(record_list=[['DocType', self.doc.name]])
export_to_files(record_list=[['DocType', self.name]])
def import_doc(self):
from frappe.modules.import_module import import_from_files
import_from_files(record_list=[[self.doc.module, 'doctype', self.doc.name]])
import_from_files(record_list=[[self.module, 'doctype', self.name]])

def make_controller_template(self):
from frappe.modules import get_doc_path, get_module_path, scrub
pypath = os.path.join(get_doc_path(self.doc.module,
self.doc.doctype, self.doc.name), scrub(self.doc.name) + '.py')
pypath = os.path.join(get_doc_path(self.module,
self.doctype, self.name), scrub(self.name) + '.py')

if not os.path.exists(pypath):
# get app publisher for copyright
app = frappe.local.module_app[frappe.scrub(self.doc.module)]
app = frappe.local.module_app[frappe.scrub(self.module)]
if not app:
frappe.throw("App not found!")
app_publisher = frappe.get_hooks(hook="app_publisher", app_name=app)[0]
@@ -143,23 +140,22 @@ class DocType:
"""
if is_submittable is set, add amended_from docfields
"""
if self.doc.is_submittable:
if self.is_submittable:
if not frappe.db.sql("""select name from tabDocField
where fieldname = 'amended_from' and parent = %s""", self.doc.name):
new = self.doc.addchild('fields', 'DocField', self.doclist)
new.label = 'Amended From'
new.fieldtype = 'Link'
new.fieldname = 'amended_from'
new.options = self.doc.name
new.permlevel = 0
new.read_only = 1
new.print_hide = 1
new.no_copy = 1
new.idx = self.get_max_idx() + 1
where fieldname = 'amended_from' and parent = %s""", self.name):
self.append("fields", {
"label": "Amended From",
"fieldtype": "Link",
"fieldname": "amended_from",
"options": self.name,
"read_only": 1,
"print_hide": 1,
"no_copy": 1
})
def get_max_idx(self):
max_idx = frappe.db.sql("""select max(idx) from `tabDocField` where parent = %s""",
self.doc.name)
self.name)
return max_idx and max_idx[0][0] or 0

def validate_fields_for_doctype(doctype):
@@ -329,14 +325,14 @@ def validate_permissions(permissions, for_remove=False):
check_level_zero_is_set(d)
remove_rights_for_single(d)

def make_module_and_roles(doclist, perm_doctype="DocPerm"):
def make_module_and_roles(doc, perm_fieldname="permissions"):
try:
if not frappe.db.exists("Module Def", doclist[0].module):
m = frappe.bean({"doctype": "Module Def", "module_name": doclist[0].module})
if not frappe.db.exists("Module Def", doc.module):
m = frappe.bean({"doctype": "Module Def", "module_name": doc.module})
m.insert()
default_roles = ["Administrator", "Guest", "All"]
roles = [p.role for p in doclist.get({"doctype": perm_doctype})] + default_roles
roles = [p.role for p in doc.get(permissions)] + default_roles
for role in list(set(roles)):
if not frappe.db.exists("Role", role):


+ 29
- 27
frappe/core/doctype/page/page.py 查看文件

@@ -3,11 +3,9 @@

from __future__ import unicode_literals
import frappe
from frappe.model.document import Document

class DocType:
def __init__(self, d, dl):
self.doc, self.doclist = d,dl

class Page(Document):
def autoname(self):
"""
Creates a url friendly name for this page.
@@ -15,17 +13,17 @@ class DocType:
it will add name-1, name-2 etc.
"""
from frappe.utils import cint
if (self.doc.name and self.doc.name.startswith('New Page')) or not self.doc.name:
self.doc.name = self.doc.page_name.lower().replace('"','').replace("'",'').\
if (self.name and self.name.startswith('New Page')) or not self.name:
self.name = self.page_name.lower().replace('"','').replace("'",'').\
replace(' ', '-')[:20]
if frappe.db.exists('Page',self.doc.name):
if frappe.db.exists('Page',self.name):
cnt = frappe.db.sql("""select name from tabPage
where name like "%s-%%" order by name desc limit 1""" % self.doc.name)
where name like "%s-%%" order by name desc limit 1""" % self.name)
if cnt:
cnt = cint(cnt[0][0].split('-')[-1]) + 1
else:
cnt = 1
self.doc.name += '-' + str(cnt)
self.name += '-' + str(cnt)

# export
def on_update(self):
@@ -35,16 +33,16 @@ class DocType:
"""
from frappe import conf
from frappe.core.doctype.doctype.doctype import make_module_and_roles
make_module_and_roles(self.doclist, "Page Role")
make_module_and_roles(self, "roles")
if not frappe.flags.in_import and getattr(conf,'developer_mode', 0) and self.doc.standard=='Yes':
if not frappe.flags.in_import and getattr(conf,'developer_mode', 0) and self.standard=='Yes':
from frappe.modules.export_file import export_to_files
from frappe.modules import get_module_path, scrub
import os
export_to_files(record_list=[['Page', self.doc.name]])
export_to_files(record_list=[['Page', self.name]])
# write files
path = os.path.join(get_module_path(self.doc.module), 'page', scrub(self.doc.name), scrub(self.doc.name))
path = os.path.join(get_module_path(self.module), 'page', scrub(self.name), scrub(self.name))
# js
if not os.path.exists(path + '.js'):
@@ -55,35 +53,39 @@ class DocType:
title: '%s',
single_column: true
});
}""" % (self.doc.name, self.doc.title))
}""" % (self.name, self.title))

def get_from_files(self):
"""
Loads page info from files in module
"""
def as_dict(self):
d = super(Page, self).as_dict()
for key in ("script", "style", "content"):
d[key] = self.get(key)
return d

def load_assets(self):
from frappe.modules import get_module_path, scrub
import os
path = os.path.join(get_module_path(self.doc.module), 'page', scrub(self.doc.name))
path = os.path.join(get_module_path(self.module), 'page', scrub(self.name))

# script
fpath = os.path.join(path, scrub(self.doc.name) + '.js')
fpath = os.path.join(path, scrub(self.name) + '.js')
if os.path.exists(fpath):
with open(fpath, 'r') as f:
self.doc.script = f.read()
self.script = f.read()

# css
fpath = os.path.join(path, scrub(self.doc.name) + '.css')
fpath = os.path.join(path, scrub(self.name) + '.css')
if os.path.exists(fpath):
with open(fpath, 'r') as f:
self.doc.style = f.read()
self.style = f.read()
# html
fpath = os.path.join(path, scrub(self.doc.name) + '.html')
fpath = os.path.join(path, scrub(self.name) + '.html')
if os.path.exists(fpath):
with open(fpath, 'r') as f:
self.doc.content = f.read()
self.content = f.read()
if frappe.lang != 'en':
from frappe.translate import get_lang_js
self.doc.script += get_lang_js("page", self.doc.name)
self.script += get_lang_js("page", self.name)

+ 2
- 4
frappe/core/doctype/user/user.js 查看文件

@@ -151,8 +151,7 @@ frappe.RoleEditor = Class.extend({
.each(function(i, checkbox) { checkbox.checked = false; });
// set user roles as checked
$.each(frappe.model.get("UserRole", {parent: cur_frm.doc.name,
parentfield: "user_roles"}), function(i, user_role) {
$.each((cur_frm.doc.user_roles || []), function(i, user_role) {
var checkbox = $(me.wrapper)
.find('[data-user-role="'+user_role.role+'"] input[type="checkbox"]').get(0);
if(checkbox) checkbox.checked = true;
@@ -163,8 +162,7 @@ frappe.RoleEditor = Class.extend({
var existing_roles_map = {};
var existing_roles_list = [];
$.each(frappe.model.get("UserRole", {parent: cur_frm.doc.name,
parentfield: "user_roles"}), function(i, user_role) {
$.each((cur_frm.doc.user_roles || []), function(i, user_role) {
existing_roles_map[user_role.role] = user_role.name;
existing_roles_list.push(user_role.role);
});


+ 3
- 5
frappe/core/doctype/workflow/workflow.js 查看文件

@@ -20,11 +20,9 @@ frappe.core.Workflow = frappe.ui.form.Controller.extend({
}
},
update_field_options: function() {
var fields = $.map(frappe.model.get("DocField", {
parent: this.frm.doc.document_type,
fieldtype: ["not in", frappe.model.no_value_type]
}),
function(d) { return d.fieldname; });
var fields = $.map(frappe.model.get_doc("DocType", this.frm.doc.document_type).fields, function(d) {
return frappe.model.no_value_type.indexOf(d.fieldtype)===-1 ? d.fieldname : null;
})
frappe.meta.get_docfield("Workflow Document State", "update_field", this.frm.doc.name).options
= [""].concat(fields);
}


+ 6
- 8
frappe/core/page/permission_manager/permission_manager.js 查看文件

@@ -428,17 +428,15 @@ frappe.PermissionEngine = Class.extend({
});
},
get_user_fields: function(doctype) {
var user_fields = frappe.model.get("DocField", {parent:doctype,
fieldtype:"Link", options:"User"});
user_fields = user_fields.concat(frappe.model.get("DocField", {parent:doctype,
fieldtype:"Select", link_doctype:"User"}))
var user_fields = frappe.model.get_children("DocType", doctype, "fields", {fieldtype:"Link", options:"User"})
user_fields = user_fields.concat(frappe.model.get_children("DocType", doctype, "fields",
{fieldtype:"Select", link_doctype:"User"}))
return user_fields
},
get_link_fields: function(doctype) {
return link_fields = frappe.model.get("DocField", {parent:doctype,
fieldtype:"Link", options:["not in", ["User", '[Select]']]});
return frappe.model.get_children("DocType", doctype, "fields",
{fieldtype:"Link", options:["not in", ["User", '[Select]']]});
}
})



+ 1
- 1
frappe/handler.py 查看文件

@@ -65,7 +65,7 @@ def handle():
if cmd!='login':
execute_cmd(cmd)
return build_response("json")

def execute_cmd(cmd):


+ 10
- 0
frappe/model/base_document.py 查看文件

@@ -110,6 +110,16 @@ class BaseDocument(object):
return self._valid_columns
def as_dict(self):
doc = self.get_valid_dict()
doc["doctype"] = self.doctype
for df in self.get_table_fields():
doc[df.fieldname] = [d.as_dict() for d in (self.get(df.fieldname) or [])]
return doc
def get_table_fields(self):
return self.meta.get('fields', {"fieldtype":"Table"})
def get_table_field_doctype(self, fieldname):
return self.meta.get("fields", {"fieldname":fieldname})[0].options


+ 1
- 0
frappe/model/code.py 查看文件

@@ -41,6 +41,7 @@ def get_obj(dt = None, dn = None, doc=None, doclist=None, with_children = 0):
def get_server_obj(doc, doclist = [], basedoctype = ''):
# for test
module = get_doctype_module(doc.doctype)
classname = doc.doctype.replace(" ", "")
return load_doctype_module(doc.doctype, module).DocType(doc, doclist)

def run_server_obj(server_obj, method_name, arg=None):


+ 2
- 0
frappe/model/doctype.py 查看文件

@@ -242,6 +242,8 @@ def clear_cache(doctype=None):
def clear_single(dt):
frappe.cache().delete_value(cache_name(dt, False))
frappe.cache().delete_value(cache_name(dt, True))
frappe.cache().delete_value("meta:" + dt)
frappe.cache().delete_value("form_meta:" + dt)

if doctype_cache and (dt in doctype_cache):
del doctype_cache[dt]


+ 43
- 23
frappe/model/document.py 查看文件

@@ -16,15 +16,20 @@ def get_doc(arg1, arg2=None):
doctype = arg1
else:
doctype = arg1.get("doctype")
controller = get_controller(doctype)
if controller:
return controller(arg1, arg2)
return Document(arg1, arg2)

def get_controller(doctype):
module = load_doctype_module(doctype)
classname = doctype.replace(" ", "")
if hasattr(module, classname):
_class = getattr(module, classname)
if issubclass(_class, Document):
return getattr(module, classname)(arg1, arg2)
return Document(arg1, arg2)
return getattr(module, classname)

class Document(BaseDocument):
def __init__(self, arg1, arg2=None):
@@ -59,6 +64,9 @@ class Document(BaseDocument):
else:
d = frappe.db.get_value(self.doctype, self.name, "*", as_dict=1)
if not d:
frappe.throw("{}: {}, {}".format(_("Not Found"),
self.doctype, self.name), frappe.DoesNotExistError)
self.update(d, valid_columns = d.keys())

for df in self.get_table_fields():
@@ -70,9 +78,6 @@ class Document(BaseDocument):
else:
self.set(df.fieldname, [])
def get_table_fields(self):
return self.meta.get('fields', {"fieldtype":"Table"})

def has_permission(self, permtype):
if getattr(self, "_ignore_permissions", False):
return True
@@ -293,23 +298,10 @@ class Document(BaseDocument):
def run_method(self, method, *args, **kwargs):
"""run standard triggers, plus those in frappe"""
def add_to_response(out, new_response):
if isinstance(new_response, dict):
out.update(new_response)

if hasattr(self, method):
add_to_response(frappe.local.response,
getattr(self, method)(*args, **kwargs))

args = [self, method] + list(args or [])

for handler in frappe.get_hooks("bean_event:" + self.doctype + ":" + method) \
+ frappe.get_hooks("bean_event:*:" + method):
add_to_response(frappe.local.response,
frappe.call(frappe.get_attr(handler), *args, **kwargs))
return frappe.local.response

fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs)
return Document.hook(fn)(self, *args, **kwargs)
def run_before_save_methods(self):
if self._action=="save":
self.run_method("validate")
@@ -332,3 +324,31 @@ class Document(BaseDocument):
self.run_method("on_cancel")
elif self._action=="update_after_submit":
self.run_method("on_update_after_submit")
@staticmethod
def hook(f):
def add_to_response(new_response):
if isinstance(new_response, dict):
frappe.local.response.update(new_response)

def compose(fn, *hooks):
def runner(self, method, *args, **kwargs):
add_to_response(fn(self, *args, **kwargs))
for f in hooks:
add_to_response(f(self, method, *args, **kwargs))
return frappe.local.response
return runner
def composer(self, *args, **kwargs):
hooks = []
method = f.__name__
for handler in frappe.get_hooks("bean_event:" + self.doctype + ":" + method) \
+ frappe.get_hooks("bean_event:*:" + method):
hooks.append(frappe.getattr(handler))

composed = compose(f, *hooks)
return composed(self, method, *args, **kwargs)

return composer


+ 2
- 0
frappe/model/meta.py 查看文件

@@ -12,6 +12,8 @@ from frappe.model.document import Document
######

def get_meta(doctype, cached=True):
# TODO: cache to be cleared

if cached:
if doctype not in frappe.local.meta:
frappe.local.meta[doctype] = frappe.cache().get_value("meta:" + doctype, lambda: Meta(doctype))


+ 1
- 1
frappe/public/build.json 查看文件

@@ -83,7 +83,6 @@
"public/js/frappe/model/model.js",
"public/js/frappe/model/meta.js",
"public/js/frappe/model/doclist.js",
"public/js/frappe/model/sync.js",
"public/js/frappe/model/create_new.js",
"public/js/frappe/model/perm.js",
@@ -135,6 +134,7 @@
"public/js/frappe/form/toolbar.js",
"public/js/frappe/form/infobar.js",
"public/js/frappe/form/dashboard.js",
"public/js/frappe/form/save.js",
"public/js/frappe/form/script_manager.js",
"public/js/frappe/form/control.js",
"public/js/frappe/form/link_selector.js",


+ 3
- 2
frappe/public/js/frappe/form/control.js 查看文件

@@ -980,8 +980,9 @@ frappe.ui.form.ControlTable = frappe.ui.form.Control.extend({
this._super();
// add title if prev field is not column / section heading or html
var prev_fieldtype = frappe.model.get("DocField",
{parent: this.frm.doctype, idx: this.df.idx-1})[0].fieldtype;
var prev_fieldtype = frappe.model.get_children("DocType", this.frm.doctype, "fields",
{idx: this.df.idx-1});
prev_fieldtype = prev_fieldtype ? prev_fieldtype[0].fieldtype : "";
if(["Column Break", "Section Break", "HTML"].indexOf(prev_fieldtype)===-1) {
$("<label>" + this.df.label + "<label>").appendTo(this.wrapper);


+ 1
- 1
frappe/public/js/frappe/form/formatters.js 查看文件

@@ -98,7 +98,7 @@ frappe.form.formatters = {
return "<pre>" + (value==null ? "" : $("<div>").text(value).html()) + "</pre>"
},
WorkflowState: function(value) {
workflow_state = frappe.model.get("Workflow State", value)[0];
workflow_state = frappe.model.get_doc("Workflow State", value);
if(workflow_state) {
return repl("<span class='label label-%(style)s' \
data-workflow-state='%(value)s'\


+ 1
- 5
frappe/public/js/frappe/form/grid.js 查看文件

@@ -118,11 +118,7 @@ frappe.ui.form.Grid = Class.extend({
});
},
get_data: function() {
var data = frappe.model.get(this.df.options, {
"parenttype": this.frm.doctype,
"parentfield": this.df.fieldname,
"parent": this.frm.docname
});
var data = this.frm.doc[this.df.fieldname] || [];
data.sort(function(a, b) { return a.idx - b.idx});
return data;
},


+ 1
- 3
frappe/public/js/frappe/form/linked_with.js 查看文件

@@ -1,9 +1,7 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

// for license information please see license.txt

frappe.provide("frappe.ui.form")
frappe.provide("frappe.ui.form");

frappe.ui.form.LinkedWith = Class.extend({
init: function(opts) {


frappe/public/js/frappe/model/doclist.js → frappe/public/js/frappe/form/save.js 查看文件

@@ -1,45 +1,38 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
// MIT License. See license.txt

frappe.provide("frappe.model");
frappe.model.DocList = Class.extend({
init: function(doctype, name) {
this.doctype = doctype; this.name = name;
this.doclist = frappe.model.get_doclist(this.doctype, this.name);
this.doc = this.doclist[0];
},
save: function(action, callback, btn) {
this.check_name();
if(this.check_mandatory()) {
var me = this;
this._call({
frappe.provide("frappe.ui.form");

frappe.ui.form.save = function(frm, action, callback, btn) {
var save = function() {
check_name();
if(check_mandatory()) {
_call({
method: "frappe.widgets.form.save.savedocs",
args: { docs: frappe.model.compress(this.doclist), action:action},
args: { docs: frm.doc, action:action},
callback: function(r) {
$(document).trigger("save", me.doc);
$(document).trigger("save", frm.doc);
callback(r);
},
btn: btn
});
}
},
};
cancel: function(callback, btn) {
var me = this;
this._call({
var cancel = function() {
_call({
method: "frappe.widgets.form.save.cancel",
args: { doctype: this.doctype, name: this.name },
args: { doctype: frm.doc.doctype, name: frm.doc.name },
callback: function(r) {
$(document).trigger("save", frappe.model.get_doc(me.doctype, me.name));
$(document).trigger("save", frm.doc);
callback(r);
},
btn: btn
});
},
};
check_name: function() {
var doc = this.doclist[0];
var check_name = function() {
var doc = frm.doc;
var meta = locals.DocType[doc.doctype];
if(doc.__islocal && (meta && meta.autoname
&& meta.autoname.toLowerCase()=='prompt')) {
@@ -51,23 +44,22 @@ frappe.model.DocList = Class.extend({
throw "name required";
}
}
},
};
check_mandatory: function() {
var me = this;
var check_mandatory = function() {
var has_errors = false;
this.scroll_set = false;
frm.scroll_set = false;
if(this.doc.docstatus==2) return true; // don't check for cancel
if(frm.doc.docstatus==2) return true; // don't check for cancel
$.each(this.doclist, function(i, doc) {
$.each(frm.model.get_all_docs(frm.doc), function(i, doc) {
var error_fields = [];
$.each(frappe.meta.docfield_list[doc.doctype] || [], function(i, docfield) {
if(docfield.fieldname) {
var df = frappe.meta.get_docfield(doc.doctype,
docfield.fieldname, me.doclist[0].name);
docfield.fieldname, frm.doc.name);

if(df.reqd && !frappe.model.has_value(doc.doctype, doc.name, df.fieldname)) {
has_errors = true;
@@ -87,17 +79,17 @@ frappe.model.DocList = Class.extend({
});
return !has_errors;
},
};
scroll_to: function(fieldname) {
var scroll_to = function(fieldname) {
var f = cur_frm.fields_dict[fieldname];
if(f) {
$(document).scrollTop($(f.wrapper).offset().top - 100);
}
this.scroll_set = true;
},
frm.scroll_set = true;
};

_call: function(opts) {
var _call = function(opts) {
// opts = {
// method: "some server method",
// args: {args to be passed},
@@ -114,5 +106,11 @@ frappe.model.DocList = Class.extend({
opts.callback && opts.callback(r);
}
})
},
});
};
if(action==="cancel") {
cancel();
} else {
save();
}
}

+ 1
- 1
frappe/public/js/frappe/form/script_manager.js 查看文件

@@ -109,7 +109,7 @@ frappe.ui.form.ScriptManager = Class.extend({
}
},
copy_from_first_row: function(parentfield, current_row, fieldnames) {
var doclist = frappe.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, {parentfield: parentfield});
var doclist = this.frm.doc[parentfield];
if(doclist.length===1 || doclist[0]===current_row) return;
$.each(fieldnames, function(i, fieldname) {


+ 2
- 2
frappe/public/js/frappe/form/workflow.js 查看文件

@@ -79,7 +79,7 @@ frappe.ui.form.States = Class.extend({
// show current state on the button
this.workflow_button.find(".state-text").text(state);
var state_doc = frappe.model.get("Workflow State", {name:state})[0];
var state_doc = frappe.model.get_doc("Workflow State", state);

if (state_doc) {
// set the icon
@@ -109,7 +109,7 @@ frappe.ui.form.States = Class.extend({

$.each(frappe.workflow.get_transitions(this.frm.doctype, state), function(i, d) {
if(in_list(user_roles, d.allowed)) {
d.icon = frappe.model.get("Workflow State", {name:d.next_state})[0].icon;
d.icon = frappe.model.get("Workflow State", d.next_state).icon;
$(repl('<li><a href="#" data-action="%(action)s">\
<i class="icon icon-%(icon)s"></i> %(action)s</a></li>', d))


+ 22
- 6
frappe/public/js/frappe/model/create_new.js 查看文件

@@ -122,20 +122,36 @@ $.extend(frappe.model, {
return d;
},
copy_doc: function(dt, dn, from_amend) {
copy_doc: function(doc, from_amend) {
var no_copy_list = ['name','amended_from','amendment_date','cancel_reason'];
var newdoc = frappe.model.get_new_doc(dt);
var newdoc = frappe.model.get_new_doc(doc.doctype);

for(var key in locals[dt][dn]) {
for(var key in doc) {
// dont copy name and blank fields
var df = frappe.meta.get_docfield(dt, key);
var df = frappe.meta.get_docfield(doc.doctype, key);
if(key.substr(0,2)!='__'
&& !in_list(no_copy_list, key)
&& !(df && (!from_amend && cint(df.no_copy)==1))) {
newdoc[key] = locals[dt][dn][key];
value = doc[key];
if(df.fieldtype==="Table") {
newdoc[key] = [];
$.each(value || [], function(i, d) {
newdoc[key].push(frappe.model.copy_doc(d, from_amend))
})
} else {
newdoc[key] = doc[key];
}
}
}

newdoc.__islocal = 1;
newdoc.docstatus = 0;
newdoc.owner = user;
newdoc.creation = '';
newdoc.modified_by = user;
newdoc.modified = '';

return newdoc;
},
@@ -167,7 +183,7 @@ $.extend(frappe.model, {
method: opts.method,
args: {
"source_name": opts.source_name,
"target_doclist": frappe.model.get_doclist(cur_frm.doc.doctype, cur_frm.doc.name)
"target_doclist": cur_frm.doc
},
callback: function(r) {
if(!r.exc) {


+ 9
- 2
frappe/public/js/frappe/model/meta.js 查看文件

@@ -8,6 +8,13 @@ frappe.provide('frappe.meta.doctypes');
frappe.provide("frappe.meta.precision_map");

$.extend(frappe.meta, {
sync: function(doc) {
$.each(doc.fields, function(i, df) {
frappe.meta.add_field(df);
})
frappe.meta.sync_messages(doc);
},
// build docfield_map and docfield_list
add_field: function(df) {
frappe.provide('frappe.meta.docfield_map.' + df.parent);
@@ -84,8 +91,8 @@ $.extend(frappe.meta, {
},

get_parentfield: function(parent_dt, child_dt) {
var df = frappe.model.get("DocField", {parent:parent_dt, fieldtype:"Table",
options:child_dt})
var df = (frappe.model.get_doc("DocType", parent_dt).fields || []).filter(function(d)
{ return d.fieldtype==="Table" && options===child_dt })
if(!df.length)
throw "parentfield not found for " + parent_dt + ", " + child_dt;
return df[0].fieldname;


+ 31
- 58
frappe/public/js/frappe/model/model.js 查看文件

@@ -46,8 +46,8 @@ $.extend(frappe.model, {
} else {
var cached_timestamp = null;
if(localStorage["_doctype:" + doctype]) {
var cached_doclist = JSON.parse(localStorage["_doctype:" + doctype]);
cached_timestamp = cached_doclist[0].modified;
var cached_doc = JSON.parse(localStorage["_doctype:" + doctype]);
cached_timestamp = cached_doc.modified;
}
return frappe.call({
method:'frappe.widgets.form.load.getdoctype',
@@ -64,7 +64,7 @@ $.extend(frappe.model, {
return;
}
if(r.message=="use_cache") {
frappe.model.sync(cached_doclist);
frappe.model.sync(cached_doc);
} else {
localStorage["_doctype:" + doctype] = JSON.stringify(r.docs);
}
@@ -206,10 +206,10 @@ $.extend(frappe.model, {
},

get: function(doctype, filters) {
var src = locals[doctype] || locals[":" + doctype] || [];
if($.isEmptyObject(src))
var docsdict = locals[doctype] || locals[":" + doctype] || [];
if($.isEmptyObject(docsdict))
return [];
return frappe.utils.filter_dict(src, filters);
return frappe.utils.filter_dict(docsdict, filters);
},
get_value: function(doctype, filters, fieldname) {
@@ -277,55 +277,16 @@ $.extend(frappe.model, {
return locals[doctype] ? locals[doctype][name] : null;
},
get_doclist: function(doctype, name, filters) {
var doclist = [];
if(!locals[doctype])
return doclist;

doclist[0] = locals[doctype][name];

$.each(frappe.model.get("DocField", {parent:doctype, fieldtype:"Table"}),
function(i, table_field) {
var child_doclist = frappe.model.get(table_field.options, {
parent:name, parenttype: doctype,
parentfield: table_field.fieldname});
if($.isArray(child_doclist)) {
child_doclist.sort(function(a, b) { return a.idx - b.idx; });
doclist = doclist.concat(child_doclist);
}
}
);
if(filters) {
doclist = frappe.utils.filter_dict(doclist, filters);
}
return doclist;
},

get_children: function(doctype, parent, parentfield, parenttype) {
if(parenttype) {
var l = frappe.model.get(doctype, {parent:parent,
parentfield:parentfield, parenttype:parenttype});
get_children: function(doctype, parent, parentfield, filters) {
if($.isPlainObject(parentfield)) {
var doc = doctype;
var filters = parentfield;
var parentfield = parent;
return frappe.utils.filter_dict((doc[parentfield] || []), filters);
} else {
var l = frappe.model.get(doctype, {parent:parent,
parentfield:parentfield});
return frappe.utils.filter_dict((frappe.model.get_doc(doctype, parent)[parentfield] || []), filters);
}

if(l.length) {
l.sort(function(a,b) { return flt(a.idx) - flt(b.idx) });
// renumber
$.each(l, function(i, v) { v.idx = i+1; }); // for chrome bugs ???
}
return l;
},

clear_doclist: function(doctype, name) {
$.each(frappe.model.get_doclist(doctype, name), function(i, d) {
if(d) frappe.model.clear_doc(d.doctype, d.name);
});
},
clear_table: function(doctype, parenttype, parent, parentfield) {
@@ -337,7 +298,7 @@ $.extend(frappe.model, {
},

remove_from_locals: function(doctype, name) {
this.clear_doclist(doctype, name);
this.clear_doc(doctype, name);
if(frappe.views.formview[doctype]) {
delete frappe.views.formview[doctype].frm.opendocs[name];
}
@@ -358,13 +319,14 @@ $.extend(frappe.model, {
get_no_copy_list: function(doctype) {
var no_copy_list = ['name','amended_from','amendment_date','cancel_reason'];
$.each(frappe.model.get("DocField", {parent:doctype}), function(i, df) {
$.each(frappe.model.get_doc("DocType", doctype).fields || [], function(i, df) {
if(cint(df.no_copy)) no_copy_list.push(df.fieldname);
})
return no_copy_list;
},

// args: source (doclist), target (doctype), table_map, field_map, callback
// args: source (doc), target (doc), table_map, field_map, callback
map: function(args) {
frappe.model.with_doctype(args.target, function() {
var map_info = frappe.model.map_info[args.target]
@@ -436,7 +398,7 @@ $.extend(frappe.model, {
},
callback: function(r, rt) {
if(!r.exc) {
frappe.model.clear_doclist(doctype, docname);
frappe.model.clear_doc(doctype, docname);
if(frappe.ui.toolbar.recent)
frappe.ui.toolbar.recent.remove(doctype, docname);
if(callback) callback(r,rt);
@@ -499,9 +461,20 @@ $.extend(frappe.model, {
frappe.throw(frappe._("Please specify") + ": " +
frappe._(frappe.meta.get_label(doc.doctype, fieldname, doc.parent || doc.name)));
}
},
get_all_docs: function(doc) {
var all = [doc];
for(key in doc) {
if($.isArray(doc[key])) {
$.each(doc[key], function(i, d) {
all.push(d);
});
}
}
return all;
}
});

// legacy
getchildren = frappe.model.get_children
make_doclist = frappe.model.get_doclist

+ 2
- 10
frappe/public/js/frappe/model/perm.js 查看文件

@@ -38,17 +38,9 @@ $.extend(frappe.perm, {
var perm = [{read: 0}];
var meta = frappe.model.get_doc("DocType", doctype);
if(!meta) {
return perm;
} else if(meta.istable) {
// if a child table, use permissions of parent form
var parent_df = frappe.model.get("DocField", {fieldtype: "Table", options: doctype});
if(parent_df.length) {
if(docname) {
docname = frappe.model.get_doc(doctype, docname).parent;
}
doctype = parent_df[0].parent;
}
}
if(user==="Administrator" || user_roles.indexOf("Administrator")!==-1) {
@@ -60,7 +52,7 @@ $.extend(frappe.perm, {
return perm;
}
var docperms = frappe.model.get("DocPerm", {parent: doctype});
var docperms = frappe.model.get_doc("DocType", doctype).permissions || [];
$.each(docperms, function(i, p) {
// if user has this role
if(user_roles.indexOf(p.role)!==-1) {


+ 12
- 86
frappe/public/js/frappe/model/sync.js 查看文件

@@ -5,35 +5,21 @@ $.extend(frappe.model, {
docinfo: {},
sync: function(r) {
/* docs:
extract doclist, docinfo (attachments, comments, assignments)
extract docs, docinfo (attachments, comments, assignments)
from incoming request and set in `locals` and `frappe.model.docinfo`
*/

if(!r.docs && !r.docinfo) r = {docs:r};

if(r.docs) {
var doclist = r.docs;
if(doclist._kl)
doclist = frappe.model.expand(doclist);

if(doclist && doclist.length)
frappe.model.clear_doclist(doclist[0].doctype, doclist[0].name)

var last_parent_name = null;
var dirty = [];
$.each(doclist, function(i, d) {

$.each(r.docs, function(i, d) {
if(!d.name && d.__islocal) { // get name (local if required)
frappe.model.clear_doc(d)
d.name = frappe.model.get_new_name(d.doctype);
frappe.provide("frappe.model.docinfo." + d.doctype + "." + d.name);
if(!d.parenttype)
last_parent_name = d.name;
if(dirty.indexOf(d.parenttype || d.doctype)===-1) dirty.push(d.parenttype || d.doctype);
}

// set parent for subsequent orphans
if(d.parenttype && !d.parent && d.__islocal) {
d.parent = last_parent_name;
}

if(!locals[d.doctype])
@@ -41,14 +27,15 @@ $.extend(frappe.model, {

locals[d.doctype][d.name] = d;
d.__last_sync_on = new Date();
if(d.doctype==="DocType") {
frappe.meta.sync(d);
}

if(cur_frm && cur_frm.doctype==d.doctype && cur_frm.docname==d.name) {
cur_frm.doc = d;
}

if(d.doctype=='DocField') frappe.meta.add_field(d);
if(d.doctype=='DocType') frappe.meta.sync_messages(d);

if(d.localname) {
frappe.model.new_names[d.localname] = d.name;
$(document).trigger('rename', [d.doctype, d.localname, d.name]);
@@ -66,10 +53,10 @@ $.extend(frappe.model, {

}
// set docinfo
// set docinfo (comments, assign, attachments)
if(r.docinfo) {
if(doclist) {
var doc = doclist[0];
if(r.docs) {
var doc = r.docs[0];
} else {
var doc = cur_frm.doc;
}
@@ -78,68 +65,7 @@ $.extend(frappe.model, {
frappe.model.docinfo[doc.doctype][doc.name] = r.docinfo;
}
return doclist;
return r.docs;
},
expand: function(data) {
function zip(k,v) {
var obj = {};
for(var i=0;i<k.length;i++) {
obj[k[i]] = v[i];
}
return obj;
}

var l = [];
for(var i=0;i<data._vl.length;i++) {
l.push(zip(data._kl[data._vl[i][0]], data._vl[i]));
}
return l;
},
compress: function(doclist) {
var all_keys = {}; var values = [];
function get_key_list(doctype) {
// valid standard keys
var key_list = ['doctype', 'name', 'docstatus', 'owner', 'parent',
'parentfield', 'parenttype', 'idx', 'creation', 'modified',
'modified_by', '__islocal', '__newname', '__modified',
'_user_tags', '__temp', '_comments'];

for(key in frappe.meta.docfield_map[doctype]) { // all other values
if(!in_list(key_list, key)
&& !in_list(frappe.model.no_value_type, frappe.meta.docfield_map[doctype][key].fieldtype)
&& !frappe.meta.docfield_map[doctype][key].no_column) {
key_list[key_list.length] = key
}
}
return key_list;
}
for(var i=0; i<doclist.length;i++) {
var doc = doclist[i];
// make keys
if(!all_keys[doc.doctype]) {
all_keys[doc.doctype] = get_key_list(doc.doctype);
// doctype must be first
}
var row = []
var key_list = all_keys[doc.doctype];
// make data rows
for(var j=0;j<key_list.length;j++) {
row.push(doc[key_list[j]]);
}
values.push(row);
}

return JSON.stringify({'_vl':values, '_kl':all_keys});
}
});

// legacy
compress_doclist = frappe.model.compress;

+ 8
- 26
frappe/public/js/frappe/model/workflow.js 查看文件

@@ -23,31 +23,19 @@ frappe.workflow = {
},
get_default_state: function(doctype) {
frappe.workflow.setup(doctype);
return frappe.model.get("Workflow Document State", {
parent: frappe.workflow.workflows[doctype].name,
idx: 1
})[0].state;
return frappe.workflow.workflows[doctype].workflow_document_states[0].state;
},
get_transitions: function(doctype, state) {
frappe.workflow.setup(doctype);
return frappe.model.get("Workflow Transition", {
parent: frappe.workflow.workflows[doctype].name,
state: state
});
return frappe.model.get_children(frappe.workflow.workflows[doctype], "workflow_transitions", {state:state});
},
get_document_state: function(doctype, state) {
frappe.workflow.setup(doctype);
return frappe.model.get("Workflow Document State", {
parent: frappe.workflow.workflows[doctype].name,
state: state
})[0];
return frappe.model.get_children(frappe.workflow.workflows[doctype], "workflow_document_states", {state:state})[0];
},
get_next_state: function(doctype, state, action) {
return frappe.model.get("Workflow Transition", {
parent: frappe.workflow.workflows[doctype].name,
state: state,
action: action
})[0].next_state;
return frappe.model.get_children(frappe.workflow.workflows[doctype], "workflow_transitions", {
state:state, action:action})[0].next_state;
},
is_read_only: function(doctype, name) {
var state_fieldname = frappe.workflow.get_state_fieldname(doctype);
@@ -60,13 +48,7 @@ frappe.workflow = {
var state = locals[doctype][name][state_fieldname] ||
frappe.workflow.get_default_state(doctype);

var workflow_doc_state = frappe.model.get("Workflow Document State",
{
parent: frappe.workflow.workflows[doctype].name,
state: state
});
var allow_edit = workflow_doc_state.length ?
workflow_doc_state[0].allow_edit : null;
var allow_edit = state ? frappe.workflow.get_document_state(doctype, state).allow_edit : null;

if(user_roles.indexOf(allow_edit)==-1) {
return true;
@@ -75,8 +57,8 @@ frappe.workflow = {
return false;
},
get_update_fields: function(doctype) {
var update_fields = $.unique($.map(frappe.model.get("Workflow Document State",
{parent:frappe.workflow.workflows[doctype].name}), function(d) {
var update_fields = $.unique($.map(frappe.workflow.workflows[doctype].workflow_document_states || [],
function(d) {
return d.update_field;
}));
return update_fields;


+ 1
- 2
frappe/public/js/frappe/request.js 查看文件

@@ -16,8 +16,7 @@ frappe.call = function(opts) {
} else if(opts.doc) {
$.extend(args, {
cmd: "runserverobj",
docs: frappe.model.compress(frappe.model.get_doclist(opts.doc.doctype,
opts.doc.name)),
docs: frappe.model.get_doc(opts.doc.doctype, opts.doc.name),
method: opts.method,
args: opts.args,
});


+ 6
- 6
frappe/public/js/frappe/views/reportview.js 查看文件

@@ -337,13 +337,13 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
callback: function(r) {
if(!r.exc) {
d.hide();
var doclist = r.message;
var doc = r.message;
$.each(me.dataView.getItems(), function(i, item) {
if (item.name === doclist[0].name) {
var new_item = $.extend({}, item, doclist[0]);
$.each(doclist, function(i, doc) {
if(item[doc.doctype + ":name"]===doc.name) {
$.each(doc, function(k, v) {
if (item.name === doc.name) {
var new_item = $.extend({}, item);
$.each(frappe.model.get_all_docs(doc), function(i, d) {
if(item[d.doctype + ":name"]===d.name) {
$.each(d, function(k, v) {
if(frappe.model.std_fields_list.indexOf(k)===-1) {
new_item[k] = v;
}


+ 2
- 9
frappe/public/js/legacy/clientscriptAPI.js 查看文件

@@ -4,10 +4,7 @@
get_server_fields = function(method, arg, table_field, doc, dt, dn, allow_edit, call_back) {
frappe.dom.freeze();
return $c('runserverobj',
args={'method':method,
'docs':frappe.model.compress(make_doclist(doc.doctype, doc.name)),
'arg':arg
},
args={'method': method, 'docs': doc, 'arg': arg },
function(r, rt) {
frappe.dom.unfreeze();
if (r.message) {
@@ -125,10 +122,6 @@ _f.Frm.prototype.get_doc = function() {
return locals[this.doctype][this.docname];
}

_f.Frm.prototype.get_doclist = function(filters) {
return frappe.model.get_doclist(this.doctype, this.docname, filters);
}

_f.Frm.prototype.field_map = function(fnames, fn) {
if(typeof fnames==='string') {
if(fnames == '*') {
@@ -170,7 +163,7 @@ _f.Frm.prototype.toggle_display = function(fnames, show) {
}

_f.Frm.prototype.call_server = function(method, args, callback) {
return $c_obj(cur_frm.get_doclist(), method, args, callback);
return $c_obj(cur_frm.doc, method, args, callback);
}

_f.Frm.prototype.get_files = function() {


+ 20
- 57
frappe/public/js/legacy/form.js 查看文件

@@ -548,13 +548,11 @@ _f.Frm.prototype.setnewdoc = function() {
_f.Frm.prototype.runscript = function(scriptname, callingfield, onrefresh) {
var me = this;
if(this.docname) {
// make doc list
var doclist = frappe.model.compress(make_doclist(this.doctype, this.docname));
// send to run
if(callingfield)
$(callingfield.input).set_working();

return $c('runserverobj', {'docs':doclist, 'method':scriptname },
return $c('runserverobj', {'docs':this.doc, 'method':scriptname },
function(r, rtxt) {
// run refresh
if(onrefresh)
@@ -577,45 +575,10 @@ _f.Frm.prototype.copy_doc = function(onload, from_amend) {
return;
}
var dn = this.docname;
// copy parent
var newdoc = frappe.model.copy_doc(this.doctype, dn, from_amend);
newdoc.idx = null;
// copy chidren
var dl = make_doclist(this.doctype, dn);

// table fields dict - for no_copy check
var tf_dict = {};

for(var d in dl) {
d1 = dl[d];
// get tabel field
if(d1.parentfield && !tf_dict[d1.parentfield]) {
tf_dict[d1.parentfield] = frappe.meta.get_docfield(d1.parenttype, d1.parentfield);
}
if(d1.parent==dn && cint(tf_dict[d1.parentfield].no_copy)!=1) {
var ch = frappe.model.copy_doc(d1.doctype, d1.name, from_amend);
ch.parent = newdoc.name;
ch.docstatus = 0;
ch.owner = user;
ch.creation = '';
ch.modified_by = user;
ch.modified = '';
}
}

newdoc.__islocal = 1;
newdoc.docstatus = 0;
newdoc.owner = user;
newdoc.creation = '';
newdoc.modified_by = user;
newdoc.modified = '';
var newdoc = frappe.model.copy_doc(this.doc, from_amend);

newdoc.idx = null;
if(onload)onload(newdoc);

loaddoc(newdoc.doctype, newdoc.name);
}

@@ -651,19 +614,15 @@ _f.Frm.prototype._save = function(save_action, callback, btn, on_error) {
scroll(0, 0);
// validate
if(save_action!="Cancel") {
validated = true;
this.script_manager.trigger("validate");
if(!validated) {
if(on_error)
on_error();
return;
}
validated = true;
this.script_manager.trigger("validate");
if(!validated) {
if(on_error)
on_error();
return;
}

var doclist = new frappe.model.DocList(this.doctype, this.docname);

doclist.save(save_action || "Save", function(r) {
var after_save = function(r) {
if(!r.exc) {
me.refresh();
} else {
@@ -680,7 +639,9 @@ _f.Frm.prototype._save = function(save_action, callback, btn, on_error) {
}
frappe._from_link = null;
}
}, btn);
}
frappe.ui.form.save(this, save_action || "Save", after_save, btn);
}


@@ -713,14 +674,16 @@ _f.Frm.prototype.savecancel = function(btn, on_error) {
on_error();
return;
}
var doclist = new frappe.model.DocList(me.doctype, me.docname);
doclist.cancel(function(r) {

var after_cancel = function(r) {
if(!r.exc) {
me.refresh();
me.script_manager.trigger("after_cancel");
} else {
on_error();
}
}, btn, on_error);
}
frappe.ui.form.save(this, "cancel", after_cancel, btn);
});
}



+ 10
- 9
frappe/public/js/legacy/handler.js 查看文件

@@ -13,7 +13,7 @@ function $c(command, args, callback, error, no_spinner, freeze_msg, btn) {
}

// For calling an object
function $c_obj(doclist, method, arg, callback, no_spinner, freeze_msg, btn) {
function $c_obj(doc, method, arg, callback, no_spinner, freeze_msg, btn) {
if(arg && typeof arg!='string') arg = JSON.stringify(arg);
args = {
@@ -22,10 +22,11 @@ function $c_obj(doclist, method, arg, callback, no_spinner, freeze_msg, btn) {
method: method
};
if(typeof doclist=='string')
args.doctype = doclist;
else
args.docs = frappe.model.compress(doclist)
if(typeof doc=='string') {
args.doctype = doc;
} else {
args.docs = doc
}
return frappe.request.call({
args: args,
@@ -53,7 +54,7 @@ function $c_page(module, page, method, arg, callback, no_spinner, freeze_msg, bt
}

// For calling an for output as csv
function $c_obj_csv(doclist, method, arg) {
function $c_obj_csv(doc, method, arg) {
// single
var args = {}
@@ -62,10 +63,10 @@ function $c_obj_csv(doclist, method, arg) {
args.method = method;
args.arg = arg;
if(doclist.substr)
args.doctype = doclist;
if(doc.substr)
args.doctype = doc;
else
args.docs = frappe.model.compress(doclist);
args.docs = doc;

// open
open_url_post(frappe.request.url, args);


+ 1
- 0
frappe/tests/test_document.py 查看文件

@@ -120,5 +120,6 @@ class TestDocument(unittest.TestCase):
d.starts_on = "2014-01-01"
d.ends_on = "2013-01-01"
self.assertRaises(frappe.ValidationError, d.validate)
self.assertRaises(frappe.ValidationError, d.run_method, "validate")
self.assertRaises(frappe.ValidationError, d.save)

+ 16
- 0
frappe/tests/test_form_load.py 查看文件

@@ -0,0 +1,16 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt

import frappe, unittest
from frappe.widgets.form.meta import get_meta
from frappe.widgets.form.load import getdoctype, getdoc

class TestFormLoad(unittest.TestCase):
def test_load(self):
getdoctype("DocType")
self.assertEquals(frappe.response.docs[0].name, "DocType")
self.assertTrue(frappe.response.docs[0].get("__js"))

frappe.response.docs = []
d = getdoctype("Event")
self.assertTrue(frappe.response.docs[0].get("__calendar_js"))

+ 10
- 8
frappe/utils/response.py 查看文件

@@ -9,7 +9,8 @@ import mimetypes
import os
import frappe
from frappe import _
from frappe.model.doc import Document
import frappe.model.doc
import frappe.model.document
import frappe.utils
import frappe.sessions
import frappe.model.utils
@@ -28,6 +29,9 @@ def report_error(status_code):
return response

def build_response(response_type=None):
if "docs" in frappe.local.response and not frappe.local.response.docs:
del frappe.local.response["docs"]
response_type_map = {
'csv': as_csv,
'download': as_raw,
@@ -54,7 +58,6 @@ def as_raw():

def as_json():
make_logs()
cleanup_docs()
response = Response()
response.headers["Content-Type"] = "application/json; charset: utf-8"
response = gzip(json.dumps(frappe.local.response, default=json_handler, separators=(',',':')),
@@ -68,14 +71,11 @@ def make_logs():
frappe.response['exc'] = json.dumps([frappe.utils.cstr(d) for d in frappe.local.error_log])

if frappe.local.message_log:
frappe.response['_server_messages'] = json.dumps([frappe.utils.cstr(d) for d in frappe.local.message_log])
frappe.response['_server_messages'] = json.dumps([frappe.utils.cstr(d) for
d in frappe.local.message_log])
if frappe.debug_log and frappe.conf.get("logging") or False:
frappe.response['_debug_messages'] = json.dumps(frappe.local.debug_log)

def cleanup_docs():
if frappe.response.get('docs') and type(frappe.response['docs'])!=dict:
frappe.response['docs'] = frappe.model.utils.compress(frappe.response['docs'])
def gzip(data, response):
data = data.encode('utf-8')
@@ -107,8 +107,10 @@ def json_handler(obj):
return unicode(obj)
elif isinstance(obj, LocalProxy):
return unicode(obj)
elif isinstance(obj, Document):
elif isinstance(obj, frappe.model.doc.Document):
return obj.fields
elif isinstance(obj, frappe.model.document.Document):
return obj.as_dict()
else:
raise TypeError, """Object of type %s with value of %s is not JSON serializable""" % \
(type(obj), repr(obj))


+ 1
- 1
frappe/website/doctype/blog_post/blog_post.js 查看文件

@@ -5,7 +5,7 @@ cur_frm.cscript.refresh = function(doc) {
if(!doc.__islocal && doc.published) {
if(!doc.email_sent) {
cur_frm.add_custom_button('Email Subscribers', function() {
$c_obj(make_doclist(doc.doctype, doc.name), 'send_emails', '', function(r) {
$c_obj(doc, 'send_emails', '', function(r) {
cur_frm.refresh();
});
});


+ 19
- 19
frappe/widgets/form/load.py 查看文件

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe, json
import frappe.model.doc
import frappe.utils
import frappe.widgets.form.meta

@frappe.whitelist()
def getdoc(doctype, name, user=None):
@@ -13,8 +14,6 @@ def getdoc(doctype, name, user=None):
Requries "doctype", "name" as form variables.
Will also call the "onload" method on the document.
"""

import frappe
if not (doctype and name):
raise Exception, 'doctype and name required!'
@@ -26,14 +25,12 @@ def getdoc(doctype, name, user=None):
return []

try:
bean = frappe.bean(doctype, name)
bean.run_method("onload")
doc = frappe.get_doc(doctype, name)
doc.run_method("onload")
if not bean.has_read_perm():
if not doc.has_permission("read"):
raise frappe.PermissionError

doclist = bean.doclist

# add file list
get_docinfo(doctype, name)
@@ -42,35 +39,38 @@ def getdoc(doctype, name, user=None):
frappe.msgprint('Did not load.')
raise

if bean and not name.startswith('_'):
if doc and not name.startswith('_'):
frappe.user.update_recent(doctype, name)
frappe.response['docs'] = doclist
frappe.response.docs.append(doc)

@frappe.whitelist()
def getdoctype(doctype, with_parent=False, cached_timestamp=None):
"""load doctype"""
import frappe.model.doctype
import frappe.model.meta
doclist = []
docs = []
# with parent (called from report builder)
if with_parent:
parent_dt = frappe.model.meta.get_parent_dt(doctype)
if parent_dt:
doclist = frappe.model.doctype.get(parent_dt, processed=True)
docs = get_meta_bundle(parent_dt)
frappe.response['parent_dt'] = parent_dt
if not doclist:
doclist = frappe.model.doctype.get(doctype, processed=True)
if not docs:
docs = get_meta_bundle(doctype)
frappe.response['restrictions'] = get_restrictions(doclist)
frappe.response['restrictions'] = get_restrictions(docs[0])
if cached_timestamp and doclist[0].modified==cached_timestamp:
if cached_timestamp and docs[0].modified==cached_timestamp:
return "use_cache"
frappe.response['docs'] = doclist
frappe.response.docs.extend(docs)

def get_meta_bundle(doctype):
bundle = [frappe.widgets.form.meta.get_meta(doctype)]
for df in bundle[0].get_table_fields():
bundle.append(frappe.widgets.form.meta.get_meta(df.options))
return bundle

def get_docinfo(doctype, name):
frappe.response["docinfo"] = {


+ 157
- 0
frappe/widgets/form/meta.py 查看文件

@@ -0,0 +1,157 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt

# metadata

from __future__ import unicode_literals
import frappe, os
from frappe.utils import cstr, cint
from frappe.model.meta import Meta
from frappe.modules import scrub, get_module_path
from frappe.model.workflow import get_workflow_name

######

def get_meta(doctype, cached=True):
if cached:
meta = frappe.cache().get_value("form_meta:" + doctype, lambda: FormMeta(doctype))
else:
meta = FormMeta(doctype)

if frappe.local.lang != 'en':
meta.set("__messages", frappe.get_lang_dict("doctype", doctype))
return meta
class FormMeta(Meta):
def __init__(self, doctype):
super(FormMeta, self).__init__(doctype)
self.load_assets()
def load_assets(self):
self.expand_selects()
self.add_search_fields()

if not self.istable:
self.add_linked_with()
self.add_code()
self.load_print_formats()
self.load_workflows()
def as_dict(self):
d = super(FormMeta, self).as_dict()
for k in ("__js", "__css", "__list_js", "__calendar_js", "__map_js", "__linked_with", "__messages"):
d[k] = self.get(k)
for i, df in enumerate(d.get("fields")):
for k in ("link_doctype", "search_fields"):
df[k] = self.get("fields")[i].get(k)
return d
def add_code(self):
path = os.path.join(get_module_path(self.module), 'doctype', scrub(self.name))
def _get_path(fname):
return os.path.join(path, scrub(fname))
self._add_code(_get_path(self.name + '.js'), '__js')
self._add_code(_get_path(self.name + '.css'), "__css")
self._add_code(_get_path(self.name + '_list.js'), '__list_js')
self._add_code(_get_path(self.name + '_calendar.js'), '__calendar_js')
self._add_code(_get_path(self.name + '_map.js'), '__map_js')
self.add_custom_script()
self.add_code_via_hook("doctype_js", "__js")
def _add_code(self, path, fieldname):
js = frappe.read_file(path)
if js:
self.set(fieldname, (self.get(fieldname) or "") + "\n\n" + render_jinja(js))
def add_code_via_hook(self, hook, fieldname):
hook = "{}:{}".format(hook, self.name)
for app_name in frappe.get_installed_apps():
for file in frappe.get_hooks(hook, app_name=app_name):
path = frappe.get_app_path(app_name, *file.strip("/").split("/"))
self._add_code(path, fieldname)
def add_custom_script(self):
"""embed all require files"""
# custom script
custom = frappe.db.get_value("Custom Script", {"dt": self.name,
"script_type": "Client"}, "script") or ""
self.set("__js", (self.get('__js') or '') + "\n\n" + custom)
def render_jinja(content):
if "{% include" in content:
content = frappe.get_jenv().from_string(content).render()
return content

def expand_selects(self):
for df in self.get("fields", {"fieldtype": "Select"}):
if df.options and df.options.startswith("link:"):
df.link_doctype = df.options.split("\n")[0][5:]
df.options = '\n'.join([''] + [o.name for o in frappe.db.sql("""select
name from `tab%s` where docstatus<2 order by name asc""" % df.link_doctype, as_dict=1)])
def add_search_fields(self):
"""add search fields found in the doctypes indicated by link fields' options"""
for df in self.get("fields", {"fieldtype": "Link", "options":["!=", "[Select]"]}):
if df.options:
search_fields = frappe.get_meta(df.options).search_fields
if search_fields:
df.search_fields = map(lambda sf: sf.strip(), search_fields.split(","))

def add_linked_with(self):
"""add list of doctypes this doctype is 'linked' with"""
links = frappe.db.sql("""select parent, fieldname from tabDocField
where (fieldtype="Link" and options=%s)
or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name))
links += frappe.db.sql("""select dt as parent, fieldname from `tabCustom Field`
where (fieldtype="Link" and options=%s)
or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name))

links = dict(links)

if not links:
return {}

ret = {}

for dt in links:
ret[dt] = { "fieldname": links[dt] }

for grand_parent, options in frappe.db.sql("""select parent, options from tabDocField
where fieldtype="Table"
and options in (select name from tabDocType
where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)):

ret[grand_parent] = {"child_doctype": options, "fieldname": links[options] }
if options in ret:
del ret[options]
self.set("__linked_with", ret)
def load_print_formats(self):
frappe.response.docs.extend(frappe.db.sql("""select * FROM `tabPrint Format`
WHERE doc_type=%s AND docstatus<2""", (self.name,), as_dict=1, update={"doctype":"Print Format"}))
def load_workflows(self):
# get active workflow
workflow_name = get_workflow_name(self.name)

if workflow_name and frappe.db.exists("Workflow", workflow_name):
workflow = frappe.get_doc("Workflow", workflow_name)
frappe.response.docs.append(workflow)
for d in workflow.get("workflow_document_states"):
frappe.response.docs.append(frappe.get_doc("Workflow State", d.state))

def render_jinja(content):
if "{% include" in content:
content = frappe.get_jenv().from_string(content).render()
return content


+ 0
- 2
frappe/widgets/form/run_method.py 查看文件

@@ -20,8 +20,6 @@ def runserverobj():
dt = frappe.form_dict.get('doctype')
dn = frappe.form_dict.get('docname')
frappe.response["docs"] = []
if dt: # not called from a doctype (from a page)
if not dn: dn = dt # single
so = frappe.model.code.get_obj(dt, dn)


+ 0
- 2
frappe/widgets/form/utils.py 查看文件

@@ -112,8 +112,6 @@ def get_linked_docs(doctype, name, metadata_loaded=None):
results[dt] = ret
if not dt in metadata_loaded:
if not "docs" in frappe.local.response:
frappe.local.response.docs = []
frappe.local.response.docs += linkmeta
return results

+ 14
- 19
frappe/widgets/page.py 查看文件

@@ -11,14 +11,14 @@ def get(name):
"""
Return the :term:`doclist` of the `Page` specified by `name`
"""
page = frappe.bean("Page", name)
if has_permission(page.doclist):
page.run_method("get_from_files")
return page.doclist
page = frappe.get_doc('Page', name)
if has_permission(page):
page.load_assets()
return page
else:
frappe.response['403'] = 1
raise frappe.PermissionError, 'No read permission for Page %s' % \
(page.doclist[0].title or name,)
(page.title or name)

@frappe.whitelist(allow_guest=True)
def getpage():
@@ -26,24 +26,19 @@ def getpage():
Load the page from `frappe.form` and send it via `frappe.response`
"""
page = frappe.form_dict.get('name')
doclist = get(page)
doc = get(page)

if has_permission(doclist):
# load translations
if frappe.lang != "en":
frappe.response["__messages"] = frappe.get_lang_dict("page", page)
# load translations
if frappe.lang != "en":
frappe.response["__messages"] = frappe.get_lang_dict("page", page)

frappe.response['docs'] = doclist
else:
frappe.response['403'] = 1
raise frappe.PermissionError, 'No read permission for Page %s' % \
(doclist[0].title or page, )
def has_permission(page_doclist):
frappe.response.docs.append(doc)

def has_permission(page):
if frappe.user.name == "Administrator" or "System Manager" in frappe.user.get_roles():
return True
page_roles = [d.role for d in page_doclist if d.fields.get("doctype")=="Page Role"]
page_roles = [d.role for d in page.get("roles")]
if page_roles:
if frappe.session.user == "Guest" and "Guest" not in page_roles:
return False
@@ -51,7 +46,7 @@ def has_permission(page_doclist):
# check if roles match
return False
if not frappe.has_permission("Page", ptype="read", refdoc=page_doclist[0].name):
if not frappe.has_permission("Page", ptype="read", doc=page):
# check if there are any restrictions
return False
else:


Loading…
取消
儲存