25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 
 

643 satır
18 KiB

  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. """
  4. globals attached to frappe module
  5. + some utility functions that should probably be moved
  6. """
  7. from __future__ import unicode_literals
  8. from werkzeug.local import Local, release_local
  9. from werkzeug.exceptions import NotFound
  10. from MySQLdb import ProgrammingError as SQLError
  11. import os, sys, importlib, inspect
  12. import json
  13. import semantic_version
  14. from frappe.core.doctype.print_format.print_format import get_html as get_print_html
  15. from .exceptions import *
  16. local = Local()
  17. class _dict(dict):
  18. """dict like object that exposes keys as attributes"""
  19. def __getattr__(self, key):
  20. ret = self.get(key)
  21. if not ret and key.startswith("__"):
  22. raise AttributeError()
  23. return ret
  24. def __setattr__(self, key, value):
  25. self[key] = value
  26. def __getstate__(self):
  27. return self
  28. def __setstate__(self, d):
  29. self.update(d)
  30. def update(self, d):
  31. """update and return self -- the missing dict feature in python"""
  32. super(_dict, self).update(d)
  33. return self
  34. def copy(self):
  35. return _dict(dict(self).copy())
  36. def _(msg):
  37. """translate object in current lang, if exists"""
  38. if local.lang == "en":
  39. return msg
  40. from frappe.translate import get_full_dict
  41. return get_full_dict(local.lang).get(msg, msg)
  42. def get_lang_dict(fortype, name=None):
  43. if local.lang=="en":
  44. return {}
  45. from frappe.translate import get_dict
  46. return get_dict(fortype, name)
  47. def set_user_lang(user, user_language=None):
  48. from frappe.translate import get_lang_dict
  49. if not user_language:
  50. user_language = db.get_value("User", user, "language")
  51. if user_language:
  52. lang_dict = get_lang_dict()
  53. if user_language in lang_dict:
  54. local.lang = lang_dict[user_language]
  55. # local-globals
  56. db = local("db")
  57. conf = local("conf")
  58. form = form_dict = local("form_dict")
  59. request = local("request")
  60. request_method = local("request_method")
  61. response = local("response")
  62. session = local("session")
  63. user = local("user")
  64. flags = local("flags")
  65. restrictions = local("restrictions")
  66. error_log = local("error_log")
  67. debug_log = local("debug_log")
  68. message_log = local("message_log")
  69. lang = local("lang")
  70. def init(site, sites_path=None):
  71. if getattr(local, "initialised", None):
  72. return
  73. if not sites_path:
  74. sites_path = '.'
  75. local.error_log = []
  76. local.site = site
  77. local.sites_path = sites_path
  78. local.site_path = os.path.join(sites_path, site)
  79. local.message_log = []
  80. local.debug_log = []
  81. local.lang = "en"
  82. local.request_method = request.method if request else None
  83. local.response = _dict({"docs":[]})
  84. local.conf = _dict(get_site_config())
  85. local.initialised = True
  86. local.flags = _dict({})
  87. local.rollback_observers = []
  88. local.module_app = None
  89. local.app_modules = None
  90. local.user = None
  91. local.restrictions = None
  92. local.user_perms = {}
  93. local.test_objects = {}
  94. local.jenv = None
  95. local.jloader =None
  96. local.meta = {}
  97. setup_module_map()
  98. def connect(site=None, db_name=None):
  99. from database import Database
  100. if site:
  101. init(site)
  102. local.db = Database(user=db_name or local.conf.db_name)
  103. local.form_dict = _dict()
  104. local.session = _dict()
  105. set_user("Administrator")
  106. def get_site_config(sites_path=None, site_path=None):
  107. config = {}
  108. sites_path = sites_path or getattr(local, "sites_path", None)
  109. site_path = site_path or getattr(local, "site_path", None)
  110. if sites_path:
  111. common_site_config = os.path.join(sites_path, "common_site_config.json")
  112. if os.path.exists(common_site_config):
  113. config.update(get_file_json(common_site_config))
  114. if site_path:
  115. site_config = os.path.join(site_path, "site_config.json")
  116. if os.path.exists(site_config):
  117. config.update(get_file_json(site_config))
  118. return _dict(config)
  119. def destroy():
  120. """closes connection and releases werkzeug local"""
  121. if db:
  122. db.close()
  123. release_local(local)
  124. _memc = None
  125. # memcache
  126. def cache():
  127. global _memc
  128. if not _memc:
  129. from frappe.memc import MClient
  130. _memc = MClient(['localhost:11211'])
  131. return _memc
  132. def get_traceback():
  133. import utils
  134. return utils.get_traceback()
  135. def errprint(msg):
  136. from utils import cstr
  137. if not request:
  138. print cstr(msg)
  139. error_log.append(cstr(msg))
  140. def log(msg):
  141. if not request:
  142. if conf.get("logging") or False:
  143. print repr(msg)
  144. from utils import cstr
  145. debug_log.append(cstr(msg))
  146. def msgprint(msg, small=0, raise_exception=0, as_table=False):
  147. def _raise_exception():
  148. if raise_exception:
  149. if flags.rollback_on_exception:
  150. db.rollback()
  151. import inspect
  152. if inspect.isclass(raise_exception) and issubclass(raise_exception, Exception):
  153. raise raise_exception, msg
  154. else:
  155. raise ValidationError, msg
  156. if flags.mute_messages:
  157. _raise_exception()
  158. return
  159. from utils import cstr
  160. if as_table and type(msg) in (list, tuple):
  161. msg = '<table border="1px" style="border-collapse: collapse" cellpadding="2px">' + ''.join(['<tr>'+''.join(['<td>%s</td>' % c for c in r])+'</tr>' for r in msg]) + '</table>'
  162. if flags.print_messages:
  163. print "Message: " + repr(msg)
  164. message_log.append((small and '__small:' or '')+cstr(msg or ''))
  165. _raise_exception()
  166. def throw(msg, exc=ValidationError):
  167. msgprint(msg, raise_exception=exc)
  168. def create_folder(path):
  169. if not os.path.exists(path): os.makedirs(path)
  170. def set_user(username):
  171. from frappe.utils.user import User
  172. local.session["user"] = username
  173. local.user = User(username)
  174. local.restrictions = None
  175. local.user_perms = {}
  176. def get_request_header(key, default=None):
  177. return request.headers.get(key, default)
  178. def sendmail(recipients=(), sender="", subject="No Subject", message="No Message", as_markdown=False):
  179. import frappe.utils.email_lib
  180. if as_markdown:
  181. frappe.utils.email_lib.sendmail_md(recipients, sender=sender, subject=subject, msg=message)
  182. else:
  183. frappe.utils.email_lib.sendmail(recipients, sender=sender, subject=subject, msg=message)
  184. logger = None
  185. whitelisted = []
  186. guest_methods = []
  187. def whitelist(allow_guest=False):
  188. """
  189. decorator for whitelisting a function
  190. Note: if the function is allowed to be accessed by a guest user,
  191. it must explicitly be marked as allow_guest=True
  192. for specific roles, set allow_roles = ['Administrator'] etc.
  193. """
  194. def innerfn(fn):
  195. global whitelisted, guest_methods
  196. whitelisted.append(fn)
  197. if allow_guest:
  198. guest_methods.append(fn)
  199. return fn
  200. return innerfn
  201. def only_for(roles):
  202. if not isinstance(roles, (tuple, list)):
  203. roles = (roles,)
  204. roles = set(roles)
  205. myroles = set(get_roles())
  206. if not roles.intersection(myroles):
  207. raise PermissionError
  208. def clear_cache(user=None, doctype=None):
  209. """clear cache"""
  210. import frappe.sessions
  211. if doctype:
  212. import frappe.model.doctype
  213. frappe.model.doctype.clear_cache(doctype)
  214. reset_metadata_version()
  215. elif user:
  216. frappe.sessions.clear_cache(user)
  217. else: # everything
  218. import translate
  219. frappe.sessions.clear_cache()
  220. translate.clear_cache()
  221. reset_metadata_version()
  222. def get_roles(username=None):
  223. from frappe.utils.user import User
  224. if not local.session:
  225. return ["Guest"]
  226. elif not username or username==local.session.user:
  227. return local.user.get_roles()
  228. else:
  229. return User(username).get_roles()
  230. def has_permission(doctype, ptype="read", refdoc=None):
  231. import frappe.permissions
  232. return frappe.permissions.has_permission(doctype, ptype, refdoc)
  233. def is_table(doctype):
  234. tables = cache().get_value("is_table")
  235. if tables==None:
  236. tables = db.sql_list("select name from tabDocType where ifnull(istable,0)=1")
  237. cache().set_value("is_table", tables)
  238. return doctype in tables
  239. def clear_perms(doctype):
  240. db.sql("""delete from tabDocPerm where parent=%s""", doctype)
  241. def reset_perms(doctype):
  242. clear_perms(doctype)
  243. reload_doc(db.get_value("DocType", doctype, "module"),
  244. "DocType", doctype, force=True)
  245. def generate_hash(txt=None):
  246. """Generates random hash for session id"""
  247. import hashlib, time
  248. return hashlib.sha224((txt or "") + str(time.time())).hexdigest()
  249. def reset_metadata_version():
  250. v = generate_hash()
  251. cache().set_value("metadata_version", v)
  252. return v
  253. def get_obj(dt = None, dn = None, doc=None, doclist=None, with_children = True):
  254. from frappe.model.code import get_obj
  255. return get_obj(dt, dn, doc, doclist, with_children)
  256. def doc(doctype=None, name=None, fielddata=None):
  257. from frappe.model.doc import Document
  258. return Document(doctype, name, fielddata)
  259. def new_doc(doctype, parent_doc=None, parentfield=None):
  260. from frappe.model.create_new import get_new_doc
  261. return get_new_doc(doctype, parent_doc, parentfield)
  262. def new_bean(doctype):
  263. from frappe.model.create_new import get_new_doc
  264. return bean([get_new_doc(doctype)])
  265. def doclist(lst=None):
  266. from frappe.model.doclist import DocList
  267. return DocList(lst)
  268. def bean(doctype=None, name=None, copy=None):
  269. """return an instance of the object, wrapped as a Bean (frappe.model.bean)"""
  270. from frappe.model.bean import Bean
  271. if copy:
  272. return Bean(copy_doclist(copy))
  273. else:
  274. return Bean(doctype, name)
  275. def set_value(doctype, docname, fieldname, value):
  276. import frappe.client
  277. return frappe.client.set_value(doctype, docname, fieldname, value)
  278. def get_doclist(doctype, name=None):
  279. return bean(doctype, name).doclist
  280. def get_doc(arg1, arg2=None):
  281. import frappe.model.document
  282. return frappe.model.document.get_doc(arg1, arg2)
  283. def get_doctype(doctype, processed=False):
  284. import frappe.model.doctype
  285. return frappe.model.doctype.get(doctype, processed)
  286. def get_meta(doctype, cached=True):
  287. import frappe.model.meta
  288. return frappe.model.meta.get_meta(doctype, cached=cached)
  289. def delete_doc(doctype=None, name=None, doclist = None, force=0, ignore_doctypes=None,
  290. for_reload=False, ignore_permissions=False):
  291. import frappe.model.delete_doc
  292. if not ignore_doctypes:
  293. ignore_doctypes = []
  294. if isinstance(name, list):
  295. for n in name:
  296. frappe.model.delete_doc.delete_doc(doctype, n, doclist, force, ignore_doctypes,
  297. for_reload, ignore_permissions)
  298. else:
  299. frappe.model.delete_doc.delete_doc(doctype, name, doclist, force, ignore_doctypes,
  300. for_reload, ignore_permissions)
  301. def reload_doc(module, dt=None, dn=None, force=False):
  302. import frappe.modules
  303. return frappe.modules.reload_doc(module, dt, dn, force=force)
  304. def rename_doc(doctype, old, new, debug=0, force=False, merge=False, ignore_permissions=False):
  305. from frappe.model.rename_doc import rename_doc
  306. return rename_doc(doctype, old, new, force=force, merge=merge, ignore_permissions=ignore_permissions)
  307. def insert(doclist):
  308. import frappe.model
  309. return frappe.model.insert(doclist)
  310. def get_module(modulename):
  311. return importlib.import_module(modulename)
  312. def scrub(txt):
  313. return txt.replace(' ','_').replace('-', '_').replace('/', '_').lower()
  314. def get_module_path(module, *joins):
  315. module = scrub(module)
  316. return get_pymodule_path(local.module_app[module] + "." + module, *joins)
  317. def get_app_path(app_name, *joins):
  318. return get_pymodule_path(app_name, *joins)
  319. def get_site_path(*joins):
  320. return os.path.join(local.site_path, *joins)
  321. def get_pymodule_path(modulename, *joins):
  322. joins = [scrub(part) for part in joins]
  323. return os.path.join(os.path.dirname(get_module(scrub(modulename)).__file__), *joins)
  324. def get_module_list(app_name):
  325. return get_file_items(os.path.join(os.path.dirname(get_module(app_name).__file__), "modules.txt"))
  326. def get_all_apps(with_frappe=False, with_internal_apps=True, sites_path=None):
  327. if not sites_path:
  328. sites_path = local.sites_path
  329. apps = get_file_items(os.path.join(sites_path, "apps.txt"), raise_not_found=True)
  330. if with_internal_apps:
  331. apps.extend(get_file_items(os.path.join(local.site_path, "apps.txt")))
  332. if with_frappe:
  333. apps.insert(0, 'frappe')
  334. return apps
  335. def get_installed_apps():
  336. if flags.in_install_db:
  337. return []
  338. installed = json.loads(db.get_global("installed_apps") or "[]")
  339. return installed
  340. def get_hooks(hook=None, app_name=None):
  341. def load_app_hooks(app_name=None):
  342. hooks = {}
  343. for app in [app_name] if app_name else get_installed_apps():
  344. if app=="webnotes": app="frappe"
  345. for item in get_file_items(get_pymodule_path(app, "hooks.txt")):
  346. key, value = item.split("=", 1)
  347. key, value = key.strip(), value.strip()
  348. hooks.setdefault(key, [])
  349. hooks[key].append(value)
  350. return hooks
  351. if app_name:
  352. hooks = _dict(load_app_hooks(app_name))
  353. else:
  354. hooks = _dict(cache().get_value("app_hooks", load_app_hooks))
  355. if hook:
  356. return hooks.get(hook) or []
  357. else:
  358. return hooks
  359. def setup_module_map():
  360. _cache = cache()
  361. if conf.db_name:
  362. local.app_modules = _cache.get_value("app_modules")
  363. local.module_app = _cache.get_value("module_app")
  364. if not local.app_modules:
  365. local.module_app, local.app_modules = {}, {}
  366. for app in get_all_apps(True):
  367. if app=="webnotes": app="frappe"
  368. local.app_modules.setdefault(app, [])
  369. for module in get_module_list(app):
  370. local.module_app[module] = app
  371. local.app_modules[app].append(module)
  372. if conf.db_name:
  373. _cache.set_value("app_modules", local.app_modules)
  374. _cache.set_value("module_app", local.module_app)
  375. def get_file_items(path, raise_not_found=False):
  376. content = read_file(path, raise_not_found=raise_not_found)
  377. if content:
  378. return [p.strip() for p in content.splitlines() if p.strip() and not p.startswith("#")]
  379. else:
  380. return []
  381. def get_file_json(path):
  382. with open(path, 'r') as f:
  383. return json.load(f)
  384. def read_file(path, raise_not_found=False):
  385. from frappe.utils import cstr
  386. if os.path.exists(path):
  387. with open(path, "r") as f:
  388. return cstr(f.read())
  389. elif raise_not_found:
  390. raise IOError("{} Not Found".format(path))
  391. else:
  392. return None
  393. def get_attr(method_string):
  394. modulename = '.'.join(method_string.split('.')[:-1])
  395. methodname = method_string.split('.')[-1]
  396. return getattr(get_module(modulename), methodname)
  397. def call(fn, *args, **kwargs):
  398. if hasattr(fn, 'fnargs'):
  399. fnargs = fn.fnargs
  400. else:
  401. fnargs, varargs, varkw, defaults = inspect.getargspec(fn)
  402. newargs = {}
  403. for a in fnargs:
  404. if a in kwargs:
  405. newargs[a] = kwargs.get(a)
  406. return fn(*args, **newargs)
  407. def make_property_setter(args):
  408. args = _dict(args)
  409. bean([{
  410. 'doctype': "Property Setter",
  411. 'doctype_or_field': args.doctype_or_field or "DocField",
  412. 'doc_type': args.doctype,
  413. 'field_name': args.fieldname,
  414. 'property': args.property,
  415. 'value': args.value,
  416. 'property_type': args.property_type or "Data",
  417. '__islocal': 1
  418. }]).save()
  419. def get_application_home_page(user='Guest'):
  420. """get home page for user"""
  421. hpl = db.sql("""select home_page
  422. from `tabDefault Home Page`
  423. where parent='Control Panel'
  424. and role in ('%s') order by idx asc limit 1""" % "', '".join(get_roles(user)))
  425. if hpl:
  426. return hpl[0][0]
  427. else:
  428. return db.get_value("Control Panel", None, "home_page")
  429. def import_doclist(path, ignore_links=False, ignore_insert=False, insert=False):
  430. from frappe.core.page.data_import_tool import data_import_tool
  431. data_import_tool.import_doclist(path, ignore_links=ignore_links, ignore_insert=ignore_insert, insert=insert)
  432. def copy_doclist(in_doclist):
  433. new_doclist = []
  434. parent_doc = None
  435. for i, d in enumerate(in_doclist):
  436. is_dict = False
  437. if isinstance(d, dict):
  438. is_dict = True
  439. values = _dict(d.copy())
  440. else:
  441. values = _dict(d.fields.copy())
  442. newd = new_doc(values.doctype, parent_doc=(None if i==0 else parent_doc), parentfield=values.parentfield)
  443. newd.fields.update(values)
  444. if i==0:
  445. parent_doc = newd
  446. new_doclist.append(newd.fields if is_dict else newd)
  447. return doclist(new_doclist)
  448. def compare(val1, condition, val2):
  449. import frappe.utils
  450. return frappe.utils.compare(val1, condition, val2)
  451. def respond_as_web_page(title, html, success=None, http_status_code=None):
  452. local.message_title = title
  453. local.message = html
  454. local.message_success = success
  455. local.response['type'] = 'page'
  456. local.response['page_name'] = 'message.html'
  457. if http_status_code:
  458. local.response['http_status_code'] = http_status_code
  459. def build_match_conditions(doctype, as_condition=True):
  460. import frappe.widgets.reportview
  461. return frappe.widgets.reportview.build_match_conditions(doctype, as_condition)
  462. def get_list(doctype, filters=None, fields=None, docstatus=None,
  463. group_by=None, order_by=None, limit_start=0, limit_page_length=None,
  464. as_list=False, debug=False, ignore_permissions=False):
  465. import frappe.model.db_query
  466. return frappe.model.db_query.DatabaseQuery(doctype).execute(filters=filters, fields=fields, docstatus=docstatus,
  467. group_by=group_by, order_by=order_by, limit_start=limit_start, limit_page_length=limit_page_length,
  468. as_list=as_list, debug=debug, ignore_permissions=ignore_permissions)
  469. run_query = get_list
  470. def get_jenv():
  471. if not local.jenv:
  472. from jinja2 import Environment, DebugUndefined
  473. import frappe.utils
  474. # frappe will be loaded last, so app templates will get precedence
  475. jenv = Environment(loader = get_jloader(), undefined=DebugUndefined)
  476. set_filters(jenv)
  477. jenv.globals.update({
  478. "frappe": sys.modules[__name__],
  479. "frappe.utils": frappe.utils,
  480. "_": _
  481. })
  482. local.jenv = jenv
  483. return local.jenv
  484. def get_jloader():
  485. if not local.jloader:
  486. from jinja2 import ChoiceLoader, PackageLoader
  487. apps = get_installed_apps()
  488. apps.remove("frappe")
  489. local.jloader = ChoiceLoader([PackageLoader(app, ".") \
  490. for app in apps + ["frappe"]])
  491. return local.jloader
  492. def set_filters(jenv):
  493. from frappe.utils import global_date_format
  494. from frappe.website.utils import get_hex_shade
  495. from markdown2 import markdown
  496. from json import dumps
  497. jenv.filters["global_date_format"] = global_date_format
  498. jenv.filters["markdown"] = markdown
  499. jenv.filters["json"] = dumps
  500. jenv.filters["get_hex_shade"] = get_hex_shade
  501. # load jenv_filters from hooks.txt
  502. for app in get_all_apps(True):
  503. for jenv_filter in (get_hooks(app_name=app).jenv_filter or []):
  504. filter_name, filter_function = jenv_filter.split(":")
  505. jenv.filters[filter_name] = get_attr(filter_function)
  506. def get_template(path):
  507. return get_jenv().get_template(path)
  508. def get_website_route(doctype, name):
  509. return db.get_value("Website Route", {"ref_doctype": doctype, "docname": name})
  510. def add_version(doclist):
  511. bean({
  512. "doctype": "Version",
  513. "ref_doctype": doclist[0].get("doctype"),
  514. "docname": doclist[0].get("name"),
  515. "doclist_json": json.dumps([d.fields for d in doclist])
  516. }).insert(ignore_permissions=True)