您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

407 行
12 KiB

  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. """
  4. Get metadata (main doctype with fields and permissions with all table doctypes)
  5. - if exists in cache, get it from cache
  6. - add custom fields
  7. - override properties from PropertySetter
  8. - sort based on prev_field
  9. - optionally, post process (add js, css, select fields), or without
  10. """
  11. from __future__ import unicode_literals
  12. # imports
  13. import json
  14. import webnotes
  15. import webnotes.model
  16. import webnotes.model.doc
  17. import webnotes.model.doclist
  18. from webnotes.utils import cint
  19. doctype_cache = webnotes.local('doctype_doctype_cache')
  20. docfield_types = webnotes.local('doctype_docfield_types')
  21. # doctype_cache = {}
  22. # docfield_types = None
  23. def get(doctype, processed=False, cached=True):
  24. """return doclist"""
  25. if cached:
  26. doclist = from_cache(doctype, processed)
  27. if doclist:
  28. if processed:
  29. update_language(doclist)
  30. return DocTypeDocList(doclist)
  31. load_docfield_types()
  32. # main doctype doclist
  33. doclist = get_doctype_doclist(doctype)
  34. # add doctypes of table fields
  35. table_types = [d.options for d in doclist \
  36. if d.doctype=='DocField' and d.fieldtype=='Table']
  37. for table_doctype in table_types:
  38. doclist += get_doctype_doclist(table_doctype)
  39. if processed:
  40. add_code(doctype, doclist)
  41. expand_selects(doclist)
  42. add_print_formats(doclist)
  43. add_search_fields(doclist)
  44. add_workflows(doclist)
  45. add_linked_with(doclist)
  46. to_cache(doctype, processed, doclist)
  47. if processed:
  48. update_language(doclist)
  49. return DocTypeDocList(doclist)
  50. def load_docfield_types():
  51. webnotes.local.doctype_docfield_types = dict(webnotes.conn.sql("""select fieldname, fieldtype from tabDocField
  52. where parent='DocField'"""))
  53. def add_workflows(doclist):
  54. from webnotes.model.workflow import get_workflow_name
  55. doctype = doclist[0].name
  56. # get active workflow
  57. workflow_name = get_workflow_name(doctype)
  58. if workflow_name and webnotes.conn.exists("Workflow", workflow_name):
  59. doclist += webnotes.get_doclist("Workflow", workflow_name)
  60. # add workflow states (for icons and style)
  61. for state in map(lambda d: d.state, doclist.get({"doctype":"Workflow Document State"})):
  62. doclist += webnotes.get_doclist("Workflow State", state)
  63. def get_doctype_doclist(doctype):
  64. """get doclist of single doctype"""
  65. doclist = webnotes.get_doclist('DocType', doctype)
  66. add_custom_fields(doctype, doclist)
  67. apply_property_setters(doctype, doclist)
  68. sort_fields(doclist)
  69. return doclist
  70. def sort_fields(doclist):
  71. """sort on basis of previous_field"""
  72. from webnotes.model.doclist import DocList
  73. newlist = DocList([])
  74. pending = doclist.get({"doctype":"DocField"})
  75. if doclist[0].get("_idx"):
  76. for fieldname in json.loads(doclist[0].get("_idx")):
  77. d = doclist.get({"fieldname": fieldname})
  78. if d:
  79. newlist.append(d[0])
  80. pending.remove(d[0])
  81. else:
  82. maxloops = 20
  83. while (pending and maxloops>0):
  84. maxloops -= 1
  85. for d in pending[:]:
  86. if d.previous_field:
  87. # field already added
  88. for n in newlist:
  89. if n.fieldname==d.previous_field:
  90. newlist.insert(newlist.index(n)+1, d)
  91. pending.remove(d)
  92. break
  93. else:
  94. newlist.append(d)
  95. pending.remove(d)
  96. # recurring at end
  97. if pending:
  98. newlist += pending
  99. # renum
  100. idx = 1
  101. for d in newlist:
  102. d.idx = idx
  103. idx += 1
  104. doclist.get({"doctype":["!=", "DocField"]}).extend(newlist)
  105. def apply_property_setters(doctype, doclist):
  106. for ps in webnotes.conn.sql("""select * from `tabProperty Setter` where
  107. doc_type=%s""", (doctype,), as_dict=1):
  108. if ps['doctype_or_field']=='DocType':
  109. if ps.get('property_type', None) in ('Int', 'Check'):
  110. ps['value'] = cint(ps['value'])
  111. doclist[0].fields[ps['property']] = ps['value']
  112. else:
  113. docfield = filter(lambda d: d.doctype=="DocField" and d.fieldname==ps['field_name'],
  114. doclist)
  115. if not docfield: continue
  116. if docfield_types.get(ps['property'], None) in ('Int', 'Check'):
  117. ps['value'] = cint(ps['value'])
  118. docfield[0].fields[ps['property']] = ps['value']
  119. def add_custom_fields(doctype, doclist):
  120. try:
  121. res = webnotes.conn.sql("""SELECT * FROM `tabCustom Field`
  122. WHERE dt = %s AND docstatus < 2""", (doctype,), as_dict=1)
  123. except Exception, e:
  124. if e.args[0]==1146:
  125. return doclist
  126. else:
  127. raise
  128. for r in res:
  129. custom_field = webnotes.model.doc.Document(fielddata=r)
  130. # convert to DocField
  131. custom_field.fields.update({
  132. 'doctype': 'DocField',
  133. 'parent': doctype,
  134. 'parentfield': 'fields',
  135. 'parenttype': 'DocType',
  136. '__custom_field': 1
  137. })
  138. doclist.append(custom_field)
  139. return doclist
  140. def add_linked_with(doclist):
  141. """add list of doctypes this doctype is 'linked' with"""
  142. doctype = doclist[0].name
  143. links = webnotes.conn.sql("""select parent, fieldname from tabDocField
  144. where (fieldtype="Link" and options=%s)
  145. or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype))
  146. links += webnotes.conn.sql("""select dt as parent, fieldname from `tabCustom Field`
  147. where (fieldtype="Link" and options=%s)
  148. or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype))
  149. links = dict(links)
  150. if not links:
  151. return {}
  152. ret = {}
  153. for dt in links:
  154. ret[dt] = { "fieldname": links[dt] }
  155. for grand_parent, options in webnotes.conn.sql("""select parent, options from tabDocField
  156. where fieldtype="Table"
  157. and options in (select name from tabDocType
  158. where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)):
  159. ret[grand_parent] = {"child_doctype": options, "fieldname": links[options] }
  160. if options in ret:
  161. del ret[options]
  162. doclist[0].fields["__linked_with"] = ret
  163. def from_cache(doctype, processed):
  164. """ load doclist from cache.
  165. sets flag __from_cache in first doc of doclist if loaded from cache"""
  166. # from memory
  167. if doctype_cache and not processed and doctype in doctype_cache:
  168. doclist = doctype_cache[doctype]
  169. doclist[0].fields["__from_cache"] = 1
  170. return doclist
  171. doclist = webnotes.cache().get_value(cache_name(doctype, processed))
  172. if doclist:
  173. from webnotes.model.doclist import DocList
  174. doclist = DocList([webnotes.model.doc.Document(fielddata=d)
  175. for d in doclist])
  176. doclist[0].fields["__from_cache"] = 1
  177. return doclist
  178. def to_cache(doctype, processed, doclist):
  179. if not doctype_cache:
  180. webnotes.local.doctype_doctype_cache = {}
  181. webnotes.cache().set_value(cache_name(doctype, processed),
  182. [d.fields for d in doclist])
  183. if not processed:
  184. doctype_cache[doctype] = doclist
  185. def cache_name(doctype, processed):
  186. """returns cache key"""
  187. suffix = ""
  188. if processed:
  189. suffix = ":Raw"
  190. return "doctype:" + doctype + suffix
  191. def clear_cache(doctype=None):
  192. def clear_single(dt):
  193. webnotes.cache().delete_value(cache_name(dt, False))
  194. webnotes.cache().delete_value(cache_name(dt, True))
  195. if doctype_cache and (dt in doctype_cache):
  196. del doctype_cache[dt]
  197. if doctype:
  198. clear_single(doctype)
  199. # clear all parent doctypes
  200. for dt in webnotes.conn.sql("""select parent from tabDocField
  201. where fieldtype="Table" and options=%s""", (doctype,)):
  202. clear_single(dt[0])
  203. # clear all notifications
  204. from webnotes.core.doctype.notification_count.notification_count import delete_notification_count_for
  205. delete_notification_count_for(doctype)
  206. else:
  207. # clear all
  208. for dt in webnotes.conn.sql("""select name from tabDocType"""):
  209. clear_single(dt[0])
  210. def add_code(doctype, doclist):
  211. import os
  212. from webnotes.modules import scrub, get_module_path
  213. doc = doclist[0]
  214. path = os.path.join(get_module_path(doc.module), 'doctype', scrub(doc.name))
  215. def _add_code(fname, fieldname):
  216. fpath = os.path.join(path, fname)
  217. if os.path.exists(fpath):
  218. with open(fpath, 'r') as f:
  219. doc.fields[fieldname] = f.read()
  220. _add_code(scrub(doc.name) + '.js', '__js')
  221. _add_code(scrub(doc.name) + '.css', '__css')
  222. _add_code('%s_list.js' % scrub(doc.name), '__list_js')
  223. _add_code('%s_calendar.js' % scrub(doc.name), '__calendar_js')
  224. _add_code('%s_map.js' % scrub(doc.name), '__map_js')
  225. add_embedded_js(doc)
  226. def add_embedded_js(doc):
  227. """embed all require files"""
  228. import re, os
  229. from webnotes import conf
  230. js = doc.fields.get('__js') or ''
  231. # custom script
  232. custom = webnotes.conn.get_value("Custom Script", {"dt": doc.name,
  233. "script_type": "Client"}, "script") or ""
  234. js = (js + '\n' + custom).encode("utf-8")
  235. if "{% include" in js:
  236. js = webnotes.get_jenv().from_string(js).render()
  237. doc.fields["__js"] = js
  238. def expand_selects(doclist):
  239. for d in filter(lambda d: d.fieldtype=='Select' \
  240. and (d.options or '').startswith('link:'), doclist):
  241. doctype = d.options.split("\n")[0][5:]
  242. d.link_doctype = doctype
  243. d.options = '\n'.join([''] + [o.name for o in webnotes.conn.sql("""select
  244. name from `tab%s` where docstatus<2 order by name asc""" % doctype, as_dict=1)])
  245. def add_print_formats(doclist):
  246. print_formats = webnotes.conn.sql("""select * FROM `tabPrint Format`
  247. WHERE doc_type=%s AND docstatus<2""", (doclist[0].name,), as_dict=1)
  248. for pf in print_formats:
  249. doclist.append(webnotes.model.doc.Document('Print Format', fielddata=pf))
  250. def get_property(dt, prop, fieldname=None):
  251. """get a doctype property"""
  252. doctypelist = get(dt)
  253. if fieldname:
  254. field = doctypelist.get_field(fieldname)
  255. return field and field.fields.get(prop) or None
  256. else:
  257. return doctypelist[0].fields.get(prop)
  258. def get_link_fields(doctype):
  259. """get docfields of links and selects with "link:" """
  260. doctypelist = get(doctype)
  261. return doctypelist.get({"fieldtype":"Link"}).extend(doctypelist.get({"fieldtype":"Select",
  262. "options": "^link:"}))
  263. def add_validators(doctype, doclist):
  264. for validator in webnotes.conn.sql("""select name from `tabDocType Validator` where
  265. for_doctype=%s""", (doctype,), as_dict=1):
  266. doclist.extend(webnotes.get_doclist('DocType Validator', validator.name))
  267. def add_search_fields(doclist):
  268. """add search fields found in the doctypes indicated by link fields' options"""
  269. for lf in doclist.get({"fieldtype": "Link", "options":["!=", "[Select]"]}):
  270. if lf.options:
  271. search_fields = get(lf.options)[0].search_fields
  272. if search_fields:
  273. lf.search_fields = map(lambda sf: sf.strip(), search_fields.split(","))
  274. def update_language(doclist):
  275. """update language"""
  276. if webnotes.local.lang != 'en':
  277. doclist[0].fields["__messages"] = webnotes.get_lang_dict("doctype", doclist[0].name)
  278. class DocTypeDocList(webnotes.model.doclist.DocList):
  279. def get_field(self, fieldname, parent=None, parentfield=None):
  280. filters = {"doctype":"DocField"}
  281. if isinstance(fieldname, dict):
  282. filters.update(fieldname)
  283. else:
  284. filters["fieldname"] = fieldname
  285. # if parentfield, get the name of the parent table
  286. if parentfield:
  287. parent = self.get_options(parentfield)
  288. if parent:
  289. filters["parent"] = parent
  290. else:
  291. filters["parent"] = self[0].name
  292. fields = self.get(filters)
  293. if fields:
  294. return fields[0]
  295. def get_fieldnames(self, filters=None):
  296. if not filters: filters = {}
  297. filters.update({"doctype": "DocField", "parent": self[0].name})
  298. return map(lambda df: df.fieldname, self.get(filters))
  299. def get_options(self, fieldname, parent=None, parentfield=None):
  300. return self.get_field(fieldname, parent, parentfield).options
  301. def get_label(self, fieldname, parent=None, parentfield=None):
  302. return self.get_field(fieldname, parent, parentfield).label
  303. def get_table_fields(self):
  304. return self.get({"doctype": "DocField", "fieldtype": "Table"})
  305. def get_parent_doclist(self):
  306. return webnotes.doclist([self[0]] + self.get({"parent": self[0].name}))
  307. def get_restricted_fields(self, restricted_types):
  308. restricted_fields = self.get({
  309. "doctype":"DocField",
  310. "fieldtype":"Link",
  311. "parent": self[0].name,
  312. "ignore_restrictions":("!=", 1),
  313. "options":("in", restricted_types)
  314. })
  315. if self[0].name in restricted_types:
  316. restricted_fields.append(webnotes._dict({"label":"Name", "fieldname":"name", "options": self[0].name}))
  317. return restricted_fields
  318. def get_permissions(self, user=None):
  319. user_roles = webnotes.get_roles(user)
  320. return [p for p in self.get({"doctype": "DocPerm"})
  321. if cint(p.permlevel)==0 and (p.role=="All" or p.role in user_roles)]