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.

пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 10 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 10 година
пре 10 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import frappe, os
  5. from frappe.website.utils import can_cache, delete_page_cache
  6. from frappe.model.document import get_controller
  7. from frappe import _
  8. def get_page_context(path):
  9. page_context = None
  10. if can_cache():
  11. page_context_cache = frappe.cache().hget("page_context", path) or {}
  12. page_context = page_context_cache.get(frappe.local.lang, None)
  13. if not page_context:
  14. page_context = make_page_context(path)
  15. if can_cache(page_context.no_cache):
  16. page_context_cache[frappe.local.lang] = page_context
  17. frappe.cache().hset("page_context", path, page_context_cache)
  18. return page_context
  19. def make_page_context(path):
  20. context = resolve_route(path)
  21. if not context:
  22. raise frappe.DoesNotExistError
  23. context.doctype = context.ref_doctype
  24. context.title = context.page_title
  25. context.pathname = frappe.local.path
  26. return context
  27. def resolve_route(path):
  28. """Returns the page route object based on searching in pages and generators.
  29. The `www` folder is also a part of generator **Web Page**.
  30. The only exceptions are `/about` and `/contact` these will be searched in Web Pages
  31. first before checking the standard pages."""
  32. if path not in ("about", "contact"):
  33. context = get_page_context_from_template(path)
  34. if context:
  35. return context
  36. return get_page_context_from_doctype(path)
  37. else:
  38. context = get_page_context_from_doctype(path)
  39. if context:
  40. return context
  41. return get_page_context_from_template(path)
  42. def get_page_context_from_template(path):
  43. '''Return page_info from path'''
  44. for app in frappe.get_installed_apps():
  45. app_path = frappe.get_app_path(app)
  46. for start in ('www', 'templates/pages'):
  47. search_path = os.path.join(app_path, start, path)
  48. options = (search_path, search_path + '.html', search_path + '.md',
  49. search_path + '/index.html', search_path + '/index.md')
  50. for o in options:
  51. if os.path.exists(o) and not os.path.isdir(o):
  52. return get_page_info(o, app, app_path=app_path)
  53. return None
  54. def get_page_context_from_doctype(path):
  55. page_info = get_page_info_from_doctypes(path)
  56. if page_info:
  57. return frappe.get_doc(page_info.get("doctype"), page_info.get("name")).get_page_info()
  58. def clear_sitemap():
  59. delete_page_cache("*")
  60. def get_all_page_context_from_doctypes():
  61. '''Get all doctype generated routes (for sitemap.xml)'''
  62. routes = frappe.cache().get_value("website_generator_routes")
  63. if not routes:
  64. routes = get_page_info_from_doctypes()
  65. frappe.cache().set_value("website_generator_routes", routes)
  66. return routes
  67. def get_page_info_from_doctypes(path=None):
  68. routes = {}
  69. for app in frappe.get_installed_apps():
  70. for doctype in frappe.get_hooks("website_generators", app_name = app):
  71. condition = ""
  72. values = []
  73. controller = get_controller(doctype)
  74. if controller.website.condition_field:
  75. condition ="where {0}=1".format(controller.website.condition_field)
  76. if path:
  77. condition += ' {0} `route`=%s limit 1'.format('and' if 'where' in condition else 'where')
  78. values.append(path)
  79. for r in frappe.db.sql("""select route, name, modified from `tab{0}`
  80. {1}""".format(doctype, condition), values=values, as_dict=True):
  81. routes[r.route] = {"doctype": doctype, "name": r.name, "modified": r.modified}
  82. # just want one path, return it!
  83. if path:
  84. return routes[r.route]
  85. return routes
  86. def get_pages():
  87. '''Get all pages. Called for docs / sitemap'''
  88. pages = {}
  89. frappe.local.flags.in_get_all_pages = True
  90. for app in frappe.get_installed_apps():
  91. app_path = frappe.get_app_path(app)
  92. for start in ('templates/pages', 'www'):
  93. path = os.path.join(app_path, start)
  94. pages.update(get_pages_from_path(path, app, app_path))
  95. frappe.local.flags.in_get_all_pages = False
  96. return pages
  97. def get_pages_from_path(path, app, app_path):
  98. pages = {}
  99. if os.path.exists(path):
  100. for basepath, folders, files in os.walk(path):
  101. # add missing __init__.py
  102. if not '__init__.py' in files:
  103. open(os.path.join(basepath, '__init__.py'), 'a').close()
  104. for fname in files:
  105. fname = frappe.utils.cstr(fname)
  106. page_name, extn = fname.rsplit(".", 1)
  107. if extn in ('js', 'css') and os.path.exists(os.path.join(basepath, fname + '.html')):
  108. # js, css is linked to html, skip
  109. continue
  110. if extn in ("html", "xml", "js", "css", "md"):
  111. page_info = get_page_info(path, app, basepath, app_path, fname)
  112. pages[page_info.route] = page_info
  113. # print frappe.as_json(pages[-1])
  114. return pages
  115. def get_page_info(path, app, basepath=None, app_path=None, fname=None):
  116. '''Load page info'''
  117. if not fname:
  118. fname = os.path.basename(path)
  119. if not app_path:
  120. app_path = frappe.get_app_path(app)
  121. if not basepath:
  122. basepath = os.path.dirname(path)
  123. page_name, extn = fname.rsplit(".", 1)
  124. # add website route
  125. page_info = frappe._dict()
  126. page_info.basename = page_name if extn in ('html', 'md') else fname
  127. page_info.basepath = basepath
  128. page_info.page_or_generator = "Page"
  129. page_info.template = os.path.relpath(os.path.join(basepath, fname), app_path)
  130. if page_info.basename == 'index':
  131. page_info.basename = os.path.dirname(path).strip('.').strip('/')
  132. page_info.route = page_info.name = page_info.page_name = os.path.join(os.path.relpath(basepath, path),
  133. page_info.basename).strip('/.')
  134. # controller
  135. page_info.controller_path = os.path.join(basepath, page_name.replace("-", "_") + ".py")
  136. if os.path.exists(page_info.controller_path):
  137. controller = app + "." + os.path.relpath(page_info.controller_path,
  138. app_path).replace(os.path.sep, ".")[:-3]
  139. page_info.controller = controller
  140. # get the source
  141. page_info.source = get_source(page_info)
  142. if page_info.only_content:
  143. # extract properties from HTML comments
  144. load_properties(page_info)
  145. return page_info
  146. def get_source(page_info):
  147. '''Get the HTML source of the template'''
  148. from markdown2 import markdown
  149. jenv = frappe.get_jenv()
  150. source = jenv.loader.get_source(jenv, page_info.template)[0]
  151. html = ''
  152. if page_info.template.endswith('.md'):
  153. source = markdown(source)
  154. # if only content
  155. if page_info.template.endswith('.html') or page_info.template.endswith('.md'):
  156. if ('</body>' not in source) and ('{% block' not in source):
  157. page_info.only_content = True
  158. js, css = '', ''
  159. js_path = os.path.join(page_info.basepath, page_info.basename + '.js')
  160. if os.path.exists(js_path):
  161. js = unicode(open(js_path, 'r').read(), 'utf-8')
  162. css_path = os.path.join(page_info.basepath, page_info.basename + '.css')
  163. if os.path.exists(css_path):
  164. js = unicode(open(css_path, 'r').read(), 'utf-8')
  165. html = '{% extends "templates/web.html" %}'
  166. if css:
  167. html += '\n{% block style %}\n<style>\n' + css + '\n</style>\n{% endblock %}'
  168. html += '\n{% block page_content %}\n' + source + '\n{% endblock %}'
  169. if js:
  170. html += '\n{% block script %}<script>' + js + '\n</script>\n{% endblock %}'
  171. else:
  172. html = source
  173. # show table of contents
  174. setup_index(page_info)
  175. return html
  176. def setup_index(page_info):
  177. '''Insert full index (table of contents) for {index} tag'''
  178. from frappe.website.utils import get_full_index
  179. if page_info.basename=='index':
  180. if frappe.local.flags.in_get_all_pages:
  181. # load index.txt if loading all pages
  182. index_txt_path = os.path.join(page_info.basepath, 'index.txt')
  183. if os.path.exists(index_txt_path):
  184. page_info.index = open(index_txt_path, 'r').read().splitlines()
  185. elif '\n{index}' in page_info.source:
  186. html = frappe.get_template("templates/includes/full_index.html").render({
  187. "full_index": get_full_index(),
  188. "url_prefix": None
  189. })
  190. page_info.source.replace('{index}', html)
  191. if (not frappe.local.flags.in_get_all_pages) and ('\n{next}' in page_info.source):
  192. # insert next link
  193. next_item = None
  194. children_map = get_full_index()
  195. parent_route = os.path.dirname(page_info.route)
  196. children = children_map[parent_route]
  197. if parent_route and children:
  198. for i, c in enumerate(children):
  199. if c.route == page_info.route and i < (len(children) - 1):
  200. next_item = children[i+1]
  201. if next_item:
  202. html = ('<p class="btn-next-wrapper">'+_("Next")\
  203. +': <a class="btn-next" href="{route}.html">{title}</a></p>').format(**next_item)
  204. page_info.source.replace('{next}', html)
  205. def load_properties(page_info):
  206. '''Load properties like no_cache, title from raw'''
  207. import re
  208. if "<!-- title:" in page_info.source:
  209. page_info.title = re.findall('<!-- title:([^>]*) -->', page_info.source)[0].strip()
  210. else:
  211. page_info.title = os.path.basename(page_info.name).replace('_', ' ').replace('-', ' ').title()
  212. if not '{% block title %}' in page_info.source:
  213. page_info.source += '\n{% block title %}' + page_info.title + '{% endblock %}'
  214. if "<!-- no-breadcrumbs -->" in page_info.source:
  215. page_info.no_breadcrumbs = 1
  216. if "<!-- no-header -->" in page_info.source:
  217. page_info.no_header = 1
  218. else:
  219. # every page needs a header
  220. # add missing header if there is no <h1> tag
  221. if (not '{% block header %}' in page_info.source) and (not '<h1' in page_info.source):
  222. page_info.source += '\n{% block header %}<h1>' + page_info.title + '</h1>{% endblock %}'
  223. if "<!-- no-cache -->" in page_info.source:
  224. page_info.no_cache = 1
  225. def process_generators(func):
  226. for app in frappe.get_installed_apps():
  227. for doctype in frappe.get_hooks("website_generators", app_name = app):
  228. order_by = "name asc"
  229. condition_field = None
  230. controller = get_controller(doctype)
  231. if hasattr(controller, "condition_field"):
  232. condition_field = controller.condition_field
  233. if hasattr(controller, "order_by"):
  234. order_by = controller.order_by
  235. val = func(doctype, condition_field, order_by)
  236. if val:
  237. return val