OLD | NEW |
| 1 |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 4 # found in the LICENSE file. |
4 | 5 |
5 """Utils.""" | 6 """Utils.""" |
6 | 7 |
| 8 import re |
| 9 import time |
| 10 import random |
| 11 import logging |
| 12 import sys |
| 13 import string |
| 14 import json |
| 15 |
7 from google.appengine.api import users | 16 from google.appengine.api import users |
8 | 17 |
9 | 18 |
10 def admin_only(func): | 19 def admin_only(func): |
11 """Valid for BasePage objects only.""" | 20 """Valid for BasePage objects only.""" |
12 def decorated(self, *args, **kwargs): | 21 def decorated(self, *args, **kwargs): |
13 if self.is_admin: | 22 if self.is_admin: |
14 return func(self, *args, **kwargs) | 23 return func(self, *args, **kwargs) |
15 else: | 24 else: |
16 self.response.headers['Content-Type'] = 'text/plain' | 25 self.response.headers['Content-Type'] = 'text/plain' |
(...skipping 11 matching lines...) Expand all Loading... |
28 | 37 |
29 | 38 |
30 def require_user(func): | 39 def require_user(func): |
31 """A user must be logged in.""" | 40 """A user must be logged in.""" |
32 def decorated(self, *args, **kwargs): | 41 def decorated(self, *args, **kwargs): |
33 if not self.user: | 42 if not self.user: |
34 self.redirect(users.create_login_url(self.request.url)) | 43 self.redirect(users.create_login_url(self.request.url)) |
35 else: | 44 else: |
36 return func(self, *args, **kwargs) | 45 return func(self, *args, **kwargs) |
37 return decorated | 46 return decorated |
| 47 |
| 48 |
| 49 ############ |
| 50 # Decorators |
| 51 ############ |
| 52 |
| 53 def render(template_filename, jinja_environment): |
| 54 """Use a template to render results. The wrapped function is expected to |
| 55 return a dict.""" |
| 56 def _render(fn): |
| 57 def wrapper(self, *args, **kwargs): |
| 58 results = fn(self, *args, **kwargs) |
| 59 template = jinja_environment.get_template(template_filename) |
| 60 self.response.out.write(template.render(results)) |
| 61 return wrapper |
| 62 return _render |
| 63 |
| 64 |
| 65 def render_iff_new_flag_set(template_filename, jinja_environment): |
| 66 """Use the given template if and only if the 'new' flag is set by: |
| 67 * The presence of the 'new' cookie. |
| 68 * 'new' is passed in as an url parameter.""" |
| 69 def _render(fn): |
| 70 def wrapper(self, *args, **kwargs): |
| 71 new = self.request.get('new') or self.request.cookies.get('new') |
| 72 kwargs.update({'new': new}) |
| 73 results = fn(self, *args, **kwargs) |
| 74 if new: |
| 75 template = jinja_environment.get_template(template_filename) |
| 76 try: |
| 77 self.response.out.write(template.render(results)) |
| 78 except Exception as e: |
| 79 logging.error('Caught exception while calling %s with template %s' % |
| 80 (self.__class__.__name__, template_filename)) |
| 81 raise e, None, sys.exc_info()[2] |
| 82 else: |
| 83 # Just treat the results as a large string blob. |
| 84 self.response.out.write(results) |
| 85 return wrapper |
| 86 return _render |
| 87 |
| 88 |
| 89 def render_json(fn): |
| 90 """The function is expected to return a dict, and we want to render json.""" |
| 91 def wrapper(self, *args, **kwargs): |
| 92 results = fn(self, *args, **kwargs) |
| 93 self.response.out.write(json.dumps(results)) |
| 94 return wrapper |
| 95 |
| 96 |
| 97 def maybe_render_json(template_filename, jinja_environment): |
| 98 """If the variable 'json' exists in the request, return a json object. |
| 99 Otherwise render the page using the template""" |
| 100 def _render(fn): |
| 101 def wrapper(self, *args, **kwargs): |
| 102 results = fn(self, *args, **kwargs) |
| 103 if self.request.get('json'): |
| 104 self.response.out.write(json.dumps(results)) |
| 105 else: |
| 106 template = jinja_environment.get_template(template_filename) |
| 107 self.response.out.write(template.render(results)) |
| 108 return wrapper |
| 109 return _render |
| 110 |
| 111 |
| 112 def login_required(fn): |
| 113 """Redirect user to a login page.""" |
| 114 def wrapper(self, *args, **kwargs): |
| 115 user = users.get_current_user() |
| 116 if not user: |
| 117 self.redirect(users.create_login_url(self.request.uri)) |
| 118 return |
| 119 else: |
| 120 return fn(self, *args, **kwargs) |
| 121 return wrapper |
| 122 |
| 123 |
| 124 def google_login_required(fn): |
| 125 """Return 403 unless the user is logged in from a @google.com domain.""" |
| 126 def wrapper(self, *args, **kwargs): |
| 127 user = users.get_current_user() |
| 128 if not user: |
| 129 self.redirect(users.create_login_url(self.request.uri)) |
| 130 return |
| 131 email_match = re.match('^(.*)@(.*)$', user.email()) |
| 132 if email_match: |
| 133 _, domain = email_match.groups() |
| 134 if domain == 'google.com': |
| 135 return fn(self, *args, **kwargs) |
| 136 self.error(403) # Unrecognized email or unauthroized domain. |
| 137 self.response.out.write('unauthroized email %s' % user.user_id()) |
| 138 return wrapper |
| 139 |
| 140 |
| 141 def admin_required(fn): |
| 142 """Return 403 unless an admin is logged in.""" |
| 143 def wrapper(self, *args, **kwargs): |
| 144 user = users.get_current_user() |
| 145 if not user: |
| 146 self.redirect(users.create_login_url(self.request.uri)) |
| 147 return |
| 148 elif not users.is_current_user_admin(): |
| 149 self.error(403) |
| 150 return |
| 151 else: |
| 152 return fn(self, *args, **kwargs) |
| 153 return wrapper |
| 154 |
| 155 |
| 156 def expect_request_param(*request_args): |
| 157 """Strips out the expected args from a request and feeds it into the function |
| 158 as the arguments. Optionally, typecast the argument from a string into a |
| 159 different class. Examples include: |
| 160 name (Get the request object called "name") |
| 161 time as timestamp (Get "time", pass it in as "timestamp") |
| 162 """ |
| 163 def _decorator(fn): |
| 164 def wrapper(self, *args, **kwargs): |
| 165 request_kwargs = {} |
| 166 for arg in request_args: |
| 167 # TODO(hinoka): Optional typecasting? |
| 168 arg_match = re.match(r'^(\((\w+)\))?\s*(\w+)( as (\w+))?$', arg) |
| 169 if arg_match: |
| 170 _, _, name, _, target_name = arg_match.groups() |
| 171 if not target_name: |
| 172 target_name = name |
| 173 request_item = self.request.get(name) |
| 174 request_kwargs[target_name] = request_item |
| 175 else: |
| 176 raise Exception('Incorrect format %s' % arg) |
| 177 kwargs.update(request_kwargs) |
| 178 return fn(self, *args, **kwargs) |
| 179 return wrapper |
| 180 return _decorator |
| 181 |
| 182 |
| 183 ############### |
| 184 # Jinja filters |
| 185 ############### |
| 186 |
| 187 def delta_time(delta): |
| 188 hours = int(delta/60/60) |
| 189 minutes = int((delta - hours * 3600)/60) |
| 190 seconds = int(delta - (hours * 3600) - (minutes * 60)) |
| 191 result = '' |
| 192 if hours > 1: |
| 193 result += '%d hrs, ' % hours |
| 194 elif hours: |
| 195 result += '%d hr, ' % hours |
| 196 if minutes > 1: |
| 197 result += '%d mins ' % minutes |
| 198 elif minutes: |
| 199 result += '%d min ' % minutes |
| 200 if not hours: |
| 201 if seconds > 1 or seconds == 0: |
| 202 result += '%d secs.' % seconds |
| 203 else: |
| 204 result += '%d sec.' % seconds |
| 205 return result |
| 206 |
| 207 |
| 208 def time_since(timestamp): |
| 209 delta = time.time() - timestamp |
| 210 return delta_time(delta) |
| 211 |
| 212 |
| 213 def nl2br(value): |
| 214 return value.replace('\n','<br>\n') |
| 215 |
| 216 |
| 217 def rot13_email(value): |
| 218 nonce = ''.join(random.choice( |
| 219 string.ascii_uppercase + string.digits) for x in range(6)) |
| 220 rep = ('<span id="obf-%s"><script>document.getElementById("obf-%s").' |
| 221 'innerHTML="<n uers=\\"znvygb:%s\\" gnetrg=\\"_oynax\\">%s</n>".' |
| 222 'replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((' |
| 223 'c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);});</script>' |
| 224 '<noscript><span style="unicode-bidi:bidi-override;direction:rtl;"' |
| 225 '>%s</span></noscript></span>') |
| 226 return rep % (nonce, nonce, value.encode('rot13'), |
| 227 value.encode('rot13'), value[::-1]) |
| 228 |
| 229 |
| 230 def _blockquote(value): |
| 231 """Wrap blockquote levels recursively.""" |
| 232 new_value = '' |
| 233 blockquote = False |
| 234 for line in value.splitlines(): |
| 235 if blockquote: |
| 236 if line.startswith('>'): |
| 237 new_value += '%s\n' % line[1:].strip() |
| 238 else: |
| 239 blockquote = False |
| 240 new_value += '</blockquote>%s\n' % line |
| 241 else: |
| 242 if line.startswith('>'): |
| 243 blockquote = True |
| 244 new_value += '<blockquote>%s\n' % line[1:].strip() |
| 245 else: |
| 246 new_value += '%s\n' % line |
| 247 if blockquote: |
| 248 new_value += '</blockquote>' |
| 249 if re.search(r'^>', new_value, re.M): |
| 250 return _blockquote(new_value) |
| 251 else: |
| 252 return new_value |
| 253 |
| 254 |
| 255 def _resolve_crbug(match): |
| 256 results = [] |
| 257 bugs = match.group(1).split(',') |
| 258 for bug in bugs: |
| 259 results.append('<a href="http://crbug.com/%s">%s</a>' % (bug, bug)) |
| 260 return 'BUG=%s' % ','.join(results) |
| 261 |
| 262 |
| 263 def cl_comment(value): |
| 264 """Add links to https:// addresses, BUG=####, and trim excessive newlines.""" |
| 265 value = re.sub(r'(https?://.*)', r'<a href="\1">\1</a>', value) |
| 266 value = re.sub(r'BUG=([\d,]+)', _resolve_crbug, value) |
| 267 # Add blockquotes. |
| 268 value = _blockquote(value) |
| 269 value = re.sub(r'\n', r'<br>', value) |
| 270 # Obfuscure email addresses with rot13 encoding. |
| 271 value = re.sub(r'(\w+@[\w.]+)', lambda m: rot13_email(m.group(1)), value) |
| 272 return value |
OLD | NEW |