summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorJason Pellerin <jpellerin@gmail.com>2006-11-29 20:02:43 +0000
committerJason Pellerin <jpellerin@gmail.com>2006-11-29 20:02:43 +0000
commit71012a4be3830b1040815243ebb7c0d48dbd8e57 (patch)
treee66d2227298cc0bccc4ef874d023a4ef29c60c52 /django
parentf6d48b5d02acc7cd8d71ffe895fbf41c7c9ae2b7 (diff)
[multi-db] Merge trunk to [3812]. Some tests still failing.
git-svn-id: http://code.djangoproject.com/svn/django/branches/multiple-db-support@4139 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
-rw-r--r--django/conf/global_settings.py4
-rw-r--r--django/contrib/admin/templatetags/admin_modify.py4
-rw-r--r--django/contrib/admin/views/main.py2
-rw-r--r--django/contrib/auth/decorators.py8
-rw-r--r--django/contrib/auth/handlers/modpython.py31
-rw-r--r--django/core/context_processors.py6
-rw-r--r--django/core/handlers/modpython.py7
-rw-r--r--django/core/handlers/wsgi.py28
-rw-r--r--django/core/management.py22
-rw-r--r--django/core/serializers/json.py2
-rw-r--r--django/core/servers/basehttp.py9
-rw-r--r--django/core/servers/fastcgi.py2
-rw-r--r--django/core/validators.py9
-rw-r--r--django/core/xheaders.py5
-rw-r--r--django/db/backends/util.py6
-rw-r--r--django/db/models/fields/generic.py2
-rw-r--r--django/forms/__init__.py27
-rw-r--r--django/http/__init__.py31
-rw-r--r--django/middleware/common.py3
-rw-r--r--django/middleware/doc.py9
-rw-r--r--django/template/defaultfilters.py2
-rw-r--r--django/template/defaulttags.py12
-rw-r--r--django/utils/datastructures.py3
-rw-r--r--django/utils/text.py6
24 files changed, 167 insertions, 73 deletions
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index 20ed49b583..7b699f6b84 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -275,6 +275,10 @@ CACHE_MIDDLEWARE_KEY_PREFIX = ''
COMMENTS_ALLOW_PROFANITIES = False
+# The profanities that will trigger a validation error in the
+# 'hasNoProfanities' validator. All of these should be in lower-case.
+PROFANITIES_LIST = ['asshat', 'asshead', 'asshole', 'cunt', 'fuck', 'gook', 'nigger', 'shit']
+
# The group ID that designates which users are banned.
# Set to None if you're not using it.
COMMENTS_BANNED_USERS_GROUP = None
diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py
index 55d8bb2b95..875e61f959 100644
--- a/django/contrib/admin/templatetags/admin_modify.py
+++ b/django/contrib/admin/templatetags/admin_modify.py
@@ -160,8 +160,10 @@ class EditInlineNode(template.Node):
context.push()
if relation.field.rel.edit_inline == models.TABULAR:
bound_related_object_class = TabularBoundRelatedObject
- else:
+ elif relation.field.rel.edit_inline == models.STACKED:
bound_related_object_class = StackedBoundRelatedObject
+ else:
+ bound_related_object_class = relation.field.rel.edit_inline
original = context.get('original', None)
bound_related_object = relation.bind(context['form'], original, bound_related_object_class)
context['bound_related_object'] = bound_related_object
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index 7c942ca5b8..38d64bd73b 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -727,6 +727,8 @@ class ChangeList(object):
for bit in self.query.split():
or_queries = [models.Q(**{construct_search(field_name): bit}) for field_name in self.lookup_opts.admin.search_fields]
other_qs = QuerySet(self.model)
+ if qs._select_related:
+ other_qs = other_qs.select_related()
other_qs = other_qs.filter(reduce(operator.or_, or_queries))
qs = qs & other_qs
diff --git a/django/contrib/auth/decorators.py b/django/contrib/auth/decorators.py
index 0102496a33..8164d8314e 100644
--- a/django/contrib/auth/decorators.py
+++ b/django/contrib/auth/decorators.py
@@ -26,3 +26,11 @@ login_required.__doc__ = (
to the log-in page if necessary.
"""
)
+
+def permission_required(perm, login_url=LOGIN_URL):
+ """
+ Decorator for views that checks if a user has a particular permission
+ enabled, redirectiing to the log-in page if necessary.
+ """
+ return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
+
diff --git a/django/contrib/auth/handlers/modpython.py b/django/contrib/auth/handlers/modpython.py
index e6719794a1..c7d921313d 100644
--- a/django/contrib/auth/handlers/modpython.py
+++ b/django/contrib/auth/handlers/modpython.py
@@ -22,6 +22,8 @@ def authenhandler(req, **kwargs):
os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
from django.contrib.auth.models import User
+ from django import db
+ db.reset_queries()
# check that the username is valid
kwargs = {'username': req.user, 'is_active': True}
@@ -30,18 +32,21 @@ def authenhandler(req, **kwargs):
if superuser_only:
kwargs['is_superuser'] = True
try:
- user = User.objects.get(**kwargs)
- except User.DoesNotExist:
- return apache.HTTP_UNAUTHORIZED
-
- # check the password and any permission given
- if user.check_password(req.get_basic_auth_pw()):
- if permission_name:
- if user.has_perm(permission_name):
- return apache.OK
+ try:
+ user = User.objects.get(**kwargs)
+ except User.DoesNotExist:
+ return apache.HTTP_UNAUTHORIZED
+
+ # check the password and any permission given
+ if user.check_password(req.get_basic_auth_pw()):
+ if permission_name:
+ if user.has_perm(permission_name):
+ return apache.OK
+ else:
+ return apache.HTTP_UNAUTHORIZED
else:
- return apache.HTTP_UNAUTHORIZED
+ return apache.OK
else:
- return apache.OK
- else:
- return apache.HTTP_UNAUTHORIZED
+ return apache.HTTP_UNAUTHORIZED
+ finally:
+ db.connection.close()
diff --git a/django/core/context_processors.py b/django/core/context_processors.py
index 2ae9a6d972..f4b288dfc4 100644
--- a/django/core/context_processors.py
+++ b/django/core/context_processors.py
@@ -51,15 +51,19 @@ def request(request):
class PermLookupDict(object):
def __init__(self, user, module_name):
self.user, self.module_name = user, module_name
+
def __repr__(self):
- return str(self.user.get_permission_list())
+ return str(self.user.get_all_permissions())
+
def __getitem__(self, perm_name):
return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
+
def __nonzero__(self):
return self.user.has_module_perms(self.module_name)
class PermWrapper(object):
def __init__(self, user):
self.user = user
+
def __getitem__(self, module_name):
return PermLookupDict(self.user, module_name)
diff --git a/django/core/handlers/modpython.py b/django/core/handlers/modpython.py
index 07c98e3b59..db3c33147b 100644
--- a/django/core/handlers/modpython.py
+++ b/django/core/handlers/modpython.py
@@ -155,8 +155,11 @@ def populate_apache_request(http_response, mod_python_req):
for c in http_response.cookies.values():
mod_python_req.headers_out.add('Set-Cookie', c.output(header=''))
mod_python_req.status = http_response.status_code
- for chunk in http_response.iterator:
- mod_python_req.write(chunk)
+ try:
+ for chunk in http_response:
+ mod_python_req.write(chunk)
+ finally:
+ http_response.close()
def handler(req):
# mod_python hooks into this function.
diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py
index 5c48c9dace..87e67e9c5b 100644
--- a/django/core/handlers/wsgi.py
+++ b/django/core/handlers/wsgi.py
@@ -4,6 +4,11 @@ from django.dispatch import dispatcher
from django.utils import datastructures
from django import http
from pprint import pformat
+from shutil import copyfileobj
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
STATUS_CODE_TEXT = {
@@ -50,6 +55,21 @@ STATUS_CODE_TEXT = {
505: 'HTTP VERSION NOT SUPPORTED',
}
+def safe_copyfileobj(fsrc, fdst, length=16*1024, size=0):
+ """
+ A version of shutil.copyfileobj that will not read more than 'size' bytes.
+ This makes it safe from clients sending more than CONTENT_LENGTH bytes of
+ data in the body.
+ """
+ if not size:
+ return copyfileobj(fsrc, fdst, length)
+ while size > 0:
+ buf = fsrc.read(min(length, size))
+ if not buf:
+ break
+ fdst.write(buf)
+ size -= len(buf)
+
class WSGIRequest(http.HttpRequest):
def __init__(self, environ):
self.environ = environ
@@ -119,7 +139,11 @@ class WSGIRequest(http.HttpRequest):
try:
return self._raw_post_data
except AttributeError:
- self._raw_post_data = self.environ['wsgi.input'].read(int(self.environ["CONTENT_LENGTH"]))
+ buf = StringIO()
+ content_length = int(self.environ['CONTENT_LENGTH'])
+ safe_copyfileobj(self.environ['wsgi.input'], buf, size=content_length)
+ self._raw_post_data = buf.getvalue()
+ buf.close()
return self._raw_post_data
GET = property(_get_get, _set_get)
@@ -163,4 +187,4 @@ class WSGIHandler(BaseHandler):
for c in response.cookies.values():
response_headers.append(('Set-Cookie', c.output(header='')))
start_response(status, response_headers)
- return response.iterator
+ return response
diff --git a/django/core/management.py b/django/core/management.py
index 5164585dbb..bd162a1e8d 100644
--- a/django/core/management.py
+++ b/django/core/management.py
@@ -812,7 +812,8 @@ def get_validation_errors(outfile, app=None):
try:
f = opts.get_field(fn)
except models.FieldDoesNotExist:
- e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn)
+ if not hasattr(cls, fn):
+ e.add(opts, '"admin.list_display_links" refers to %r, which isn\'t an attribute, method or property.' % fn)
if fn not in opts.admin.list_display:
e.add(opts, '"admin.list_display_links" refers to %r, which is not defined in "admin.list_display".' % fn)
# list_filter
@@ -870,10 +871,12 @@ def get_validation_errors(outfile, app=None):
return len(e.errors)
-def validate(outfile=sys.stdout):
+def validate(outfile=sys.stdout, silent_success=False):
"Validates all installed models."
try:
num_errors = get_validation_errors(outfile)
+ if silent_success and num_errors == 0:
+ return
outfile.write('%s error%s found.\n' % (num_errors, num_errors != 1 and 's' or ''))
except ImproperlyConfigured:
outfile.write("Skipping validation because things aren't configured properly.")
@@ -896,7 +899,7 @@ def _check_for_validation_errors(app=None):
sys.stderr.write(s.read())
sys.exit(1)
-def runserver(addr, port, use_reloader=True):
+def runserver(addr, port, use_reloader=True, admin_media_dir=''):
"Starts a lightweight Web server for development."
from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
from django.core.handlers.wsgi import WSGIHandler
@@ -914,7 +917,10 @@ def runserver(addr, port, use_reloader=True):
print "Development server is running at http://%s:%s/" % (addr, port)
print "Quit the server with %s." % quit_command
try:
- run(addr, int(port), AdminMediaHandler(WSGIHandler()))
+ import django
+ path = admin_media_dir or django.__path__[0] + '/contrib/admin/media'
+ handler = AdminMediaHandler(WSGIHandler(), path)
+ run(addr, int(port), handler)
except WSGIServerException, e:
# Use helpful error messages instead of ugly tracebacks.
ERRORS = {
@@ -935,7 +941,7 @@ def runserver(addr, port, use_reloader=True):
autoreload.main(inner_run)
else:
inner_run()
-runserver.args = '[--noreload] [optional port number, or ipaddr:port]'
+runserver.args = '[--noreload] [--adminmedia=ADMIN_MEDIA_PATH] [optional port number, or ipaddr:port]'
def createcachetable(tablename):
"Creates the table needed to use the SQL cache backend"
@@ -1121,7 +1127,8 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
help='Tells Django to NOT use the auto-reloader when running the development server.')
parser.add_option('--verbosity', action='store', dest='verbosity', default='2',
type='choice', choices=['0', '1', '2'],
- help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
+ help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
+ parser.add_option('--adminmedia', dest='admin_media_path', default='', help='Lets you manually specify the directory to serve admin media from when running the development server.'),
options, args = parser.parse_args(argv[1:])
@@ -1185,11 +1192,12 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
addr, port = args[1].split(':')
except ValueError:
addr, port = '', args[1]
- action_mapping[action](addr, port, options.use_reloader)
+ action_mapping[action](addr, port, options.use_reloader, options.admin_media_path)
elif action == 'runfcgi':
action_mapping[action](args[1:])
else:
from django.db import models
+ validate(silent_success=True)
try:
mod_list = [models.get_app(app_label) for app_label in args[1:]]
except ImportError, e:
diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py
index 72234a624b..15770f160e 100644
--- a/django/core/serializers/json.py
+++ b/django/core/serializers/json.py
@@ -16,7 +16,7 @@ class Serializer(PythonSerializer):
Convert a queryset to JSON.
"""
def end_serialization(self):
- simplejson.dump(self.objects, self.stream, cls=DateTimeAwareJSONEncoder)
+ simplejson.dump(self.objects, self.stream, cls=DateTimeAwareJSONEncoder, **self.options)
def getvalue(self):
return self.stream.getvalue()
diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py
index 4bd0e50e53..fe534d5da0 100644
--- a/django/core/servers/basehttp.py
+++ b/django/core/servers/basehttp.py
@@ -594,11 +594,14 @@ class AdminMediaHandler(object):
Use this ONLY LOCALLY, for development! This hasn't been tested for
security and is not super efficient.
"""
- def __init__(self, application):
+ def __init__(self, application, media_dir = None):
from django.conf import settings
- import django
self.application = application
- self.media_dir = django.__path__[0] + '/contrib/admin/media'
+ if not media_dir:
+ import django
+ self.media_dir = django.__path__[0] + '/contrib/admin/media'
+ else:
+ self.media_dir = media_dir
self.media_url = settings.ADMIN_MEDIA_PREFIX
def __call__(self, environ, start_response):
diff --git a/django/core/servers/fastcgi.py b/django/core/servers/fastcgi.py
index 7377bed1c5..c6507fe173 100644
--- a/django/core/servers/fastcgi.py
+++ b/django/core/servers/fastcgi.py
@@ -74,7 +74,7 @@ def fastcgi_help(message=None):
print message
return False
-def runfastcgi(argset, **kwargs):
+def runfastcgi(argset=[], **kwargs):
options = FASTCGI_OPTIONS.copy()
options.update(kwargs)
for x in argset:
diff --git a/django/core/validators.py b/django/core/validators.py
index 8f40ceb51a..705425ea18 100644
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -227,9 +227,8 @@ def hasNoProfanities(field_data, all_data):
catch 'motherfucker' as well. Raises a ValidationError such as:
Watch your mouth! The words "f--k" and "s--t" are not allowed here.
"""
- bad_words = ['asshat', 'asshead', 'asshole', 'cunt', 'fuck', 'gook', 'nigger', 'shit'] # all in lower case
field_data = field_data.lower() # normalize
- words_seen = [w for w in bad_words if field_data.find(w) > -1]
+ words_seen = [w for w in settings.PROFANITIES_LIST if field_data.find(w) > -1]
if words_seen:
from django.utils.text import get_text_list
plural = len(words_seen) > 1
@@ -352,10 +351,12 @@ class IsValidFloat(object):
float(data)
except ValueError:
raise ValidationError, gettext("Please enter a valid decimal number.")
- if len(data) > (self.max_digits + 1):
+ # Negative floats require more space to input.
+ max_allowed_length = data.startswith('-') and (self.max_digits + 2) or (self.max_digits + 1)
+ if len(data) > max_allowed_length:
raise ValidationError, ngettext("Please enter a valid decimal number with at most %s total digit.",
"Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits
- if (not '.' in data and len(data) > (self.max_digits - self.decimal_places)) or ('.' in data and len(data) > (self.max_digits - (self.decimal_places - len(data.split('.')[1])) + 1)):
+ if (not '.' in data and len(data) > (max_allowed_length - self.decimal_places)) or ('.' in data and len(data) > (self.max_digits - (self.decimal_places - len(data.split('.')[1])) + 1)):
raise ValidationError, ngettext( "Please enter a valid decimal number with a whole part of at most %s digit.",
"Please enter a valid decimal number with a whole part of at most %s digits.", str(self.max_digits-self.decimal_places)) % str(self.max_digits-self.decimal_places)
if '.' in data and len(data.split('.')[1]) > self.decimal_places:
diff --git a/django/core/xheaders.py b/django/core/xheaders.py
index e173bcbca8..69f6115839 100644
--- a/django/core/xheaders.py
+++ b/django/core/xheaders.py
@@ -13,9 +13,10 @@ def populate_xheaders(request, response, model, object_id):
"""
Adds the "X-Object-Type" and "X-Object-Id" headers to the given
HttpResponse according to the given model and object_id -- but only if the
- given HttpRequest object has an IP address within the INTERNAL_IPS setting.
+ given HttpRequest object has an IP address within the INTERNAL_IPS setting
+ or if the request is from a logged in staff member.
"""
from django.conf import settings
- if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
+ if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or (request.user.is_authenticated() and request.user.is_staff):
response['X-Object-Type'] = "%s.%s" % (model._meta.app_label, model._meta.object_name.lower())
response['X-Object-Id'] = str(object_id)
diff --git a/django/db/backends/util.py b/django/db/backends/util.py
index 88318941c8..3ec1b41485 100644
--- a/django/db/backends/util.py
+++ b/django/db/backends/util.py
@@ -110,9 +110,11 @@ def dictfetchone(cursor):
def dictfetchmany(cursor, number):
"Returns a certain number of rows from a cursor as a dict"
desc = cursor.description
- return [_dict_helper(desc, row) for row in cursor.fetchmany(number)]
+ for row in cursor.fetchmany(number):
+ yield _dict_helper(desc, row)
def dictfetchall(cursor):
"Returns all rows from a cursor as a dict"
desc = cursor.description
- return [_dict_helper(desc, row) for row in cursor.fetchall()]
+ for row in cursor.fetchall():
+ yield _dict_helper(desc, row)
diff --git a/django/db/models/fields/generic.py b/django/db/models/fields/generic.py
index 5f4de40e69..7d7651029c 100644
--- a/django/db/models/fields/generic.py
+++ b/django/db/models/fields/generic.py
@@ -117,7 +117,7 @@ class GenericRelation(RelatedField, Field):
return self.object_id_field_name
def m2m_reverse_name(self):
- return self.model._meta.pk.attname
+ return self.object_id_field_name
def contribute_to_class(self, cls, name):
super(GenericRelation, self).contribute_to_class(cls, name)
diff --git a/django/forms/__init__.py b/django/forms/__init__.py
index 730f7a54da..537a109527 100644
--- a/django/forms/__init__.py
+++ b/django/forms/__init__.py
@@ -434,11 +434,11 @@ class HiddenField(FormField):
(self.get_id(), self.field_name, escape(data))
class CheckboxField(FormField):
- def __init__(self, field_name, checked_by_default=False, validator_list=None):
+ def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False):
if validator_list is None: validator_list = []
self.field_name = field_name
self.checked_by_default = checked_by_default
- self.is_required = False # because the validator looks for these
+ self.is_required = is_required
self.validator_list = validator_list[:]
def render(self, data):
@@ -639,8 +639,8 @@ class CheckboxSelectMultipleField(SelectMultipleField):
checked_html = ' checked="checked"'
field_name = '%s%s' % (self.field_name, value)
output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s /> <label for="%s">%s</label></li>' % \
- (self.get_id() + value , self.__class__.__name__, field_name, checked_html,
- self.get_id() + value, choice))
+ (self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html,
+ self.get_id() + escape(value), choice))
output.append('</ul>')
return '\n'.join(output)
@@ -743,7 +743,7 @@ class FloatField(TextField):
if validator_list is None: validator_list = []
self.max_digits, self.decimal_places = max_digits, decimal_places
validator_list = [self.isValidFloat] + validator_list
- TextField.__init__(self, field_name, max_digits+1, max_digits+1, is_required, validator_list)
+ TextField.__init__(self, field_name, max_digits+2, max_digits+2, is_required, validator_list)
def isValidFloat(self, field_data, all_data):
v = validators.IsValidFloat(self.max_digits, self.decimal_places)
@@ -952,10 +952,7 @@ class USStateField(TextField):
raise validators.CriticalValidationError, e.messages
def html2python(data):
- if data:
- return data.upper() # Should always be stored in upper case
- else:
- return None
+ return data.upper() # Should always be stored in upper case
html2python = staticmethod(html2python)
class CommaSeparatedIntegerField(TextField):
@@ -972,9 +969,19 @@ class CommaSeparatedIntegerField(TextField):
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
+ def render(self, data):
+ if data is None:
+ data = ''
+ elif isinstance(data, (list, tuple)):
+ data = ','.join(data)
+ return super(CommaSeparatedIntegerField, self).render(data)
+
class RawIdAdminField(CommaSeparatedIntegerField):
def html2python(data):
- return data.split(',')
+ if data:
+ return data.split(',')
+ else:
+ return []
html2python = staticmethod(html2python)
class XMLLargeTextField(LargeTextField):
diff --git a/django/http/__init__.py b/django/http/__init__.py
index c4ac302ec5..bb0e973aae 100644
--- a/django/http/__init__.py
+++ b/django/http/__init__.py
@@ -161,10 +161,10 @@ class HttpResponse(object):
if not mimetype:
mimetype = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE, settings.DEFAULT_CHARSET)
if hasattr(content, '__iter__'):
- self._iterator = content
+ self._container = content
self._is_string = False
else:
- self._iterator = [content]
+ self._container = [content]
self._is_string = True
self.headers = {'Content-Type': mimetype}
self.cookies = SimpleCookie()
@@ -213,32 +213,37 @@ class HttpResponse(object):
self.cookies[key]['max-age'] = 0
def _get_content(self):
- content = ''.join(self._iterator)
+ content = ''.join(self._container)
if isinstance(content, unicode):
content = content.encode(self._charset)
return content
def _set_content(self, value):
- self._iterator = [value]
+ self._container = [value]
self._is_string = True
content = property(_get_content, _set_content)
- def _get_iterator(self):
- "Output iterator. Converts data into client charset if necessary."
- for chunk in self._iterator:
- if isinstance(chunk, unicode):
- chunk = chunk.encode(self._charset)
- yield chunk
+ def __iter__(self):
+ self._iterator = self._container.__iter__()
+ return self
- iterator = property(_get_iterator)
+ def next(self):
+ chunk = self._iterator.next()
+ if isinstance(chunk, unicode):
+ chunk = chunk.encode(self._charset)
+ return chunk
+
+ def close(self):
+ if hasattr(self._container, 'close'):
+ self._container.close()
# The remaining methods partially implement the file-like object interface.
# See http://docs.python.org/lib/bltin-file-objects.html
def write(self, content):
if not self._is_string:
raise Exception, "This %s instance is not writable" % self.__class__
- self._iterator.append(content)
+ self._container.append(content)
def flush(self):
pass
@@ -246,7 +251,7 @@ class HttpResponse(object):
def tell(self):
if not self._is_string:
raise Exception, "This %s instance cannot tell its position" % self.__class__
- return sum([len(chunk) for chunk in self._iterator])
+ return sum([len(chunk) for chunk in self._container])
class HttpResponseRedirect(HttpResponse):
def __init__(self, redirect_to):
diff --git a/django/middleware/common.py b/django/middleware/common.py
index d63b71fed7..4f060b8590 100644
--- a/django/middleware/common.py
+++ b/django/middleware/common.py
@@ -64,8 +64,9 @@ class CommonMiddleware(object):
is_internal = referer and (domain in referer)
path = request.get_full_path()
if referer and not _is_ignorable_404(path) and (is_internal or '?' not in referer):
+ ua = request.META.get('HTTP_USER_AGENT','<none>')
mail_managers("Broken %slink on %s" % ((is_internal and 'INTERNAL ' or ''), domain),
- "Referrer: %s\nRequested URL: %s\n" % (referer, request.get_full_path()))
+ "Referrer: %s\nRequested URL: %s\nUser Agent: %s\n" % (referer, request.get_full_path(), ua))
return response
# Use ETags, if requested.
diff --git a/django/middleware/doc.py b/django/middleware/doc.py
index 6600e588cd..48c155c392 100644
--- a/django/middleware/doc.py
+++ b/django/middleware/doc.py
@@ -7,11 +7,12 @@ class XViewMiddleware(object):
"""
def process_view(self, request, view_func, view_args, view_kwargs):
"""
- If the request method is HEAD and the IP is internal, quickly return
- with an x-header indicating the view function. This is used by the
- documentation module to lookup the view function for an arbitrary page.
+ If the request method is HEAD and either the IP is internal or the
+ user is a logged-in staff member, quickly return with an x-header
+ indicating the view function. This is used by the documentation module
+ to lookup the view function for an arbitrary page.
"""
- if request.method == 'HEAD' and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
+ if request.method == 'HEAD' and (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or (request.user.is_authenticated() and request.user.is_staff)):
response = http.HttpResponse()
response['X-View'] = "%s.%s" % (view_func.__module__, view_func.__name__)
return response
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index a2e9d2f405..cf1d3d5f6d 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -15,7 +15,7 @@ register = Library()
def addslashes(value):
"Adds slashes - useful for passing strings to JavaScript, for example."
- return value.replace('"', '\\"').replace("'", "\\'")
+ return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
def capfirst(value):
"Capitalizes the first character of the value"
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index e8a58824dc..f7585368d1 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -13,14 +13,18 @@ class CommentNode(Node):
return ''
class CycleNode(Node):
- def __init__(self, cyclevars):
+ def __init__(self, cyclevars, variable_name=None):
self.cyclevars = cyclevars
self.cyclevars_len = len(cyclevars)
self.counter = -1
+ self.variable_name = variable_name
def render(self, context):
self.counter += 1
- return self.cyclevars[self.counter % self.cyclevars_len]
+ value = self.cyclevars[self.counter % self.cyclevars_len]
+ if self.variable_name:
+ context[self.variable_name] = value
+ return value
class DebugNode(Node):
def render(self, context):
@@ -125,6 +129,8 @@ class IfChangedNode(Node):
self._last_seen = None
def render(self, context):
+ if context.has_key('forloop') and context['forloop']['first']:
+ self._last_seen = None
content = self.nodelist.render(context)
if content != self._last_seen:
firstloop = (self._last_seen == None)
@@ -385,7 +391,7 @@ def cycle(parser, token):
raise TemplateSyntaxError("Second 'cycle' argument must be 'as'")
cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks
name = args[3]
- node = CycleNode(cyclevars)
+ node = CycleNode(cyclevars, name)
if not hasattr(parser, '_namedCycleNodes'):
parser._namedCycleNodes = {}
diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py
index 6aef313d35..cecb4da170 100644
--- a/django/utils/datastructures.py
+++ b/django/utils/datastructures.py
@@ -14,6 +14,9 @@ class MergeDict(object):
pass
raise KeyError
+ def __contains__(self, key):
+ return self.has_key(key)
+
def get(self, key, default):
try:
return self[key]
diff --git a/django/utils/text.py b/django/utils/text.py
index 7df9bc03b7..9e7bb3b6c4 100644
--- a/django/utils/text.py
+++ b/django/utils/text.py
@@ -94,7 +94,8 @@ def compress_string(s):
return zbuf.getvalue()
ustring_re = re.compile(u"([\u0080-\uffff])")
-def javascript_quote(s):
+
+def javascript_quote(s, quote_double_quotes=False):
def fix(match):
return r"\u%04x" % ord(match.group(1))
@@ -104,9 +105,12 @@ def javascript_quote(s):
elif type(s) != unicode:
raise TypeError, s
s = s.replace('\\', '\\\\')
+ s = s.replace('\r', '\\r')
s = s.replace('\n', '\\n')
s = s.replace('\t', '\\t')
s = s.replace("'", "\\'")
+ if quote_double_quotes:
+ s = s.replace('"', '&quot;')
return str(ustring_re.sub(fix, s))
smart_split_re = re.compile('("(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'|[^\\s]+)')