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.

webutils.py 12 KiB

11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
12 vuotta sitten
11 vuotta sitten
12 vuotta sitten
11 vuotta sitten
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import webnotes
  5. import json, os, time
  6. from webnotes import _
  7. import webnotes.utils
  8. from webnotes.utils import get_request_site_address, encode, cint
  9. from webnotes.model import default_fields
  10. from webnotes.model.controller import DocListController
  11. from urllib import quote
  12. import mimetypes
  13. from webnotes.website.doctype.website_sitemap.website_sitemap import add_to_sitemap, update_sitemap, remove_sitemap
  14. # for access as webnotes.webutils.fn
  15. from webnotes.website.doctype.website_sitemap_permission.website_sitemap_permission \
  16. import get_access
  17. class PageNotFoundError(Exception): pass
  18. def render(page_name):
  19. """render html page"""
  20. page_name = scrub_page_name(page_name)
  21. try:
  22. data = render_page(page_name)
  23. except Exception:
  24. page_name = "error"
  25. data = render_page(page_name)
  26. data = insert_traceback(data)
  27. data = set_content_type(data, page_name)
  28. webnotes._response.data = data
  29. webnotes._response.headers["Page Name"] = page_name
  30. def render_page(page_name):
  31. """get page html"""
  32. cache_key = ("page_context:{}" if is_ajax() else "page:{}").format(page_name)
  33. out = None
  34. # try memcache
  35. if can_cache():
  36. out = webnotes.cache().get_value(cache_key)
  37. if is_ajax():
  38. out = out.get("data")
  39. if out:
  40. webnotes._response.headers["From Cache"] = True
  41. return out
  42. return build(page_name)
  43. def build(page_name):
  44. if not webnotes.conn:
  45. webnotes.connect()
  46. build_method = (build_json if is_ajax() else build_page)
  47. try:
  48. return build_method(page_name)
  49. except webnotes.DoesNotExistError:
  50. hooks = webnotes.get_hooks()
  51. if hooks.website_catch_all:
  52. return build_method(hooks.website_catch_all[0])
  53. else:
  54. return build_method("404")
  55. def build_json(page_name):
  56. return get_context(page_name).data
  57. def build_page(page_name):
  58. context = get_context(page_name)
  59. context.update(get_website_settings())
  60. jenv = webnotes.get_jenv()
  61. html = jenv.get_template(context.base_template_path).render(context)
  62. if can_cache(context.no_cache):
  63. webnotes.cache().set_value("page:" + page_name, html)
  64. return html
  65. def get_context(page_name):
  66. context = None
  67. cache_key = "page_context:{}".format(page_name)
  68. # try from memcache
  69. if can_cache():
  70. context = webnotes.cache().get_value(cache_key)
  71. if not context:
  72. sitemap_options = build_sitemap_options(page_name)
  73. context = build_context(sitemap_options)
  74. if can_cache(context.no_cache):
  75. webnotes.cache().set_value(cache_key, context)
  76. context.update(context.data or {})
  77. return context
  78. def build_sitemap_options(page_name):
  79. sitemap_options = webnotes.doc("Website Sitemap", page_name).fields
  80. # only non default fields
  81. for fieldname in default_fields:
  82. if fieldname in sitemap_options:
  83. del sitemap_options[fieldname]
  84. sitemap_config = webnotes.doc("Website Sitemap Config",
  85. sitemap_options.get("website_sitemap_config")).fields
  86. # get sitemap config fields too
  87. for fieldname in ("base_template_path", "template_path", "controller", "no_cache", "no_sitemap",
  88. "page_name_field", "condition_field"):
  89. sitemap_options[fieldname] = sitemap_config.get(fieldname)
  90. # establish hierarchy
  91. sitemap_options.parents = webnotes.conn.sql("""select name, page_title from `tabWebsite Sitemap`
  92. where lft < %s and rgt > %s order by lft asc""", (sitemap_options.lft, sitemap_options.rgt), as_dict=True)
  93. sitemap_options.children = webnotes.conn.sql("""select * from `tabWebsite Sitemap`
  94. where parent_website_sitemap=%s""", (sitemap_options.page_name,))
  95. # determine templates to be used
  96. if not sitemap_options.base_template_path:
  97. sitemap_options.base_template_path = "templates/base.html"
  98. sitemap_options.template = webnotes.get_jenv().get_template(sitemap_options.template_path)
  99. return sitemap_options
  100. def build_context(sitemap_options):
  101. """get_context method of bean or module is supposed to render content templates and push it into context"""
  102. context = webnotes._dict({ "_": webnotes._ })
  103. context.update(sitemap_options)
  104. if sitemap_options.get("controller"):
  105. module = webnotes.get_module(sitemap_options.get("controller"))
  106. if module and hasattr(module, "get_context"):
  107. context.data = module.get_context(context) or {}
  108. return context
  109. def can_cache(no_cache=False):
  110. return not (webnotes.conf.disable_website_cache or no_cache)
  111. def get_home_page():
  112. return webnotes.cache().get_value("home_page", \
  113. lambda: webnotes.conn.get_value("Website Settings", None, "home_page") or "login")
  114. def get_website_settings():
  115. # TODO Cache this
  116. hooks = webnotes.get_hooks()
  117. all_top_items = webnotes.conn.sql("""\
  118. select * from `tabTop Bar Item`
  119. where parent='Website Settings' and parentfield='top_bar_items'
  120. order by idx asc""", as_dict=1)
  121. top_items = [d for d in all_top_items if not d['parent_label']]
  122. # attach child items to top bar
  123. for d in all_top_items:
  124. if d['parent_label']:
  125. for t in top_items:
  126. if t['label']==d['parent_label']:
  127. if not 'child_items' in t:
  128. t['child_items'] = []
  129. t['child_items'].append(d)
  130. break
  131. context = webnotes._dict({
  132. 'top_bar_items': top_items,
  133. 'footer_items': webnotes.conn.sql("""\
  134. select * from `tabTop Bar Item`
  135. where parent='Website Settings' and parentfield='footer_items'
  136. order by idx asc""", as_dict=1),
  137. "webnotes": webnotes,
  138. "utils": webnotes.utils,
  139. "post_login": [
  140. {"label": "Reset Password", "url": "update-password", "icon": "icon-key"},
  141. {"label": "Logout", "url": "/?cmd=web_logout", "icon": "icon-signout"}
  142. ]
  143. })
  144. settings = webnotes.doc("Website Settings", "Website Settings")
  145. for k in ["banner_html", "brand_html", "copyright", "twitter_share_via",
  146. "favicon", "facebook_share", "google_plus_one", "twitter_share", "linked_in_share",
  147. "disable_signup"]:
  148. if k in settings.fields:
  149. context[k] = settings.fields.get(k)
  150. if settings.address:
  151. context["footer_address"] = settings.address
  152. for k in ["facebook_share", "google_plus_one", "twitter_share", "linked_in_share",
  153. "disable_signup"]:
  154. context[k] = cint(context.get(k) or 0)
  155. context.url = quote(str(get_request_site_address(full_address=True)), str(""))
  156. context.encoded_title = quote(encode(context.title or ""), str(""))
  157. for update_website_context in hooks.update_website_context or []:
  158. webnotes.get_attr(update_website_context)(context)
  159. context.web_include_js = hooks.web_include_js or []
  160. context.web_include_css = hooks.web_include_css or []
  161. return context
  162. def is_ajax():
  163. return webnotes.get_request_header("X-Requested-With")=="XMLHttpRequest"
  164. def scrub_page_name(page_name):
  165. if not page_name:
  166. page_name = "index"
  167. if "/" in page_name:
  168. page_name = page_name.split("/")[0]
  169. if page_name.endswith('.html'):
  170. page_name = page_name[:-5]
  171. return page_name
  172. def insert_traceback(data):
  173. if isinstance(data, dict):
  174. data["error"] = webnotes.get_traceback()
  175. else:
  176. data = data.replace("%(error)s", webnotes.get_traceback())
  177. return data
  178. def set_content_type(data, page_name):
  179. if isinstance(data, dict):
  180. webnotes._response.headers["Content-Type"] = "application/json; charset: utf-8"
  181. data = json.dumps(data)
  182. return data
  183. webnotes._response.headers["Content-Type"] = "text/html; charset: utf-8"
  184. if "." in page_name and not page_name.endswith(".html"):
  185. content_type, encoding = mimetypes.guess_type(page_name)
  186. webnotes._response.headers["Content-Type"] = content_type
  187. return data
  188. def clear_cache(page_name=None):
  189. if page_name:
  190. delete_page_cache(page_name)
  191. else:
  192. cache = webnotes.cache()
  193. for p in webnotes.conn.sql_list("""select name from `tabWebsite Sitemap`"""):
  194. if p is not None:
  195. cache.delete_value("page:" + p)
  196. cache.delete_value("home_page")
  197. cache.delete_value("page:index")
  198. cache.delete_value("website_sitemap")
  199. cache.delete_value("website_sitemap_config")
  200. def delete_page_cache(page_name):
  201. if page_name:
  202. cache = webnotes.cache()
  203. cache.delete_value("page:" + page_name)
  204. cache.delete_value("website_sitemap")
  205. def is_signup_enabled():
  206. if getattr(webnotes.local, "is_signup_enabled", None) is None:
  207. webnotes.local.is_signup_enabled = True
  208. if webnotes.utils.cint(webnotes.conn.get_value("Website Settings",
  209. "Website Settings", "disable_signup")):
  210. webnotes.local.is_signup_enabled = False
  211. return webnotes.local.is_signup_enabled
  212. def call_website_generator(bean, method):
  213. getattr(WebsiteGenerator(bean.doc, bean.doclist), method)()
  214. class WebsiteGenerator(DocListController):
  215. def setup_generator(self):
  216. if webnotes.flags.in_install_app:
  217. return
  218. self._website_config = webnotes.conn.get_values("Website Sitemap Config",
  219. {"ref_doctype": self.doc.doctype}, "*")[0]
  220. def on_update(self):
  221. self.update_sitemap()
  222. def after_rename(self, olddn, newdn, merge):
  223. webnotes.conn.sql("""update `tabWebsite Sitemap`
  224. set docname=%s where ref_doctype=%s and docname=%s""", (newdn, self.doc.doctype, olddn))
  225. if merge:
  226. self.setup_generator()
  227. remove_sitemap(ref_doctype=self.doc.doctype, docname=olddn)
  228. def on_trash(self):
  229. self.setup_generator()
  230. remove_sitemap(ref_doctype=self.doc.doctype, docname=self.doc.name)
  231. def update_sitemap(self):
  232. if webnotes.flags.in_install_app:
  233. return
  234. self.setup_generator()
  235. if self._website_config.condition_field and \
  236. not self.doc.fields.get(self._website_config.condition_field):
  237. # condition field failed, remove and return!
  238. remove_sitemap(ref_doctype=self.doc.doctype, docname=self.doc.name)
  239. return
  240. self.add_or_update_sitemap()
  241. def add_or_update_sitemap(self):
  242. page_name = self.get_page_name()
  243. existing_page_name = webnotes.conn.get_value("Website Sitemap", {"ref_doctype": self.doc.doctype,
  244. "docname": self.doc.name})
  245. opts = webnotes._dict({
  246. "page_or_generator": "Generator",
  247. "ref_doctype":self.doc.doctype,
  248. "docname": self.doc.name,
  249. "page_name": page_name,
  250. "link_name": self._website_config.name,
  251. "lastmod": webnotes.utils.get_datetime(self.doc.modified).strftime("%Y-%m-%d"),
  252. "parent_website_sitemap": self.doc.parent_website_sitemap
  253. })
  254. if self.meta.get_field("public_read"):
  255. opts.public_read = self.doc.public_read
  256. opts.public_write = self.doc.public_write
  257. else:
  258. opts.public_read = 1
  259. if existing_page_name:
  260. if existing_page_name != page_name:
  261. webnotes.rename_doc("Website Sitemap", existing_page_name, page_name, ignore_permissions=True)
  262. update_sitemap(page_name, opts)
  263. else:
  264. add_to_sitemap(opts)
  265. def get_page_name(self):
  266. if not self.doc.fields.get(self._website_config.page_name_field):
  267. new_page_name = cleanup_page_name(self.get_page_title() \
  268. if hasattr(self, "get_page_title") else (self.doc.title or self.doc.name))
  269. webnotes.conn.set(self.doc, self._website_config.page_name_field, new_page_name)
  270. return self.doc.fields.get(self._website_config.page_name_field)
  271. def cleanup_page_name(title):
  272. """make page name from title"""
  273. import re
  274. name = title.lower()
  275. name = re.sub('[~!@#$%^&*+()<>,."\'\?]', '', name)
  276. name = re.sub('[:/]', '-', name)
  277. name = '-'.join(name.split())
  278. # replace repeating hyphens
  279. name = re.sub(r"(-)\1+", r"\1", name)
  280. return name
  281. def get_hex_shade(color, percent):
  282. def p(c):
  283. v = int(c, 16) + int(int('ff', 16) * (float(percent)/100))
  284. if v < 0:
  285. v=0
  286. if v > 255:
  287. v=255
  288. h = hex(v)[2:]
  289. if len(h) < 2:
  290. h = "0" + h
  291. return h
  292. r, g, b = color[0:2], color[2:4], color[4:6]
  293. avg = (float(int(r, 16) + int(g, 16) + int(b, 16)) / 3)
  294. # switch dark and light shades
  295. if avg > 128:
  296. percent = -percent
  297. # stronger diff for darker shades
  298. if percent < 25 and avg < 64:
  299. percent = percent * 2
  300. return p(r) + p(g) + p(b)
  301. def get_access(sitemap):
  302. pass