Skip to content

Commit dfe495f

Browse files
committed
[soc2009/model-validation] Merged to trunk at r11603
SECURITY ALERT: Corrected regular expressions for URL and email fields. git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@11617 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent 83a3588 commit dfe495f

File tree

42 files changed

+553
-371
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+553
-371
lines changed

AUTHORS

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ answer newbie questions, and generally made Django that much better:
202202
Kieran Holland <http://www.kieranholland.com>
203203
Sung-Jin Hong <serialx.net@gmail.com>
204204
Leo "hylje" Honkanen <sealage@gmail.com>
205+
Tareque Hossain <http://www.codexn.com>
205206
Richard House <Richard.House@i-logue.com>
206207
Robert Rock Howard <http://djangomojo.com/>
207208
John Huddleston <huddlej@wwu.edu>
@@ -241,6 +242,7 @@ answer newbie questions, and generally made Django that much better:
241242
Igor Kolar <ike@email.si>
242243
Gasper Koren
243244
Martin Kosír <martin@martinkosir.net>
245+
Arthur Koziel <http://arthurkoziel.com>
244246
Honza Kral <honza.kral@gmail.com>
245247
Meir Kriheli <http://mksoft.co.il/>
246248
Bruce Kroeze <http://coderseye.com/>
@@ -402,7 +404,7 @@ answer newbie questions, and generally made Django that much better:
402404
Vasiliy Stavenko <stavenko@gmail.com>
403405
Thomas Steinacher <http://www.eggdrop.ch/>
404406
Johan C. Stöver <johan@nilling.nl>
405-
nowell strite
407+
Nowell Strite <http://nowell.strite.org/>
406408
Thomas Stromberg <tstromberg@google.com>
407409
Pascal Varet
408410
SuperJared
13 Bytes
Binary file not shown.

django/conf/locale/he/LC_MESSAGES/django.po

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ msgstr "הוספת %s"
391391
#: contrib/admin/options.py:1003
392392
#, python-format
393393
msgid "%(name)s object with primary key %(key)r does not exist."
394-
msgstr "הפריט %(name)s עם המקש %(key)r אינו קיים."
394+
msgstr "הפריט %(name)s עם המפתח הראשי %(key)r אינו קיים."
395395

396396
#: contrib/admin/options.py:860
397397
#, python-format

django/contrib/auth/decorators.py

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
try:
2-
from functools import update_wrapper
2+
from functools import update_wrapper, wraps
33
except ImportError:
4-
from django.utils.functional import update_wrapper # Python 2.3, 2.4 fallback.
4+
from django.utils.functional import update_wrapper, wraps # Python 2.3, 2.4 fallback.
55

66
from django.contrib.auth import REDIRECT_FIELD_NAME
77
from django.http import HttpResponseRedirect
88
from django.utils.http import urlquote
9+
from django.utils.decorators import auto_adapt_to_methods
910

1011
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
1112
"""
1213
Decorator for views that checks that the user passes the given test,
1314
redirecting to the log-in page if necessary. The test should be a callable
1415
that takes the user object and returns True if the user passes.
1516
"""
16-
def decorate(view_func):
17-
return _CheckLogin(view_func, test_func, login_url, redirect_field_name)
18-
return decorate
17+
if not login_url:
18+
from django.conf import settings
19+
login_url = settings.LOGIN_URL
20+
21+
def decorator(view_func):
22+
def _wrapped_view(request, *args, **kwargs):
23+
if test_func(request.user):
24+
return view_func(request, *args, **kwargs)
25+
path = urlquote(request.get_full_path())
26+
tup = login_url, redirect_field_name, path
27+
return HttpResponseRedirect('%s?%s=%s' % tup)
28+
return wraps(view_func)(_wrapped_view)
29+
return auto_adapt_to_methods(decorator)
1930

