No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

__init__.py 18 KiB

hace 11 años
hace 12 años
hace 11 años
hace 11 años
hace 12 años
hace 12 años
hace 12 años
hace 12 años
hace 12 años
hace 12 años
hace 12 años
hace 11 años
hace 12 años
hace 12 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 12 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
hace 11 años
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  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)