1 """Miscellaneous utility functions."""
2
3 __all__ = [
4 'absolute_url',
5 'add_global_tmpl_vars',
6 'code2html',
7 'get_user_id',
8 'get_server_name',
9 'get_static_content',
10 'message_of_the_day',
11 'txt2html',
12 'wrap_text',
13 ]
14
15 import logging
16 import random
17 import textwrap
18
19 from os.path import join
20
21 import cherrypy
22 from turbogears import config, identity, url
23 from turbogears.release import version as tg_version
24 from pygments import highlight
25 from pygments.lexers import get_lexer_by_name
26 from pygments.styles import get_style_by_name
27 from pygments.formatters import HtmlFormatter
28
29 try:
30 from spammcan.rest import HTML
31 config.update({'has_docutils': True})
32 except ImportError:
33 config.update({'has_docutils': False})
34
35
36 log = logging.getLogger('spammcan.controllers')
37 _static_dir = config.get('static_filter.dir', path='/static')
38
39
41 """Returns absolute URL (including schema and host to this server).
42
43 Tries to account for 'Host' header and reverse proxing ('X-Forwarded-Host').
44
45 """
46
47 h = cherrypy.request.headers
48 use_xfh = config.get('base_url_filter.use_x_forwarded_host', False)
49 if h.get('X-Use-SSL'):
50 scheme = 'https'
51 else:
52 scheme = cherrypy.request.scheme
53 base_url = '%s://%s' % (scheme, get_server_name())
54 if config.get('base_url_filter.on', False) and not use_xfh:
55 base_url = config.get('base_url_filter.base_url').rstrip('/')
56 return '%s%s' % (base_url, url(tgpath, params, **kw))
57
59 """Add custom global template variables.
60
61 Adds the following variables:
62
63 ``tg.motd`` - The message of the day as a plain string
64
65 """
66 vars['motd'] = message_of_the_day()
67 vars['abs_url'] = absolute_url
68 vars['tg_version'] = tg_version
69
70 -def code2html(code, format, style=None, cssclass="source", hl_lines=None,
71 linenos=False):
72 """Syntax highlight given code with Pygments and format as HTML."""
73 formatter_options = dict(
74 style=style or 'default',
75 linenos=linenos,
76 cssclass=cssclass,
77 lineanchors='L')
78 if hl_lines:
79 formatter_options['hl_lines'] = hl_lines
80 lexer = get_lexer_by_name(format)
81 formatter = HtmlFormatter(**formatter_options)
82 return highlight(code, lexer, formatter), formatter.get_style_defs(
83 '.' + cssclass)
84
86 """Return id of current visitor.
87
88 This will be a string with 32-char MD5 hash, which will be retrieved either
89 from a request cookie or default to the visit_key of the current identity.
90
91 As a side affect this function sets a response cookie for the user ID
92 called 'spammcan_uid' with an expiry time set by the
93 'spammcan.uid_cookie_expiry' config setting (defaults to 90 days).
94
95 """
96 cookie = cherrypy.request.simple_cookie.get('spammcan_uid')
97 log.debug("User ID cookie: %r", cookie)
98 if cookie:
99 uid = cookie.value
100 else:
101 uid = identity.current.visit_key
102 cherrypy.response.simple_cookie['spammcan_uid'] = uid
103 cherrypy.response.simple_cookie['spammcan_uid']['expires'] = config.get(
104 'spammcan.uid_cookie_expiry', 3600*24*90)
105 return uid
106
108 """Return name of the server this application runs on.
109
110 Tries to account for 'Host' header and reverse proxing.
111
112 """
113 h = cherrypy.request.headers
114 return config.get('server.domain', h.get('X-Forwarded-Host', h['Host']))
115
117 """Read text file from static directory and convert it to HTML."""
118 filename = join(_static_dir, name)
119 try:
120 fo = open(filename, 'rb')
121 except (OSError, IOError):
122 text = _(u"Could not read static content resource '%s'.") % name
123 else:
124 text = fo.read().decode('utf-8')
125 fo.close()
126 return txt2html(text)
127
129 """Return random message of the day from a text file with one msg per line.
130 """
131 totd = join(_static_dir, filename)
132 return random.choice(filter(None, open(totd).readlines()))
133
135 """Try to convert text into HTML with docutils.
136
137 If conversion fails or using docutils is turned off by the configuration,
138 return text wrapped in a PRE element with CSS class "plaintext".
139
140 """
141 use_docutils = use_docutils and config.get('has_docutils', False)
142 if use_docutils:
143 try:
144 text = HTML(text, report_level=0, initial_header_level=2)
145 except Exception, exc:
146 log.debug("txt2html failed: %r,", exc)
147 use_docutils = False
148 return use_docutils and text or '<pre class="plaintext">%s</pre>' % text
149
151 """Wrap single paragraph of text with custom options."""
152 return textwrap.wrap(s, width=width, subsequent_indent=' ',
153 expand_tabs=True, replace_whitespace=False, break_long_words=False)
154
155 -def wrap_text(s, width=100):
156 """Wrap each paragraph in s with wrap().
157
158 Returns 2-item tuple with a list of line nummers of continuation lines, and
159 the wrapped text.
160
161 """
162 i = 1; wrapped_lines = []; output= []
163 for line in s.splitlines():
164 newlines = _wrap_line(line, width) or ['']
165 if len(newlines) > 1:
166 wrapped_lines.extend(range(i+1,i+len(newlines)))
167 i += len(newlines)
168 output.extend(newlines)
169 return wrapped_lines, "\n".join(output)
170