2031
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME):
2132
"""
@@ -36,46 +47,3 @@ def permission_required(perm, login_url=None):
3647
enabled, redirecting to the log-in page if necessary.
3748
"""
3849
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
39-
40-
class _CheckLogin(object):
41-
"""
42-
Class that checks that the user passes the given test, redirecting to
43-
the log-in page if necessary. If the test is passed, the view function
44-
is invoked. The test should be a callable that takes the user object
45-
and returns True if the user passes.
46-
47-
We use a class here so that we can define __get__. This way, when a
48-
_CheckLogin object is used as a method decorator, the view function
49-
is properly bound to its instance.
50-
"""
51-
def __init__(self, view_func, test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
52-
if not login_url:
53-
from django.conf import settings
54-
login_url = settings.LOGIN_URL
55-
self.view_func = view_func
56-
self.test_func = test_func
57-
self.login_url = login_url
58-
self.redirect_field_name = redirect_field_name
59-
60-
# We can't blindly apply update_wrapper because it udpates __dict__ and
61-
# if the view function is already a _CheckLogin object then
62-
# self.test_func and friends will get stomped. However, we also can't
63-
# *not* update the wrapper's dict because then view function attributes
64-
# don't get updated into the wrapper. So we need to split the
65-
# difference: don't let update_wrapper update __dict__, but then update
66-
# the (parts of) __dict__ that we care about ourselves.
67-
update_wrapper(self, view_func, updated=())
68-
for k in view_func.__dict__:
69-
if k not in self.__dict__:
70-
self.__dict__[k] = view_func.__dict__[k]
71-
72-
def __get__(self, obj, cls=None):
73-
view_func = self.view_func.__get__(obj, cls)
74-
return _CheckLogin(view_func, self.test_func, self.login_url, self.redirect_field_name)
75-
76-
def __call__(self, request, *args, **kwargs):
77-
if self.test_func(request.user):
78-
return self.view_func(request, *args, **kwargs)
79-
path = urlquote(request.get_full_path())
80-
tup = self.login_url, self.redirect_field_name, path
81-
return HttpResponseRedirect('%s?%s=%s' % tup)

django/contrib/comments/admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class CommentsAdmin(admin.ModelAdmin):
2020
list_filter = ('submit_date', 'site', 'is_public', 'is_removed')
2121
date_hierarchy = 'submit_date'
2222
ordering = ('-submit_date',)
23+
raw_id_fields = ('user',)
2324
search_fields = ('comment', 'user__username', 'user_name', 'user_email', 'user_url', 'ip_address')
2425

2526
# Only register the default admin if the model is the built-in comment model

django/contrib/gis/db/models/sql/aggregates.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def convert_geom(wkt, geo_field):
1616

1717
if SpatialBackend.postgis:
1818
def convert_extent(box):
19-
# Box text will be something like "BOX(-90.0 30.0, -85.0 40.0)";
19+
# Box text will be something like "BOX(-90.0 30.0, -85.0 40.0)";
2020
# parsing out and returning as a 4-tuple.
2121
ll, ur = box[4:-1].split(',')
2222
xmin, ymin = map(float, ll.split())
@@ -32,19 +32,28 @@ def convert_geom(hex, geo_field):
3232

3333
def convert_extent(clob):
3434
if clob:
35-
# Oracle returns a polygon for the extent, we construct
36-
# the 4-tuple from the coordinates in the polygon.
37-
poly = SpatialBackend.Geometry(clob.read())
38-
shell = poly.shell
39-
ll, ur = shell[0], shell[2]
35+
# Generally, Oracle returns a polygon for the extent -- however,
36+
# it can return a single point if there's only one Point in the
37+
# table.
38+
ext_geom = SpatialBackend.Geometry(clob.read())
39+
gtype = str(ext_geom.geom_type)
40+
if gtype == 'Polygon':
41+
# Construct the 4-tuple from the coordinates in the polygon.
42+
shell = ext_geom.shell
43+
ll, ur = shell[0][:2], shell[2][:2]
44+
elif gtype == 'Point':
45+
ll = ext_geom.coords[:2]
46+
ur = ll
47+
else:
48+
raise Exception('Unexpected geometry type returned for extent: %s' % gtype)
4049
xmin, ymin = ll
4150
xmax, ymax = ur
4251
return (xmin, ymin, xmax, ymax)
4352
else:
4453
return None
45-
54+
4655
def convert_geom(clob, geo_field):
47-
if clob:
56+
if clob:
4857
return SpatialBackend.Geometry(clob.read(), geo_field.srid)
4958
else:
5059
return None
@@ -73,7 +82,7 @@ def __init__(self, col, source=None, is_summary=False, **extra):
7382
self.extra.setdefault('tolerance', 0.05)
7483

7584
# Can't use geographic aggregates on non-geometry fields.
76-
if not isinstance(self.source, GeometryField):
85+
if not isinstance(self.source, GeometryField):
7786
raise ValueError('Geospatial aggregates only allowed on geometry fields.')
7887

7988
# Making sure the SQL function is available for this spatial backend.
@@ -87,7 +96,7 @@ class Collect(GeoAggregate):
8796
class Extent(GeoAggregate):
8897
is_extent = True
8998
sql_function = SpatialBackend.extent
90-
99+
91100
if SpatialBackend.oracle:
92101
# Have to change Extent's attributes here for Oracle.
93102
Extent.conversion_class = GeomField

django/contrib/gis/shortcuts.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import cStringIO, zipfile
2+
from django.conf import settings
23
from django.http import HttpResponse
34
from django.template import loader
45

56
def compress_kml(kml):
67
"Returns compressed KMZ from the given KML string."
78
kmz = cStringIO.StringIO()
89
zf = zipfile.ZipFile(kmz, 'a', zipfile.ZIP_DEFLATED)
9-
zf.writestr('doc.kml', kml)
10+
zf.writestr('doc.kml', kml.encode(settings.DEFAULT_CHARSET))
1011
zf.close()
1112
kmz.seek(0)
1213
return kmz.read()

django/contrib/gis/tests/geoapp/test_regress.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os, unittest
22
from django.contrib.gis.db.backend import SpatialBackend
3-
from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_postgis
3+
from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_postgis, no_spatialite
4+
from django.contrib.gis.shortcuts import render_to_kmz
45
from models import City
56

67
class GeoRegressionTests(unittest.TestCase):
@@ -16,3 +17,19 @@ def test01_update(self):
1617
self.assertEqual(pnt, City.objects.get(name='Pueblo').point)
1718
City.objects.filter(name='Pueblo').update(point=bak)
1819
self.assertEqual(bak, City.objects.get(name='Pueblo').point)
20+
21+
def test02_kmz(self):
22+
"Testing `render_to_kmz` with non-ASCII data, see #11624."
23+
name = '\xc3\x85land Islands'.decode('iso-8859-1')
24+
places = [{'name' : name,
25+
'description' : name,
26+
'kml' : '<Point><coordinates>5.0,23.0</coordinates></Point>'
27+
}]
28+
kmz = render_to_kmz('gis/kml/placemarks.kml', {'places' : places})
29+
30+
@no_spatialite
31+
def test03_extent(self):
32+
"Testing `extent` on a table with a single point, see #11827."
33+
pnt = City.objects.get(name='Pueblo').point
34+
ref_ext = (pnt.x, pnt.y, pnt.x, pnt.y)
35+
self.assertEqual(ref_ext, City.objects.filter(name='Pueblo').extent())

django/contrib/localflavor/fr/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class FRPhoneNumberField(Field):
2828
'0X XX XX XX XX'.
2929
"""
3030
default_error_messages = {
31-
'invalid': u'Phone numbers must be in 0X XX XX XX XX format.',
31+
'invalid': _('Phone numbers must be in 0X XX XX XX XX format.'),
3232
}
3333

3434
def clean(self, value):

django/core/handlers/modpython.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ def _get_meta(self):
134134
if not hasattr(self, '_meta'):
135135
self._meta = {
136136
'AUTH_TYPE': self._req.ap_auth_type,
137-
'CONTENT_LENGTH': self._req.clength, # This may be wrong
138-
'CONTENT_TYPE': self._req.content_type, # This may be wrong
137+
'CONTENT_LENGTH': self._req.headers_in.get('content-length', 0),
138+
'CONTENT_TYPE': self._req.headers_in.get('content-type'),
139139
'GATEWAY_INTERFACE': 'CGI/1.1',
140140
'PATH_INFO': self.path_info,
141141
'PATH_TRANSLATED': None, # Not supported

django/core/management/__init__.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,82 @@ def fetch_command(self, subcommand):
261261
sys.exit(1)
262262
return klass
263263

264+
def autocomplete(self):
265+
"""
266+
Output completion suggestions for BASH.
267+
268+
The output of this function is passed to BASH's `COMREPLY` variable and
269+
treated as completion suggestions. `COMREPLY` expects a space
270+
separated string as the result.
271+
272+
The `COMP_WORDS` and `COMP_CWORD` BASH environment variables are used
273+
to get information about the cli input. Please refer to the BASH
274+
man-page for more information about this variables.
275+
276+
Subcommand options are saved as pairs. A pair consists of
277+
the long option string (e.g. '--exclude') and a boolean
278+
value indicating if the option requires arguments. When printing to
279+
stdout, a equal sign is appended to options which require arguments.
280+
281+
Note: If debugging this function, it is recommended to write the debug
282+
output in a separate file. Otherwise the debug output will be treated
283+
and formatted as potential completion suggestions.
284+
"""
285+
# Don't complete if user hasn't sourced bash_completion file.
286+
if not os.environ.has_key('DJANGO_AUTO_COMPLETE'):
287+
return
288+
289+
cwords = os.environ['COMP_WORDS'].split()[1:]
290+
cword = int(os.environ['COMP_CWORD'])
291+
292+
try:
293+
curr = cwords[cword-1]
294+
except IndexError:
295+
curr = ''
296+
297+
subcommands = get_commands().keys() + ['help']
298+
options = [('--help', None)]
299+
300+
# subcommand
301+
if cword == 1:
302+
print ' '.join(filter(lambda x: x.startswith(curr), subcommands))
303+
# subcommand options
304+
# special case: the 'help' subcommand has no options
305+
elif cwords[0] in subcommands and cwords[0] != 'help':
306+
subcommand_cls = self.fetch_command(cwords[0])
307+
# special case: 'runfcgi' stores additional options as
308+
# 'key=value' pairs
309+
if cwords[0] == 'runfcgi':
310+
from django.core.servers.fastcgi import FASTCGI_OPTIONS
311+
options += [(k, 1) for k in FASTCGI_OPTIONS]
312+
# special case: add the names of installed apps to options
313+
elif cwords[0] in ('dumpdata', 'reset', 'sql', 'sqlall',
314+
'sqlclear', 'sqlcustom', 'sqlindexes',
315+
'sqlreset', 'sqlsequencereset', 'test'):
316+
try:
317+
from django.conf import settings
318+
# Get the last part of the dotted path as the app name.
319+
options += [(a.split('.')[-1], 0) for a in settings.INSTALLED_APPS]
320+
except ImportError:
321+
# Fail silently if DJANGO_SETTINGS_MODULE isn't set. The
322+
# user will find out once they execute the command.
323+
pass
324+
options += [(s_opt.get_opt_string(), s_opt.nargs) for s_opt in
325+
subcommand_cls.option_list]
326+
# filter out previously specified options from available options
327+
prev_opts = [x.split('=')[0] for x in cwords[1:cword-1]]
328+
options = filter(lambda (x, v): x not in prev_opts, options)
329+
330+
# filter options by current input
331+
options = [(k, v) for k, v in options if k.startswith(curr)]
332+
for option in options:
333+
opt_label = option[0]
334+
# append '=' to options which require args
335+
if option[1]:
336+
opt_label += '='
337+
print opt_label
338+
sys.exit(1)
339+
264340
def execute(self):
265341
"""
266342
Given the command-line arguments, this figures out which subcommand is
@@ -272,6 +348,7 @@ def execute(self):
272348
parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
273349
version=get_version(),
274350
option_list=BaseCommand.option_list)
351+
self.autocomplete()
275352
try:
276353
options, args = parser.parse_args(self.argv)
277354
handle_default_options(options)

