Added template field to master branch. Added monkeypatch for telling if a node includ...
[philo.git] / validators.py
1 from django.utils.translation import ugettext_lazy as _
2 from django.core.validators import RegexValidator
3 from django.core.exceptions import ValidationError
4 from django.utils import simplejson as json
5 import re
6
7
8 LOADED_TEMPLATE_ATTR = '_philo_loaded_template'
9 INSECURE_TAGS = (
10         'load',
11         'extends',
12         'include',
13 )
14
15
16 class RedirectValidator(RegexValidator):
17         """Based loosely on the URLValidator, but no option to verify_exists"""
18         regex = re.compile(
19                 r'^(?:https?://' # http:// or https://
20                 r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|' #domain...
21                 r'localhost|' #localhost...
22                 r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
23                 r'(?::\d+)?' # optional port
24                 r'(?:/?|[/?#]?\S+)|'
25                 r'[^?#\s]\S*)$',
26                 re.IGNORECASE)
27         message = _(u'Enter a valid absolute or relative redirect target')
28
29
30 class URLLinkValidator(RegexValidator):
31         """Based loosely on the URLValidator, but no option to verify_exists"""
32         regex = re.compile(
33                 r'^(?:https?://' # http:// or https://
34                 r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|' #domain...
35                 r'localhost|' #localhost...
36                 r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
37                 r'(?::\d+)?' # optional port
38                 r'|)' # also allow internal links
39                 r'(?:/?|[/?#]?\S+)$', re.IGNORECASE)
40         message = _(u'Enter a valid absolute or relative redirect target')
41
42
43 def json_validator(value):
44         try:
45                 json.loads(value)
46         except:
47                 raise ValidationError(u'\'%s\' is not valid JSON' % value)
48
49
50 from django.template import Template, Parser, Lexer, TOKEN_BLOCK
51
52
53 class TemplateValidationParser(Parser):
54         def __init__(self, tokens, allow=None, disallow=None, secure=True):
55                 super(TemplateValidationParser, self).__init__(tokens)
56                 
57                 allow, disallow = set(allow or []), set(disallow or [])
58                 
59                 if secure:
60                         disallow |= set(INSECURE_TAGS)
61                 
62                 self.allow, self.disallow = allow, disallow
63         
64         def parse(self, parse_until=None):
65                 if parse_until is None:
66                         parse_until = []
67                 
68                 nodelist = self.create_nodelist()
69                 while self.tokens:
70                         token = self.next_token()
71                         # We only need to parse var and block tokens.
72                         if token.token_type == TOKEN_VAR:
73                                 if not token.contents:
74                                         self.empty_variable(token)
75                                 
76                                 filter_expression = self.compile_filter(token.contents)
77                                 var_node = self.create_variable_node(filter_expression)
78                                 self.extend_nodelist(nodelist, var_node,token)
79                         elif token.token_type == TOKEN_BLOCK:
80                                 if token.contents in parse_until:
81                                         # put token back on token list so calling code knows why it terminated
82                                         self.prepend_token(token)
83                                         return nodelist
84                                 
85                                 try:
86                                         command = token.contents.split()[0]
87                                 except IndexError:
88                                         self.empty_block_tag(token)
89                                 
90                                 if (self.allow and command not in self.allow) or (self.disallow and command in self.disallow):
91                                         self.disallowed_tag(command)
92                                 
93                                 self.enter_command(command, token)
94                                 
95                                 try:
96                                         compile_func = self.tags[command]
97                                 except KeyError:
98                                         self.invalid_block_tag(token, command, parse_until)
99                                 
100                                 try:
101                                         compiled_result = compile_func(self, token)
102                                 except TemplateSyntaxError, e:
103                                         if not self.compile_function_error(token, e):
104                                                 raise
105                                 
106                                 self.extend_nodelist(nodelist, compiled_result, token)
107                                 self.exit_command()
108                 
109                 if parse_until:
110                         self.unclosed_block_tag(parse_until)
111                 
112                 return nodelist
113         
114         def disallowed_tag(self, command):
115                 raise ValidationError("Tag not allowed: %s" % command)
116
117
118 class TemplateValidator(object): 
119         def __init__(self, allow=None, disallow=None, secure=True):
120                 self.allow = allow
121                 self.disallow = disallow
122                 self.secure = secure
123         
124         def __call__(self, value):
125                 try:
126                         self.validate_template(value)
127                 except ValidationError:
128                         raise
129                 except Exception, e:
130                         raise ValidationError("Template code invalid. Error was: %s: %s" % (e.__class__.__name__, e))
131         
132         def validate_template(self, template_string):
133                 # We want to tokenize like normal, then use a custom parser.
134                 lexer = Lexer(template_string, None)
135                 tokens = lexer.tokenize()
136                 parser = TemplateValidationParser(tokens, self.allow, self.disallow, self.secure)
137                 
138                 for node in parser.parse():
139                         template = getattr(node, LOADED_TEMPLATE_ATTR, None)