選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

602 行
16 KiB

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