django/core/validators.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def __call__(self, value):
4040
class URLValidator(RegexValidator):
4141
regex = re.compile(
4242
r'^https?://' # http:// or https://
43-
r'(?:(?:[A-Z0-9]+(?:-*[A-Z0-9]+)*\.)+[A-Z]{2,6}|' #domain...
43+
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|' #domain...
4444
r'localhost|' #localhost...
4545
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
4646
r'(?::\d+)?' # optional port
@@ -81,7 +81,7 @@ def validate_integer(value):
8181
email_re = re.compile(
8282
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
8383
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string
84-
r')@(?:[A-Z0-9]+(?:-*[A-Z0-9]+)*\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
84+
r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', re.IGNORECASE) # domain
8585
validate_email = RegexValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid')
8686

8787
slug_re = re.compile(r'^[-\w]+$')

django/db/models/sql/expressions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def evaluate_node(self, node, qn):
6666
else:
6767
sql, params = '%s', (child,)
6868

69-
if hasattr(child, 'children') > 1:
69+
if len(getattr(child, 'children', [])) > 1:
7070
format = '(%s)'
7171
else:
7272
format = '%s'

django/forms/fields.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, \
3333
FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, \
3434
DateInput, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget
35-
from django.core.files.uploadedfile import SimpleUploadedFile as UploadedFile
3635

3736
__all__ = (
3837
'Field', 'CharField', 'IntegerField',

0 commit comments

Comments
 (0)