You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

meta.py 6.8 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. # metadata
  4. from __future__ import unicode_literals
  5. import frappe, os
  6. from frappe.model.meta import Meta
  7. from frappe.modules import scrub, get_module_path, load_doctype_module
  8. from frappe.model.workflow import get_workflow_name
  9. from frappe.utils import get_html_format
  10. from frappe.translate import make_dict_from_messages, extract_messages_from_code
  11. from frappe.utils.jinja import render_include
  12. from frappe.build import html_to_js_template
  13. ######
  14. def get_meta(doctype, cached=True):
  15. if cached and not frappe.conf.developer_mode:
  16. meta = frappe.cache().hget("form_meta", doctype, lambda: FormMeta(doctype))
  17. else:
  18. meta = FormMeta(doctype)
  19. if frappe.local.lang != 'en':
  20. meta.set_translations(frappe.local.lang)
  21. return meta
  22. class FormMeta(Meta):
  23. def __init__(self, doctype):
  24. super(FormMeta, self).__init__(doctype)
  25. self.load_assets()
  26. def load_assets(self):
  27. self.add_search_fields()
  28. if not self.istable:
  29. self.add_linked_with()
  30. self.add_code()
  31. self.load_print_formats()
  32. self.load_workflows()
  33. self.load_templates()
  34. def as_dict(self, no_nulls=False):
  35. d = super(FormMeta, self).as_dict(no_nulls=no_nulls)
  36. for k in ("__js", "__css", "__list_js", "__calendar_js", "__map_js",
  37. "__linked_with", "__messages", "__print_formats", "__workflow_docs",
  38. "__form_grid_templates", "__listview_template"):
  39. d[k] = self.get(k)
  40. for i, df in enumerate(d.get("fields")):
  41. for k in ("link_doctype", "search_fields", "is_custom_field"):
  42. df[k] = self.get("fields")[i].get(k)
  43. return d
  44. def add_code(self):
  45. path = os.path.join(get_module_path(self.module), 'doctype', scrub(self.name))
  46. def _get_path(fname):
  47. return os.path.join(path, scrub(fname))
  48. self._add_code(_get_path(self.name + '.js'), '__js')
  49. self._add_code(_get_path(self.name + '.css'), "__css")
  50. self._add_code(_get_path(self.name + '_list.js'), '__list_js')
  51. self._add_code(_get_path(self.name + '_calendar.js'), '__calendar_js')
  52. listview_template = _get_path(self.name + '_list.html')
  53. if os.path.exists(listview_template):
  54. self.set("__listview_template", get_html_format(listview_template))
  55. self.add_code_via_hook("doctype_js", "__js")
  56. self.add_code_via_hook("doctype_list_js", "__list_js")
  57. self.add_custom_script()
  58. self.add_html_templates(path)
  59. def _add_code(self, path, fieldname):
  60. js = frappe.read_file(path)
  61. if js:
  62. self.set(fieldname, (self.get(fieldname) or "") + "\n\n" + render_include(js))
  63. def add_html_templates(self, path):
  64. if self.custom:
  65. return
  66. js = ""
  67. for fname in os.listdir(path):
  68. if fname.endswith(".html"):
  69. with open(os.path.join(path, fname), 'r') as f:
  70. template = unicode(f.read(), "utf-8")
  71. js += html_to_js_template(fname, template)
  72. self.set("__js", (self.get("__js") or "") + js)
  73. def add_code_via_hook(self, hook, fieldname):
  74. for app_name in frappe.get_installed_apps():
  75. code_hook = frappe.get_hooks(hook, default={}, app_name=app_name)
  76. if not code_hook:
  77. continue
  78. files = code_hook.get(self.name, [])
  79. if not isinstance(files, list):
  80. files = [files]
  81. for file in files:
  82. path = frappe.get_app_path(app_name, *file.strip("/").split("/"))
  83. self._add_code(path, fieldname)
  84. def add_custom_script(self):
  85. """embed all require files"""
  86. # custom script
  87. custom = frappe.db.get_value("Custom Script", {"dt": self.name,
  88. "script_type": "Client"}, "script") or ""
  89. self.set("__js", (self.get('__js') or '') + "\n\n" + custom)
  90. def add_search_fields(self):
  91. """add search fields found in the doctypes indicated by link fields' options"""
  92. for df in self.get("fields", {"fieldtype": "Link", "options":["!=", "[Select]"]}):
  93. if df.options:
  94. search_fields = frappe.get_meta(df.options).search_fields
  95. if search_fields:
  96. df.search_fields = map(lambda sf: sf.strip(), search_fields.split(","))
  97. def add_linked_with(self):
  98. """add list of doctypes this doctype is 'linked' with.
  99. Example, for Customer:
  100. {"Address": {"fieldname": "customer"}..}
  101. """
  102. # find fields where this doctype is linked
  103. links = frappe.db.sql("""select parent, fieldname from tabDocField
  104. where (fieldtype="Link" and options=%s)
  105. or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name))
  106. links += frappe.db.sql("""select dt as parent, fieldname from `tabCustom Field`
  107. where (fieldtype="Link" and options=%s)
  108. or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name))
  109. links = dict(links)
  110. ret = {}
  111. for dt in links:
  112. ret[dt] = { "fieldname": links[dt] }
  113. if links:
  114. # find out if linked in a child table
  115. for parent, options in frappe.db.sql("""select parent, options from tabDocField
  116. where fieldtype="Table"
  117. and options in (select name from tabDocType
  118. where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)):
  119. ret[parent] = {"child_doctype": options, "fieldname": links[options] }
  120. if options in ret:
  121. del ret[options]
  122. # find links of parents
  123. links = frappe.db.sql("""select dt from `tabCustom Field`
  124. where (fieldtype="Table" and options=%s)""", (self.name))
  125. links += frappe.db.sql("""select parent from tabDocField
  126. where (fieldtype="Table" and options=%s)""", (self.name))
  127. for dt, in links:
  128. if not dt in ret:
  129. ret[dt] = {"get_parent": True}
  130. self.set("__linked_with", ret, as_value=True)
  131. def load_print_formats(self):
  132. print_formats = frappe.db.sql("""select * FROM `tabPrint Format`
  133. WHERE doc_type=%s AND docstatus<2 and ifnull(disabled, 0)=0""", (self.name,), as_dict=1,
  134. update={"doctype":"Print Format"})
  135. self.set("__print_formats", print_formats, as_value=True)
  136. def load_workflows(self):
  137. # get active workflow
  138. workflow_name = get_workflow_name(self.name)
  139. workflow_docs = []
  140. if workflow_name and frappe.db.exists("Workflow", workflow_name):
  141. workflow = frappe.get_doc("Workflow", workflow_name)
  142. workflow_docs.append(workflow)
  143. for d in workflow.get("states"):
  144. workflow_docs.append(frappe.get_doc("Workflow State", d.state))
  145. self.set("__workflow_docs", workflow_docs, as_value=True)
  146. def load_templates(self):
  147. if not self.custom:
  148. module = load_doctype_module(self.name)
  149. app = module.__name__.split(".")[0]
  150. templates = {}
  151. if hasattr(module, "form_grid_templates"):
  152. for key, path in module.form_grid_templates.iteritems():
  153. templates[key] = get_html_format(frappe.get_app_path(app, path))
  154. self.set("__form_grid_templates", templates)
  155. def set_translations(self, lang):
  156. self.set("__messages", frappe.get_lang_dict("doctype", self.name))
  157. # set translations for grid templates
  158. if self.get("__form_grid_templates"):
  159. for content in self.get("__form_grid_templates").values():
  160. messages = extract_messages_from_code(content)
  161. messages = make_dict_from_messages(messages)
  162. self.get("__messages").update(messages, as_value=True)