anonymous group photoblog software
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.
 
 
 
 
 
 

207 lines
6.0 KiB

  1. import os
  2. import glob
  3. import random
  4. import re
  5. import jinja2
  6. import config, config_defaults
  7. import strings
  8. import misc
  9. from util import local, FileLock
  10. import str_format
  11. import staff_tasks
  12. TEMPLATES_DIR = os.path.join('templates')
  13. CACHE_DIR = os.path.join(TEMPLATES_DIR, '.cache')
  14. _filters = []
  15. _functions = []
  16. def filter(f):
  17. _filters.append(f.__name__)
  18. return f
  19. def function(f):
  20. _functions.append(f.__name__)
  21. return f
  22. class Template(object):
  23. def __init__(self, name, **vars):
  24. if not os.path.exists(CACHE_DIR):
  25. os.makedirs(CACHE_DIR)
  26. # Environment init
  27. self.env = jinja2.Environment(
  28. loader=jinja2.FileSystemLoader(TEMPLATES_DIR),
  29. bytecode_cache=jinja2.FileSystemBytecodeCache(CACHE_DIR)
  30. )
  31. for filter in _filters:
  32. self.env.filters[filter] = getattr(self, filter)
  33. for function in _functions:
  34. self.env.globals[function] = getattr(self, function)
  35. # Current template init
  36. self.template = self.env.get_template(name + '.html')
  37. self.environ = local.environ
  38. self.board = self.environ['waka.board']
  39. vars['environ'] = self.environ
  40. vars['board'] = self.board
  41. vars['stylesheets'] = list(self.get_stylesheets(self.board))
  42. self.env.globals['config'] = config
  43. self.env.globals['strings'] = strings
  44. self.vars = vars
  45. def __iter__(self):
  46. yield self.template.render(**self.vars).encode("utf-8")
  47. def render_to_file(self, filename):
  48. contents = self.template.render(**self.vars).encode("utf-8")
  49. if config.USE_TEMPFILES:
  50. tempname = os.path.join(os.path.dirname(filename),
  51. 'tmp' + str(random.randint(1, 1000000000)))
  52. with FileLock(tempname):
  53. with open(tempname, 'w') as rc:
  54. rc.write(contents)
  55. with FileLock(filename) as rc:
  56. os.rename(tempname, filename)
  57. else:
  58. with FileLock(filename) as rc:
  59. with open(filename, 'w') as rc:
  60. rc.write(contents)
  61. os.chmod(filename, 0644)
  62. def update_parameters(self, **kwargs):
  63. self.vars.update(kwargs)
  64. @filter
  65. def reverse_format(self, value, tplstring):
  66. return tplstring % value
  67. @filter
  68. def expand_url(self, filename, force_http=False):
  69. return self.board.expand_url(filename, force_http)
  70. @filter
  71. def expand_image_url(self, filename):
  72. # TODO: load balancing support?
  73. return self.expand_url(filename, '/')
  74. @filter
  75. def root_path_to_filename(self, filename):
  76. if filename.startswith("/") or filename.startswith("http"):
  77. return filename
  78. return self.environ['waka.rootpath'] + filename
  79. @filter
  80. def basename(self, path):
  81. return os.path.basename(path)
  82. @filter
  83. def get_captcha_key(self, thread):
  84. # TODO: captcha not implemented
  85. pass
  86. @function
  87. def get_script_name(self):
  88. return misc.get_script_name()
  89. @function
  90. def get_secure_script_name(self):
  91. return misc.get_secure_script_name()
  92. @filter
  93. def get_reply_link(self, reply, parent, abbreviated=False,
  94. force_http=False):
  95. return self.board.get_reply_link(reply, parent, abbreviated,
  96. force_http)
  97. @filter
  98. def redirect_reply_links(self, comment, min_res):
  99. res_re = re.compile('(' + os.path.join(self.board.url,
  100. self.board.options['RES_DIR'],
  101. r')(?P<op_num>\d+)('
  102. + config.PAGE_EXT + r'#?)'
  103. r'(?P<res_num>\d+)?'), re.I)
  104. # If the reply number is at least
  105. def change_to_abbr(match):
  106. res_num = match.group('res_num')
  107. op_num = match.group('op_num')
  108. if not res_num or int(res_num) >= min_res:
  109. op_num = op_num + '_abbr'
  110. if not res_num:
  111. return match.group(1) + op_num + match.group(3)
  112. return match.group(1) + op_num + match.group(3) + res_num
  113. return res_re.sub(change_to_abbr, comment)
  114. @filter
  115. def clean_string(self, string):
  116. return str_format.clean_string(string)
  117. @filter
  118. def tag_killa(self, string):
  119. return str_format.tag_killa(string)
  120. @filter
  121. def dec_to_dot(self, numip):
  122. if not numip:
  123. return ''
  124. return misc.dec_to_dot(numip)
  125. @filter
  126. def get_action_name(self, action, debug=0):
  127. return staff_tasks.get_action_name(action, debug=debug)
  128. @filter
  129. def make_date(self, timestamp, style='futaba'):
  130. return misc.make_date(timestamp, style)
  131. @function
  132. def get_filetypes(self):
  133. filetypes = self.board.options.get('EXTRA_FILETYPES', [])
  134. ret_list = []
  135. for pictype in ('gif', 'jpg', 'png'):
  136. if pictype not in filetypes:
  137. ret_list.append(pictype.upper())
  138. ret_list.extend(filetypes)
  139. return ', '.join(ret_list)
  140. def get_stylesheets(self, board=None):
  141. files = glob.glob(os.path.abspath\
  142. (os.path.join(self.environ['DOCUMENT_ROOT'],
  143. config.BOARD_DIR,
  144. 'include/boards/css/*.css')))
  145. if board is not None:
  146. # Add board CSS directory, if present.
  147. files.extend(glob.glob(os.path.abspath\
  148. (os.path.join(board.path, 'css/*.css'))))
  149. for file in files:
  150. title = os.path.basename(file) \
  151. .replace('.css', '') \
  152. .replace('_', ' ').title()
  153. default = (title == self.board.options['DEFAULT_STYLE'].title())
  154. url = file.replace(self.environ['DOCUMENT_ROOT'].rstrip("/"), '')
  155. yield {
  156. 'filename': url,
  157. 'title': title,
  158. 'default': default,
  159. }