You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

638 lines
17 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
  9. from werkzeug.exceptions import NotFound
  10. from MySQLdb import ProgrammingError as SQLError
  11. import os
  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 hasattr(local, 'translations'):
  36. return local.translations.get(lang, {}).get(msg, msg)
  37. return msg
  38. def set_user_lang(user, user_language=None):
  39. from webnotes.translate import get_lang_dict
  40. if not user_language:
  41. user_language = conn.get_value("Profile", user, "language")
  42. if user_language:
  43. lang_dict = get_lang_dict()
  44. if user_language in lang_dict:
  45. local.lang = lang_dict[user_language]
  46. def load_translations(module, doctype, name):
  47. from webnotes.translate import load_doc_messages
  48. load_doc_messages(module, doctype, name)
  49. # local-globals
  50. conn = local("conn")
  51. conf = local("conf")
  52. form = form_dict = local("form_dict")
  53. request = local("request")
  54. request_method = local("request_method")
  55. response = local("response")
  56. _response = local("_response")
  57. session = local("session")
  58. user = local("user")
  59. flags = local("flags")
  60. error_log = local("error_log")
  61. debug_log = local("debug_log")
  62. message_log = local("message_log")
  63. lang = local("lang")
  64. def init(site=None):
  65. if getattr(local, "initialised", None):
  66. return
  67. local.error_log = []
  68. local.site = site
  69. local.message_log = []
  70. local.debug_log = []
  71. local.response = _dict({})
  72. local.lang = "en"
  73. local.request_method = request.method if request else None
  74. local.conf = get_conf(site)
  75. local.initialised = True
  76. local.flags = _dict({})
  77. local.rollback_observers = []
  78. def destroy():
  79. """closes connection and releases werkzeug local"""
  80. if conn:
  81. conn.close()
  82. from werkzeug.local import release_local
  83. release_local(local)
  84. _memc = None
  85. test_objects = {}
  86. # memcache
  87. def cache():
  88. global _memc
  89. if not _memc:
  90. from webnotes.memc import MClient
  91. _memc = MClient(['localhost:11211'])
  92. return _memc
  93. class DuplicateEntryError(Exception): pass
  94. class ValidationError(Exception): pass
  95. class AuthenticationError(Exception): pass
  96. class PermissionError(Exception): pass
  97. class UnknownDomainError(Exception): pass
  98. class SessionStopped(Exception): pass
  99. class MappingMismatchError(ValidationError): pass
  100. class InvalidStatusError(ValidationError): pass
  101. class DoesNotExistError(ValidationError): pass
  102. class MandatoryError(ValidationError): pass
  103. class InvalidSignatureError(ValidationError): pass
  104. def getTraceback():
  105. import utils
  106. return utils.getTraceback()
  107. def errprint(msg):
  108. from utils import cstr
  109. if not request:
  110. print cstr(msg)
  111. error_log.append(cstr(msg))
  112. def log(msg):
  113. if not request:
  114. import conf
  115. if conf.get("logging") or False:
  116. print repr(msg)
  117. from utils import cstr
  118. debug_log.append(cstr(msg))
  119. def msgprint(msg, small=0, raise_exception=0, as_table=False):
  120. def _raise_exception():
  121. if raise_exception:
  122. if flags.rollback_on_exception:
  123. conn.rollback()
  124. import inspect
  125. if inspect.isclass(raise_exception) and issubclass(raise_exception, Exception):
  126. raise raise_exception, msg
  127. else:
  128. raise ValidationError, msg
  129. if flags.mute_messages:
  130. _raise_exception()
  131. return
  132. from utils import cstr
  133. if as_table and type(msg) in (list, tuple):
  134. 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>'
  135. if flags.print_messages:
  136. print "Message: " + repr(msg)
  137. message_log.append((small and '__small:' or '')+cstr(msg or ''))
  138. _raise_exception()
  139. def throw(msg, exc=ValidationError):
  140. msgprint(msg, raise_exception=exc)
  141. def create_folder(path):
  142. import os
  143. try:
  144. os.makedirs(path)
  145. except OSError, e:
  146. if e.args[0]!=17:
  147. raise
  148. def create_symlink(source_path, link_path):
  149. import os
  150. try:
  151. os.symlink(source_path, link_path)
  152. except OSError, e:
  153. if e.args[0]!=17:
  154. raise
  155. def remove_file(path):
  156. import os
  157. try:
  158. os.remove(path)
  159. except OSError, e:
  160. if e.args[0]!=2:
  161. raise
  162. def connect(db_name=None, password=None, site=None):
  163. import webnotes.db
  164. init(site=site)
  165. local.conn = webnotes.db.Database(user=db_name, password=password)
  166. local.response = _dict()
  167. local.form_dict = _dict()
  168. local.session = _dict()
  169. set_user("Administrator")
  170. def set_user(username):
  171. import webnotes.profile
  172. local.session["user"] = username
  173. local.user = webnotes.profile.Profile(username)
  174. def get_request_header(key, default=None):
  175. try:
  176. return request.headers.get(key, default)
  177. except Exception, e:
  178. return None
  179. logger = None
  180. def get_db_password(db_name):
  181. """get db password from conf"""
  182. if 'get_db_password' in conf:
  183. return conf.get_db_password(db_name)
  184. elif 'db_password' in conf:
  185. return conf.db_password
  186. else:
  187. return db_name
  188. whitelisted = []
  189. guest_methods = []
  190. def whitelist(allow_guest=False, allow_roles=None):
  191. """
  192. decorator for whitelisting a function
  193. Note: if the function is allowed to be accessed by a guest user,
  194. it must explicitly be marked as allow_guest=True
  195. for specific roles, set allow_roles = ['Administrator'] etc.
  196. """
  197. def innerfn(fn):
  198. global whitelisted, guest_methods
  199. whitelisted.append(fn)
  200. if allow_guest:
  201. guest_methods.append(fn)
  202. if allow_roles:
  203. roles = get_roles()
  204. allowed = False
  205. for role in allow_roles:
  206. if role in roles:
  207. allowed = True
  208. break
  209. if not allowed:
  210. raise PermissionError, "Method not allowed"
  211. return fn
  212. return innerfn
  213. class HashAuthenticatedCommand(object):
  214. def __init__(self):
  215. if hasattr(self, 'command'):
  216. import inspect
  217. self.fnargs, varargs, varkw, defaults = inspect.getargspec(self.command)
  218. self.fnargs.append('signature')
  219. def __call__(self, *args, **kwargs):
  220. signature = kwargs.pop('signature')
  221. if self.verify_signature(kwargs, signature):
  222. return self.command(*args, **kwargs)
  223. else:
  224. self.signature_error()
  225. def command(self):
  226. raise NotImplementedError
  227. def signature_error(self):
  228. raise InvalidSignatureError
  229. def get_signature(self, params, ignore_params=None):
  230. import hmac
  231. params = self.get_param_string(params, ignore_params=ignore_params)
  232. secret = "secret"
  233. signature = hmac.new(self.get_nonce())
  234. signature.update(secret)
  235. signature.update(params)
  236. return signature.hexdigest()
  237. def get_param_string(self, params, ignore_params=None):
  238. if not ignore_params:
  239. ignore_params = []
  240. params = [unicode(param) for param in params if param not in ignore_params]
  241. params = ''.join(params)
  242. return params
  243. def get_nonce():
  244. raise NotImplementedError
  245. def verify_signature(self, params, signature):
  246. if signature == self.get_signature(params):
  247. return True
  248. return False
  249. def clear_cache(user=None, doctype=None):
  250. """clear cache"""
  251. if doctype:
  252. from webnotes.model.doctype import clear_cache
  253. clear_cache(doctype)
  254. reset_metadata_version()
  255. elif user:
  256. from webnotes.sessions import clear_cache
  257. clear_cache(user)
  258. else: # everything
  259. from webnotes.sessions import clear_cache
  260. clear_cache()
  261. reset_metadata_version()
  262. def get_roles(username=None):
  263. import webnotes.profile
  264. if not username or username==session.user:
  265. return user.get_roles()
  266. else:
  267. return webnotes.profile.Profile(username).get_roles()
  268. def check_admin_or_system_manager():
  269. if ("System Manager" not in get_roles()) and \
  270. (session.user!="Administrator"):
  271. msgprint("Only Allowed for Role System Manager or Administrator", raise_exception=True)
  272. def has_permission(doctype, ptype="read", refdoc=None):
  273. """check if user has permission"""
  274. from webnotes.utils import cint
  275. if session.user=="Administrator" or conn.get_value("DocType", doctype, "istable")==1:
  276. return True
  277. meta = get_doctype(doctype)
  278. # get user permissions
  279. user_roles = get_roles()
  280. perms = [p for p in meta.get({"doctype": "DocPerm"})
  281. if cint(p.get(ptype))==1 and cint(p.permlevel)==0 and (p.role=="All" or p.role in user_roles)]
  282. if refdoc:
  283. return has_match(meta, perms, refdoc)
  284. else:
  285. return perms and True or False
  286. def has_match(meta, perms, refdoc):
  287. from webnotes.defaults import get_user_default_as_list
  288. if isinstance(refdoc, basestring):
  289. refdoc = doc(meta[0].name, refdoc)
  290. match_failed = {}
  291. for p in perms:
  292. if p.match:
  293. if ":" in p.match:
  294. keys = p.match.split(":")
  295. else:
  296. keys = [p.match, p.match]
  297. if refdoc.fields.get(keys[0],"[No Value]") in get_user_default_as_list(keys[1]):
  298. return True
  299. else:
  300. match_failed[keys[0]] = refdoc.fields.get(keys[0],"[No Value]")
  301. else:
  302. # found a permission without a match
  303. return True
  304. # no valid permission found
  305. if match_failed:
  306. msg = _("Not allowed for: ")
  307. for key in match_failed:
  308. msg += "\n" + (meta.get_field(key) and meta.get_label(key) or key) \
  309. + " = " + (match_failed[key] or "None")
  310. msgprint(msg)
  311. return False
  312. def generate_hash():
  313. """Generates random hash for session id"""
  314. import hashlib, time
  315. return hashlib.sha224(str(time.time())).hexdigest()
  316. def reset_metadata_version():
  317. v = generate_hash()
  318. cache().set_value("metadata_version", v)
  319. return v
  320. def get_obj(dt = None, dn = None, doc=None, doclist=[], with_children = True):
  321. from webnotes.model.code import get_obj
  322. return get_obj(dt, dn, doc, doclist, with_children)
  323. def doc(doctype=None, name=None, fielddata=None):
  324. from webnotes.model.doc import Document
  325. return Document(doctype, name, fielddata)
  326. def new_doc(doctype, parent_doc=None, parentfield=None):
  327. from webnotes.model.create_new import get_new_doc
  328. return get_new_doc(doctype, parent_doc, parentfield)
  329. def new_bean(doctype):
  330. from webnotes.model.create_new import get_new_doc
  331. return bean([get_new_doc(doctype)])
  332. def doclist(lst=None):
  333. from webnotes.model.doclist import DocList
  334. return DocList(lst)
  335. def bean(doctype=None, name=None, copy=None):
  336. """return an instance of the object, wrapped as a Bean (webnotes.model.bean)"""
  337. from webnotes.model.bean import Bean
  338. if copy:
  339. return Bean(copy_doclist(copy))
  340. else:
  341. return Bean(doctype, name)
  342. def set_value(doctype, docname, fieldname, value):
  343. import webnotes.client
  344. return webnotes.client.set_value(doctype, docname, fieldname, value)
  345. def get_doclist(doctype, name=None):
  346. return bean(doctype, name).doclist
  347. def get_doctype(doctype, processed=False):
  348. import webnotes.model.doctype
  349. return webnotes.model.doctype.get(doctype, processed)
  350. def delete_doc(doctype=None, name=None, doclist = None, force=0, ignore_doctypes=None, for_reload=False, ignore_permissions=False):
  351. import webnotes.model.utils
  352. if not ignore_doctypes:
  353. ignore_doctypes = []
  354. if isinstance(name, list):
  355. for n in name:
  356. webnotes.model.utils.delete_doc(doctype, n, doclist, force, ignore_doctypes, for_reload, ignore_permissions)
  357. else:
  358. webnotes.model.utils.delete_doc(doctype, name, doclist, force, ignore_doctypes, for_reload, ignore_permissions)
  359. def clear_perms(doctype):
  360. conn.sql("""delete from tabDocPerm where parent=%s""", doctype)
  361. def reset_perms(doctype):
  362. clear_perms(doctype)
  363. reload_doc(conn.get_value("DocType", doctype, "module"), "DocType", doctype, force=True)
  364. def reload_doc(module, dt=None, dn=None, plugin=None, force=False):
  365. import webnotes.modules
  366. return webnotes.modules.reload_doc(module, dt, dn, plugin=plugin, force=force)
  367. def rename_doc(doctype, old, new, debug=0, force=False, merge=False):
  368. from webnotes.model.rename_doc import rename_doc
  369. return rename_doc(doctype, old, new, force=force, merge=merge)
  370. def insert(doclist):
  371. import webnotes.model
  372. return webnotes.model.insert(doclist)
  373. def get_module(modulename):
  374. __import__(modulename)
  375. import sys
  376. return sys.modules[modulename]
  377. def get_method(method_string):
  378. modulename = '.'.join(method_string.split('.')[:-1])
  379. methodname = method_string.split('.')[-1]
  380. return getattr(get_module(modulename), methodname)
  381. def make_property_setter(args):
  382. args = _dict(args)
  383. bean([{
  384. 'doctype': "Property Setter",
  385. 'doctype_or_field': args.doctype_or_field or "DocField",
  386. 'doc_type': args.doctype,
  387. 'field_name': args.fieldname,
  388. 'property': args.property,
  389. 'value': args.value,
  390. 'property_type': args.property_type or "Data",
  391. '__islocal': 1
  392. }]).save()
  393. def get_application_home_page(user='Guest'):
  394. """get home page for user"""
  395. hpl = conn.sql("""select home_page
  396. from `tabDefault Home Page`
  397. where parent='Control Panel'
  398. and role in ('%s') order by idx asc limit 1""" % "', '".join(get_roles(user)))
  399. if hpl:
  400. return hpl[0][0]
  401. else:
  402. return conn.get_value("Control Panel", None, "home_page")
  403. def copy_doclist(in_doclist):
  404. new_doclist = []
  405. parent_doc = None
  406. for i, d in enumerate(in_doclist):
  407. is_dict = False
  408. if isinstance(d, dict):
  409. is_dict = True
  410. values = _dict(d.copy())
  411. else:
  412. values = _dict(d.fields.copy())
  413. newd = new_doc(values.doctype, parent_doc=(None if i==0 else parent_doc), parentfield=values.parentfield)
  414. newd.fields.update(values)
  415. if i==0:
  416. parent_doc = newd
  417. new_doclist.append(newd.fields if is_dict else newd)
  418. return doclist(new_doclist)
  419. def compare(val1, condition, val2):
  420. import webnotes.utils
  421. return webnotes.utils.compare(val1, condition, val2)
  422. def repsond_as_web_page(title, html):
  423. local.message_title = title
  424. local.message = "<h3>" + title + "</h3>" + html
  425. local.response['type'] = 'page'
  426. local.response['page_name'] = 'message.html'
  427. def load_json(obj):
  428. if isinstance(obj, basestring):
  429. import json
  430. try:
  431. obj = json.loads(obj)
  432. except ValueError:
  433. pass
  434. return obj
  435. def build_match_conditions(doctype, fields=None, as_condition=True):
  436. import webnotes.widgets.reportview
  437. return webnotes.widgets.reportview.build_match_conditions(doctype, fields, as_condition)
  438. def get_list(doctype, filters=None, fields=None, docstatus=None,
  439. group_by=None, order_by=None, limit_start=0, limit_page_length=None,
  440. as_list=False, debug=False):
  441. import webnotes.widgets.reportview
  442. return webnotes.widgets.reportview.execute(doctype, filters=filters, fields=fields, docstatus=docstatus,
  443. group_by=group_by, order_by=order_by, limit_start=limit_start, limit_page_length=limit_page_length,
  444. as_list=as_list, debug=debug)
  445. def get_jenv():
  446. from jinja2 import Environment, FileSystemLoader
  447. from webnotes.utils import get_base_path, global_date_format
  448. from markdown2 import markdown
  449. from json import dumps
  450. jenv = Environment(loader = FileSystemLoader(get_base_path()))
  451. jenv.filters["global_date_format"] = global_date_format
  452. jenv.filters["markdown"] = markdown
  453. jenv.filters["json"] = dumps
  454. return jenv
  455. def get_template(path):
  456. return get_jenv().get_template(path)
  457. _config = None
  458. def get_config():
  459. global _config
  460. if not _config:
  461. import webnotes.utils, json
  462. _config = _dict()
  463. def update_config(path):
  464. try:
  465. with open(path, "r") as configfile:
  466. this_config = json.loads(configfile.read())
  467. for key, val in this_config.items():
  468. if isinstance(val, dict):
  469. _config.setdefault(key, _dict()).update(val)
  470. else:
  471. _config[key] = val
  472. except IOError:
  473. pass
  474. update_config(webnotes.utils.get_path("lib", "config.json"))
  475. update_config(webnotes.utils.get_path("app", "config.json"))
  476. return _config
  477. def get_conf(site):
  478. # TODO Should be heavily cached!
  479. import conf
  480. site_config = _dict({})
  481. conf = site_config.update(conf.__dict__)
  482. if not conf.get("files_path"):
  483. conf["files_path"] = os.path.join("public", "files")
  484. if not conf.get("plugins_path"):
  485. conf["plugins_path"] = "plugins"
  486. if conf.sites_dir and site:
  487. out = get_site_config(conf.sites_dir, site)
  488. if not out:
  489. raise NotFound()
  490. site_config.update(out)
  491. site_config["site_config"] = out
  492. site_config['site'] = site
  493. return site_config
  494. else:
  495. return conf
  496. def get_site_config(sites_dir, site):
  497. conf_path = get_conf_path(sites_dir, site)
  498. if os.path.exists(conf_path):
  499. with open(conf_path, 'r') as f:
  500. return json.load(f)
  501. def get_conf_path(sites_dir, site):
  502. from webnotes.utils import get_site_base_path
  503. return os.path.join(get_site_base_path(sites_dir=sites_dir,
  504. hostname=site), 'site_config.json')
  505. def validate_versions():
  506. config = get_config()
  507. framework_version = semantic_version.Version(config['framework_version'])
  508. spec = semantic_version.Spec(config['requires_framework_version'])
  509. if not spec.match(framework_version):
  510. raise Exception, "Framework version out of sync"