25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

__init__.py 15 KiB

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