Merged recent changes from trunk.
This commit is contained in:
commit
8e3fd703d0
21
AUTHORS
21
AUTHORS
|
@ -29,6 +29,8 @@ The PRIMARY AUTHORS are (and/or have been):
|
||||||
* Julien Phalip
|
* Julien Phalip
|
||||||
* Aymeric Augustin
|
* Aymeric Augustin
|
||||||
* Claude Paroz
|
* Claude Paroz
|
||||||
|
* Anssi Kääriäinen
|
||||||
|
* Florian Apolloner
|
||||||
|
|
||||||
More information on the main contributors to Django can be found in
|
More information on the main contributors to Django can be found in
|
||||||
docs/internals/committers.txt.
|
docs/internals/committers.txt.
|
||||||
|
@ -50,6 +52,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Antoni Aloy
|
Antoni Aloy
|
||||||
Daniel Alves Barbosa de Oliveira Vaz <danielvaz@gmail.com>
|
Daniel Alves Barbosa de Oliveira Vaz <danielvaz@gmail.com>
|
||||||
AgarFu <heaven@croasanaso.sytes.net>
|
AgarFu <heaven@croasanaso.sytes.net>
|
||||||
|
James Aylett
|
||||||
Dagur Páll Ammendrup <dagurp@gmail.com>
|
Dagur Páll Ammendrup <dagurp@gmail.com>
|
||||||
Collin Anderson <cmawebsite@gmail.com>
|
Collin Anderson <cmawebsite@gmail.com>
|
||||||
Jeff Anderson <jefferya@programmerq.net>
|
Jeff Anderson <jefferya@programmerq.net>
|
||||||
|
@ -59,7 +62,6 @@ answer newbie questions, and generally made Django that much better:
|
||||||
andy@jadedplanet.net
|
andy@jadedplanet.net
|
||||||
Fabrice Aneche <akh@nobugware.com>
|
Fabrice Aneche <akh@nobugware.com>
|
||||||
ant9000@netwise.it
|
ant9000@netwise.it
|
||||||
Florian Apolloner <florian@apolloner.eu>
|
|
||||||
arien <regexbot@gmail.com>
|
arien <regexbot@gmail.com>
|
||||||
David Ascher <http://ascher.ca/>
|
David Ascher <http://ascher.ca/>
|
||||||
atlithorn <atlithorn@gmail.com>
|
atlithorn <atlithorn@gmail.com>
|
||||||
|
@ -84,6 +86,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Esdras Beleza <linux@esdrasbeleza.com>
|
Esdras Beleza <linux@esdrasbeleza.com>
|
||||||
Chris Bennett <chrisrbennett@yahoo.com>
|
Chris Bennett <chrisrbennett@yahoo.com>
|
||||||
James Bennett
|
James Bennett
|
||||||
|
Danilo Bargen
|
||||||
Shai Berger <shai@platonix.com>
|
Shai Berger <shai@platonix.com>
|
||||||
Julian Bez
|
Julian Bez
|
||||||
Arvis Bickovskis <viestards.lists@gmail.com>
|
Arvis Bickovskis <viestards.lists@gmail.com>
|
||||||
|
@ -136,9 +139,10 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Robert Coup
|
Robert Coup
|
||||||
Pete Crosier <pete.crosier@gmail.com>
|
Pete Crosier <pete.crosier@gmail.com>
|
||||||
Matt Croydon <http://www.postneo.com/>
|
Matt Croydon <http://www.postneo.com/>
|
||||||
Leah Culver <leah.culver@gmail.com>
|
|
||||||
flavio.curella@gmail.com
|
|
||||||
Jure Cuhalev <gandalf@owca.info>
|
Jure Cuhalev <gandalf@owca.info>
|
||||||
|
Leah Culver <leah.culver@gmail.com>
|
||||||
|
Raúl Cumplido <raulcumplido@gmail.com>
|
||||||
|
flavio.curella@gmail.com
|
||||||
John D'Agostino <john.dagostino@gmail.com>
|
John D'Agostino <john.dagostino@gmail.com>
|
||||||
dackze+django@gmail.com
|
dackze+django@gmail.com
|
||||||
Jim Dalton <jim.dalton@gmail.com>
|
Jim Dalton <jim.dalton@gmail.com>
|
||||||
|
@ -157,6 +161,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Rajesh Dhawan <rajesh.dhawan@gmail.com>
|
Rajesh Dhawan <rajesh.dhawan@gmail.com>
|
||||||
Sander Dijkhuis <sander.dijkhuis@gmail.com>
|
Sander Dijkhuis <sander.dijkhuis@gmail.com>
|
||||||
Jordan Dimov <s3x3y1@gmail.com>
|
Jordan Dimov <s3x3y1@gmail.com>
|
||||||
|
Riccardo Di Virgilio
|
||||||
Nebojša Dorđević
|
Nebojša Dorđević
|
||||||
dne@mayonnaise.net
|
dne@mayonnaise.net
|
||||||
dready <wil@mojipage.com>
|
dready <wil@mojipage.com>
|
||||||
|
@ -171,6 +176,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Clint Ecker
|
Clint Ecker
|
||||||
Nick Efford <nick@efford.org>
|
Nick Efford <nick@efford.org>
|
||||||
eibaan@gmail.com
|
eibaan@gmail.com
|
||||||
|
David Eklund
|
||||||
Julia Elman
|
Julia Elman
|
||||||
enlight
|
enlight
|
||||||
Enrico <rico.bl@gmail.com>
|
Enrico <rico.bl@gmail.com>
|
||||||
|
@ -219,6 +225,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
pradeep.gowda@gmail.com
|
pradeep.gowda@gmail.com
|
||||||
Collin Grady <collin@collingrady.com>
|
Collin Grady <collin@collingrady.com>
|
||||||
Gabriel Grant <g@briel.ca>
|
Gabriel Grant <g@briel.ca>
|
||||||
|
Daniel Greenfeld
|
||||||
Simon Greenhill <dev@simon.net.nz>
|
Simon Greenhill <dev@simon.net.nz>
|
||||||
Owen Griffiths
|
Owen Griffiths
|
||||||
Espen Grindhaug <http://grindhaug.org/>
|
Espen Grindhaug <http://grindhaug.org/>
|
||||||
|
@ -229,6 +236,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Scot Hacker <shacker@birdhouse.org>
|
Scot Hacker <shacker@birdhouse.org>
|
||||||
dAniel hAhler
|
dAniel hAhler
|
||||||
hambaloney
|
hambaloney
|
||||||
|
Will Hardy <django@willhardy.com.au>
|
||||||
Brian Harring <ferringb@gmail.com>
|
Brian Harring <ferringb@gmail.com>
|
||||||
Brant Harris
|
Brant Harris
|
||||||
Ronny Haryanto <http://ronny.haryan.to/>
|
Ronny Haryanto <http://ronny.haryan.to/>
|
||||||
|
@ -273,7 +281,6 @@ answer newbie questions, and generally made Django that much better:
|
||||||
jpellerin@gmail.com
|
jpellerin@gmail.com
|
||||||
junzhang.jn@gmail.com
|
junzhang.jn@gmail.com
|
||||||
Xia Kai <http://blog.xiaket.org/>
|
Xia Kai <http://blog.xiaket.org/>
|
||||||
Anssi Kääriäinen
|
|
||||||
Antti Kaihola <http://djangopeople.net/akaihola/>
|
Antti Kaihola <http://djangopeople.net/akaihola/>
|
||||||
Peter van Kampen
|
Peter van Kampen
|
||||||
Bahadır Kandemir <bahadir@pardus.org.tr>
|
Bahadır Kandemir <bahadir@pardus.org.tr>
|
||||||
|
@ -366,6 +373,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
michael.mcewan@gmail.com
|
michael.mcewan@gmail.com
|
||||||
Paul McLanahan <paul@mclanahan.net>
|
Paul McLanahan <paul@mclanahan.net>
|
||||||
Tobias McNulty <http://www.caktusgroup.com/blog>
|
Tobias McNulty <http://www.caktusgroup.com/blog>
|
||||||
|
Andrews Medina <andrewsmedina@gmail.com>
|
||||||
Zain Memon
|
Zain Memon
|
||||||
Christian Metts
|
Christian Metts
|
||||||
michal@plovarna.cz
|
michal@plovarna.cz
|
||||||
|
@ -422,6 +430,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
polpak@yahoo.com
|
polpak@yahoo.com
|
||||||
Ross Poulton <ross@rossp.org>
|
Ross Poulton <ross@rossp.org>
|
||||||
Mihai Preda <mihai_preda@yahoo.com>
|
Mihai Preda <mihai_preda@yahoo.com>
|
||||||
|
Daniele Procida <daniele@vurt.org>
|
||||||
Matthias Pronk <django@masida.nl>
|
Matthias Pronk <django@masida.nl>
|
||||||
Jyrki Pulliainen <jyrki.pulliainen@gmail.com>
|
Jyrki Pulliainen <jyrki.pulliainen@gmail.com>
|
||||||
Thejaswi Puthraya <thejaswi.puthraya@gmail.com>
|
Thejaswi Puthraya <thejaswi.puthraya@gmail.com>
|
||||||
|
@ -450,6 +459,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Armin Ronacher
|
Armin Ronacher
|
||||||
Daniel Roseman <http://roseman.org.uk/>
|
Daniel Roseman <http://roseman.org.uk/>
|
||||||
Rozza <ross.lawley@gmail.com>
|
Rozza <ross.lawley@gmail.com>
|
||||||
|
Audrey Roy <http://audreymroy.com/>
|
||||||
Oliver Rutherfurd <http://rutherfurd.net/>
|
Oliver Rutherfurd <http://rutherfurd.net/>
|
||||||
ryankanno
|
ryankanno
|
||||||
Gonzalo Saavedra <gonzalosaavedra@gmail.com>
|
Gonzalo Saavedra <gonzalosaavedra@gmail.com>
|
||||||
|
@ -458,6 +468,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Vinay Sajip <vinay_sajip@yahoo.co.uk>
|
Vinay Sajip <vinay_sajip@yahoo.co.uk>
|
||||||
Bartolome Sanchez Salado <i42sasab@uco.es>
|
Bartolome Sanchez Salado <i42sasab@uco.es>
|
||||||
Kadesarin Sanjek
|
Kadesarin Sanjek
|
||||||
|
Tim Saylor <tim.saylor@gmail.com>
|
||||||
Massimo Scamarcia <massimo.scamarcia@gmail.com>
|
Massimo Scamarcia <massimo.scamarcia@gmail.com>
|
||||||
Paulo Scardine <paulo@scardine.com.br>
|
Paulo Scardine <paulo@scardine.com.br>
|
||||||
David Schein
|
David Schein
|
||||||
|
@ -502,6 +513,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Aaron Swartz <http://www.aaronsw.com/>
|
Aaron Swartz <http://www.aaronsw.com/>
|
||||||
Ville Säävuori <http://www.unessa.net/>
|
Ville Säävuori <http://www.unessa.net/>
|
||||||
Mart Sõmermaa <http://mrts.pri.ee/>
|
Mart Sõmermaa <http://mrts.pri.ee/>
|
||||||
|
Marc Tamlyn
|
||||||
Christian Tanzer <tanzer@swing.co.at>
|
Christian Tanzer <tanzer@swing.co.at>
|
||||||
Tyler Tarabula <tyler.tarabula@gmail.com>
|
Tyler Tarabula <tyler.tarabula@gmail.com>
|
||||||
Tyson Tate <tyson@fallingbullets.com>
|
Tyson Tate <tyson@fallingbullets.com>
|
||||||
|
@ -550,6 +562,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Mike Wiacek <mjwiacek@google.com>
|
Mike Wiacek <mjwiacek@google.com>
|
||||||
Frank Wierzbicki
|
Frank Wierzbicki
|
||||||
charly.wilhelm@gmail.com
|
charly.wilhelm@gmail.com
|
||||||
|
Simon Williams
|
||||||
Derek Willis <http://blog.thescoop.org/>
|
Derek Willis <http://blog.thescoop.org/>
|
||||||
Rachel Willmer <http://www.willmer.com/kb/>
|
Rachel Willmer <http://www.willmer.com/kb/>
|
||||||
Jakub Wilk <ubanus@users.sf.net>
|
Jakub Wilk <ubanus@users.sf.net>
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# This works exactly like 2to3, except that it uses Django's fixers rather
|
||||||
|
# than 2to3's built-in fixers.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from lib2to3.main import main
|
||||||
|
|
||||||
|
sys.exit(main("django.utils.2to3_fixes"))
|
||||||
|
|
|
@ -15,6 +15,7 @@ from django.conf import global_settings
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils.functional import LazyObject, empty
|
from django.utils.functional import LazyObject, empty
|
||||||
from django.utils import importlib
|
from django.utils import importlib
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
|
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
|
||||||
|
|
||||||
|
@ -73,7 +74,7 @@ class BaseSettings(object):
|
||||||
elif name == "ADMIN_MEDIA_PREFIX":
|
elif name == "ADMIN_MEDIA_PREFIX":
|
||||||
warnings.warn("The ADMIN_MEDIA_PREFIX setting has been removed; "
|
warnings.warn("The ADMIN_MEDIA_PREFIX setting has been removed; "
|
||||||
"use STATIC_URL instead.", DeprecationWarning)
|
"use STATIC_URL instead.", DeprecationWarning)
|
||||||
elif name == "ALLOWED_INCLUDE_ROOTS" and isinstance(value, basestring):
|
elif name == "ALLOWED_INCLUDE_ROOTS" and isinstance(value, six.string_types):
|
||||||
raise ValueError("The ALLOWED_INCLUDE_ROOTS setting must be set "
|
raise ValueError("The ALLOWED_INCLUDE_ROOTS setting must be set "
|
||||||
"to a tuple, not a string.")
|
"to a tuple, not a string.")
|
||||||
object.__setattr__(self, name, value)
|
object.__setattr__(self, name, value)
|
||||||
|
@ -102,7 +103,10 @@ class Settings(BaseSettings):
|
||||||
if setting == setting.upper():
|
if setting == setting.upper():
|
||||||
setting_value = getattr(mod, setting)
|
setting_value = getattr(mod, setting)
|
||||||
if setting in tuple_settings and \
|
if setting in tuple_settings and \
|
||||||
isinstance(setting_value, basestring):
|
isinstance(setting_value, six.string_types):
|
||||||
|
warnings.warn("The %s setting must be a tuple. Please fix your "
|
||||||
|
"settings, as auto-correction is now deprecated." % setting,
|
||||||
|
PendingDeprecationWarning)
|
||||||
setting_value = (setting_value,) # In case the user forgot the comma.
|
setting_value = (setting_value,) # In case the user forgot the comma.
|
||||||
setattr(self, setting, setting_value)
|
setattr(self, setting, setting_value)
|
||||||
|
|
||||||
|
@ -154,7 +158,7 @@ class UserSettingsHolder(BaseSettings):
|
||||||
return getattr(self.default_settings, name)
|
return getattr(self.default_settings, name)
|
||||||
|
|
||||||
def __dir__(self):
|
def __dir__(self):
|
||||||
return self.__dict__.keys() + dir(self.default_settings)
|
return list(self.__dict__) + dir(self.default_settings)
|
||||||
|
|
||||||
# For Python < 2.6:
|
# For Python < 2.6:
|
||||||
__members__ = property(lambda self: self.__dir__())
|
__members__ = property(lambda self: self.__dir__())
|
||||||
|
|
|
@ -270,19 +270,19 @@ SECRET_KEY = ''
|
||||||
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
|
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
|
||||||
|
|
||||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||||
# Example: "/home/media/media.lawrence.com/media/"
|
# Example: "/var/www/example.com/media/"
|
||||||
MEDIA_ROOT = ''
|
MEDIA_ROOT = ''
|
||||||
|
|
||||||
# URL that handles the media served from MEDIA_ROOT.
|
# URL that handles the media served from MEDIA_ROOT.
|
||||||
# Example: "http://media.lawrence.com/media/"
|
# Examples: "http://example.com/media/", "http://media.example.com/"
|
||||||
MEDIA_URL = ''
|
MEDIA_URL = ''
|
||||||
|
|
||||||
# Absolute path to the directory that holds static files.
|
# Absolute path to the directory static files should be collected to.
|
||||||
# Example: "/home/media/media.lawrence.com/static/"
|
# Example: "/var/www/example.com/static/"
|
||||||
STATIC_ROOT = ''
|
STATIC_ROOT = ''
|
||||||
|
|
||||||
# URL that handles the static files served from STATIC_ROOT.
|
# URL that handles the static files served from STATIC_ROOT.
|
||||||
# Example: "http://media.lawrence.com/static/"
|
# Example: "http://example.com/static/", "http://static.example.com/"
|
||||||
STATIC_URL = None
|
STATIC_URL = None
|
||||||
|
|
||||||
# List of upload handler classes to be applied in order.
|
# List of upload handler classes to be applied in order.
|
||||||
|
@ -451,7 +451,7 @@ MIDDLEWARE_CLASSES = (
|
||||||
|
|
||||||
SESSION_COOKIE_NAME = 'sessionid' # Cookie name. This can be whatever you want.
|
SESSION_COOKIE_NAME = 'sessionid' # Cookie name. This can be whatever you want.
|
||||||
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds (default: 2 weeks).
|
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds (default: 2 weeks).
|
||||||
SESSION_COOKIE_DOMAIN = None # A string like ".lawrence.com", or None for standard domain cookie.
|
SESSION_COOKIE_DOMAIN = None # A string like ".example.com", or None for standard domain cookie.
|
||||||
SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only).
|
SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only).
|
||||||
SESSION_COOKIE_PATH = '/' # The path of the session cookie.
|
SESSION_COOKIE_PATH = '/' # The path of the session cookie.
|
||||||
SESSION_COOKIE_HTTPONLY = True # Whether to use the non-RFC standard httpOnly flag (IE, FF3+, others)
|
SESSION_COOKIE_HTTPONLY = True # Whether to use the non-RFC standard httpOnly flag (IE, FF3+, others)
|
||||||
|
|
|
@ -1,434 +1,436 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
LANG_INFO = {
|
LANG_INFO = {
|
||||||
'ar': {
|
'ar': {
|
||||||
'bidi': True,
|
'bidi': True,
|
||||||
'code': 'ar',
|
'code': 'ar',
|
||||||
'name': 'Arabic',
|
'name': 'Arabic',
|
||||||
'name_local': u'\u0627\u0644\u0639\u0631\u0628\u064a\u0651\u0629',
|
'name_local': '\u0627\u0644\u0639\u0631\u0628\u064a\u0651\u0629',
|
||||||
},
|
},
|
||||||
'az': {
|
'az': {
|
||||||
'bidi': True,
|
'bidi': True,
|
||||||
'code': 'az',
|
'code': 'az',
|
||||||
'name': 'Azerbaijani',
|
'name': 'Azerbaijani',
|
||||||
'name_local': u'az\u0259rbaycan dili',
|
'name_local': 'az\u0259rbaycan dili',
|
||||||
},
|
},
|
||||||
'bg': {
|
'bg': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'bg',
|
'code': 'bg',
|
||||||
'name': 'Bulgarian',
|
'name': 'Bulgarian',
|
||||||
'name_local': u'\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438',
|
'name_local': '\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438',
|
||||||
},
|
},
|
||||||
'bn': {
|
'bn': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'bn',
|
'code': 'bn',
|
||||||
'name': 'Bengali',
|
'name': 'Bengali',
|
||||||
'name_local': u'\u09ac\u09be\u0982\u09b2\u09be',
|
'name_local': '\u09ac\u09be\u0982\u09b2\u09be',
|
||||||
},
|
},
|
||||||
'bs': {
|
'bs': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'bs',
|
'code': 'bs',
|
||||||
'name': 'Bosnian',
|
'name': 'Bosnian',
|
||||||
'name_local': u'bosanski',
|
'name_local': 'bosanski',
|
||||||
},
|
},
|
||||||
'ca': {
|
'ca': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'ca',
|
'code': 'ca',
|
||||||
'name': 'Catalan',
|
'name': 'Catalan',
|
||||||
'name_local': u'catal\xe0',
|
'name_local': 'catal\xe0',
|
||||||
},
|
},
|
||||||
'cs': {
|
'cs': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'cs',
|
'code': 'cs',
|
||||||
'name': 'Czech',
|
'name': 'Czech',
|
||||||
'name_local': u'\u010desky',
|
'name_local': '\u010desky',
|
||||||
},
|
},
|
||||||
'cy': {
|
'cy': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'cy',
|
'code': 'cy',
|
||||||
'name': 'Welsh',
|
'name': 'Welsh',
|
||||||
'name_local': u'Cymraeg',
|
'name_local': 'Cymraeg',
|
||||||
},
|
},
|
||||||
'da': {
|
'da': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'da',
|
'code': 'da',
|
||||||
'name': 'Danish',
|
'name': 'Danish',
|
||||||
'name_local': u'Dansk',
|
'name_local': 'Dansk',
|
||||||
},
|
},
|
||||||
'de': {
|
'de': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'de',
|
'code': 'de',
|
||||||
'name': 'German',
|
'name': 'German',
|
||||||
'name_local': u'Deutsch',
|
'name_local': 'Deutsch',
|
||||||
},
|
},
|
||||||
'el': {
|
'el': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'el',
|
'code': 'el',
|
||||||
'name': 'Greek',
|
'name': 'Greek',
|
||||||
'name_local': u'\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac',
|
'name_local': '\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac',
|
||||||
},
|
},
|
||||||
'en': {
|
'en': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'en',
|
'code': 'en',
|
||||||
'name': 'English',
|
'name': 'English',
|
||||||
'name_local': u'English',
|
'name_local': 'English',
|
||||||
},
|
},
|
||||||
'en-gb': {
|
'en-gb': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'en-gb',
|
'code': 'en-gb',
|
||||||
'name': 'British English',
|
'name': 'British English',
|
||||||
'name_local': u'British English',
|
'name_local': 'British English',
|
||||||
},
|
},
|
||||||
'eo': {
|
'eo': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'eo',
|
'code': 'eo',
|
||||||
'name': 'Esperanto',
|
'name': 'Esperanto',
|
||||||
'name_local': u'Esperanto',
|
'name_local': 'Esperanto',
|
||||||
},
|
},
|
||||||
'es': {
|
'es': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'es',
|
'code': 'es',
|
||||||
'name': 'Spanish',
|
'name': 'Spanish',
|
||||||
'name_local': u'espa\xf1ol',
|
'name_local': 'espa\xf1ol',
|
||||||
},
|
},
|
||||||
'es-ar': {
|
'es-ar': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'es-ar',
|
'code': 'es-ar',
|
||||||
'name': 'Argentinian Spanish',
|
'name': 'Argentinian Spanish',
|
||||||
'name_local': u'espa\xf1ol de Argentina',
|
'name_local': 'espa\xf1ol de Argentina',
|
||||||
},
|
},
|
||||||
'es-mx': {
|
'es-mx': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'es-mx',
|
'code': 'es-mx',
|
||||||
'name': 'Mexican Spanish',
|
'name': 'Mexican Spanish',
|
||||||
'name_local': u'espa\xf1ol de Mexico',
|
'name_local': 'espa\xf1ol de Mexico',
|
||||||
},
|
},
|
||||||
'es-ni': {
|
'es-ni': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'es-ni',
|
'code': 'es-ni',
|
||||||
'name': 'Nicaraguan Spanish',
|
'name': 'Nicaraguan Spanish',
|
||||||
'name_local': u'espa\xf1ol de Nicaragua',
|
'name_local': 'espa\xf1ol de Nicaragua',
|
||||||
},
|
},
|
||||||
'et': {
|
'et': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'et',
|
'code': 'et',
|
||||||
'name': 'Estonian',
|
'name': 'Estonian',
|
||||||
'name_local': u'eesti',
|
'name_local': 'eesti',
|
||||||
},
|
},
|
||||||
'eu': {
|
'eu': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'eu',
|
'code': 'eu',
|
||||||
'name': 'Basque',
|
'name': 'Basque',
|
||||||
'name_local': u'Basque',
|
'name_local': 'Basque',
|
||||||
},
|
},
|
||||||
'fa': {
|
'fa': {
|
||||||
'bidi': True,
|
'bidi': True,
|
||||||
'code': 'fa',
|
'code': 'fa',
|
||||||
'name': 'Persian',
|
'name': 'Persian',
|
||||||
'name_local': u'\u0641\u0627\u0631\u0633\u06cc',
|
'name_local': '\u0641\u0627\u0631\u0633\u06cc',
|
||||||
},
|
},
|
||||||
'fi': {
|
'fi': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'fi',
|
'code': 'fi',
|
||||||
'name': 'Finnish',
|
'name': 'Finnish',
|
||||||
'name_local': u'suomi',
|
'name_local': 'suomi',
|
||||||
},
|
},
|
||||||
'fr': {
|
'fr': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'fr',
|
'code': 'fr',
|
||||||
'name': 'French',
|
'name': 'French',
|
||||||
'name_local': u'Fran\xe7ais',
|
'name_local': 'Fran\xe7ais',
|
||||||
},
|
},
|
||||||
'fy-nl': {
|
'fy-nl': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'fy-nl',
|
'code': 'fy-nl',
|
||||||
'name': 'Frisian',
|
'name': 'Frisian',
|
||||||
'name_local': u'Frisian',
|
'name_local': 'Frisian',
|
||||||
},
|
},
|
||||||
'ga': {
|
'ga': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'ga',
|
'code': 'ga',
|
||||||
'name': 'Irish',
|
'name': 'Irish',
|
||||||
'name_local': u'Gaeilge',
|
'name_local': 'Gaeilge',
|
||||||
},
|
},
|
||||||
'gl': {
|
'gl': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'gl',
|
'code': 'gl',
|
||||||
'name': 'Galician',
|
'name': 'Galician',
|
||||||
'name_local': u'galego',
|
'name_local': 'galego',
|
||||||
},
|
},
|
||||||
'he': {
|
'he': {
|
||||||
'bidi': True,
|
'bidi': True,
|
||||||
'code': 'he',
|
'code': 'he',
|
||||||
'name': 'Hebrew',
|
'name': 'Hebrew',
|
||||||
'name_local': u'\u05e2\u05d1\u05e8\u05d9\u05ea',
|
'name_local': '\u05e2\u05d1\u05e8\u05d9\u05ea',
|
||||||
},
|
},
|
||||||
'hi': {
|
'hi': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'hi',
|
'code': 'hi',
|
||||||
'name': 'Hindi',
|
'name': 'Hindi',
|
||||||
'name_local': u'Hindi',
|
'name_local': 'Hindi',
|
||||||
},
|
},
|
||||||
'hr': {
|
'hr': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'hr',
|
'code': 'hr',
|
||||||
'name': 'Croatian',
|
'name': 'Croatian',
|
||||||
'name_local': u'Hrvatski',
|
'name_local': 'Hrvatski',
|
||||||
},
|
},
|
||||||
'hu': {
|
'hu': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'hu',
|
'code': 'hu',
|
||||||
'name': 'Hungarian',
|
'name': 'Hungarian',
|
||||||
'name_local': u'Magyar',
|
'name_local': 'Magyar',
|
||||||
},
|
},
|
||||||
'id': {
|
'id': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'id',
|
'code': 'id',
|
||||||
'name': 'Indonesian',
|
'name': 'Indonesian',
|
||||||
'name_local': u'Bahasa Indonesia',
|
'name_local': 'Bahasa Indonesia',
|
||||||
},
|
},
|
||||||
'is': {
|
'is': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'is',
|
'code': 'is',
|
||||||
'name': 'Icelandic',
|
'name': 'Icelandic',
|
||||||
'name_local': u'\xcdslenska',
|
'name_local': '\xcdslenska',
|
||||||
},
|
},
|
||||||
'it': {
|
'it': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'it',
|
'code': 'it',
|
||||||
'name': 'Italian',
|
'name': 'Italian',
|
||||||
'name_local': u'italiano',
|
'name_local': 'italiano',
|
||||||
},
|
},
|
||||||
'ja': {
|
'ja': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'ja',
|
'code': 'ja',
|
||||||
'name': 'Japanese',
|
'name': 'Japanese',
|
||||||
'name_local': u'\u65e5\u672c\u8a9e',
|
'name_local': '\u65e5\u672c\u8a9e',
|
||||||
},
|
},
|
||||||
'ka': {
|
'ka': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'ka',
|
'code': 'ka',
|
||||||
'name': 'Georgian',
|
'name': 'Georgian',
|
||||||
'name_local': u'\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8',
|
'name_local': '\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8',
|
||||||
},
|
},
|
||||||
'kk': {
|
'kk': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'kk',
|
'code': 'kk',
|
||||||
'name': 'Kazakh',
|
'name': 'Kazakh',
|
||||||
'name_local': u'\u049a\u0430\u0437\u0430\u049b',
|
'name_local': '\u049a\u0430\u0437\u0430\u049b',
|
||||||
},
|
},
|
||||||
'km': {
|
'km': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'km',
|
'code': 'km',
|
||||||
'name': 'Khmer',
|
'name': 'Khmer',
|
||||||
'name_local': u'Khmer',
|
'name_local': 'Khmer',
|
||||||
},
|
},
|
||||||
'kn': {
|
'kn': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'kn',
|
'code': 'kn',
|
||||||
'name': 'Kannada',
|
'name': 'Kannada',
|
||||||
'name_local': u'Kannada',
|
'name_local': 'Kannada',
|
||||||
},
|
},
|
||||||
'ko': {
|
'ko': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'ko',
|
'code': 'ko',
|
||||||
'name': 'Korean',
|
'name': 'Korean',
|
||||||
'name_local': u'\ud55c\uad6d\uc5b4',
|
'name_local': '\ud55c\uad6d\uc5b4',
|
||||||
},
|
},
|
||||||
'lt': {
|
'lt': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'lt',
|
'code': 'lt',
|
||||||
'name': 'Lithuanian',
|
'name': 'Lithuanian',
|
||||||
'name_local': u'Lithuanian',
|
'name_local': 'Lithuanian',
|
||||||
},
|
},
|
||||||
'lv': {
|
'lv': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'lv',
|
'code': 'lv',
|
||||||
'name': 'Latvian',
|
'name': 'Latvian',
|
||||||
'name_local': u'latvie\u0161u',
|
'name_local': 'latvie\u0161u',
|
||||||
},
|
},
|
||||||
'mk': {
|
'mk': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'mk',
|
'code': 'mk',
|
||||||
'name': 'Macedonian',
|
'name': 'Macedonian',
|
||||||
'name_local': u'\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438',
|
'name_local': '\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438',
|
||||||
},
|
},
|
||||||
'ml': {
|
'ml': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'ml',
|
'code': 'ml',
|
||||||
'name': 'Malayalam',
|
'name': 'Malayalam',
|
||||||
'name_local': u'Malayalam',
|
'name_local': 'Malayalam',
|
||||||
},
|
},
|
||||||
'mn': {
|
'mn': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'mn',
|
'code': 'mn',
|
||||||
'name': 'Mongolian',
|
'name': 'Mongolian',
|
||||||
'name_local': u'Mongolian',
|
'name_local': 'Mongolian',
|
||||||
},
|
},
|
||||||
'nb': {
|
'nb': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'nb',
|
'code': 'nb',
|
||||||
'name': 'Norwegian Bokmal',
|
'name': 'Norwegian Bokmal',
|
||||||
'name_local': u'Norsk (bokm\xe5l)',
|
'name_local': 'Norsk (bokm\xe5l)',
|
||||||
},
|
},
|
||||||
'ne': {
|
'ne': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'ne',
|
'code': 'ne',
|
||||||
'name': 'Nepali',
|
'name': 'Nepali',
|
||||||
'name_local': u'\u0928\u0947\u092a\u093e\u0932\u0940',
|
'name_local': '\u0928\u0947\u092a\u093e\u0932\u0940',
|
||||||
},
|
},
|
||||||
'nl': {
|
'nl': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'nl',
|
'code': 'nl',
|
||||||
'name': 'Dutch',
|
'name': 'Dutch',
|
||||||
'name_local': u'Nederlands',
|
'name_local': 'Nederlands',
|
||||||
},
|
},
|
||||||
'nn': {
|
'nn': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'nn',
|
'code': 'nn',
|
||||||
'name': 'Norwegian Nynorsk',
|
'name': 'Norwegian Nynorsk',
|
||||||
'name_local': u'Norsk (nynorsk)',
|
'name_local': 'Norsk (nynorsk)',
|
||||||
},
|
},
|
||||||
'no': {
|
'no': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'no',
|
'code': 'no',
|
||||||
'name': 'Norwegian',
|
'name': 'Norwegian',
|
||||||
'name_local': u'Norsk',
|
'name_local': 'Norsk',
|
||||||
},
|
},
|
||||||
'pa': {
|
'pa': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'pa',
|
'code': 'pa',
|
||||||
'name': 'Punjabi',
|
'name': 'Punjabi',
|
||||||
'name_local': u'Punjabi',
|
'name_local': 'Punjabi',
|
||||||
},
|
},
|
||||||
'pl': {
|
'pl': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'pl',
|
'code': 'pl',
|
||||||
'name': 'Polish',
|
'name': 'Polish',
|
||||||
'name_local': u'polski',
|
'name_local': 'polski',
|
||||||
},
|
},
|
||||||
'pt': {
|
'pt': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'pt',
|
'code': 'pt',
|
||||||
'name': 'Portuguese',
|
'name': 'Portuguese',
|
||||||
'name_local': u'Portugu\xeas',
|
'name_local': 'Portugu\xeas',
|
||||||
},
|
},
|
||||||
'pt-br': {
|
'pt-br': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'pt-br',
|
'code': 'pt-br',
|
||||||
'name': 'Brazilian Portuguese',
|
'name': 'Brazilian Portuguese',
|
||||||
'name_local': u'Portugu\xeas Brasileiro',
|
'name_local': 'Portugu\xeas Brasileiro',
|
||||||
},
|
},
|
||||||
'ro': {
|
'ro': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'ro',
|
'code': 'ro',
|
||||||
'name': 'Romanian',
|
'name': 'Romanian',
|
||||||
'name_local': u'Rom\xe2n\u0103',
|
'name_local': 'Rom\xe2n\u0103',
|
||||||
},
|
},
|
||||||
'ru': {
|
'ru': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'ru',
|
'code': 'ru',
|
||||||
'name': 'Russian',
|
'name': 'Russian',
|
||||||
'name_local': u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439',
|
'name_local': '\u0420\u0443\u0441\u0441\u043a\u0438\u0439',
|
||||||
},
|
},
|
||||||
'sk': {
|
'sk': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'sk',
|
'code': 'sk',
|
||||||
'name': 'Slovak',
|
'name': 'Slovak',
|
||||||
'name_local': u'slovensk\xfd',
|
'name_local': 'slovensk\xfd',
|
||||||
},
|
},
|
||||||
'sl': {
|
'sl': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'sl',
|
'code': 'sl',
|
||||||
'name': 'Slovenian',
|
'name': 'Slovenian',
|
||||||
'name_local': u'Sloven\u0161\u010dina',
|
'name_local': 'Sloven\u0161\u010dina',
|
||||||
},
|
},
|
||||||
'sq': {
|
'sq': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'sq',
|
'code': 'sq',
|
||||||
'name': 'Albanian',
|
'name': 'Albanian',
|
||||||
'name_local': u'Albanian',
|
'name_local': 'Albanian',
|
||||||
},
|
},
|
||||||
'sr': {
|
'sr': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'sr',
|
'code': 'sr',
|
||||||
'name': 'Serbian',
|
'name': 'Serbian',
|
||||||
'name_local': u'\u0441\u0440\u043f\u0441\u043a\u0438',
|
'name_local': '\u0441\u0440\u043f\u0441\u043a\u0438',
|
||||||
},
|
},
|
||||||
'sr-latn': {
|
'sr-latn': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'sr-latn',
|
'code': 'sr-latn',
|
||||||
'name': 'Serbian Latin',
|
'name': 'Serbian Latin',
|
||||||
'name_local': u'srpski (latinica)',
|
'name_local': 'srpski (latinica)',
|
||||||
},
|
},
|
||||||
'sv': {
|
'sv': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'sv',
|
'code': 'sv',
|
||||||
'name': 'Swedish',
|
'name': 'Swedish',
|
||||||
'name_local': u'Svenska',
|
'name_local': 'Svenska',
|
||||||
},
|
},
|
||||||
'sw': {
|
'sw': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'sw',
|
'code': 'sw',
|
||||||
'name': 'Swahili',
|
'name': 'Swahili',
|
||||||
'name_local': u'Kiswahili',
|
'name_local': 'Kiswahili',
|
||||||
},
|
},
|
||||||
'ta': {
|
'ta': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'ta',
|
'code': 'ta',
|
||||||
'name': 'Tamil',
|
'name': 'Tamil',
|
||||||
'name_local': u'\u0ba4\u0bae\u0bbf\u0bb4\u0bcd',
|
'name_local': '\u0ba4\u0bae\u0bbf\u0bb4\u0bcd',
|
||||||
},
|
},
|
||||||
'te': {
|
'te': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'te',
|
'code': 'te',
|
||||||
'name': 'Telugu',
|
'name': 'Telugu',
|
||||||
'name_local': u'\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41',
|
'name_local': '\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41',
|
||||||
},
|
},
|
||||||
'th': {
|
'th': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'th',
|
'code': 'th',
|
||||||
'name': 'Thai',
|
'name': 'Thai',
|
||||||
'name_local': u'Thai',
|
'name_local': 'Thai',
|
||||||
},
|
},
|
||||||
'tr': {
|
'tr': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'tr',
|
'code': 'tr',
|
||||||
'name': 'Turkish',
|
'name': 'Turkish',
|
||||||
'name_local': u'T\xfcrk\xe7e',
|
'name_local': 'T\xfcrk\xe7e',
|
||||||
},
|
},
|
||||||
'tt': {
|
'tt': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'tt',
|
'code': 'tt',
|
||||||
'name': 'Tatar',
|
'name': 'Tatar',
|
||||||
'name_local': u'\u0422\u0430\u0442\u0430\u0440\u0447\u0430',
|
'name_local': '\u0422\u0430\u0442\u0430\u0440\u0447\u0430',
|
||||||
},
|
},
|
||||||
'uk': {
|
'uk': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'uk',
|
'code': 'uk',
|
||||||
'name': 'Ukrainian',
|
'name': 'Ukrainian',
|
||||||
'name_local': u'\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430',
|
'name_local': '\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430',
|
||||||
},
|
},
|
||||||
'ur': {
|
'ur': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'ur',
|
'code': 'ur',
|
||||||
'name': 'Urdu',
|
'name': 'Urdu',
|
||||||
'name_local': u'\u0627\u0631\u062f\u0648',
|
'name_local': '\u0627\u0631\u062f\u0648',
|
||||||
},
|
},
|
||||||
'vi': {
|
'vi': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'vi',
|
'code': 'vi',
|
||||||
'name': 'Vietnamese',
|
'name': 'Vietnamese',
|
||||||
'name_local': u'Vietnamese',
|
'name_local': 'Vietnamese',
|
||||||
},
|
},
|
||||||
'zh-cn': {
|
'zh-cn': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'zh-cn',
|
'code': 'zh-cn',
|
||||||
'name': 'Simplified Chinese',
|
'name': 'Simplified Chinese',
|
||||||
'name_local': u'\u7b80\u4f53\u4e2d\u6587',
|
'name_local': '\u7b80\u4f53\u4e2d\u6587',
|
||||||
},
|
},
|
||||||
'zh-tw': {
|
'zh-tw': {
|
||||||
'bidi': False,
|
'bidi': False,
|
||||||
'code': 'zh-tw',
|
'code': 'zh-tw',
|
||||||
'name': 'Traditional Chinese',
|
'name': 'Traditional Chinese',
|
||||||
'name_local': u'\u7e41\u9ad4\u4e2d\u6587',
|
'name_local': '\u7e41\u9ad4\u4e2d\u6587',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -19,5 +20,5 @@ SHORT_DATE_FORMAT = 'd.m.Y'
|
||||||
# TIME_INPUT_FORMATS =
|
# TIME_INPUT_FORMATS =
|
||||||
# DATETIME_INPUT_FORMATS =
|
# DATETIME_INPUT_FORMATS =
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u' ' # Non-breaking space
|
THOUSAND_SEPARATOR = ' ' # Non-breaking space
|
||||||
# NUMBER_GROUPING =
|
# NUMBER_GROUPING =
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -33,5 +34,5 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%Y-%m-%d', # '2006-10-25'
|
'%Y-%m-%d', # '2006-10-25'
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
|
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
|
@ -37,7 +37,7 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%m/%d/%y %H:%M', # '10/25/06 14:30'
|
'%m/%d/%y %H:%M', # '10/25/06 14:30'
|
||||||
'%m/%d/%y', # '10/25/06'
|
'%m/%d/%y', # '10/25/06'
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = u'.'
|
DECIMAL_SEPARATOR = '.'
|
||||||
THOUSAND_SEPARATOR = u','
|
THOUSAND_SEPARATOR = ','
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
DATE_FORMAT = r'j \d\e F \d\e Y'
|
DATE_FORMAT = r'j \d\e F \d\e Y'
|
||||||
TIME_FORMAT = 'H:i:s'
|
TIME_FORMAT = 'H:i:s'
|
||||||
|
@ -24,5 +25,5 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%d/%m/%y %H:%M',
|
'%d/%m/%y %H:%M',
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = '.' # ',' is also official (less common): NOM-008-SCFI-2002
|
DECIMAL_SEPARATOR = '.' # ',' is also official (less common): NOM-008-SCFI-2002
|
||||||
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
|
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -19,5 +20,5 @@ SHORT_DATE_FORMAT = 'd.m.Y'
|
||||||
# TIME_INPUT_FORMATS =
|
# TIME_INPUT_FORMATS =
|
||||||
# DATETIME_INPUT_FORMATS =
|
# DATETIME_INPUT_FORMATS =
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u' ' # Non-breaking space
|
THOUSAND_SEPARATOR = ' ' # Non-breaking space
|
||||||
# NUMBER_GROUPING =
|
# NUMBER_GROUPING =
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
DATE_FORMAT = 'j. E Y'
|
DATE_FORMAT = 'j. E Y'
|
||||||
TIME_FORMAT = 'G.i.s'
|
TIME_FORMAT = 'G.i.s'
|
||||||
# DATETIME_FORMAT =
|
DATETIME_FORMAT = r'j. E Y \k\e\l\l\o G.i.s'
|
||||||
YEAR_MONTH_FORMAT = 'F Y'
|
YEAR_MONTH_FORMAT = 'F Y'
|
||||||
MONTH_DAY_FORMAT = 'j. F'
|
MONTH_DAY_FORMAT = 'j. F'
|
||||||
SHORT_DATE_FORMAT = 'j.n.Y'
|
SHORT_DATE_FORMAT = 'j.n.Y'
|
||||||
|
@ -19,5 +20,5 @@ SHORT_DATE_FORMAT = 'j.n.Y'
|
||||||
# TIME_INPUT_FORMATS =
|
# TIME_INPUT_FORMATS =
|
||||||
# DATETIME_INPUT_FORMATS =
|
# DATETIME_INPUT_FORMATS =
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u' ' # Non-breaking space
|
THOUSAND_SEPARATOR = ' ' # Non-breaking space
|
||||||
# NUMBER_GROUPING =
|
# NUMBER_GROUPING =
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -37,5 +38,5 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%Y-%m-%d', # '2006-10-25'
|
'%Y-%m-%d', # '2006-10-25'
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
|
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
DATE_FORMAT = 'd F Y'
|
DATE_FORMAT = r'j \d\e F \d\e Y'
|
||||||
TIME_FORMAT = 'H:i:s'
|
TIME_FORMAT = 'H:i:s'
|
||||||
# DATETIME_FORMAT =
|
DATETIME_FORMAT = r'j \d\e F \d\e Y \á\s H:i'
|
||||||
YEAR_MONTH_FORMAT = 'F Y'
|
YEAR_MONTH_FORMAT = r'F \d\e Y'
|
||||||
MONTH_DAY_FORMAT = 'j F'
|
MONTH_DAY_FORMAT = r'j \d\e F'
|
||||||
SHORT_DATE_FORMAT = 'j M, Y'
|
SHORT_DATE_FORMAT = 'd-m-Y'
|
||||||
# SHORT_DATETIME_FORMAT =
|
SHORT_DATETIME_FORMAT = 'd-m-Y, H:i'
|
||||||
# FIRST_DAY_OF_WEEK =
|
FIRST_DAY_OF_WEEK = 1 # Monday
|
||||||
|
|
||||||
# The *_INPUT_FORMATS strings use the Python strftime format syntax,
|
# The *_INPUT_FORMATS strings use the Python strftime format syntax,
|
||||||
# see http://docs.python.org/library/datetime.html#strftime-strptime-behavior
|
# see http://docs.python.org/library/datetime.html#strftime-strptime-behavior
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -28,5 +29,5 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%Y.%m.%d.', # '2006.10.25.'
|
'%Y.%m.%d.', # '2006.10.25.'
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u' ' # Non-breaking space
|
THOUSAND_SEPARATOR = ' ' # Non-breaking space
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -37,5 +38,5 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%d.%m.%y', # '25.10.06'
|
'%d.%m.%y', # '25.10.06'
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u' ' # Non-breaking space
|
THOUSAND_SEPARATOR = ' ' # Non-breaking space
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -39,5 +40,5 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%d.%m.%y', # '25.10.06'
|
'%d.%m.%y', # '25.10.06'
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
|
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -39,5 +40,5 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%d.%m.%y', # '25.10.06'
|
'%d.%m.%y', # '25.10.06'
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
|
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -33,5 +34,5 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%Y-%m-%d', # '2006-10-25'
|
'%Y-%m-%d', # '2006-10-25'
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u' '
|
THOUSAND_SEPARATOR = ' '
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -36,5 +37,5 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%Y-%m-%d', # '2006-10-25'
|
'%Y-%m-%d', # '2006-10-25'
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
|
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -33,5 +34,5 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%Y-%m-%d', # '2006-10-25'
|
'%Y-%m-%d', # '2006-10-25'
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
|
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
@ -36,5 +37,5 @@ DATETIME_INPUT_FORMATS = (
|
||||||
'%m/%d/%y', # '10/25/06'
|
'%m/%d/%y', # '10/25/06'
|
||||||
)
|
)
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
|
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
|
||||||
NUMBER_GROUPING = 3
|
NUMBER_GROUPING = 3
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
DATE_FORMAT = 'j E Y р.'
|
DATE_FORMAT = 'j E Y р.'
|
||||||
|
@ -19,5 +21,5 @@ SHORT_DATE_FORMAT = 'j M Y'
|
||||||
# TIME_INPUT_FORMATS =
|
# TIME_INPUT_FORMATS =
|
||||||
# DATETIME_INPUT_FORMATS =
|
# DATETIME_INPUT_FORMATS =
|
||||||
DECIMAL_SEPARATOR = ','
|
DECIMAL_SEPARATOR = ','
|
||||||
THOUSAND_SEPARATOR = u' '
|
THOUSAND_SEPARATOR = ' '
|
||||||
# NUMBER_GROUPING =
|
# NUMBER_GROUPING =
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# The *_FORMAT strings use the Django date format syntax,
|
# The *_FORMAT strings use the Django date format syntax,
|
||||||
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||||
|
|
|
@ -44,22 +44,22 @@ USE_L10N = True
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||||
# Example: "/home/media/media.lawrence.com/media/"
|
# Example: "/var/www/example.com/media/"
|
||||||
MEDIA_ROOT = ''
|
MEDIA_ROOT = ''
|
||||||
|
|
||||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||||
# trailing slash.
|
# trailing slash.
|
||||||
# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
|
# Examples: "http://example.com/media/", "http://media.example.com/"
|
||||||
MEDIA_URL = ''
|
MEDIA_URL = ''
|
||||||
|
|
||||||
# Absolute path to the directory static files should be collected to.
|
# Absolute path to the directory static files should be collected to.
|
||||||
# Don't put anything in this directory yourself; store your static files
|
# Don't put anything in this directory yourself; store your static files
|
||||||
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
||||||
# Example: "/home/media/media.lawrence.com/static/"
|
# Example: "/var/www/example.com/static/"
|
||||||
STATIC_ROOT = ''
|
STATIC_ROOT = ''
|
||||||
|
|
||||||
# URL prefix for static files.
|
# URL prefix for static files.
|
||||||
# Example: "http://media.lawrence.com/static/"
|
# Example: "http://example.com/static/", "http://static.example.com/"
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
|
|
||||||
# Additional locations of static files
|
# Additional locations of static files
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.core.urlresolvers import (RegexURLPattern,
|
||||||
RegexURLResolver, LocaleRegexURLResolver)
|
RegexURLResolver, LocaleRegexURLResolver)
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['handler403', 'handler404', 'handler500', 'include', 'patterns', 'url']
|
__all__ = ['handler403', 'handler404', 'handler500', 'include', 'patterns', 'url']
|
||||||
|
@ -20,7 +21,7 @@ def include(arg, namespace=None, app_name=None):
|
||||||
# No namespace hint - use manually provided namespace
|
# No namespace hint - use manually provided namespace
|
||||||
urlconf_module = arg
|
urlconf_module = arg
|
||||||
|
|
||||||
if isinstance(urlconf_module, basestring):
|
if isinstance(urlconf_module, six.string_types):
|
||||||
urlconf_module = import_module(urlconf_module)
|
urlconf_module = import_module(urlconf_module)
|
||||||
patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
|
patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ def url(regex, view, kwargs=None, name=None, prefix=''):
|
||||||
urlconf_module, app_name, namespace = view
|
urlconf_module, app_name, namespace = view
|
||||||
return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace)
|
return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace)
|
||||||
else:
|
else:
|
||||||
if isinstance(view, basestring):
|
if isinstance(view, six.string_types):
|
||||||
if not view:
|
if not view:
|
||||||
raise ImproperlyConfigured('Empty URL pattern view name not permitted (for pattern %r)' % regex)
|
raise ImproperlyConfigured('Empty URL pattern view name not permitted (for pattern %r)' % regex)
|
||||||
if prefix:
|
if prefix:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls import patterns
|
from django.conf.urls import patterns, url
|
||||||
from django.core.urlresolvers import LocaleRegexURLResolver
|
from django.core.urlresolvers import LocaleRegexURLResolver
|
||||||
|
|
||||||
def i18n_patterns(prefix, *args):
|
def i18n_patterns(prefix, *args):
|
||||||
|
@ -16,5 +16,5 @@ def i18n_patterns(prefix, *args):
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
(r'^setlang/$', 'django.views.i18n.set_language'),
|
url(r'^setlang/$', 'django.views.i18n.set_language', name='set_language'),
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.contrib.admin import helpers
|
||||||
from django.contrib.admin.util import get_deleted_objects, model_ngettext
|
from django.contrib.admin.util import get_deleted_objects, model_ngettext
|
||||||
from django.db import router
|
from django.db import router
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ugettext_lazy, ugettext as _
|
from django.utils.translation import ugettext_lazy, ugettext as _
|
||||||
|
|
||||||
def delete_selected(modeladmin, request, queryset):
|
def delete_selected(modeladmin, request, queryset):
|
||||||
|
@ -42,7 +42,7 @@ def delete_selected(modeladmin, request, queryset):
|
||||||
n = queryset.count()
|
n = queryset.count()
|
||||||
if n:
|
if n:
|
||||||
for obj in queryset:
|
for obj in queryset:
|
||||||
obj_display = force_unicode(obj)
|
obj_display = force_text(obj)
|
||||||
modeladmin.log_deletion(request, obj, obj_display)
|
modeladmin.log_deletion(request, obj, obj_display)
|
||||||
queryset.delete()
|
queryset.delete()
|
||||||
modeladmin.message_user(request, _("Successfully deleted %(count)d %(items)s.") % {
|
modeladmin.message_user(request, _("Successfully deleted %(count)d %(items)s.") % {
|
||||||
|
@ -52,9 +52,9 @@ def delete_selected(modeladmin, request, queryset):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if len(queryset) == 1:
|
if len(queryset) == 1:
|
||||||
objects_name = force_unicode(opts.verbose_name)
|
objects_name = force_text(opts.verbose_name)
|
||||||
else:
|
else:
|
||||||
objects_name = force_unicode(opts.verbose_name_plural)
|
objects_name = force_text(opts.verbose_name_plural)
|
||||||
|
|
||||||
if perms_needed or protected:
|
if perms_needed or protected:
|
||||||
title = _("Cannot delete %(name)s") % {"name": objects_name}
|
title = _("Cannot delete %(name)s") % {"name": objects_name}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import datetime
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_text
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ class RelatedFieldListFilter(FieldListFilter):
|
||||||
}
|
}
|
||||||
for pk_val, val in self.lookup_choices:
|
for pk_val, val in self.lookup_choices:
|
||||||
yield {
|
yield {
|
||||||
'selected': self.lookup_val == smart_unicode(pk_val),
|
'selected': self.lookup_val == smart_text(pk_val),
|
||||||
'query_string': cl.get_query_string({
|
'query_string': cl.get_query_string({
|
||||||
self.lookup_kwarg: pk_val,
|
self.lookup_kwarg: pk_val,
|
||||||
}, [self.lookup_kwarg_isnull]),
|
}, [self.lookup_kwarg_isnull]),
|
||||||
|
@ -272,7 +272,7 @@ class ChoicesFieldListFilter(FieldListFilter):
|
||||||
}
|
}
|
||||||
for lookup, title in self.field.flatchoices:
|
for lookup, title in self.field.flatchoices:
|
||||||
yield {
|
yield {
|
||||||
'selected': smart_unicode(lookup) == self.lookup_val,
|
'selected': smart_text(lookup) == self.lookup_val,
|
||||||
'query_string': cl.get_query_string({
|
'query_string': cl.get_query_string({
|
||||||
self.lookup_kwarg: lookup}),
|
self.lookup_kwarg: lookup}),
|
||||||
'display': title,
|
'display': title,
|
||||||
|
@ -381,7 +381,7 @@ class AllValuesFieldListFilter(FieldListFilter):
|
||||||
if val is None:
|
if val is None:
|
||||||
include_none = True
|
include_none = True
|
||||||
continue
|
continue
|
||||||
val = smart_unicode(val)
|
val = smart_text(val)
|
||||||
yield {
|
yield {
|
||||||
'selected': self.lookup_val == val,
|
'selected': self.lookup_val == val,
|
||||||
'query_string': cl.get_query_string({
|
'query_string': cl.get_query_string({
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from django.contrib.auth.forms import AuthenticationForm
|
from django.contrib.auth.forms import AuthenticationForm
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy, ugettext as _
|
from django.utils.translation import ugettext_lazy, ugettext as _
|
||||||
|
|
||||||
ERROR_MESSAGE = ugettext_lazy("Please enter the correct username and password "
|
ERROR_MESSAGE = ugettext_lazy("Please enter the correct username and password "
|
||||||
|
@ -26,7 +27,7 @@ class AdminAuthenticationForm(AuthenticationForm):
|
||||||
if username and password:
|
if username and password:
|
||||||
self.user_cache = authenticate(username=username, password=password)
|
self.user_cache = authenticate(username=username, password=password)
|
||||||
if self.user_cache is None:
|
if self.user_cache is None:
|
||||||
if u'@' in username:
|
if '@' in username:
|
||||||
# Mistakenly entered e-mail address instead of username? Look it up.
|
# Mistakenly entered e-mail address instead of username? Look it up.
|
||||||
try:
|
try:
|
||||||
user = User.objects.get(email=username)
|
user = User.objects.get(email=username)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.admin.util import (flatten_fieldsets, lookup_field,
|
from django.contrib.admin.util import (flatten_fieldsets, lookup_field,
|
||||||
display_for_field, label_for_field, help_text_for_field)
|
display_for_field, label_for_field, help_text_for_field)
|
||||||
|
@ -7,9 +9,10 @@ from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db.models.fields.related import ManyToManyRel
|
from django.db.models.fields.related import ManyToManyRel
|
||||||
from django.forms.util import flatatt
|
from django.forms.util import flatatt
|
||||||
from django.template.defaultfilters import capfirst
|
from django.template.defaultfilters import capfirst
|
||||||
from django.utils.encoding import force_unicode, smart_unicode
|
from django.utils.encoding import force_text, smart_text
|
||||||
from django.utils.html import escape, conditional_escape
|
from django.utils.html import conditional_escape, format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils import six
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
@ -47,7 +50,7 @@ class AdminForm(object):
|
||||||
try:
|
try:
|
||||||
fieldset_name, fieldset_options = self.fieldsets[0]
|
fieldset_name, fieldset_options = self.fieldsets[0]
|
||||||
field_name = fieldset_options['fields'][0]
|
field_name = fieldset_options['fields'][0]
|
||||||
if not isinstance(field_name, basestring):
|
if not isinstance(field_name, six.string_types):
|
||||||
field_name = field_name[0]
|
field_name = field_name[0]
|
||||||
return self.form[field_name]
|
return self.form[field_name]
|
||||||
except (KeyError, IndexError):
|
except (KeyError, IndexError):
|
||||||
|
@ -69,7 +72,7 @@ class Fieldset(object):
|
||||||
description=None, model_admin=None):
|
description=None, model_admin=None):
|
||||||
self.form = form
|
self.form = form
|
||||||
self.name, self.fields = name, fields
|
self.name, self.fields = name, fields
|
||||||
self.classes = u' '.join(classes)
|
self.classes = ' '.join(classes)
|
||||||
self.description = description
|
self.description = description
|
||||||
self.model_admin = model_admin
|
self.model_admin = model_admin
|
||||||
self.readonly_fields = readonly_fields
|
self.readonly_fields = readonly_fields
|
||||||
|
@ -91,7 +94,7 @@ class Fieldset(object):
|
||||||
class Fieldline(object):
|
class Fieldline(object):
|
||||||
def __init__(self, form, field, readonly_fields=None, model_admin=None):
|
def __init__(self, form, field, readonly_fields=None, model_admin=None):
|
||||||
self.form = form # A django.forms.Form instance
|
self.form = form # A django.forms.Form instance
|
||||||
if not hasattr(field, "__iter__"):
|
if not hasattr(field, "__iter__") or isinstance(field, six.text_type):
|
||||||
self.fields = [field]
|
self.fields = [field]
|
||||||
else:
|
else:
|
||||||
self.fields = field
|
self.fields = field
|
||||||
|
@ -109,7 +112,7 @@ class Fieldline(object):
|
||||||
yield AdminField(self.form, field, is_first=(i == 0))
|
yield AdminField(self.form, field, is_first=(i == 0))
|
||||||
|
|
||||||
def errors(self):
|
def errors(self):
|
||||||
return mark_safe(u'\n'.join([self.form[f].errors.as_ul() for f in self.fields if f not in self.readonly_fields]).strip('\n'))
|
return mark_safe('\n'.join([self.form[f].errors.as_ul() for f in self.fields if f not in self.readonly_fields]).strip('\n'))
|
||||||
|
|
||||||
class AdminField(object):
|
class AdminField(object):
|
||||||
def __init__(self, form, field, is_first):
|
def __init__(self, form, field, is_first):
|
||||||
|
@ -119,16 +122,16 @@ class AdminField(object):
|
||||||
|
|
||||||
def label_tag(self):
|
def label_tag(self):
|
||||||
classes = []
|
classes = []
|
||||||
contents = conditional_escape(force_unicode(self.field.label))
|
contents = conditional_escape(force_text(self.field.label))
|
||||||
if self.is_checkbox:
|
if self.is_checkbox:
|
||||||
classes.append(u'vCheckboxLabel')
|
classes.append('vCheckboxLabel')
|
||||||
else:
|
else:
|
||||||
contents += u':'
|
contents += ':'
|
||||||
if self.field.field.required:
|
if self.field.field.required:
|
||||||
classes.append(u'required')
|
classes.append('required')
|
||||||
if not self.is_first:
|
if not self.is_first:
|
||||||
classes.append(u'inline')
|
classes.append('inline')
|
||||||
attrs = classes and {'class': u' '.join(classes)} or {}
|
attrs = classes and {'class': ' '.join(classes)} or {}
|
||||||
return self.field.label_tag(contents=mark_safe(contents), attrs=attrs)
|
return self.field.label_tag(contents=mark_safe(contents), attrs=attrs)
|
||||||
|
|
||||||
def errors(self):
|
def errors(self):
|
||||||
|
@ -161,11 +164,9 @@ class AdminReadonlyField(object):
|
||||||
if not self.is_first:
|
if not self.is_first:
|
||||||
attrs["class"] = "inline"
|
attrs["class"] = "inline"
|
||||||
label = self.field['label']
|
label = self.field['label']
|
||||||
contents = capfirst(force_unicode(escape(label))) + u":"
|
return format_html('<label{0}>{1}:</label>',
|
||||||
return mark_safe('<label%(attrs)s>%(contents)s</label>' % {
|
flatatt(attrs),
|
||||||
"attrs": flatatt(attrs),
|
capfirst(force_text(label)))
|
||||||
"contents": contents,
|
|
||||||
})
|
|
||||||
|
|
||||||
def contents(self):
|
def contents(self):
|
||||||
from django.contrib.admin.templatetags.admin_list import _boolean_icon
|
from django.contrib.admin.templatetags.admin_list import _boolean_icon
|
||||||
|
@ -181,14 +182,14 @@ class AdminReadonlyField(object):
|
||||||
if boolean:
|
if boolean:
|
||||||
result_repr = _boolean_icon(value)
|
result_repr = _boolean_icon(value)
|
||||||
else:
|
else:
|
||||||
result_repr = smart_unicode(value)
|
result_repr = smart_text(value)
|
||||||
if getattr(attr, "allow_tags", False):
|
if getattr(attr, "allow_tags", False):
|
||||||
result_repr = mark_safe(result_repr)
|
result_repr = mark_safe(result_repr)
|
||||||
else:
|
else:
|
||||||
if value is None:
|
if value is None:
|
||||||
result_repr = EMPTY_CHANGELIST_VALUE
|
result_repr = EMPTY_CHANGELIST_VALUE
|
||||||
elif isinstance(f.rel, ManyToManyRel):
|
elif isinstance(f.rel, ManyToManyRel):
|
||||||
result_repr = ", ".join(map(unicode, value.all()))
|
result_repr = ", ".join(map(six.text_type, value.all()))
|
||||||
else:
|
else:
|
||||||
result_repr = display_for_field(value, f)
|
result_repr = display_for_field(value, f)
|
||||||
return conditional_escape(result_repr)
|
return conditional_escape(result_repr)
|
||||||
|
@ -324,11 +325,11 @@ class AdminErrorList(forms.util.ErrorList):
|
||||||
"""
|
"""
|
||||||
def __init__(self, form, inline_formsets):
|
def __init__(self, form, inline_formsets):
|
||||||
if form.is_bound:
|
if form.is_bound:
|
||||||
self.extend(form.errors.values())
|
self.extend(list(six.itervalues(form.errors)))
|
||||||
for inline_formset in inline_formsets:
|
for inline_formset in inline_formsets:
|
||||||
self.extend(inline_formset.non_form_errors())
|
self.extend(inline_formset.non_form_errors())
|
||||||
for errors_in_inline_form in inline_formset.errors:
|
for errors_in_inline_form in inline_formset.errors:
|
||||||
self.extend(errors_in_inline_form.values())
|
self.extend(list(six.itervalues(errors_in_inline_form)))
|
||||||
|
|
||||||
def normalize_fieldsets(fieldsets):
|
def normalize_fieldsets(fieldsets):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.admin.util import quote
|
from django.contrib.admin.util import quote
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_text
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
ADDITION = 1
|
ADDITION = 1
|
||||||
CHANGE = 2
|
CHANGE = 2
|
||||||
|
@ -13,10 +15,11 @@ DELETION = 3
|
||||||
|
|
||||||
class LogEntryManager(models.Manager):
|
class LogEntryManager(models.Manager):
|
||||||
def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
|
def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
|
||||||
e = self.model(None, None, user_id, content_type_id, smart_unicode(object_id), object_repr[:200], action_flag, change_message)
|
e = self.model(None, None, user_id, content_type_id, smart_text(object_id), object_repr[:200], action_flag, change_message)
|
||||||
e.save()
|
e.save()
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class LogEntry(models.Model):
|
class LogEntry(models.Model):
|
||||||
action_time = models.DateTimeField(_('action time'), auto_now=True)
|
action_time = models.DateTimeField(_('action time'), auto_now=True)
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
||||||
|
@ -35,9 +38,9 @@ class LogEntry(models.Model):
|
||||||
ordering = ('-action_time',)
|
ordering = ('-action_time',)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return smart_unicode(self.action_time)
|
return smart_text(self.action_time)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
if self.action_flag == ADDITION:
|
if self.action_flag == ADDITION:
|
||||||
return _('Added "%(object)s".') % {'object': self.object_repr}
|
return _('Added "%(object)s".') % {'object': self.object_repr}
|
||||||
elif self.action_flag == CHANGE:
|
elif self.action_flag == CHANGE:
|
||||||
|
@ -66,5 +69,5 @@ class LogEntry(models.Model):
|
||||||
This is relative to the Django admin index page.
|
This is relative to the Django admin index page.
|
||||||
"""
|
"""
|
||||||
if self.content_type and self.object_id:
|
if self.content_type and self.object_id:
|
||||||
return mark_safe(u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, quote(self.object_id)))
|
return "%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, quote(self.object_id))
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -24,10 +24,11 @@ from django.utils.decorators import method_decorator
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.html import escape, escapejs
|
from django.utils.html import escape, escapejs
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils import six
|
||||||
from django.utils.text import capfirst, get_text_list
|
from django.utils.text import capfirst, get_text_list
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import ungettext
|
from django.utils.translation import ungettext
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
HORIZONTAL, VERTICAL = 1, 2
|
HORIZONTAL, VERTICAL = 1, 2
|
||||||
# returns the <ul> class for a given radio_admin field
|
# returns the <ul> class for a given radio_admin field
|
||||||
|
@ -49,7 +50,7 @@ FORMFIELD_FOR_DBFIELD_DEFAULTS = {
|
||||||
models.TextField: {'widget': widgets.AdminTextareaWidget},
|
models.TextField: {'widget': widgets.AdminTextareaWidget},
|
||||||
models.URLField: {'widget': widgets.AdminURLFieldWidget},
|
models.URLField: {'widget': widgets.AdminURLFieldWidget},
|
||||||
models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget},
|
models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget},
|
||||||
models.BigIntegerField: {'widget': widgets.AdminIntegerFieldWidget},
|
models.BigIntegerField: {'widget': widgets.AdminBigIntegerFieldWidget},
|
||||||
models.CharField: {'widget': widgets.AdminTextInputWidget},
|
models.CharField: {'widget': widgets.AdminTextInputWidget},
|
||||||
models.ImageField: {'widget': widgets.AdminFileWidget},
|
models.ImageField: {'widget': widgets.AdminFileWidget},
|
||||||
models.FileField: {'widget': widgets.AdminFileWidget},
|
models.FileField: {'widget': widgets.AdminFileWidget},
|
||||||
|
@ -57,9 +58,8 @@ FORMFIELD_FOR_DBFIELD_DEFAULTS = {
|
||||||
|
|
||||||
csrf_protect_m = method_decorator(csrf_protect)
|
csrf_protect_m = method_decorator(csrf_protect)
|
||||||
|
|
||||||
class BaseModelAdmin(object):
|
class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
|
||||||
"""Functionality common to both ModelAdmin and InlineAdmin."""
|
"""Functionality common to both ModelAdmin and InlineAdmin."""
|
||||||
__metaclass__ = forms.MediaDefiningClass
|
|
||||||
|
|
||||||
raw_id_fields = ()
|
raw_id_fields = ()
|
||||||
fields = None
|
fields = None
|
||||||
|
@ -425,7 +425,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
if self.declared_fieldsets:
|
if self.declared_fieldsets:
|
||||||
return self.declared_fieldsets
|
return self.declared_fieldsets
|
||||||
form = self.get_form(request, obj)
|
form = self.get_form(request, obj)
|
||||||
fields = form.base_fields.keys() + list(self.get_readonly_fields(request, obj))
|
fields = list(form.base_fields) + list(self.get_readonly_fields(request, obj))
|
||||||
return [(None, {'fields': fields})]
|
return [(None, {'fields': fields})]
|
||||||
|
|
||||||
def get_form(self, request, obj=None, **kwargs):
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
|
@ -520,7 +520,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
user_id = request.user.pk,
|
user_id = request.user.pk,
|
||||||
content_type_id = ContentType.objects.get_for_model(object).pk,
|
content_type_id = ContentType.objects.get_for_model(object).pk,
|
||||||
object_id = object.pk,
|
object_id = object.pk,
|
||||||
object_repr = force_unicode(object),
|
object_repr = force_text(object),
|
||||||
action_flag = ADDITION
|
action_flag = ADDITION
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -535,7 +535,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
user_id = request.user.pk,
|
user_id = request.user.pk,
|
||||||
content_type_id = ContentType.objects.get_for_model(object).pk,
|
content_type_id = ContentType.objects.get_for_model(object).pk,
|
||||||
object_id = object.pk,
|
object_id = object.pk,
|
||||||
object_repr = force_unicode(object),
|
object_repr = force_text(object),
|
||||||
action_flag = CHANGE,
|
action_flag = CHANGE,
|
||||||
change_message = message
|
change_message = message
|
||||||
)
|
)
|
||||||
|
@ -560,7 +560,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
"""
|
"""
|
||||||
A list_display column containing a checkbox widget.
|
A list_display column containing a checkbox widget.
|
||||||
"""
|
"""
|
||||||
return helpers.checkbox.render(helpers.ACTION_CHECKBOX_NAME, force_unicode(obj.pk))
|
return helpers.checkbox.render(helpers.ACTION_CHECKBOX_NAME, force_text(obj.pk))
|
||||||
action_checkbox.short_description = mark_safe('<input type="checkbox" id="action-toggle" />')
|
action_checkbox.short_description = mark_safe('<input type="checkbox" id="action-toggle" />')
|
||||||
action_checkbox.allow_tags = True
|
action_checkbox.allow_tags = True
|
||||||
|
|
||||||
|
@ -608,7 +608,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
tuple (name, description).
|
tuple (name, description).
|
||||||
"""
|
"""
|
||||||
choices = [] + default_choices
|
choices = [] + default_choices
|
||||||
for func, name, description in self.get_actions(request).itervalues():
|
for func, name, description in six.itervalues(self.get_actions(request)):
|
||||||
choice = (name, description % model_format_dict(self.opts))
|
choice = (name, description % model_format_dict(self.opts))
|
||||||
choices.append(choice)
|
choices.append(choice)
|
||||||
return choices
|
return choices
|
||||||
|
@ -674,17 +674,17 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
for formset in formsets:
|
for formset in formsets:
|
||||||
for added_object in formset.new_objects:
|
for added_object in formset.new_objects:
|
||||||
change_message.append(_('Added %(name)s "%(object)s".')
|
change_message.append(_('Added %(name)s "%(object)s".')
|
||||||
% {'name': force_unicode(added_object._meta.verbose_name),
|
% {'name': force_text(added_object._meta.verbose_name),
|
||||||
'object': force_unicode(added_object)})
|
'object': force_text(added_object)})
|
||||||
for changed_object, changed_fields in formset.changed_objects:
|
for changed_object, changed_fields in formset.changed_objects:
|
||||||
change_message.append(_('Changed %(list)s for %(name)s "%(object)s".')
|
change_message.append(_('Changed %(list)s for %(name)s "%(object)s".')
|
||||||
% {'list': get_text_list(changed_fields, _('and')),
|
% {'list': get_text_list(changed_fields, _('and')),
|
||||||
'name': force_unicode(changed_object._meta.verbose_name),
|
'name': force_text(changed_object._meta.verbose_name),
|
||||||
'object': force_unicode(changed_object)})
|
'object': force_text(changed_object)})
|
||||||
for deleted_object in formset.deleted_objects:
|
for deleted_object in formset.deleted_objects:
|
||||||
change_message.append(_('Deleted %(name)s "%(object)s".')
|
change_message.append(_('Deleted %(name)s "%(object)s".')
|
||||||
% {'name': force_unicode(deleted_object._meta.verbose_name),
|
% {'name': force_text(deleted_object._meta.verbose_name),
|
||||||
'object': force_unicode(deleted_object)})
|
'object': force_text(deleted_object)})
|
||||||
change_message = ' '.join(change_message)
|
change_message = ' '.join(change_message)
|
||||||
return change_message or _('No fields changed.')
|
return change_message or _('No fields changed.')
|
||||||
|
|
||||||
|
@ -745,7 +745,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
'has_file_field': True, # FIXME - this should check if form or formsets have a FileField,
|
'has_file_field': True, # FIXME - this should check if form or formsets have a FileField,
|
||||||
'has_absolute_url': hasattr(self.model, 'get_absolute_url'),
|
'has_absolute_url': hasattr(self.model, 'get_absolute_url'),
|
||||||
'ordered_objects': ordered_objects,
|
'ordered_objects': ordered_objects,
|
||||||
'form_url': mark_safe(form_url),
|
'form_url': form_url,
|
||||||
'opts': opts,
|
'opts': opts,
|
||||||
'content_type_id': ContentType.objects.get_for_model(self.model).id,
|
'content_type_id': ContentType.objects.get_for_model(self.model).id,
|
||||||
'save_as': self.save_as,
|
'save_as': self.save_as,
|
||||||
|
@ -769,7 +769,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
opts = obj._meta
|
opts = obj._meta
|
||||||
pk_value = obj._get_pk_val()
|
pk_value = obj._get_pk_val()
|
||||||
|
|
||||||
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)}
|
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_text(opts.verbose_name), 'obj': force_text(obj)}
|
||||||
# Here, we distinguish between different save types by checking for
|
# Here, we distinguish between different save types by checking for
|
||||||
# the presence of keys in request.POST.
|
# the presence of keys in request.POST.
|
||||||
if "_continue" in request.POST:
|
if "_continue" in request.POST:
|
||||||
|
@ -782,10 +782,10 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
'<!DOCTYPE html><html><head><title></title></head><body>'
|
'<!DOCTYPE html><html><head><title></title></head><body>'
|
||||||
'<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script></body></html>' % \
|
'<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script></body></html>' % \
|
||||||
# escape() calls force_unicode.
|
# escape() calls force_text.
|
||||||
(escape(pk_value), escapejs(obj)))
|
(escape(pk_value), escapejs(obj)))
|
||||||
elif "_addanother" in request.POST:
|
elif "_addanother" in request.POST:
|
||||||
self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
|
self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_text(opts.verbose_name)))
|
||||||
return HttpResponseRedirect(request.path)
|
return HttpResponseRedirect(request.path)
|
||||||
else:
|
else:
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
|
@ -819,7 +819,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
|
|
||||||
pk_value = obj._get_pk_val()
|
pk_value = obj._get_pk_val()
|
||||||
|
|
||||||
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(verbose_name), 'obj': force_unicode(obj)}
|
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_text(verbose_name), 'obj': force_text(obj)}
|
||||||
if "_continue" in request.POST:
|
if "_continue" in request.POST:
|
||||||
self.message_user(request, msg + ' ' + _("You may edit it again below."))
|
self.message_user(request, msg + ' ' + _("You may edit it again below."))
|
||||||
if "_popup" in request.REQUEST:
|
if "_popup" in request.REQUEST:
|
||||||
|
@ -827,14 +827,14 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
else:
|
else:
|
||||||
return HttpResponseRedirect(request.path)
|
return HttpResponseRedirect(request.path)
|
||||||
elif "_saveasnew" in request.POST:
|
elif "_saveasnew" in request.POST:
|
||||||
msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(verbose_name), 'obj': obj}
|
msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_text(verbose_name), 'obj': obj}
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
return HttpResponseRedirect(reverse('admin:%s_%s_change' %
|
return HttpResponseRedirect(reverse('admin:%s_%s_change' %
|
||||||
(opts.app_label, module_name),
|
(opts.app_label, module_name),
|
||||||
args=(pk_value,),
|
args=(pk_value,),
|
||||||
current_app=self.admin_site.name))
|
current_app=self.admin_site.name))
|
||||||
elif "_addanother" in request.POST:
|
elif "_addanother" in request.POST:
|
||||||
self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(verbose_name)))
|
self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_text(verbose_name)))
|
||||||
return HttpResponseRedirect(reverse('admin:%s_%s_add' %
|
return HttpResponseRedirect(reverse('admin:%s_%s_add' %
|
||||||
(opts.app_label, module_name),
|
(opts.app_label, module_name),
|
||||||
current_app=self.admin_site.name))
|
current_app=self.admin_site.name))
|
||||||
|
@ -995,10 +995,9 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
media = media + inline_admin_formset.media
|
media = media + inline_admin_formset.media
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'title': _('Add %s') % force_unicode(opts.verbose_name),
|
'title': _('Add %s') % force_text(opts.verbose_name),
|
||||||
'adminform': adminForm,
|
'adminform': adminForm,
|
||||||
'is_popup': "_popup" in request.REQUEST,
|
'is_popup': "_popup" in request.REQUEST,
|
||||||
'show_delete': False,
|
|
||||||
'media': media,
|
'media': media,
|
||||||
'inline_admin_formsets': inline_admin_formsets,
|
'inline_admin_formsets': inline_admin_formsets,
|
||||||
'errors': helpers.AdminErrorList(form, formsets),
|
'errors': helpers.AdminErrorList(form, formsets),
|
||||||
|
@ -1020,7 +1019,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
if obj is None:
|
if obj is None:
|
||||||
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)})
|
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_text(opts.verbose_name), 'key': escape(object_id)})
|
||||||
|
|
||||||
if request.method == 'POST' and "_saveasnew" in request.POST:
|
if request.method == 'POST' and "_saveasnew" in request.POST:
|
||||||
return self.add_view(request, form_url=reverse('admin:%s_%s_add' %
|
return self.add_view(request, form_url=reverse('admin:%s_%s_add' %
|
||||||
|
@ -1086,7 +1085,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
media = media + inline_admin_formset.media
|
media = media + inline_admin_formset.media
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'title': _('Change %s') % force_unicode(opts.verbose_name),
|
'title': _('Change %s') % force_text(opts.verbose_name),
|
||||||
'adminform': adminForm,
|
'adminform': adminForm,
|
||||||
'object_id': object_id,
|
'object_id': object_id,
|
||||||
'original': obj,
|
'original': obj,
|
||||||
|
@ -1195,14 +1194,14 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
|
|
||||||
if changecount:
|
if changecount:
|
||||||
if changecount == 1:
|
if changecount == 1:
|
||||||
name = force_unicode(opts.verbose_name)
|
name = force_text(opts.verbose_name)
|
||||||
else:
|
else:
|
||||||
name = force_unicode(opts.verbose_name_plural)
|
name = force_text(opts.verbose_name_plural)
|
||||||
msg = ungettext("%(count)s %(name)s was changed successfully.",
|
msg = ungettext("%(count)s %(name)s was changed successfully.",
|
||||||
"%(count)s %(name)s were changed successfully.",
|
"%(count)s %(name)s were changed successfully.",
|
||||||
changecount) % {'count': changecount,
|
changecount) % {'count': changecount,
|
||||||
'name': name,
|
'name': name,
|
||||||
'obj': force_unicode(obj)}
|
'obj': force_text(obj)}
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
@ -1229,7 +1228,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
'All %(total_count)s selected', cl.result_count)
|
'All %(total_count)s selected', cl.result_count)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'module_name': force_unicode(opts.verbose_name_plural),
|
'module_name': force_text(opts.verbose_name_plural),
|
||||||
'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)},
|
'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)},
|
||||||
'selection_note_all': selection_note_all % {'total_count': cl.result_count},
|
'selection_note_all': selection_note_all % {'total_count': cl.result_count},
|
||||||
'title': cl.title,
|
'title': cl.title,
|
||||||
|
@ -1264,7 +1263,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
if obj is None:
|
if obj is None:
|
||||||
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)})
|
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_text(opts.verbose_name), 'key': escape(object_id)})
|
||||||
|
|
||||||
using = router.db_for_write(self.model)
|
using = router.db_for_write(self.model)
|
||||||
|
|
||||||
|
@ -1276,11 +1275,11 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
if request.POST: # The user has already confirmed the deletion.
|
if request.POST: # The user has already confirmed the deletion.
|
||||||
if perms_needed:
|
if perms_needed:
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
obj_display = force_unicode(obj)
|
obj_display = force_text(obj)
|
||||||
self.log_deletion(request, obj, obj_display)
|
self.log_deletion(request, obj, obj_display)
|
||||||
self.delete_model(request, obj)
|
self.delete_model(request, obj)
|
||||||
|
|
||||||
self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)})
|
self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_text(opts.verbose_name), 'obj': force_text(obj_display)})
|
||||||
|
|
||||||
if not self.has_change_permission(request, None):
|
if not self.has_change_permission(request, None):
|
||||||
return HttpResponseRedirect(reverse('admin:index',
|
return HttpResponseRedirect(reverse('admin:index',
|
||||||
|
@ -1289,7 +1288,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
(opts.app_label, opts.module_name),
|
(opts.app_label, opts.module_name),
|
||||||
current_app=self.admin_site.name))
|
current_app=self.admin_site.name))
|
||||||
|
|
||||||
object_name = force_unicode(opts.verbose_name)
|
object_name = force_text(opts.verbose_name)
|
||||||
|
|
||||||
if perms_needed or protected:
|
if perms_needed or protected:
|
||||||
title = _("Cannot delete %(name)s") % {"name": object_name}
|
title = _("Cannot delete %(name)s") % {"name": object_name}
|
||||||
|
@ -1321,15 +1320,15 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
app_label = opts.app_label
|
app_label = opts.app_label
|
||||||
action_list = LogEntry.objects.filter(
|
action_list = LogEntry.objects.filter(
|
||||||
object_id = object_id,
|
object_id = unquote(object_id),
|
||||||
content_type__id__exact = ContentType.objects.get_for_model(model).id
|
content_type__id__exact = ContentType.objects.get_for_model(model).id
|
||||||
).select_related().order_by('action_time')
|
).select_related().order_by('action_time')
|
||||||
# If no history was found, see whether this object even exists.
|
# If no history was found, see whether this object even exists.
|
||||||
obj = get_object_or_404(model, pk=unquote(object_id))
|
obj = get_object_or_404(model, pk=unquote(object_id))
|
||||||
context = {
|
context = {
|
||||||
'title': _('Change history: %s') % force_unicode(obj),
|
'title': _('Change history: %s') % force_text(obj),
|
||||||
'action_list': action_list,
|
'action_list': action_list,
|
||||||
'module_name': capfirst(force_unicode(opts.verbose_name_plural)),
|
'module_name': capfirst(force_text(opts.verbose_name_plural)),
|
||||||
'object': obj,
|
'object': obj,
|
||||||
'app_label': app_label,
|
'app_label': app_label,
|
||||||
'opts': opts,
|
'opts': opts,
|
||||||
|
@ -1416,7 +1415,7 @@ class InlineModelAdmin(BaseModelAdmin):
|
||||||
if self.declared_fieldsets:
|
if self.declared_fieldsets:
|
||||||
return self.declared_fieldsets
|
return self.declared_fieldsets
|
||||||
form = self.get_formset(request, obj).form
|
form = self.get_formset(request, obj).form
|
||||||
fields = form.base_fields.keys() + list(self.get_readonly_fields(request, obj))
|
fields = list(form.base_fields) + list(self.get_readonly_fields(request, obj))
|
||||||
return [(None, {'fields': fields})]
|
return [(None, {'fields': fields})]
|
||||||
|
|
||||||
def queryset(self, request):
|
def queryset(self, request):
|
||||||
|
|
|
@ -10,6 +10,7 @@ from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.core.urlresolvers import reverse, NoReverseMatch
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils import six
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.views.decorators.cache import never_cache
|
from django.views.decorators.cache import never_cache
|
||||||
|
@ -136,7 +137,7 @@ class AdminSite(object):
|
||||||
"""
|
"""
|
||||||
Get all the enabled actions as an iterable of (name, func).
|
Get all the enabled actions as an iterable of (name, func).
|
||||||
"""
|
"""
|
||||||
return self._actions.iteritems()
|
return six.iteritems(self._actions)
|
||||||
|
|
||||||
def has_permission(self, request):
|
def has_permission(self, request):
|
||||||
"""
|
"""
|
||||||
|
@ -234,14 +235,15 @@ class AdminSite(object):
|
||||||
wrap(self.i18n_javascript, cacheable=True),
|
wrap(self.i18n_javascript, cacheable=True),
|
||||||
name='jsi18n'),
|
name='jsi18n'),
|
||||||
url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
|
url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
|
||||||
wrap(contenttype_views.shortcut)),
|
wrap(contenttype_views.shortcut),
|
||||||
|
name='view_on_site'),
|
||||||
url(r'^(?P<app_label>\w+)/$',
|
url(r'^(?P<app_label>\w+)/$',
|
||||||
wrap(self.app_index),
|
wrap(self.app_index),
|
||||||
name='app_list')
|
name='app_list')
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add in each model's views.
|
# Add in each model's views.
|
||||||
for model, model_admin in self._registry.iteritems():
|
for model, model_admin in six.iteritems(self._registry):
|
||||||
urlpatterns += patterns('',
|
urlpatterns += patterns('',
|
||||||
url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
|
url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
|
||||||
include(model_admin.urls))
|
include(model_admin.urls))
|
||||||
|
@ -373,7 +375,7 @@ class AdminSite(object):
|
||||||
}
|
}
|
||||||
|
|
||||||
# Sort the apps alphabetically.
|
# Sort the apps alphabetically.
|
||||||
app_list = app_dict.values()
|
app_list = list(six.itervalues(app_dict))
|
||||||
app_list.sort(key=lambda x: x['name'])
|
app_list.sort(key=lambda x: x['name'])
|
||||||
|
|
||||||
# Sort the models alphabetically within each app.
|
# Sort the models alphabetically within each app.
|
||||||
|
|
|
@ -226,6 +226,10 @@ body.popup .submit-row {
|
||||||
width: 5em;
|
width: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vBigIntegerField {
|
||||||
|
width: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
.vForeignKeyRawIdAdminField {
|
.vForeignKeyRawIdAdminField {
|
||||||
width: 5em;
|
width: 5em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,8 @@
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selector .selector-filter label {
|
.selector .selector-filter label,
|
||||||
|
.inline-group .aligned .selector .selector-filter label {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
(function(a){a.fn.actions=function(m){var b=a.extend({},a.fn.actions.defaults,m),f=a(this),e=!1,j=function(c){c?h():i();a(f).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)},g=function(){var c=a(f).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},!0));a(b.allToggle).attr("checked",function(){c==f.length?(value=!0,h()):(value=!1,k());return value})},h=function(){a(b.acrossClears).hide();
|
(function(a){a.fn.actions=function(n){var b=a.extend({},a.fn.actions.defaults,n),e=a(this),g=false,k=function(c){c?i():j();a(e).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)},f=function(){var c=a(e).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},true));a(b.allToggle).attr("checked",function(){if(c==e.length){value=true;i()}else{value=false;l()}return value})},i=
|
||||||
a(b.acrossQuestions).show();a(b.allContainer).hide()},l=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},i=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},k=function(){i();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};a(b.counterContainer).show();a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass);
|
function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()},m=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},j=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},l=function(){j();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};a(b.counterContainer).show();
|
||||||
g();1==a(b.acrossInput).val()&&l()});a(b.allToggle).show().click(function(){j(a(this).attr("checked"));g()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);l()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",!1);k();j(0);g()});lastChecked=null;a(f).click(function(c){c||(c=window.event);var d=c.target?c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&!0===c.shiftKey){var e=!1;a(lastChecked).attr("checked",
|
a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass);f();a(b.acrossInput).val()==1&&m()});a(b.allToggle).show().click(function(){k(a(this).attr("checked"));f()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);m()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",false);l();k(0);f()});lastChecked=null;a(e).click(function(c){if(!c)c=window.event;var d=c.target?
|
||||||
d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(f).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))e=e?false:true;e&&a(this).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);lastChecked=d;g()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){e=!0});a('form#changelist-form button[name="index"]').click(function(){if(e)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});
|
c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&c.shiftKey===true){var h=false;a(lastChecked).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(e).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))h=h?false:true;h&&a(this).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);lastChecked=d;f()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){g=
|
||||||
a('form#changelist-form input[name="_save"]').click(function(){var b=!1;a("div.actions select option:selected").each(function(){a(this).val()&&(b=!0)});if(b)return e?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};
|
true});a('form#changelist-form button[name="index"]').click(function(){if(g)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').click(function(){var c=false;a("div.actions select option:selected").each(function(){if(a(this).val())c=true});if(c)return g?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):
|
||||||
a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery);
|
confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery);
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
(function(a){a(document).ready(function(){a("fieldset.collapse").each(function(c,b){0==a(b).find("div.errors").length&&a(b).addClass("collapsed").find("h2").first().append(' (<a id="fieldsetcollapser'+c+'" class="collapse-toggle" href="#">'+gettext("Show")+"</a>)")});a("fieldset.collapse a.collapse-toggle").toggle(function(){a(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset",[a(this).attr("id")]);return!1},function(){a(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset",
|
(function(a){a(document).ready(function(){a("fieldset.collapse").each(function(c,b){a(b).find("div.errors").length==0&&a(b).addClass("collapsed").find("h2").first().append(' (<a id="fieldsetcollapser'+c+'" class="collapse-toggle" href="#">'+gettext("Show")+"</a>)")});a("fieldset.collapse a.collapse-toggle").toggle(function(){a(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset",[a(this).attr("id")]);return false},function(){a(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset",
|
||||||
[a(this).attr("id")]);return!1})})})(django.jQuery);
|
[a(this).attr("id")]);return false})})})(django.jQuery);
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
if ($(this).attr("tagName") == "TR") {
|
if ($(this).attr("tagName") == "TR") {
|
||||||
// If forms are laid out as table rows, insert the
|
// If forms are laid out as table rows, insert the
|
||||||
// "add" button in a new table row:
|
// "add" button in a new table row:
|
||||||
var numCols = this.eq(0).children().length;
|
var numCols = this.eq(-1).children().length;
|
||||||
$(this).parent().append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
|
$(this).parent().append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
|
||||||
addButton = $(this).parent().find("tr:last a");
|
addButton = $(this).parent().find("tr:last a");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
(function(b){b.fn.formset=function(c){var a=b.extend({},b.fn.formset.defaults,c),j=function(a,f,d){var e=RegExp("("+f+"-(\\d+|__prefix__))"),f=f+"-"+d;b(a).attr("for")&&b(a).attr("for",b(a).attr("for").replace(e,f));a.id&&(a.id=a.id.replace(e,f));a.name&&(a.name=a.name.replace(e,f))},c=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off"),h=parseInt(c.val(),10),g=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off"),c=""===g.val()||0<g.val()-c.val();b(this).each(function(){b(this).not("."+
|
(function(b){b.fn.formset=function(g){var a=b.extend({},b.fn.formset.defaults,g),k=function(c,f,e){var d=RegExp("("+f+"-(\\d+|__prefix__))");f=f+"-"+e;b(c).attr("for")&&b(c).attr("for",b(c).attr("for").replace(d,f));if(c.id)c.id=c.id.replace(d,f);if(c.name)c.name=c.name.replace(d,f)};g=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off");var l=parseInt(g.val(),10),h=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off");g=h.val()===""||h.val()-g.val()>0;b(this).each(function(){b(this).not("."+
|
||||||
a.emptyCssClass).addClass(a.formCssClass)});if(b(this).length&&c){var i;"TR"==b(this).attr("tagName")?(c=this.eq(0).children().length,b(this).parent().append('<tr class="'+a.addCssClass+'"><td colspan="'+c+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>"),i=b(this).parent().find("tr:last a")):(b(this).filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>"),i=b(this).filter(":last").next().find("a"));i.click(function(c){c.preventDefault();
|
a.emptyCssClass).addClass(a.formCssClass)});if(b(this).length&&g){var j;if(b(this).attr("tagName")=="TR"){g=this.eq(-1).children().length;b(this).parent().append('<tr class="'+a.addCssClass+'"><td colspan="'+g+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>");j=b(this).parent().find("tr:last a")}else{b(this).filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>");j=b(this).filter(":last").next().find("a")}j.click(function(c){c.preventDefault();
|
||||||
var f=b("#id_"+a.prefix+"-TOTAL_FORMS"),c=b("#"+a.prefix+"-empty"),d=c.clone(true);d.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+h);d.is("tr")?d.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>"):d.is("ul")||d.is("ol")?d.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):d.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+
|
var f=b("#id_"+a.prefix+"-TOTAL_FORMS");c=b("#"+a.prefix+"-empty");var e=c.clone(true);e.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+l);if(e.is("tr"))e.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>");else e.is("ul")||e.is("ol")?e.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):e.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+
|
||||||
a.deleteText+"</a></span>");d.find("*").each(function(){j(this,a.prefix,f.val())});d.insertBefore(b(c));b(f).val(parseInt(f.val(),10)+1);h=h+1;g.val()!==""&&g.val()-f.val()<=0&&i.parent().hide();d.find("a."+a.deleteCssClass).click(function(e){e.preventDefault();e=b(this).parents("."+a.formCssClass);e.remove();h=h-1;a.removed&&a.removed(e);e=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(e.length);(g.val()===""||g.val()-e.length>0)&&i.parent().show();for(var c=0,d=e.length;c<d;c++){j(b(e).get(c),
|
a.deleteText+"</a></span>");e.find("*").each(function(){k(this,a.prefix,f.val())});e.insertBefore(b(c));b(f).val(parseInt(f.val(),10)+1);l+=1;h.val()!==""&&h.val()-f.val()<=0&&j.parent().hide();e.find("a."+a.deleteCssClass).click(function(d){d.preventDefault();d=b(this).parents("."+a.formCssClass);d.remove();l-=1;a.removed&&a.removed(d);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);if(h.val()===""||h.val()-d.length>0)j.parent().show();for(var i=0,m=d.length;i<m;i++){k(b(d).get(i),
|
||||||
a.prefix,c);b(e.get(c)).find("*").each(function(){j(this,a.prefix,c)})}});a.added&&a.added(d)})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null}})(django.jQuery);
|
a.prefix,i);b(d.get(i)).find("*").each(function(){k(this,a.prefix,i)})}});a.added&&a.added(e)})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null}})(django.jQuery);
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
(function(a){a.fn.prepopulate=function(d,e){return this.each(function(){var b=a(this);b.data("_changed",!1);b.change(function(){b.data("_changed",!0)});var c=function(){if(!0!=b.data("_changed")){var c=[];a.each(d,function(b,d){0<a(d).val().length&&c.push(a(d).val())});b.val(URLify(c.join(" "),e))}};a(d.join(",")).keyup(c).change(c).focus(c)})}})(django.jQuery);
|
(function(a){a.fn.prepopulate=function(d,g){return this.each(function(){var b=a(this);b.data("_changed",false);b.change(function(){b.data("_changed",true)});var c=function(){if(b.data("_changed")!=true){var e=[];a.each(d,function(h,f){a(f).val().length>0&&e.push(a(f).val())});b.val(URLify(e.join(" "),g))}};a(d.join(",")).keyup(c).change(c).focus(c)})}})(django.jQuery);
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
{% load admin_urls %}
|
{% load admin_urls %}
|
||||||
|
|
||||||
{% block extrahead %}{{ block.super }}
|
{% block extrahead %}{{ block.super }}
|
||||||
{% url 'admin:jsi18n' as jsi18nurl %}
|
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
|
||||||
<script type="text/javascript" src="{{ jsi18nurl|default:"../../../../jsi18n/" }}"></script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
|
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
|
||||||
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
|
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
{% load admin_urls %}
|
{% load admin_urls %}
|
||||||
|
|
||||||
{% block extrahead %}{{ block.super }}
|
{% block extrahead %}{{ block.super }}
|
||||||
{% url 'admin:jsi18n' as jsi18nurl %}
|
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
|
||||||
<script type="text/javascript" src="{{ jsi18nurl|default:"../../../jsi18n/" }}"></script>
|
|
||||||
{{ media }}
|
{{ media }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -31,7 +30,7 @@
|
||||||
<ul class="object-tools">
|
<ul class="object-tools">
|
||||||
{% block object-tools-items %}
|
{% block object-tools-items %}
|
||||||
<li><a href="history/" class="historylink">{% trans "History" %}</a></li>
|
<li><a href="history/" class="historylink">{% trans "History" %}</a></li>
|
||||||
{% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
|
{% if has_absolute_url %}<li><a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}{% endif %}
|
{% endif %}{% endif %}
|
||||||
|
@ -65,7 +64,7 @@
|
||||||
|
|
||||||
{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}
|
{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}
|
||||||
|
|
||||||
{% if adminform and add %}
|
{% if adminform.first_field and add %}
|
||||||
<script type="text/javascript">document.getElementById("{{ adminform.first_field.id_for_label }}").focus();</script>
|
<script type="text/javascript">document.getElementById("{{ adminform.first_field.id_for_label }}").focus();</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,7 @@
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />
|
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if cl.formset or action_form %}
|
{% if cl.formset or action_form %}
|
||||||
{% url 'admin:jsi18n' as jsi18nurl %}
|
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
|
||||||
<script type="text/javascript" src="{{ jsi18nurl|default:'../../jsi18n/' }}"></script>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ media.css }}
|
{{ media.css }}
|
||||||
{% if not actions_on_top and not actions_on_bottom %}
|
{% if not actions_on_top and not actions_on_bottom %}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst }}</a>
|
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst }}</a>
|
||||||
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst|escape }}</a>
|
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst|escape }}</a>
|
||||||
› <a href="{% url opts|admin_urlname:'changelist' %}{{ object.pk }}">{{ object|truncatewords:"18" }}</a>
|
› <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
|
||||||
› {% trans 'Delete' %}
|
› {% trans 'Delete' %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
{% for inline_admin_form in inline_admin_formset %}<div class="inline-related{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
|
{% for inline_admin_form in inline_admin_formset %}<div class="inline-related{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
|
||||||
<h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b> <span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %}#{{ forloop.counter }}{% endif %}</span>
|
<h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b> <span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %}#{{ forloop.counter }}{% endif %}</span>
|
||||||
{% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original_content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %}
|
{% if inline_admin_form.show_url %}<a href="{% url 'admin:view_on_site' inline_admin_form.original_content_type_id inline_admin_form.original.pk %}">{% trans "View on site" %}</a>{% endif %}
|
||||||
{% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %}
|
{% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %}
|
||||||
</h3>
|
</h3>
|
||||||
{% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %}
|
{% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %}
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<td class="original">
|
<td class="original">
|
||||||
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
|
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
|
||||||
{% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
|
{% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
|
||||||
{% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original_content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %}
|
{% if inline_admin_form.show_url %}<a href="{% url 'admin:view_on_site' inline_admin_form.original_content_type_id inline_admin_form.original.pk %}">{% trans "View on site" %}</a>{% endif %}
|
||||||
</p>{% endif %}
|
</p>{% endif %}
|
||||||
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
|
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
|
||||||
{{ inline_admin_form.fk_field.field }}
|
{{ inline_admin_form.fk_field.field }}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||||
› <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
|
› <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
|
||||||
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ module_name }}</a>
|
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ module_name }}</a>
|
||||||
› <a href="{% url opts|admin_urlname:'changelist' %}{{ object.pk }}">{{ object|truncatewords:"18" }}</a>
|
› <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
|
||||||
› {% trans 'History' %}
|
› {% trans 'History' %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.contrib.admin.util import (lookup_field, display_for_field,
|
from django.contrib.admin.util import (lookup_field, display_for_field,
|
||||||
|
@ -8,11 +10,12 @@ from django.contrib.admin.templatetags.admin_static import static
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
from django.utils.html import escape, conditional_escape
|
from django.utils.html import format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils import six
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.encoding import smart_unicode, force_unicode
|
from django.utils.encoding import smart_text, force_text
|
||||||
from django.template import Library
|
from django.template import Library
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.template.context import Context
|
from django.template.context import Context
|
||||||
|
@ -27,11 +30,14 @@ def paginator_number(cl,i):
|
||||||
Generates an individual page index link in a paginated list.
|
Generates an individual page index link in a paginated list.
|
||||||
"""
|
"""
|
||||||
if i == DOT:
|
if i == DOT:
|
||||||
return u'... '
|
return '... '
|
||||||
elif i == cl.page_num:
|
elif i == cl.page_num:
|
||||||
return mark_safe(u'<span class="this-page">%d</span> ' % (i+1))
|
return format_html('<span class="this-page">{0}</span> ', i+1)
|
||||||
else:
|
else:
|
||||||
return mark_safe(u'<a href="%s"%s>%d</a> ' % (escape(cl.get_query_string({PAGE_VAR: i})), (i == cl.paginator.num_pages-1 and ' class="end"' or ''), i+1))
|
return format_html('<a href="{0}"{1}>{2}</a> ',
|
||||||
|
cl.get_query_string({PAGE_VAR: i}),
|
||||||
|
mark_safe(' class="end"' if i == cl.paginator.num_pages-1 else ''),
|
||||||
|
i+1)
|
||||||
|
|
||||||
@register.inclusion_tag('admin/pagination.html')
|
@register.inclusion_tag('admin/pagination.html')
|
||||||
def pagination(cl):
|
def pagination(cl):
|
||||||
|
@ -120,7 +126,7 @@ def result_headers(cl):
|
||||||
if i in ordering_field_columns:
|
if i in ordering_field_columns:
|
||||||
sorted = True
|
sorted = True
|
||||||
order_type = ordering_field_columns.get(i).lower()
|
order_type = ordering_field_columns.get(i).lower()
|
||||||
sort_priority = ordering_field_columns.keys().index(i) + 1
|
sort_priority = list(ordering_field_columns).index(i) + 1
|
||||||
th_classes.append('sorted %sending' % order_type)
|
th_classes.append('sorted %sending' % order_type)
|
||||||
new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type]
|
new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type]
|
||||||
|
|
||||||
|
@ -157,13 +163,14 @@ def result_headers(cl):
|
||||||
"url_primary": cl.get_query_string({ORDER_VAR: '.'.join(o_list_primary)}),
|
"url_primary": cl.get_query_string({ORDER_VAR: '.'.join(o_list_primary)}),
|
||||||
"url_remove": cl.get_query_string({ORDER_VAR: '.'.join(o_list_remove)}),
|
"url_remove": cl.get_query_string({ORDER_VAR: '.'.join(o_list_remove)}),
|
||||||
"url_toggle": cl.get_query_string({ORDER_VAR: '.'.join(o_list_toggle)}),
|
"url_toggle": cl.get_query_string({ORDER_VAR: '.'.join(o_list_toggle)}),
|
||||||
"class_attrib": mark_safe(th_classes and ' class="%s"' % ' '.join(th_classes) or '')
|
"class_attrib": format_html(' class="{0}"', ' '.join(th_classes))
|
||||||
|
if th_classes else '',
|
||||||
}
|
}
|
||||||
|
|
||||||
def _boolean_icon(field_val):
|
def _boolean_icon(field_val):
|
||||||
icon_url = static('admin/img/icon-%s.gif' %
|
icon_url = static('admin/img/icon-%s.gif' %
|
||||||
{True: 'yes', False: 'no', None: 'unknown'}[field_val])
|
{True: 'yes', False: 'no', None: 'unknown'}[field_val])
|
||||||
return mark_safe(u'<img src="%s" alt="%s" />' % (icon_url, field_val))
|
return format_html('<img src="{0}" alt="{1}" />', icon_url, field_val)
|
||||||
|
|
||||||
def items_for_result(cl, result, form):
|
def items_for_result(cl, result, form):
|
||||||
"""
|
"""
|
||||||
|
@ -179,8 +186,8 @@ def items_for_result(cl, result, form):
|
||||||
result_repr = EMPTY_CHANGELIST_VALUE
|
result_repr = EMPTY_CHANGELIST_VALUE
|
||||||
else:
|
else:
|
||||||
if f is None:
|
if f is None:
|
||||||
if field_name == u'action_checkbox':
|
if field_name == 'action_checkbox':
|
||||||
row_class = ' class="action-checkbox"'
|
row_class = mark_safe(' class="action-checkbox"')
|
||||||
allow_tags = getattr(attr, 'allow_tags', False)
|
allow_tags = getattr(attr, 'allow_tags', False)
|
||||||
boolean = getattr(attr, 'boolean', False)
|
boolean = getattr(attr, 'boolean', False)
|
||||||
if boolean:
|
if boolean:
|
||||||
|
@ -188,24 +195,22 @@ def items_for_result(cl, result, form):
|
||||||
result_repr = display_for_value(value, boolean)
|
result_repr = display_for_value(value, boolean)
|
||||||
# Strip HTML tags in the resulting text, except if the
|
# Strip HTML tags in the resulting text, except if the
|
||||||
# function has an "allow_tags" attribute set to True.
|
# function has an "allow_tags" attribute set to True.
|
||||||
if not allow_tags:
|
if allow_tags:
|
||||||
result_repr = escape(result_repr)
|
|
||||||
else:
|
|
||||||
result_repr = mark_safe(result_repr)
|
result_repr = mark_safe(result_repr)
|
||||||
if isinstance(value, (datetime.date, datetime.time)):
|
if isinstance(value, (datetime.date, datetime.time)):
|
||||||
row_class = ' class="nowrap"'
|
row_class = mark_safe(' class="nowrap"')
|
||||||
else:
|
else:
|
||||||
if isinstance(f.rel, models.ManyToOneRel):
|
if isinstance(f.rel, models.ManyToOneRel):
|
||||||
field_val = getattr(result, f.name)
|
field_val = getattr(result, f.name)
|
||||||
if field_val is None:
|
if field_val is None:
|
||||||
result_repr = EMPTY_CHANGELIST_VALUE
|
result_repr = EMPTY_CHANGELIST_VALUE
|
||||||
else:
|
else:
|
||||||
result_repr = escape(field_val)
|
result_repr = field_val
|
||||||
else:
|
else:
|
||||||
result_repr = display_for_field(value, f)
|
result_repr = display_for_field(value, f)
|
||||||
if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
|
if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
|
||||||
row_class = ' class="nowrap"'
|
row_class = mark_safe(' class="nowrap"')
|
||||||
if force_unicode(result_repr) == '':
|
if force_text(result_repr) == '':
|
||||||
result_repr = mark_safe(' ')
|
result_repr = mark_safe(' ')
|
||||||
# If list_display_links not defined, add the link tag to the first field
|
# If list_display_links not defined, add the link tag to the first field
|
||||||
if (first and not cl.list_display_links) or field_name in cl.list_display_links:
|
if (first and not cl.list_display_links) or field_name in cl.list_display_links:
|
||||||
|
@ -219,9 +224,15 @@ def items_for_result(cl, result, form):
|
||||||
else:
|
else:
|
||||||
attr = pk
|
attr = pk
|
||||||
value = result.serializable_value(attr)
|
value = result.serializable_value(attr)
|
||||||
result_id = repr(force_unicode(value))[1:]
|
result_id = repr(force_text(value))[1:]
|
||||||
yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \
|
yield format_html('<{0}{1}><a href="{2}"{3}>{4}</a></{5}>',
|
||||||
(table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag))
|
table_tag,
|
||||||
|
row_class,
|
||||||
|
url,
|
||||||
|
format_html(' onclick="opener.dismissRelatedLookupPopup(window, {0}); return false;"', result_id)
|
||||||
|
if cl.is_popup else '',
|
||||||
|
result_repr,
|
||||||
|
table_tag)
|
||||||
else:
|
else:
|
||||||
# By default the fields come from ModelAdmin.list_editable, but if we pull
|
# By default the fields come from ModelAdmin.list_editable, but if we pull
|
||||||
# the fields out of the form instead of list_editable custom admins
|
# the fields out of the form instead of list_editable custom admins
|
||||||
|
@ -230,12 +241,10 @@ def items_for_result(cl, result, form):
|
||||||
field_name == cl.model._meta.pk.name and
|
field_name == cl.model._meta.pk.name and
|
||||||
form[cl.model._meta.pk.name].is_hidden)):
|
form[cl.model._meta.pk.name].is_hidden)):
|
||||||
bf = form[field_name]
|
bf = form[field_name]
|
||||||
result_repr = mark_safe(force_unicode(bf.errors) + force_unicode(bf))
|
result_repr = mark_safe(force_text(bf.errors) + force_text(bf))
|
||||||
else:
|
yield format_html('<td{0}>{1}</td>', row_class, result_repr)
|
||||||
result_repr = conditional_escape(result_repr)
|
|
||||||
yield mark_safe(u'<td%s>%s</td>' % (row_class, result_repr))
|
|
||||||
if form and not form[cl.model._meta.pk.name].is_hidden:
|
if form and not form[cl.model._meta.pk.name].is_hidden:
|
||||||
yield mark_safe(u'<td>%s</td>' % force_unicode(form[cl.model._meta.pk.name]))
|
yield format_html('<td>{0}</td>', force_text(form[cl.model._meta.pk.name]))
|
||||||
|
|
||||||
class ResultList(list):
|
class ResultList(list):
|
||||||
# Wrapper class used to return items in a list_editable
|
# Wrapper class used to return items in a list_editable
|
||||||
|
@ -258,7 +267,7 @@ def result_hidden_fields(cl):
|
||||||
if cl.formset:
|
if cl.formset:
|
||||||
for res, form in zip(cl.result_list, cl.formset.forms):
|
for res, form in zip(cl.result_list, cl.formset.forms):
|
||||||
if form[cl.model._meta.pk.name].is_hidden:
|
if form[cl.model._meta.pk.name].is_hidden:
|
||||||
yield mark_safe(force_unicode(form[cl.model._meta.pk.name]))
|
yield mark_safe(force_text(form[cl.model._meta.pk.name]))
|
||||||
|
|
||||||
@register.inclusion_tag("admin/change_list_results.html")
|
@register.inclusion_tag("admin/change_list_results.html")
|
||||||
def result_list(cl):
|
def result_list(cl):
|
||||||
|
|
|
@ -32,7 +32,7 @@ def submit_row(context):
|
||||||
'onclick_attrib': (opts.get_ordered_objects() and change
|
'onclick_attrib': (opts.get_ordered_objects() and change
|
||||||
and 'onclick="submitOrderForm();"' or ''),
|
and 'onclick="submitOrderForm();"' or ''),
|
||||||
'show_delete_link': (not is_popup and context['has_delete_permission']
|
'show_delete_link': (not is_popup and context['has_delete_permission']
|
||||||
and (change or context['show_delete'])),
|
and change and context.get('show_delete', True)),
|
||||||
'show_save_as_new': not is_popup and change and save_as,
|
'show_save_as_new': not is_popup and change and save_as,
|
||||||
'show_save_and_add_another': context['has_add_permission'] and
|
'show_save_and_add_another': context['has_add_permission'] and
|
||||||
not is_popup and (not save_as or context['add']),
|
not is_popup and (not save_as or context['add']),
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.core.urlresolvers import reverse
|
||||||
from django import template
|
from django import template
|
||||||
|
from django.contrib.admin.util import quote
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def admin_urlname(value, arg):
|
def admin_urlname(value, arg):
|
||||||
return 'admin:%s_%s_%s' % (value.app_label, value.module_name, arg)
|
return 'admin:%s_%s_%s' % (value.app_label, value.module_name, arg)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def admin_urlquote(value):
|
||||||
|
return quote(value)
|
||||||
|
|
|
@ -17,7 +17,7 @@ class AdminLogNode(template.Node):
|
||||||
user_id = self.user
|
user_id = self.user
|
||||||
if not user_id.isdigit():
|
if not user_id.isdigit():
|
||||||
user_id = context[self.user].id
|
user_id = context[self.user].id
|
||||||
context[self.varname] = LogEntry.objects.filter(user__id__exact=user_id).select_related('content_type', 'user')[:self.limit]
|
context[self.varname] = LogEntry.objects.filter(user__id__exact=user_id).select_related('content_type', 'user')[:int(self.limit)]
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
|
||||||
|
@ -7,11 +9,11 @@ from django.db.models.deletion import Collector
|
||||||
from django.db.models.related import RelatedObject
|
from django.db.models.related import RelatedObject
|
||||||
from django.forms.forms import pretty_name
|
from django.forms.forms import pretty_name
|
||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
from django.utils.html import escape
|
from django.utils.html import format_html
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.encoding import force_unicode, smart_unicode, smart_str
|
from django.utils.encoding import force_text, smart_text, smart_bytes
|
||||||
|
from django.utils import six
|
||||||
from django.utils.translation import ungettext
|
from django.utils.translation import ungettext
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
@ -50,7 +52,7 @@ def quote(s):
|
||||||
quoting is slightly different so that it doesn't get automatically
|
quoting is slightly different so that it doesn't get automatically
|
||||||
unquoted by the Web browser.
|
unquoted by the Web browser.
|
||||||
"""
|
"""
|
||||||
if not isinstance(s, basestring):
|
if not isinstance(s, six.string_types):
|
||||||
return s
|
return s
|
||||||
res = list(s)
|
res = list(s)
|
||||||
for i in range(len(res)):
|
for i in range(len(res)):
|
||||||
|
@ -122,15 +124,15 @@ def get_deleted_objects(objs, opts, user, admin_site, using):
|
||||||
if not user.has_perm(p):
|
if not user.has_perm(p):
|
||||||
perms_needed.add(opts.verbose_name)
|
perms_needed.add(opts.verbose_name)
|
||||||
# Display a link to the admin page.
|
# Display a link to the admin page.
|
||||||
return mark_safe(u'%s: <a href="%s">%s</a>' %
|
return format_html('{0}: <a href="{1}">{2}</a>',
|
||||||
(escape(capfirst(opts.verbose_name)),
|
capfirst(opts.verbose_name),
|
||||||
admin_url,
|
admin_url,
|
||||||
escape(obj)))
|
obj)
|
||||||
else:
|
else:
|
||||||
# Don't display link to edit, because it either has no
|
# Don't display link to edit, because it either has no
|
||||||
# admin or is edited inline.
|
# admin or is edited inline.
|
||||||
return u'%s: %s' % (capfirst(opts.verbose_name),
|
return '%s: %s' % (capfirst(opts.verbose_name),
|
||||||
force_unicode(obj))
|
force_text(obj))
|
||||||
|
|
||||||
to_delete = collector.nested(format_callback)
|
to_delete = collector.nested(format_callback)
|
||||||
|
|
||||||
|
@ -205,8 +207,8 @@ def model_format_dict(obj):
|
||||||
else:
|
else:
|
||||||
opts = obj
|
opts = obj
|
||||||
return {
|
return {
|
||||||
'verbose_name': force_unicode(opts.verbose_name),
|
'verbose_name': force_text(opts.verbose_name),
|
||||||
'verbose_name_plural': force_unicode(opts.verbose_name_plural)
|
'verbose_name_plural': force_text(opts.verbose_name_plural)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -272,11 +274,11 @@ def label_for_field(name, model, model_admin=None, return_attr=False):
|
||||||
label = field.verbose_name
|
label = field.verbose_name
|
||||||
except models.FieldDoesNotExist:
|
except models.FieldDoesNotExist:
|
||||||
if name == "__unicode__":
|
if name == "__unicode__":
|
||||||
label = force_unicode(model._meta.verbose_name)
|
label = force_text(model._meta.verbose_name)
|
||||||
attr = unicode
|
attr = six.text_type
|
||||||
elif name == "__str__":
|
elif name == "__str__":
|
||||||
label = smart_str(model._meta.verbose_name)
|
label = smart_bytes(model._meta.verbose_name)
|
||||||
attr = str
|
attr = bytes
|
||||||
else:
|
else:
|
||||||
if callable(name):
|
if callable(name):
|
||||||
attr = name
|
attr = name
|
||||||
|
@ -309,7 +311,7 @@ def help_text_for_field(name, model):
|
||||||
help_text = model._meta.get_field_by_name(name)[0].help_text
|
help_text = model._meta.get_field_by_name(name)[0].help_text
|
||||||
except models.FieldDoesNotExist:
|
except models.FieldDoesNotExist:
|
||||||
help_text = ""
|
help_text = ""
|
||||||
return smart_unicode(help_text)
|
return smart_text(help_text)
|
||||||
|
|
||||||
|
|
||||||
def display_for_field(value, field):
|
def display_for_field(value, field):
|
||||||
|
@ -333,7 +335,7 @@ def display_for_field(value, field):
|
||||||
elif isinstance(field, models.FloatField):
|
elif isinstance(field, models.FloatField):
|
||||||
return formats.number_format(value)
|
return formats.number_format(value)
|
||||||
else:
|
else:
|
||||||
return smart_unicode(value)
|
return smart_text(value)
|
||||||
|
|
||||||
|
|
||||||
def display_for_value(value, boolean=False):
|
def display_for_value(value, boolean=False):
|
||||||
|
@ -348,10 +350,10 @@ def display_for_value(value, boolean=False):
|
||||||
return formats.localize(timezone.template_localtime(value))
|
return formats.localize(timezone.template_localtime(value))
|
||||||
elif isinstance(value, (datetime.date, datetime.time)):
|
elif isinstance(value, (datetime.date, datetime.time)):
|
||||||
return formats.localize(value)
|
return formats.localize(value)
|
||||||
elif isinstance(value, (decimal.Decimal, float, int, long)):
|
elif isinstance(value, six.integer_types + (decimal.Decimal, float)):
|
||||||
return formats.number_format(value)
|
return formats.number_format(value)
|
||||||
else:
|
else:
|
||||||
return smart_unicode(value)
|
return smart_text(value)
|
||||||
|
|
||||||
|
|
||||||
class NotRelationField(Exception):
|
class NotRelationField(Exception):
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.core.paginator import InvalidPage
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.fields import FieldDoesNotExist
|
from django.db.models.fields import FieldDoesNotExist
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.encoding import force_unicode, smart_str
|
from django.utils.encoding import force_text, smart_bytes
|
||||||
from django.utils.translation import ugettext, ugettext_lazy
|
from django.utils.translation import ugettext, ugettext_lazy
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ class ChangeList(object):
|
||||||
title = ugettext('Select %s')
|
title = ugettext('Select %s')
|
||||||
else:
|
else:
|
||||||
title = ugettext('Select %s to change')
|
title = ugettext('Select %s to change')
|
||||||
self.title = title % force_unicode(self.opts.verbose_name)
|
self.title = title % force_text(self.opts.verbose_name)
|
||||||
self.pk_attname = self.lookup_opts.pk.attname
|
self.pk_attname = self.lookup_opts.pk.attname
|
||||||
|
|
||||||
def get_filters(self, request):
|
def get_filters(self, request):
|
||||||
|
@ -94,7 +94,7 @@ class ChangeList(object):
|
||||||
# 'key' will be used as a keyword argument later, so Python
|
# 'key' will be used as a keyword argument later, so Python
|
||||||
# requires it to be a string.
|
# requires it to be a string.
|
||||||
del lookup_params[key]
|
del lookup_params[key]
|
||||||
lookup_params[smart_str(key)] = value
|
lookup_params[smart_bytes(key)] = value
|
||||||
|
|
||||||
if not self.model_admin.lookup_allowed(key, value):
|
if not self.model_admin.lookup_allowed(key, value):
|
||||||
raise SuspiciousOperation("Filtering by %s not allowed" % key)
|
raise SuspiciousOperation("Filtering by %s not allowed" % key)
|
||||||
|
@ -148,7 +148,7 @@ class ChangeList(object):
|
||||||
if remove is None: remove = []
|
if remove is None: remove = []
|
||||||
p = self.params.copy()
|
p = self.params.copy()
|
||||||
for r in remove:
|
for r in remove:
|
||||||
for k in p.keys():
|
for k in list(p):
|
||||||
if k.startswith(r):
|
if k.startswith(r):
|
||||||
del p[k]
|
del p[k]
|
||||||
for k, v in new_params.items():
|
for k, v in new_params.items():
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
"""
|
"""
|
||||||
Form Widget classes specific to the Django admin site.
|
Form Widget classes specific to the Django admin site.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.admin.templatetags.admin_static import static
|
from django.contrib.admin.templatetags.admin_static import static
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.forms.widgets import RadioFieldRenderer
|
from django.forms.widgets import RadioFieldRenderer
|
||||||
from django.forms.util import flatatt
|
from django.forms.util import flatatt
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape, format_html, format_html_join
|
||||||
from django.utils.text import Truncator
|
from django.utils.text import Truncator
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
|
|
||||||
class FilteredSelectMultiple(forms.SelectMultiple):
|
class FilteredSelectMultiple(forms.SelectMultiple):
|
||||||
|
@ -39,12 +42,12 @@ class FilteredSelectMultiple(forms.SelectMultiple):
|
||||||
if self.is_stacked:
|
if self.is_stacked:
|
||||||
attrs['class'] += 'stacked'
|
attrs['class'] += 'stacked'
|
||||||
output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
|
output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
|
||||||
output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
|
output.append('<script type="text/javascript">addEvent(window, "load", function(e) {')
|
||||||
# TODO: "id_" is hard-coded here. This should instead use the correct
|
# TODO: "id_" is hard-coded here. This should instead use the correct
|
||||||
# API to determine the ID dynamically.
|
# API to determine the ID dynamically.
|
||||||
output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n'
|
output.append('SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n'
|
||||||
% (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), static('admin/')))
|
% (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), static('admin/')))
|
||||||
return mark_safe(u''.join(output))
|
return mark_safe(''.join(output))
|
||||||
|
|
||||||
class AdminDateWidget(forms.DateInput):
|
class AdminDateWidget(forms.DateInput):
|
||||||
|
|
||||||
|
@ -83,24 +86,25 @@ class AdminSplitDateTime(forms.SplitDateTimeWidget):
|
||||||
forms.MultiWidget.__init__(self, widgets, attrs)
|
forms.MultiWidget.__init__(self, widgets, attrs)
|
||||||
|
|
||||||
def format_output(self, rendered_widgets):
|
def format_output(self, rendered_widgets):
|
||||||
return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \
|
return format_html('<p class="datetime">{0} {1}<br />{2} {3}</p>',
|
||||||
(_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))
|
_('Date:'), rendered_widgets[0],
|
||||||
|
_('Time:'), rendered_widgets[1])
|
||||||
|
|
||||||
class AdminRadioFieldRenderer(RadioFieldRenderer):
|
class AdminRadioFieldRenderer(RadioFieldRenderer):
|
||||||
def render(self):
|
def render(self):
|
||||||
"""Outputs a <ul> for this set of radio fields."""
|
"""Outputs a <ul> for this set of radio fields."""
|
||||||
return mark_safe(u'<ul%s>\n%s\n</ul>' % (
|
return format_html('<ul{0}>\n{1}\n</ul>',
|
||||||
flatatt(self.attrs),
|
flatatt(self.attrs),
|
||||||
u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]))
|
format_html_join('\n', '<li>{0}</li>',
|
||||||
)
|
((force_text(w),) for w in self)))
|
||||||
|
|
||||||
class AdminRadioSelect(forms.RadioSelect):
|
class AdminRadioSelect(forms.RadioSelect):
|
||||||
renderer = AdminRadioFieldRenderer
|
renderer = AdminRadioFieldRenderer
|
||||||
|
|
||||||
class AdminFileWidget(forms.ClearableFileInput):
|
class AdminFileWidget(forms.ClearableFileInput):
|
||||||
template_with_initial = (u'<p class="file-upload">%s</p>'
|
template_with_initial = ('<p class="file-upload">%s</p>'
|
||||||
% forms.ClearableFileInput.template_with_initial)
|
% forms.ClearableFileInput.template_with_initial)
|
||||||
template_with_clear = (u'<span class="clearable-file-input">%s</span>'
|
template_with_clear = ('<span class="clearable-file-input">%s</span>'
|
||||||
% forms.ClearableFileInput.template_with_clear)
|
% forms.ClearableFileInput.template_with_clear)
|
||||||
|
|
||||||
def url_params_from_lookup_dict(lookups):
|
def url_params_from_lookup_dict(lookups):
|
||||||
|
@ -113,12 +117,12 @@ def url_params_from_lookup_dict(lookups):
|
||||||
items = []
|
items = []
|
||||||
for k, v in lookups.items():
|
for k, v in lookups.items():
|
||||||
if isinstance(v, (tuple, list)):
|
if isinstance(v, (tuple, list)):
|
||||||
v = u','.join([str(x) for x in v])
|
v = ','.join([str(x) for x in v])
|
||||||
elif isinstance(v, bool):
|
elif isinstance(v, bool):
|
||||||
# See django.db.fields.BooleanField.get_prep_lookup
|
# See django.db.fields.BooleanField.get_prep_lookup
|
||||||
v = ('0', '1')[v]
|
v = ('0', '1')[v]
|
||||||
else:
|
else:
|
||||||
v = unicode(v)
|
v = six.text_type(v)
|
||||||
items.append((k, v))
|
items.append((k, v))
|
||||||
params.update(dict(items))
|
params.update(dict(items))
|
||||||
return params
|
return params
|
||||||
|
@ -148,21 +152,21 @@ class ForeignKeyRawIdWidget(forms.TextInput):
|
||||||
|
|
||||||
params = self.url_parameters()
|
params = self.url_parameters()
|
||||||
if params:
|
if params:
|
||||||
url = u'?' + u'&'.join([u'%s=%s' % (k, v) for k, v in params.items()])
|
url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()])
|
||||||
else:
|
else:
|
||||||
url = u''
|
url = ''
|
||||||
if "class" not in attrs:
|
if "class" not in attrs:
|
||||||
attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook.
|
attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook.
|
||||||
# TODO: "lookup_id_" is hard-coded here. This should instead use
|
# TODO: "lookup_id_" is hard-coded here. This should instead use
|
||||||
# the correct API to determine the ID dynamically.
|
# the correct API to determine the ID dynamically.
|
||||||
extra.append(u'<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> '
|
extra.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> '
|
||||||
% (related_url, url, name))
|
% (related_url, url, name))
|
||||||
extra.append(u'<img src="%s" width="16" height="16" alt="%s" /></a>'
|
extra.append('<img src="%s" width="16" height="16" alt="%s" /></a>'
|
||||||
% (static('admin/img/selector-search.gif'), _('Lookup')))
|
% (static('admin/img/selector-search.gif'), _('Lookup')))
|
||||||
output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra
|
output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra
|
||||||
if value:
|
if value:
|
||||||
output.append(self.label_for_value(value))
|
output.append(self.label_for_value(value))
|
||||||
return mark_safe(u''.join(output))
|
return mark_safe(''.join(output))
|
||||||
|
|
||||||
def base_url_parameters(self):
|
def base_url_parameters(self):
|
||||||
return url_params_from_lookup_dict(self.rel.limit_choices_to)
|
return url_params_from_lookup_dict(self.rel.limit_choices_to)
|
||||||
|
@ -193,7 +197,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
|
||||||
# The related object is registered with the same AdminSite
|
# The related object is registered with the same AdminSite
|
||||||
attrs['class'] = 'vManyToManyRawIdAdminField'
|
attrs['class'] = 'vManyToManyRawIdAdminField'
|
||||||
if value:
|
if value:
|
||||||
value = ','.join([force_unicode(v) for v in value])
|
value = ','.join([force_text(v) for v in value])
|
||||||
else:
|
else:
|
||||||
value = ''
|
value = ''
|
||||||
return super(ManyToManyRawIdWidget, self).render(name, value, attrs)
|
return super(ManyToManyRawIdWidget, self).render(name, value, attrs)
|
||||||
|
@ -217,7 +221,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
|
||||||
if len(initial) != len(data):
|
if len(initial) != len(data):
|
||||||
return True
|
return True
|
||||||
for pk1, pk2 in zip(initial, data):
|
for pk1, pk2 in zip(initial, data):
|
||||||
if force_unicode(pk1) != force_unicode(pk2):
|
if force_text(pk1) != force_text(pk2):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -261,11 +265,11 @@ class RelatedFieldWidgetWrapper(forms.Widget):
|
||||||
related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
|
related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
|
||||||
# TODO: "add_id_" is hard-coded here. This should instead use the
|
# TODO: "add_id_" is hard-coded here. This should instead use the
|
||||||
# correct API to determine the ID dynamically.
|
# correct API to determine the ID dynamically.
|
||||||
output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> '
|
output.append('<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> '
|
||||||
% (related_url, name))
|
% (related_url, name))
|
||||||
output.append(u'<img src="%s" width="10" height="10" alt="%s"/></a>'
|
output.append('<img src="%s" width="10" height="10" alt="%s"/></a>'
|
||||||
% (static('admin/img/icon_addlink.gif'), _('Add Another')))
|
% (static('admin/img/icon_addlink.gif'), _('Add Another')))
|
||||||
return mark_safe(u''.join(output))
|
return mark_safe(''.join(output))
|
||||||
|
|
||||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||||
"Helper function for building an attribute dictionary."
|
"Helper function for building an attribute dictionary."
|
||||||
|
@ -303,12 +307,17 @@ class AdminURLFieldWidget(forms.TextInput):
|
||||||
super(AdminURLFieldWidget, self).__init__(attrs=final_attrs)
|
super(AdminURLFieldWidget, self).__init__(attrs=final_attrs)
|
||||||
|
|
||||||
class AdminIntegerFieldWidget(forms.TextInput):
|
class AdminIntegerFieldWidget(forms.TextInput):
|
||||||
|
class_name = 'vIntegerField'
|
||||||
|
|
||||||
def __init__(self, attrs=None):
|
def __init__(self, attrs=None):
|
||||||
final_attrs = {'class': 'vIntegerField'}
|
final_attrs = {'class': self.class_name}
|
||||||
if attrs is not None:
|
if attrs is not None:
|
||||||
final_attrs.update(attrs)
|
final_attrs.update(attrs)
|
||||||
super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs)
|
super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs)
|
||||||
|
|
||||||
|
class AdminBigIntegerFieldWidget(AdminIntegerFieldWidget):
|
||||||
|
class_name = 'vBigIntegerField'
|
||||||
|
|
||||||
class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput):
|
class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput):
|
||||||
def __init__(self, attrs=None):
|
def __init__(self, attrs=None):
|
||||||
final_attrs = {'class': 'vCommaSeparatedIntegerField'}
|
final_attrs = {'class': 'vCommaSeparatedIntegerField'}
|
||||||
|
|
|
@ -22,7 +22,7 @@ your computer is "internal").</p>
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
|
|
||||||
<div id="content-main">
|
<div id="content-main">
|
||||||
<h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('HEAD',location.href,false);x.send(null);try{view=x.getResponseHeader('x-view');}catch(e){alert('No view found for this page');return;}if(view=='undefined'){alert('No view found for this page');}document.location='{{ admin_url }}doc/views/'+view+'/';})()">{% trans "Documentation for this page" %}</a></h3>
|
<h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('HEAD',location.href,false);x.send(null);try{view=x.getResponseHeader('x-view');}catch(e){alert('No view found for this page');return;}if(view=='undefined'){alert('No view found for this page');}document.location='{{ admin_url|escapejs }}doc/views/'+view+'/';})()">{% trans "Documentation for this page" %}</a></h3>
|
||||||
<p>{% trans "Jumps you from any page to the documentation for the view that generates that page." %}</p>
|
<p>{% trans "Jumps you from any page to the documentation for the view that generates that page." %}</p>
|
||||||
|
|
||||||
<h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{type=x.getResponseHeader('x-object-type');id=x.getResponseHeader('x-object-id');}catch(e){type='(none)';id='(none)';}d=document;b=d.body;e=d.createElement('div');e.id='xxxhhh';s=e.style;s.position='absolute';s.left='10px';s.top='10px';s.font='10px monospace';s.border='1px black solid';s.padding='4px';s.backgroundColor='#eee';e.appendChild(d.createTextNode('Type: '+type));e.appendChild(d.createElement('br'));e.appendChild(d.createTextNode('ID: '+id));e.appendChild(d.createElement('br'));l=d.createElement('a');l.href='#';l.onclick=function(){b.removeChild(e);};l.appendChild(d.createTextNode('[close]'));l.style.textDecoration='none';e.appendChild(l);b.appendChild(e);})();">{% trans "Show object ID" %}</a></h3>
|
<h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{type=x.getResponseHeader('x-object-type');id=x.getResponseHeader('x-object-id');}catch(e){type='(none)';id='(none)';}d=document;b=d.body;e=d.createElement('div');e.id='xxxhhh';s=e.style;s.position='absolute';s.left='10px';s.top='10px';s.font='10px monospace';s.border='1px black solid';s.padding='4px';s.backgroundColor='#eee';e.appendChild(d.createTextNode('Type: '+type));e.appendChild(d.createElement('br'));e.appendChild(d.createTextNode('ID: '+id));e.appendChild(d.createElement('br'));l=d.createElement('a');l.href='#';l.onclick=function(){b.removeChild(e);};l.appendChild(d.createTextNode('[close]'));l.style.textDecoration='none';e.appendChild(l);b.appendChild(e);})();">{% trans "Show object ID" %}</a></h3>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
from django.contrib.admindocs import views
|
from django.contrib.admindocs import views
|
||||||
from django.db.models import fields as builtin_fields
|
from django.db.models import fields as builtin_fields
|
||||||
|
@ -20,17 +20,17 @@ class TestFieldType(unittest.TestCase):
|
||||||
def test_builtin_fields(self):
|
def test_builtin_fields(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
views.get_readable_field_data_type(builtin_fields.BooleanField()),
|
views.get_readable_field_data_type(builtin_fields.BooleanField()),
|
||||||
_(u'Boolean (Either True or False)')
|
_('Boolean (Either True or False)')
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_custom_fields(self):
|
def test_custom_fields(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
views.get_readable_field_data_type(fields.CustomField()),
|
views.get_readable_field_data_type(fields.CustomField()),
|
||||||
_(u'A custom field type')
|
_('A custom field type')
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
views.get_readable_field_data_type(fields.DescriptionLackingField()),
|
views.get_readable_field_data_type(fields.DescriptionLackingField()),
|
||||||
_(u'Field of type: %(field_type)s') % {
|
_('Field of type: %(field_type)s') % {
|
||||||
'field_type': 'DescriptionLackingField'
|
'field_type': 'DescriptionLackingField'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,7 +6,7 @@ from email.errors import HeaderParseError
|
||||||
|
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_bytes
|
||||||
try:
|
try:
|
||||||
import docutils.core
|
import docutils.core
|
||||||
import docutils.nodes
|
import docutils.nodes
|
||||||
|
@ -66,7 +66,7 @@ def parse_rst(text, default_reference_context, thing_being_parsed=None):
|
||||||
"link_base" : reverse('django-admindocs-docroot').rstrip('/')
|
"link_base" : reverse('django-admindocs-docroot').rstrip('/')
|
||||||
}
|
}
|
||||||
if thing_being_parsed:
|
if thing_being_parsed:
|
||||||
thing_being_parsed = smart_str("<%s>" % thing_being_parsed)
|
thing_being_parsed = smart_bytes("<%s>" % thing_being_parsed)
|
||||||
parts = docutils.core.publish_parts(text, source_path=thing_being_parsed,
|
parts = docutils.core.publish_parts(text, source_path=thing_being_parsed,
|
||||||
destination_path=None, writer_name='html',
|
destination_path=None, writer_name='html',
|
||||||
settings_overrides=overrides)
|
settings_overrides=overrides)
|
||||||
|
|
|
@ -14,6 +14,7 @@ from django.core import urlresolvers
|
||||||
from django.contrib.admindocs import utils
|
from django.contrib.admindocs import utils
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
|
from django.utils import six
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ def bookmarklets(request):
|
||||||
admin_root = urlresolvers.reverse('admin:index')
|
admin_root = urlresolvers.reverse('admin:index')
|
||||||
return render_to_response('admin_doc/bookmarklets.html', {
|
return render_to_response('admin_doc/bookmarklets.html', {
|
||||||
'root_path': admin_root,
|
'root_path': admin_root,
|
||||||
'admin_url': mark_safe("%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root)),
|
'admin_url': "%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root),
|
||||||
}, context_instance=RequestContext(request))
|
}, context_instance=RequestContext(request))
|
||||||
|
|
||||||
@staff_member_required
|
@staff_member_required
|
||||||
|
@ -48,7 +49,7 @@ def template_tag_index(request):
|
||||||
load_all_installed_template_libraries()
|
load_all_installed_template_libraries()
|
||||||
|
|
||||||
tags = []
|
tags = []
|
||||||
app_libs = template.libraries.items()
|
app_libs = list(six.iteritems(template.libraries))
|
||||||
builtin_libs = [(None, lib) for lib in template.builtins]
|
builtin_libs = [(None, lib) for lib in template.builtins]
|
||||||
for module_name, library in builtin_libs + app_libs:
|
for module_name, library in builtin_libs + app_libs:
|
||||||
for tag_name, tag_func in library.tags.items():
|
for tag_name, tag_func in library.tags.items():
|
||||||
|
@ -83,7 +84,7 @@ def template_filter_index(request):
|
||||||
load_all_installed_template_libraries()
|
load_all_installed_template_libraries()
|
||||||
|
|
||||||
filters = []
|
filters = []
|
||||||
app_libs = template.libraries.items()
|
app_libs = list(six.iteritems(template.libraries))
|
||||||
builtin_libs = [(None, lib) for lib in template.builtins]
|
builtin_libs = [(None, lib) for lib in template.builtins]
|
||||||
for module_name, library in builtin_libs + app_libs:
|
for module_name, library in builtin_libs + app_libs:
|
||||||
for filter_name, filter_func in library.filters.items():
|
for filter_name, filter_func in library.filters.items():
|
||||||
|
|
|
@ -12,6 +12,7 @@ from django.template.response import TemplateResponse
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils import six
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
from django.views.decorators.csrf import csrf_protect
|
from django.views.decorators.csrf import csrf_protect
|
||||||
from django.views.decorators.debug import sensitive_post_parameters
|
from django.views.decorators.debug import sensitive_post_parameters
|
||||||
|
@ -128,13 +129,13 @@ class UserAdmin(admin.ModelAdmin):
|
||||||
else:
|
else:
|
||||||
form = self.change_password_form(user)
|
form = self.change_password_form(user)
|
||||||
|
|
||||||
fieldsets = [(None, {'fields': form.base_fields.keys()})]
|
fieldsets = [(None, {'fields': list(form.base_fields)})]
|
||||||
adminForm = admin.helpers.AdminForm(form, fieldsets, {})
|
adminForm = admin.helpers.AdminForm(form, fieldsets, {})
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'title': _('Change password: %s') % escape(user.username),
|
'title': _('Change password: %s') % escape(user.username),
|
||||||
'adminForm': adminForm,
|
'adminForm': adminForm,
|
||||||
'form_url': mark_safe(form_url),
|
'form_url': form_url,
|
||||||
'form': form,
|
'form': form,
|
||||||
'is_popup': '_popup' in request.REQUEST,
|
'is_popup': '_popup' in request.REQUEST,
|
||||||
'add': True,
|
'add': True,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ class ModelBackend(object):
|
||||||
if user_obj.is_anonymous() or obj is not None:
|
if user_obj.is_anonymous() or obj is not None:
|
||||||
return set()
|
return set()
|
||||||
if not hasattr(user_obj, '_perm_cache'):
|
if not hasattr(user_obj, '_perm_cache'):
|
||||||
user_obj._perm_cache = set([u"%s.%s" % (p.content_type.app_label, p.codename) for p in user_obj.user_permissions.select_related()])
|
user_obj._perm_cache = set(["%s.%s" % (p.content_type.app_label, p.codename) for p in user_obj.user_permissions.select_related()])
|
||||||
user_obj._perm_cache.update(self.get_group_permissions(user_obj))
|
user_obj._perm_cache.update(self.get_group_permissions(user_obj))
|
||||||
return user_obj._perm_cache
|
return user_obj._perm_cache
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,9 @@ class PermLookupDict(object):
|
||||||
def __getitem__(self, perm_name):
|
def __getitem__(self, perm_name):
|
||||||
return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
|
return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
|
||||||
|
|
||||||
def __nonzero__(self):
|
def __bool__(self):
|
||||||
return self.user.has_module_perms(self.module_name)
|
return self.user.has_module_perms(self.module_name)
|
||||||
|
__nonzero__ = __bool__ # Python 2
|
||||||
|
|
||||||
|
|
||||||
class PermWrapper(object):
|
class PermWrapper(object):
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import urlparse
|
try:
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
except ImportError: # Python 2
|
||||||
|
from urlparse import urlparse
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.utils.decorators import available_attrs
|
from django.utils.decorators import available_attrs
|
||||||
|
from django.utils.encoding import force_str
|
||||||
|
|
||||||
|
|
||||||
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
|
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
|
||||||
|
@ -19,11 +23,12 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
|
||||||
if test_func(request.user):
|
if test_func(request.user):
|
||||||
return view_func(request, *args, **kwargs)
|
return view_func(request, *args, **kwargs)
|
||||||
path = request.build_absolute_uri()
|
path = request.build_absolute_uri()
|
||||||
|
# urlparse chokes on lazy objects in Python 3
|
||||||
|
login_url_as_str = force_str(login_url or settings.LOGIN_URL)
|
||||||
# If the login url is the same scheme and net location then just
|
# If the login url is the same scheme and net location then just
|
||||||
# use the path as the "next" url.
|
# use the path as the "next" url.
|
||||||
login_scheme, login_netloc = urlparse.urlparse(login_url or
|
login_scheme, login_netloc = urlparse(login_url_as_str)[:2]
|
||||||
settings.LOGIN_URL)[:2]
|
current_scheme, current_netloc = urlparse(path)[:2]
|
||||||
current_scheme, current_netloc = urlparse.urlparse(path)[:2]
|
|
||||||
if ((not login_scheme or login_scheme == current_scheme) and
|
if ((not login_scheme or login_scheme == current_scheme) and
|
||||||
(not login_netloc or login_netloc == current_netloc)):
|
(not login_netloc or login_netloc == current_netloc)):
|
||||||
path = request.get_full_path()
|
path = request.get_full_path()
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms.util import flatatt
|
from django.forms.util import flatatt
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.datastructures import SortedDict
|
||||||
|
from django.utils.html import format_html, format_html_join
|
||||||
from django.utils.http import int_to_base36
|
from django.utils.http import int_to_base36
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.hashers import UNUSABLE_PASSWORD, is_password_usable, get_hasher
|
from django.contrib.auth.hashers import UNUSABLE_PASSWORD, is_password_usable, identify_hasher
|
||||||
from django.contrib.auth.tokens import default_token_generator
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
from django.contrib.sites.models import get_current_site
|
from django.contrib.sites.models import get_current_site
|
||||||
|
|
||||||
|
|
||||||
UNMASKED_DIGITS_TO_SHOW = 6
|
UNMASKED_DIGITS_TO_SHOW = 6
|
||||||
|
|
||||||
mask_password = lambda p: "%s%s" % (p[:UNMASKED_DIGITS_TO_SHOW], "*" * max(len(p) - UNMASKED_DIGITS_TO_SHOW, 0))
|
mask_password = lambda p: "%s%s" % (p[:UNMASKED_DIGITS_TO_SHOW], "*" * max(len(p) - UNMASKED_DIGITS_TO_SHOW, 0))
|
||||||
|
@ -26,23 +30,18 @@ class ReadOnlyPasswordHashWidget(forms.Widget):
|
||||||
|
|
||||||
final_attrs = self.build_attrs(attrs)
|
final_attrs = self.build_attrs(attrs)
|
||||||
|
|
||||||
encoded = smart_str(encoded)
|
|
||||||
|
|
||||||
if len(encoded) == 32 and '$' not in encoded:
|
|
||||||
algorithm = 'unsalted_md5'
|
|
||||||
else:
|
|
||||||
algorithm = encoded.split('$', 1)[0]
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
hasher = get_hasher(algorithm)
|
hasher = identify_hasher(encoded)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
summary = "<strong>Invalid password format or unknown hashing algorithm.</strong>"
|
summary = mark_safe("<strong>Invalid password format or unknown hashing algorithm.</strong>")
|
||||||
else:
|
else:
|
||||||
summary = ""
|
summary = format_html_join('',
|
||||||
for key, value in hasher.safe_summary(encoded).iteritems():
|
"<strong>{0}</strong>: {1} ",
|
||||||
summary += "<strong>%(key)s</strong>: %(value)s " % {"key": ugettext(key), "value": value}
|
((ugettext(key), value)
|
||||||
|
for key, value in hasher.safe_summary(encoded).items())
|
||||||
|
)
|
||||||
|
|
||||||
return mark_safe("<div%(attrs)s>%(summary)s</div>" % {"attrs": flatatt(final_attrs), "summary": summary})
|
return format_html("<div{0}>{1}</div>", flatatt(final_attrs), summary)
|
||||||
|
|
||||||
|
|
||||||
class ReadOnlyPasswordHashField(forms.Field):
|
class ReadOnlyPasswordHashField(forms.Field):
|
||||||
|
@ -90,9 +89,9 @@ class UserCreationForm(forms.ModelForm):
|
||||||
raise forms.ValidationError(self.error_messages['duplicate_username'])
|
raise forms.ValidationError(self.error_messages['duplicate_username'])
|
||||||
|
|
||||||
def clean_password2(self):
|
def clean_password2(self):
|
||||||
password1 = self.cleaned_data.get("password1", "")
|
password1 = self.cleaned_data.get("password1")
|
||||||
password2 = self.cleaned_data["password2"]
|
password2 = self.cleaned_data.get("password2")
|
||||||
if password1 != password2:
|
if password1 and password2 and password1 != password2:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
self.error_messages['password_mismatch'])
|
self.error_messages['password_mismatch'])
|
||||||
return password2
|
return password2
|
||||||
|
@ -296,8 +295,11 @@ class PasswordChangeForm(SetPasswordForm):
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
self.error_messages['password_incorrect'])
|
self.error_messages['password_incorrect'])
|
||||||
return old_password
|
return old_password
|
||||||
PasswordChangeForm.base_fields.keyOrder = ['old_password', 'new_password1',
|
|
||||||
'new_password2']
|
PasswordChangeForm.base_fields = SortedDict([
|
||||||
|
(k, PasswordChangeForm.base_fields[k])
|
||||||
|
for k in ['old_password', 'new_password1', 'new_password2']
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class AdminPasswordChangeForm(forms.Form):
|
class AdminPasswordChangeForm(forms.Form):
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
@ -5,7 +8,7 @@ from django.conf import settings
|
||||||
from django.test.signals import setting_changed
|
from django.test.signals import setting_changed
|
||||||
from django.utils import importlib
|
from django.utils import importlib
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_bytes
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils.crypto import (
|
from django.utils.crypto import (
|
||||||
pbkdf2, constant_time_compare, get_random_string)
|
pbkdf2, constant_time_compare, get_random_string)
|
||||||
|
@ -40,20 +43,12 @@ def check_password(password, encoded, setter=None, preferred='default'):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
preferred = get_hasher(preferred)
|
preferred = get_hasher(preferred)
|
||||||
raw_password = password
|
hasher = identify_hasher(encoded)
|
||||||
password = smart_str(password)
|
|
||||||
encoded = smart_str(encoded)
|
|
||||||
|
|
||||||
if len(encoded) == 32 and '$' not in encoded:
|
|
||||||
hasher = get_hasher('unsalted_md5')
|
|
||||||
else:
|
|
||||||
algorithm = encoded.split('$', 1)[0]
|
|
||||||
hasher = get_hasher(algorithm)
|
|
||||||
|
|
||||||
must_update = hasher.algorithm != preferred.algorithm
|
must_update = hasher.algorithm != preferred.algorithm
|
||||||
is_correct = hasher.verify(password, encoded)
|
is_correct = hasher.verify(password, encoded)
|
||||||
if setter and is_correct and must_update:
|
if setter and is_correct and must_update:
|
||||||
setter(raw_password)
|
setter(password)
|
||||||
return is_correct
|
return is_correct
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,11 +64,9 @@ def make_password(password, salt=None, hasher='default'):
|
||||||
return UNUSABLE_PASSWORD
|
return UNUSABLE_PASSWORD
|
||||||
|
|
||||||
hasher = get_hasher(hasher)
|
hasher = get_hasher(hasher)
|
||||||
password = smart_str(password)
|
|
||||||
|
|
||||||
if not salt:
|
if not salt:
|
||||||
salt = hasher.salt()
|
salt = hasher.salt()
|
||||||
salt = smart_str(salt)
|
|
||||||
|
|
||||||
return hasher.encode(password, salt)
|
return hasher.encode(password, salt)
|
||||||
|
|
||||||
|
@ -125,6 +118,21 @@ def get_hasher(algorithm='default'):
|
||||||
return HASHERS[algorithm]
|
return HASHERS[algorithm]
|
||||||
|
|
||||||
|
|
||||||
|
def identify_hasher(encoded):
|
||||||
|
"""
|
||||||
|
Returns an instance of a loaded password hasher.
|
||||||
|
|
||||||
|
Identifies hasher algorithm by examining encoded hash, and calls
|
||||||
|
get_hasher() to return hasher. Raises ValueError if
|
||||||
|
algorithm cannot be identified, or if hasher is not loaded.
|
||||||
|
"""
|
||||||
|
if len(encoded) == 32 and '$' not in encoded:
|
||||||
|
algorithm = 'unsalted_md5'
|
||||||
|
else:
|
||||||
|
algorithm = encoded.split('$', 1)[0]
|
||||||
|
return get_hasher(algorithm)
|
||||||
|
|
||||||
|
|
||||||
def mask_hash(hash, show=6, char="*"):
|
def mask_hash(hash, show=6, char="*"):
|
||||||
"""
|
"""
|
||||||
Returns the given hash, with only the first ``show`` number shown. The
|
Returns the given hash, with only the first ``show`` number shown. The
|
||||||
|
@ -211,7 +219,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
|
||||||
if not iterations:
|
if not iterations:
|
||||||
iterations = self.iterations
|
iterations = self.iterations
|
||||||
hash = pbkdf2(password, salt, iterations, digest=self.digest)
|
hash = pbkdf2(password, salt, iterations, digest=self.digest)
|
||||||
hash = hash.encode('base64').strip()
|
hash = base64.b64encode(hash).decode('ascii').strip()
|
||||||
return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
|
return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
|
||||||
|
|
||||||
def verify(self, password, encoded):
|
def verify(self, password, encoded):
|
||||||
|
@ -291,7 +299,7 @@ class SHA1PasswordHasher(BasePasswordHasher):
|
||||||
def encode(self, password, salt):
|
def encode(self, password, salt):
|
||||||
assert password
|
assert password
|
||||||
assert salt and '$' not in salt
|
assert salt and '$' not in salt
|
||||||
hash = hashlib.sha1(salt + password).hexdigest()
|
hash = hashlib.sha1(smart_bytes(salt + password)).hexdigest()
|
||||||
return "%s$%s$%s" % (self.algorithm, salt, hash)
|
return "%s$%s$%s" % (self.algorithm, salt, hash)
|
||||||
|
|
||||||
def verify(self, password, encoded):
|
def verify(self, password, encoded):
|
||||||
|
@ -319,7 +327,7 @@ class MD5PasswordHasher(BasePasswordHasher):
|
||||||
def encode(self, password, salt):
|
def encode(self, password, salt):
|
||||||
assert password
|
assert password
|
||||||
assert salt and '$' not in salt
|
assert salt and '$' not in salt
|
||||||
hash = hashlib.md5(salt + password).hexdigest()
|
hash = hashlib.md5(smart_bytes(salt + password)).hexdigest()
|
||||||
return "%s$%s$%s" % (self.algorithm, salt, hash)
|
return "%s$%s$%s" % (self.algorithm, salt, hash)
|
||||||
|
|
||||||
def verify(self, password, encoded):
|
def verify(self, password, encoded):
|
||||||
|
@ -353,7 +361,7 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def encode(self, password, salt):
|
def encode(self, password, salt):
|
||||||
return hashlib.md5(password).hexdigest()
|
return hashlib.md5(smart_bytes(password)).hexdigest()
|
||||||
|
|
||||||
def verify(self, password, encoded):
|
def verify(self, password, encoded):
|
||||||
encoded_2 = self.encode(password, '')
|
encoded_2 = self.encode(password, '')
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""
|
"""
|
||||||
Creates permissions for all installed apps that need permissions.
|
Creates permissions for all installed apps that need permissions.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import getpass
|
import getpass
|
||||||
import locale
|
import locale
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
@ -8,17 +10,18 @@ import unicodedata
|
||||||
from django.contrib.auth import models as auth_app, get_user_model
|
from django.contrib.auth import models as auth_app, get_user_model
|
||||||
from django.core import exceptions
|
from django.core import exceptions
|
||||||
from django.db.models import get_models, signals
|
from django.db.models import get_models, signals
|
||||||
|
from django.utils.six.moves import input
|
||||||
|
|
||||||
|
|
||||||
def _get_permission_codename(action, opts):
|
def _get_permission_codename(action, opts):
|
||||||
return u'%s_%s' % (action, opts.object_name.lower())
|
return '%s_%s' % (action, opts.object_name.lower())
|
||||||
|
|
||||||
|
|
||||||
def _get_all_permissions(opts):
|
def _get_all_permissions(opts):
|
||||||
"Returns (codename, name) for all permissions in the given opts."
|
"Returns (codename, name) for all permissions in the given opts."
|
||||||
perms = []
|
perms = []
|
||||||
for action in ('add', 'change', 'delete'):
|
for action in ('add', 'change', 'delete'):
|
||||||
perms.append((_get_permission_codename(action, opts), u'Can %s %s' % (action, opts.verbose_name_raw)))
|
perms.append((_get_permission_codename(action, opts), 'Can %s %s' % (action, opts.verbose_name_raw)))
|
||||||
return perms + list(opts.permissions)
|
return perms + list(opts.permissions)
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,10 +70,10 @@ def create_superuser(app, created_models, verbosity, db, **kwargs):
|
||||||
msg = ("\nYou just installed Django's auth system, which means you "
|
msg = ("\nYou just installed Django's auth system, which means you "
|
||||||
"don't have any superusers defined.\nWould you like to create one "
|
"don't have any superusers defined.\nWould you like to create one "
|
||||||
"now? (yes/no): ")
|
"now? (yes/no): ")
|
||||||
confirm = raw_input(msg)
|
confirm = input(msg)
|
||||||
while 1:
|
while 1:
|
||||||
if confirm not in ('yes', 'no'):
|
if confirm not in ('yes', 'no'):
|
||||||
confirm = raw_input('Please enter either "yes" or "no": ')
|
confirm = input('Please enter either "yes" or "no": ')
|
||||||
continue
|
continue
|
||||||
if confirm == 'yes':
|
if confirm == 'yes':
|
||||||
call_command("createsuperuser", interactive=True, database=db)
|
call_command("createsuperuser", interactive=True, database=db)
|
||||||
|
@ -84,14 +87,17 @@ def get_system_username():
|
||||||
:returns: The username as a unicode string, or an empty string if the
|
:returns: The username as a unicode string, or an empty string if the
|
||||||
username could not be determined.
|
username could not be determined.
|
||||||
"""
|
"""
|
||||||
try:
|
default_locale = locale.getdefaultlocale()[1]
|
||||||
return getpass.getuser().decode(locale.getdefaultlocale()[1])
|
if default_locale:
|
||||||
except (ImportError, KeyError, UnicodeDecodeError):
|
try:
|
||||||
# KeyError will be raised by os.getpwuid() (called by getuser())
|
return getpass.getuser().decode(default_locale)
|
||||||
# if there is no corresponding entry in the /etc/passwd file
|
except (ImportError, KeyError, UnicodeDecodeError):
|
||||||
# (a very restricted chroot environment, for example).
|
# KeyError will be raised by os.getpwuid() (called by getuser())
|
||||||
# UnicodeDecodeError - preventive treatment for non-latin Windows.
|
# if there is no corresponding entry in the /etc/passwd file
|
||||||
return u''
|
# (a very restricted chroot environment, for example).
|
||||||
|
# UnicodeDecodeError - preventive treatment for non-latin Windows.
|
||||||
|
pass
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
def get_default_username(check_db=True):
|
def get_default_username(check_db=True):
|
||||||
|
@ -111,7 +117,7 @@ def get_default_username(check_db=True):
|
||||||
default_username = get_system_username()
|
default_username = get_system_username()
|
||||||
try:
|
try:
|
||||||
default_username = unicodedata.normalize('NFKD', default_username)\
|
default_username = unicodedata.normalize('NFKD', default_username)\
|
||||||
.encode('ascii', 'ignore').replace(' ', '').lower()
|
.encode('ascii', 'ignore').decode('ascii').replace(' ', '').lower()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ from django.contrib.auth.management import get_default_username
|
||||||
from django.core import exceptions
|
from django.core import exceptions
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.db import DEFAULT_DB_ALIAS
|
from django.db import DEFAULT_DB_ALIAS
|
||||||
|
from django.utils.six.moves import input
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,7 +79,7 @@ class Command(BaseCommand):
|
||||||
input_msg = capfirst(username_field.verbose_name)
|
input_msg = capfirst(username_field.verbose_name)
|
||||||
if default_username:
|
if default_username:
|
||||||
input_msg += ' (leave blank to use %r)' % default_username
|
input_msg += ' (leave blank to use %r)' % default_username
|
||||||
raw_value = raw_input(input_msg + ': ')
|
raw_value = input(input_msg + ': ')
|
||||||
if default_username and raw_value == '':
|
if default_username and raw_value == '':
|
||||||
username = default_username
|
username = default_username
|
||||||
try:
|
try:
|
||||||
|
@ -99,9 +100,9 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
for field_name in other_fields:
|
for field_name in other_fields:
|
||||||
field = UserModel._meta.get_field(field_name)
|
field = UserModel._meta.get_field(field_name)
|
||||||
other_data[field_name] = None
|
other_data[field_name] = options.get(field_name)
|
||||||
while other_data[field_name] is None:
|
while other_data[field_name] is None:
|
||||||
raw_value = raw_input(capfirst(field.verbose_name + ': '))
|
raw_value = input(capfirst(field.verbose_name + ': '))
|
||||||
try:
|
try:
|
||||||
other_data[field_name] = field.clean(raw_value, None)
|
other_data[field_name] = field.clean(raw_value, None)
|
||||||
except exceptions.ValidationError, e:
|
except exceptions.ValidationError, e:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
import re
|
import re
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
|
@ -7,7 +8,8 @@ from django.core import validators
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.manager import EmptyManager
|
from django.db.models.manager import EmptyManager
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.http import urlquote
|
||||||
|
from django.utils import six
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
@ -17,6 +19,7 @@ from django.contrib.auth.hashers import (
|
||||||
check_password, make_password, is_password_usable, UNUSABLE_PASSWORD)
|
check_password, make_password, is_password_usable, UNUSABLE_PASSWORD)
|
||||||
from django.contrib.auth.signals import user_logged_in
|
from django.contrib.auth.signals import user_logged_in
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
|
|
||||||
def update_last_login(sender, user, **kwargs):
|
def update_last_login(sender, user, **kwargs):
|
||||||
|
@ -42,6 +45,7 @@ class PermissionManager(models.Manager):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Permission(models.Model):
|
class Permission(models.Model):
|
||||||
"""
|
"""
|
||||||
The permissions system provides a way to assign permissions to specific
|
The permissions system provides a way to assign permissions to specific
|
||||||
|
@ -77,11 +81,11 @@ class Permission(models.Model):
|
||||||
ordering = ('content_type__app_label', 'content_type__model',
|
ordering = ('content_type__app_label', 'content_type__model',
|
||||||
'codename')
|
'codename')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"%s | %s | %s" % (
|
return "%s | %s | %s" % (
|
||||||
unicode(self.content_type.app_label),
|
six.text_type(self.content_type.app_label),
|
||||||
unicode(self.content_type),
|
six.text_type(self.content_type),
|
||||||
unicode(self.name))
|
six.text_type(self.name))
|
||||||
|
|
||||||
def natural_key(self):
|
def natural_key(self):
|
||||||
return (self.codename,) + self.content_type.natural_key()
|
return (self.codename,) + self.content_type.natural_key()
|
||||||
|
@ -96,6 +100,7 @@ class GroupManager(models.Manager):
|
||||||
return self.get(name=name)
|
return self.get(name=name)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Group(models.Model):
|
class Group(models.Model):
|
||||||
"""
|
"""
|
||||||
Groups are a generic way of categorizing users to apply permissions, or
|
Groups are a generic way of categorizing users to apply permissions, or
|
||||||
|
@ -123,7 +128,7 @@ class Group(models.Model):
|
||||||
verbose_name = _('group')
|
verbose_name = _('group')
|
||||||
verbose_name_plural = _('groups')
|
verbose_name_plural = _('groups')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def natural_key(self):
|
def natural_key(self):
|
||||||
|
@ -253,7 +258,7 @@ class AbstractBaseUser(models.Model):
|
||||||
"""
|
"""
|
||||||
def setter(raw_password):
|
def setter(raw_password):
|
||||||
self.set_password(raw_password)
|
self.set_password(raw_password)
|
||||||
self.save()
|
self.save(update_fields=["password"])
|
||||||
return check_password(raw_password, self.password, setter)
|
return check_password(raw_password, self.password, setter)
|
||||||
|
|
||||||
def set_unusable_password(self):
|
def set_unusable_password(self):
|
||||||
|
@ -270,6 +275,7 @@ class AbstractBaseUser(models.Model):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class User(AbstractBaseUser):
|
class User(AbstractBaseUser):
|
||||||
"""
|
"""
|
||||||
Users within the Django authentication system are represented by this
|
Users within the Django authentication system are represented by this
|
||||||
|
@ -311,20 +317,20 @@ class User(AbstractBaseUser):
|
||||||
verbose_name_plural = _('users')
|
verbose_name_plural = _('users')
|
||||||
swappable = 'AUTH_USER_MODEL'
|
swappable = 'AUTH_USER_MODEL'
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.username
|
return self.username
|
||||||
|
|
||||||
def natural_key(self):
|
def natural_key(self):
|
||||||
return (self.username,)
|
return (self.username,)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return "/users/%s/" % urllib.quote(smart_str(self.username))
|
return "/users/%s/" % urlquote(self.username)
|
||||||
|
|
||||||
def get_full_name(self):
|
def get_full_name(self):
|
||||||
"""
|
"""
|
||||||
Returns the first_name plus the last_name, with a space in between.
|
Returns the first_name plus the last_name, with a space in between.
|
||||||
"""
|
"""
|
||||||
full_name = u'%s %s' % (self.first_name, self.last_name)
|
full_name = '%s %s' % (self.first_name, self.last_name)
|
||||||
return full_name.strip()
|
return full_name.strip()
|
||||||
|
|
||||||
def get_short_name(self):
|
def get_short_name(self):
|
||||||
|
@ -425,6 +431,7 @@ class User(AbstractBaseUser):
|
||||||
return self._profile_cache
|
return self._profile_cache
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class AnonymousUser(object):
|
class AnonymousUser(object):
|
||||||
id = None
|
id = None
|
||||||
pk = None
|
pk = None
|
||||||
|
@ -438,11 +445,8 @@ class AnonymousUser(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return 'AnonymousUser'
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return unicode(self).encode('utf-8')
|
return 'AnonymousUser'
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return isinstance(other, self.__class__)
|
return isinstance(other, self.__class__)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User, Group, Permission, AnonymousUser
|
from django.contrib.auth.models import User, Group, Permission, AnonymousUser
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
@ -51,7 +53,7 @@ class BackendTest(TestCase):
|
||||||
|
|
||||||
# reloading user to purge the _perm_cache
|
# reloading user to purge the _perm_cache
|
||||||
user = User.objects.get(username='test')
|
user = User.objects.get(username='test')
|
||||||
self.assertEqual(user.get_all_permissions() == set([u'auth.test']), True)
|
self.assertEqual(user.get_all_permissions() == set(['auth.test']), True)
|
||||||
self.assertEqual(user.get_group_permissions(), set([]))
|
self.assertEqual(user.get_group_permissions(), set([]))
|
||||||
self.assertEqual(user.has_module_perms('Group'), False)
|
self.assertEqual(user.has_module_perms('Group'), False)
|
||||||
self.assertEqual(user.has_module_perms('auth'), True)
|
self.assertEqual(user.has_module_perms('auth'), True)
|
||||||
|
@ -62,7 +64,7 @@ class BackendTest(TestCase):
|
||||||
user.user_permissions.add(perm)
|
user.user_permissions.add(perm)
|
||||||
user.save()
|
user.save()
|
||||||
user = User.objects.get(username='test')
|
user = User.objects.get(username='test')
|
||||||
self.assertEqual(user.get_all_permissions(), set([u'auth.test2', u'auth.test', u'auth.test3']))
|
self.assertEqual(user.get_all_permissions(), set(['auth.test2', 'auth.test', 'auth.test3']))
|
||||||
self.assertEqual(user.has_perm('test'), False)
|
self.assertEqual(user.has_perm('test'), False)
|
||||||
self.assertEqual(user.has_perm('auth.test'), True)
|
self.assertEqual(user.has_perm('auth.test'), True)
|
||||||
self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), True)
|
self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), True)
|
||||||
|
@ -72,9 +74,9 @@ class BackendTest(TestCase):
|
||||||
group.save()
|
group.save()
|
||||||
user.groups.add(group)
|
user.groups.add(group)
|
||||||
user = User.objects.get(username='test')
|
user = User.objects.get(username='test')
|
||||||
exp = set([u'auth.test2', u'auth.test', u'auth.test3', u'auth.test_group'])
|
exp = set(['auth.test2', 'auth.test', 'auth.test3', 'auth.test_group'])
|
||||||
self.assertEqual(user.get_all_permissions(), exp)
|
self.assertEqual(user.get_all_permissions(), exp)
|
||||||
self.assertEqual(user.get_group_permissions(), set([u'auth.test_group']))
|
self.assertEqual(user.get_group_permissions(), set(['auth.test_group']))
|
||||||
self.assertEqual(user.has_perms(['auth.test3', 'auth.test_group']), True)
|
self.assertEqual(user.has_perms(['auth.test3', 'auth.test_group']), True)
|
||||||
|
|
||||||
user = AnonymousUser()
|
user = AnonymousUser()
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
from django.test import TestCase
|
import locale
|
||||||
|
|
||||||
|
from django.contrib.auth.management.commands import createsuperuser
|
||||||
from django.contrib.auth.models import User, AnonymousUser
|
from django.contrib.auth.models import User, AnonymousUser
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from StringIO import StringIO
|
from django.test import TestCase
|
||||||
|
from django.utils.six import StringIO
|
||||||
|
|
||||||
|
|
||||||
class BasicTestCase(TestCase):
|
class BasicTestCase(TestCase):
|
||||||
|
@ -60,3 +63,87 @@ class BasicTestCase(TestCase):
|
||||||
self.assertTrue(super.is_superuser)
|
self.assertTrue(super.is_superuser)
|
||||||
self.assertTrue(super.is_active)
|
self.assertTrue(super.is_active)
|
||||||
self.assertTrue(super.is_staff)
|
self.assertTrue(super.is_staff)
|
||||||
|
|
||||||
|
def test_createsuperuser_management_command(self):
|
||||||
|
"Check the operation of the createsuperuser management command"
|
||||||
|
# We can use the management command to create a superuser
|
||||||
|
new_io = StringIO()
|
||||||
|
call_command("createsuperuser",
|
||||||
|
interactive=False,
|
||||||
|
username="joe",
|
||||||
|
email="joe@somewhere.org",
|
||||||
|
stdout=new_io
|
||||||
|
)
|
||||||
|
command_output = new_io.getvalue().strip()
|
||||||
|
self.assertEqual(command_output, 'Superuser created successfully.')
|
||||||
|
u = User.objects.get(username="joe")
|
||||||
|
self.assertEqual(u.email, 'joe@somewhere.org')
|
||||||
|
|
||||||
|
# created password should be unusable
|
||||||
|
self.assertFalse(u.has_usable_password())
|
||||||
|
|
||||||
|
# We can supress output on the management command
|
||||||
|
new_io = StringIO()
|
||||||
|
call_command("createsuperuser",
|
||||||
|
interactive=False,
|
||||||
|
username="joe2",
|
||||||
|
email="joe2@somewhere.org",
|
||||||
|
verbosity=0,
|
||||||
|
stdout=new_io
|
||||||
|
)
|
||||||
|
command_output = new_io.getvalue().strip()
|
||||||
|
self.assertEqual(command_output, '')
|
||||||
|
u = User.objects.get(username="joe2")
|
||||||
|
self.assertEqual(u.email, 'joe2@somewhere.org')
|
||||||
|
self.assertFalse(u.has_usable_password())
|
||||||
|
|
||||||
|
new_io = StringIO()
|
||||||
|
call_command("createsuperuser",
|
||||||
|
interactive=False,
|
||||||
|
username="joe+admin@somewhere.org",
|
||||||
|
email="joe@somewhere.org",
|
||||||
|
stdout=new_io
|
||||||
|
)
|
||||||
|
u = User.objects.get(username="joe+admin@somewhere.org")
|
||||||
|
self.assertEqual(u.email, 'joe@somewhere.org')
|
||||||
|
self.assertFalse(u.has_usable_password())
|
||||||
|
|
||||||
|
def test_createsuperuser_nolocale(self):
|
||||||
|
"""
|
||||||
|
Check that createsuperuser does not break when no locale is set. See
|
||||||
|
ticket #16017.
|
||||||
|
"""
|
||||||
|
|
||||||
|
old_getdefaultlocale = locale.getdefaultlocale
|
||||||
|
old_getpass = createsuperuser.getpass
|
||||||
|
try:
|
||||||
|
# Temporarily remove locale information
|
||||||
|
locale.getdefaultlocale = lambda: (None, None)
|
||||||
|
|
||||||
|
# Temporarily replace getpass to allow interactive code to be used
|
||||||
|
# non-interactively
|
||||||
|
class mock_getpass:
|
||||||
|
pass
|
||||||
|
mock_getpass.getpass = staticmethod(lambda p=None: "nopasswd")
|
||||||
|
createsuperuser.getpass = mock_getpass
|
||||||
|
|
||||||
|
# Call the command in this new environment
|
||||||
|
new_io = StringIO()
|
||||||
|
call_command("createsuperuser",
|
||||||
|
interactive=True,
|
||||||
|
username="nolocale@somewhere.org",
|
||||||
|
email="nolocale@somewhere.org",
|
||||||
|
stdout=new_io
|
||||||
|
)
|
||||||
|
|
||||||
|
except TypeError:
|
||||||
|
self.fail("createsuperuser fails if the OS provides no information about the current locale")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# Re-apply locale and getpass information
|
||||||
|
createsuperuser.getpass = old_getpass
|
||||||
|
locale.getdefaultlocale = old_getdefaultlocale
|
||||||
|
|
||||||
|
# If we were successful, a user should have been created
|
||||||
|
u = User.objects.get(username="nolocale@somewhere.org")
|
||||||
|
self.assertEqual(u.email, 'nolocale@somewhere.org')
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
|
from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
|
||||||
|
@ -6,7 +8,8 @@ from django.core import mail
|
||||||
from django.forms.fields import Field, EmailField
|
from django.forms.fields import Field, EmailField
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils import six
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
@ -25,7 +28,7 @@ class UserCreationFormTest(TestCase):
|
||||||
form = UserCreationForm(data)
|
form = UserCreationForm(data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form["username"].errors,
|
self.assertEqual(form["username"].errors,
|
||||||
[force_unicode(form.error_messages['duplicate_username'])])
|
[force_text(form.error_messages['duplicate_username'])])
|
||||||
|
|
||||||
def test_invalid_data(self):
|
def test_invalid_data(self):
|
||||||
data = {
|
data = {
|
||||||
|
@ -36,7 +39,7 @@ class UserCreationFormTest(TestCase):
|
||||||
form = UserCreationForm(data)
|
form = UserCreationForm(data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form["username"].errors,
|
self.assertEqual(form["username"].errors,
|
||||||
[force_unicode(form.fields['username'].error_messages['invalid'])])
|
[force_text(form.fields['username'].error_messages['invalid'])])
|
||||||
|
|
||||||
def test_password_verification(self):
|
def test_password_verification(self):
|
||||||
# The verification password is incorrect.
|
# The verification password is incorrect.
|
||||||
|
@ -48,13 +51,13 @@ class UserCreationFormTest(TestCase):
|
||||||
form = UserCreationForm(data)
|
form = UserCreationForm(data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form["password2"].errors,
|
self.assertEqual(form["password2"].errors,
|
||||||
[force_unicode(form.error_messages['password_mismatch'])])
|
[force_text(form.error_messages['password_mismatch'])])
|
||||||
|
|
||||||
def test_both_passwords(self):
|
def test_both_passwords(self):
|
||||||
# One (or both) passwords weren't given
|
# One (or both) passwords weren't given
|
||||||
data = {'username': 'jsmith'}
|
data = {'username': 'jsmith'}
|
||||||
form = UserCreationForm(data)
|
form = UserCreationForm(data)
|
||||||
required_error = [force_unicode(Field.default_error_messages['required'])]
|
required_error = [force_text(Field.default_error_messages['required'])]
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form['password1'].errors, required_error)
|
self.assertEqual(form['password1'].errors, required_error)
|
||||||
self.assertEqual(form['password2'].errors, required_error)
|
self.assertEqual(form['password2'].errors, required_error)
|
||||||
|
@ -63,6 +66,7 @@ class UserCreationFormTest(TestCase):
|
||||||
form = UserCreationForm(data)
|
form = UserCreationForm(data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form['password1'].errors, required_error)
|
self.assertEqual(form['password1'].errors, required_error)
|
||||||
|
self.assertEqual(form['password2'].errors, [])
|
||||||
|
|
||||||
def test_success(self):
|
def test_success(self):
|
||||||
# The success case.
|
# The success case.
|
||||||
|
@ -92,7 +96,7 @@ class AuthenticationFormTest(TestCase):
|
||||||
form = AuthenticationForm(None, data)
|
form = AuthenticationForm(None, data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form.non_field_errors(),
|
self.assertEqual(form.non_field_errors(),
|
||||||
[force_unicode(form.error_messages['invalid_login'])])
|
[force_text(form.error_messages['invalid_login'])])
|
||||||
|
|
||||||
def test_inactive_user(self):
|
def test_inactive_user(self):
|
||||||
# The user is inactive.
|
# The user is inactive.
|
||||||
|
@ -103,7 +107,7 @@ class AuthenticationFormTest(TestCase):
|
||||||
form = AuthenticationForm(None, data)
|
form = AuthenticationForm(None, data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form.non_field_errors(),
|
self.assertEqual(form.non_field_errors(),
|
||||||
[force_unicode(form.error_messages['inactive'])])
|
[force_text(form.error_messages['inactive'])])
|
||||||
|
|
||||||
def test_inactive_user_i18n(self):
|
def test_inactive_user_i18n(self):
|
||||||
with self.settings(USE_I18N=True):
|
with self.settings(USE_I18N=True):
|
||||||
|
@ -116,7 +120,7 @@ class AuthenticationFormTest(TestCase):
|
||||||
form = AuthenticationForm(None, data)
|
form = AuthenticationForm(None, data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form.non_field_errors(),
|
self.assertEqual(form.non_field_errors(),
|
||||||
[force_unicode(form.error_messages['inactive'])])
|
[force_text(form.error_messages['inactive'])])
|
||||||
|
|
||||||
def test_success(self):
|
def test_success(self):
|
||||||
# The success case
|
# The success case
|
||||||
|
@ -144,7 +148,7 @@ class SetPasswordFormTest(TestCase):
|
||||||
form = SetPasswordForm(user, data)
|
form = SetPasswordForm(user, data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form["new_password2"].errors,
|
self.assertEqual(form["new_password2"].errors,
|
||||||
[force_unicode(form.error_messages['password_mismatch'])])
|
[force_text(form.error_messages['password_mismatch'])])
|
||||||
|
|
||||||
def test_success(self):
|
def test_success(self):
|
||||||
user = User.objects.get(username='testclient')
|
user = User.objects.get(username='testclient')
|
||||||
|
@ -171,7 +175,7 @@ class PasswordChangeFormTest(TestCase):
|
||||||
form = PasswordChangeForm(user, data)
|
form = PasswordChangeForm(user, data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form["old_password"].errors,
|
self.assertEqual(form["old_password"].errors,
|
||||||
[force_unicode(form.error_messages['password_incorrect'])])
|
[force_text(form.error_messages['password_incorrect'])])
|
||||||
|
|
||||||
def test_password_verification(self):
|
def test_password_verification(self):
|
||||||
# The two new passwords do not match.
|
# The two new passwords do not match.
|
||||||
|
@ -184,7 +188,7 @@ class PasswordChangeFormTest(TestCase):
|
||||||
form = PasswordChangeForm(user, data)
|
form = PasswordChangeForm(user, data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form["new_password2"].errors,
|
self.assertEqual(form["new_password2"].errors,
|
||||||
[force_unicode(form.error_messages['password_mismatch'])])
|
[force_text(form.error_messages['password_mismatch'])])
|
||||||
|
|
||||||
def test_success(self):
|
def test_success(self):
|
||||||
# The success case.
|
# The success case.
|
||||||
|
@ -200,7 +204,7 @@ class PasswordChangeFormTest(TestCase):
|
||||||
def test_field_order(self):
|
def test_field_order(self):
|
||||||
# Regression test - check the order of fields:
|
# Regression test - check the order of fields:
|
||||||
user = User.objects.get(username='testclient')
|
user = User.objects.get(username='testclient')
|
||||||
self.assertEqual(PasswordChangeForm(user, {}).fields.keys(),
|
self.assertEqual(list(PasswordChangeForm(user, {}).fields),
|
||||||
['old_password', 'new_password1', 'new_password2'])
|
['old_password', 'new_password1', 'new_password2'])
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,7 +219,7 @@ class UserChangeFormTest(TestCase):
|
||||||
form = UserChangeForm(data, instance=user)
|
form = UserChangeForm(data, instance=user)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form['username'].errors,
|
self.assertEqual(form['username'].errors,
|
||||||
[force_unicode(form.fields['username'].error_messages['invalid'])])
|
[force_text(form.fields['username'].error_messages['invalid'])])
|
||||||
|
|
||||||
def test_bug_14242(self):
|
def test_bug_14242(self):
|
||||||
# A regression test, introduce by adding an optimization for the
|
# A regression test, introduce by adding an optimization for the
|
||||||
|
@ -270,7 +274,7 @@ class PasswordResetFormTest(TestCase):
|
||||||
form = PasswordResetForm(data)
|
form = PasswordResetForm(data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form['email'].errors,
|
self.assertEqual(form['email'].errors,
|
||||||
[force_unicode(EmailField.default_error_messages['invalid'])])
|
[force_text(EmailField.default_error_messages['invalid'])])
|
||||||
|
|
||||||
def test_nonexistant_email(self):
|
def test_nonexistant_email(self):
|
||||||
# Test nonexistant email address
|
# Test nonexistant email address
|
||||||
|
@ -278,7 +282,7 @@ class PasswordResetFormTest(TestCase):
|
||||||
form = PasswordResetForm(data)
|
form = PasswordResetForm(data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form.errors,
|
self.assertEqual(form.errors,
|
||||||
{'email': [force_unicode(form.error_messages['unknown'])]})
|
{'email': [force_text(form.error_messages['unknown'])]})
|
||||||
|
|
||||||
def test_cleaned_data(self):
|
def test_cleaned_data(self):
|
||||||
# Regression test
|
# Regression test
|
||||||
|
@ -299,7 +303,7 @@ class PasswordResetFormTest(TestCase):
|
||||||
# potential case where contrib.sites is not installed. Refs #16412.
|
# potential case where contrib.sites is not installed. Refs #16412.
|
||||||
form.save(domain_override='example.com')
|
form.save(domain_override='example.com')
|
||||||
self.assertEqual(len(mail.outbox), 1)
|
self.assertEqual(len(mail.outbox), 1)
|
||||||
self.assertEqual(mail.outbox[0].subject, u'Custom password reset on example.com')
|
self.assertEqual(mail.outbox[0].subject, 'Custom password reset on example.com')
|
||||||
|
|
||||||
def test_bug_5605(self):
|
def test_bug_5605(self):
|
||||||
# bug #5605, preserve the case of the user name (before the @ in the
|
# bug #5605, preserve the case of the user name (before the @ in the
|
||||||
|
@ -328,4 +332,4 @@ class PasswordResetFormTest(TestCase):
|
||||||
form = PasswordResetForm(data)
|
form = PasswordResetForm(data)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form["email"].errors,
|
self.assertEqual(form["email"].errors,
|
||||||
[_(u"The user account associated with this e-mail address cannot reset the password.")])
|
[_("The user account associated with this e-mail address cannot reset the password.")])
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.conf.global_settings import PASSWORD_HASHERS as default_hashers
|
from django.conf.global_settings import PASSWORD_HASHERS as default_hashers
|
||||||
from django.contrib.auth.hashers import (is_password_usable,
|
from django.contrib.auth.hashers import (is_password_usable,
|
||||||
check_password, make_password, PBKDF2PasswordHasher, load_hashers,
|
check_password, make_password, PBKDF2PasswordHasher, load_hashers,
|
||||||
PBKDF2SHA1PasswordHasher, get_hasher, UNUSABLE_PASSWORD)
|
PBKDF2SHA1PasswordHasher, get_hasher, identify_hasher, UNUSABLE_PASSWORD)
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
from django.utils.unittest import skipUnless
|
from django.utils.unittest import skipUnless
|
||||||
|
|
||||||
|
@ -26,7 +28,7 @@ class TestUtilsHashPass(unittest.TestCase):
|
||||||
encoded = make_password('letmein')
|
encoded = make_password('letmein')
|
||||||
self.assertTrue(encoded.startswith('pbkdf2_sha256$'))
|
self.assertTrue(encoded.startswith('pbkdf2_sha256$'))
|
||||||
self.assertTrue(is_password_usable(encoded))
|
self.assertTrue(is_password_usable(encoded))
|
||||||
self.assertTrue(check_password(u'letmein', encoded))
|
self.assertTrue(check_password('letmein', encoded))
|
||||||
self.assertFalse(check_password('letmeinz', encoded))
|
self.assertFalse(check_password('letmeinz', encoded))
|
||||||
|
|
||||||
def test_pkbdf2(self):
|
def test_pkbdf2(self):
|
||||||
|
@ -34,47 +36,53 @@ class TestUtilsHashPass(unittest.TestCase):
|
||||||
self.assertEqual(encoded,
|
self.assertEqual(encoded,
|
||||||
'pbkdf2_sha256$10000$seasalt$FQCNpiZpTb0zub+HBsH6TOwyRxJ19FwvjbweatNmK/Y=')
|
'pbkdf2_sha256$10000$seasalt$FQCNpiZpTb0zub+HBsH6TOwyRxJ19FwvjbweatNmK/Y=')
|
||||||
self.assertTrue(is_password_usable(encoded))
|
self.assertTrue(is_password_usable(encoded))
|
||||||
self.assertTrue(check_password(u'letmein', encoded))
|
self.assertTrue(check_password('letmein', encoded))
|
||||||
self.assertFalse(check_password('letmeinz', encoded))
|
self.assertFalse(check_password('letmeinz', encoded))
|
||||||
|
self.assertEqual(identify_hasher(encoded).algorithm, "pbkdf2_sha256")
|
||||||
|
|
||||||
def test_sha1(self):
|
def test_sha1(self):
|
||||||
encoded = make_password('letmein', 'seasalt', 'sha1')
|
encoded = make_password('letmein', 'seasalt', 'sha1')
|
||||||
self.assertEqual(encoded,
|
self.assertEqual(encoded,
|
||||||
'sha1$seasalt$fec3530984afba6bade3347b7140d1a7da7da8c7')
|
'sha1$seasalt$fec3530984afba6bade3347b7140d1a7da7da8c7')
|
||||||
self.assertTrue(is_password_usable(encoded))
|
self.assertTrue(is_password_usable(encoded))
|
||||||
self.assertTrue(check_password(u'letmein', encoded))
|
self.assertTrue(check_password('letmein', encoded))
|
||||||
self.assertFalse(check_password('letmeinz', encoded))
|
self.assertFalse(check_password('letmeinz', encoded))
|
||||||
|
self.assertEqual(identify_hasher(encoded).algorithm, "sha1")
|
||||||
|
|
||||||
def test_md5(self):
|
def test_md5(self):
|
||||||
encoded = make_password('letmein', 'seasalt', 'md5')
|
encoded = make_password('letmein', 'seasalt', 'md5')
|
||||||
self.assertEqual(encoded,
|
self.assertEqual(encoded,
|
||||||
'md5$seasalt$f5531bef9f3687d0ccf0f617f0e25573')
|
'md5$seasalt$f5531bef9f3687d0ccf0f617f0e25573')
|
||||||
self.assertTrue(is_password_usable(encoded))
|
self.assertTrue(is_password_usable(encoded))
|
||||||
self.assertTrue(check_password(u'letmein', encoded))
|
self.assertTrue(check_password('letmein', encoded))
|
||||||
self.assertFalse(check_password('letmeinz', encoded))
|
self.assertFalse(check_password('letmeinz', encoded))
|
||||||
|
self.assertEqual(identify_hasher(encoded).algorithm, "md5")
|
||||||
|
|
||||||
def test_unsalted_md5(self):
|
def test_unsalted_md5(self):
|
||||||
encoded = make_password('letmein', 'seasalt', 'unsalted_md5')
|
encoded = make_password('letmein', 'seasalt', 'unsalted_md5')
|
||||||
self.assertEqual(encoded, '0d107d09f5bbe40cade3de5c71e9e9b7')
|
self.assertEqual(encoded, '0d107d09f5bbe40cade3de5c71e9e9b7')
|
||||||
self.assertTrue(is_password_usable(encoded))
|
self.assertTrue(is_password_usable(encoded))
|
||||||
self.assertTrue(check_password(u'letmein', encoded))
|
self.assertTrue(check_password('letmein', encoded))
|
||||||
self.assertFalse(check_password('letmeinz', encoded))
|
self.assertFalse(check_password('letmeinz', encoded))
|
||||||
|
self.assertEqual(identify_hasher(encoded).algorithm, "unsalted_md5")
|
||||||
|
|
||||||
@skipUnless(crypt, "no crypt module to generate password.")
|
@skipUnless(crypt, "no crypt module to generate password.")
|
||||||
def test_crypt(self):
|
def test_crypt(self):
|
||||||
encoded = make_password('letmein', 'ab', 'crypt')
|
encoded = make_password('letmein', 'ab', 'crypt')
|
||||||
self.assertEqual(encoded, 'crypt$$abN/qM.L/H8EQ')
|
self.assertEqual(encoded, 'crypt$$abN/qM.L/H8EQ')
|
||||||
self.assertTrue(is_password_usable(encoded))
|
self.assertTrue(is_password_usable(encoded))
|
||||||
self.assertTrue(check_password(u'letmein', encoded))
|
self.assertTrue(check_password('letmein', encoded))
|
||||||
self.assertFalse(check_password('letmeinz', encoded))
|
self.assertFalse(check_password('letmeinz', encoded))
|
||||||
|
self.assertEqual(identify_hasher(encoded).algorithm, "crypt")
|
||||||
|
|
||||||
@skipUnless(bcrypt, "py-bcrypt not installed")
|
@skipUnless(bcrypt, "py-bcrypt not installed")
|
||||||
def test_bcrypt(self):
|
def test_bcrypt(self):
|
||||||
encoded = make_password('letmein', hasher='bcrypt')
|
encoded = make_password('letmein', hasher='bcrypt')
|
||||||
self.assertTrue(is_password_usable(encoded))
|
self.assertTrue(is_password_usable(encoded))
|
||||||
self.assertTrue(encoded.startswith('bcrypt$'))
|
self.assertTrue(encoded.startswith('bcrypt$'))
|
||||||
self.assertTrue(check_password(u'letmein', encoded))
|
self.assertTrue(check_password('letmein', encoded))
|
||||||
self.assertFalse(check_password('letmeinz', encoded))
|
self.assertFalse(check_password('letmeinz', encoded))
|
||||||
|
self.assertEqual(identify_hasher(encoded).algorithm, "bcrypt")
|
||||||
|
|
||||||
def test_unusable(self):
|
def test_unusable(self):
|
||||||
encoded = make_password(None)
|
encoded = make_password(None)
|
||||||
|
@ -82,13 +90,15 @@ class TestUtilsHashPass(unittest.TestCase):
|
||||||
self.assertFalse(check_password(None, encoded))
|
self.assertFalse(check_password(None, encoded))
|
||||||
self.assertFalse(check_password(UNUSABLE_PASSWORD, encoded))
|
self.assertFalse(check_password(UNUSABLE_PASSWORD, encoded))
|
||||||
self.assertFalse(check_password('', encoded))
|
self.assertFalse(check_password('', encoded))
|
||||||
self.assertFalse(check_password(u'letmein', encoded))
|
self.assertFalse(check_password('letmein', encoded))
|
||||||
self.assertFalse(check_password('letmeinz', encoded))
|
self.assertFalse(check_password('letmeinz', encoded))
|
||||||
|
self.assertRaises(ValueError, identify_hasher, encoded)
|
||||||
|
|
||||||
def test_bad_algorithm(self):
|
def test_bad_algorithm(self):
|
||||||
def doit():
|
def doit():
|
||||||
make_password('letmein', hasher='lolcat')
|
make_password('letmein', hasher='lolcat')
|
||||||
self.assertRaises(ValueError, doit)
|
self.assertRaises(ValueError, doit)
|
||||||
|
self.assertRaises(ValueError, identify_hasher, "lolcat$salt$hash")
|
||||||
|
|
||||||
def test_low_level_pkbdf2(self):
|
def test_low_level_pkbdf2(self):
|
||||||
hasher = PBKDF2PasswordHasher()
|
hasher = PBKDF2PasswordHasher()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from StringIO import StringIO
|
|
||||||
|
|
||||||
from django.contrib.auth import models, management
|
from django.contrib.auth import models, management
|
||||||
from django.contrib.auth.management.commands import changepassword
|
from django.contrib.auth.management.commands import changepassword
|
||||||
|
@ -9,6 +9,7 @@ from django.core.management import call_command
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
from django.utils.six import StringIO
|
||||||
|
|
||||||
|
|
||||||
class GetDefaultUsernameTestCase(TestCase):
|
class GetDefaultUsernameTestCase(TestCase):
|
||||||
|
@ -20,19 +21,19 @@ class GetDefaultUsernameTestCase(TestCase):
|
||||||
management.get_system_username = self._getpass_getuser
|
management.get_system_username = self._getpass_getuser
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
management.get_system_username = lambda: u'joe'
|
management.get_system_username = lambda: 'joe'
|
||||||
self.assertEqual(management.get_default_username(), 'joe')
|
self.assertEqual(management.get_default_username(), 'joe')
|
||||||
|
|
||||||
def test_existing(self):
|
def test_existing(self):
|
||||||
models.User.objects.create(username='joe')
|
models.User.objects.create(username='joe')
|
||||||
management.get_system_username = lambda: u'joe'
|
management.get_system_username = lambda: 'joe'
|
||||||
self.assertEqual(management.get_default_username(), '')
|
self.assertEqual(management.get_default_username(), '')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
management.get_default_username(check_db=False), 'joe')
|
management.get_default_username(check_db=False), 'joe')
|
||||||
|
|
||||||
def test_i18n(self):
|
def test_i18n(self):
|
||||||
# 'Julia' with accented 'u':
|
# 'Julia' with accented 'u':
|
||||||
management.get_system_username = lambda: u'J\xfalia'
|
management.get_system_username = lambda: 'J\xfalia'
|
||||||
self.assertEqual(management.get_default_username(), 'julia')
|
self.assertEqual(management.get_default_username(), 'julia')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,39 +5,29 @@ from django.contrib.auth.models import (Group, User,
|
||||||
SiteProfileNotAvailable, UserManager)
|
SiteProfileNotAvailable, UserManager)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(USE_TZ=False)
|
@override_settings(USE_TZ=False, AUTH_PROFILE_MODULE='')
|
||||||
class ProfileTestCase(TestCase):
|
class ProfileTestCase(TestCase):
|
||||||
fixtures = ['authtestdata.json']
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
"""Backs up the AUTH_PROFILE_MODULE"""
|
|
||||||
self.old_AUTH_PROFILE_MODULE = getattr(settings,
|
|
||||||
'AUTH_PROFILE_MODULE', None)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
"""Restores the AUTH_PROFILE_MODULE -- if it was not set it is deleted,
|
|
||||||
otherwise the old value is restored"""
|
|
||||||
if self.old_AUTH_PROFILE_MODULE is None and \
|
|
||||||
hasattr(settings, 'AUTH_PROFILE_MODULE'):
|
|
||||||
del settings.AUTH_PROFILE_MODULE
|
|
||||||
|
|
||||||
if self.old_AUTH_PROFILE_MODULE is not None:
|
|
||||||
settings.AUTH_PROFILE_MODULE = self.old_AUTH_PROFILE_MODULE
|
|
||||||
|
|
||||||
def test_site_profile_not_available(self):
|
def test_site_profile_not_available(self):
|
||||||
|
user = User.objects.create(username='testclient')
|
||||||
|
|
||||||
# calling get_profile without AUTH_PROFILE_MODULE set
|
# calling get_profile without AUTH_PROFILE_MODULE set
|
||||||
if hasattr(settings, 'AUTH_PROFILE_MODULE'):
|
del settings.AUTH_PROFILE_MODULE
|
||||||
del settings.AUTH_PROFILE_MODULE
|
with self.assertRaisesRegexp(SiteProfileNotAvailable,
|
||||||
user = User.objects.get(username='testclient')
|
"You need to set AUTH_PROFILE_MODULE in your project"):
|
||||||
self.assertRaises(SiteProfileNotAvailable, user.get_profile)
|
user.get_profile()
|
||||||
|
|
||||||
# Bad syntax in AUTH_PROFILE_MODULE:
|
# Bad syntax in AUTH_PROFILE_MODULE:
|
||||||
settings.AUTH_PROFILE_MODULE = 'foobar'
|
settings.AUTH_PROFILE_MODULE = 'foobar'
|
||||||
self.assertRaises(SiteProfileNotAvailable, user.get_profile)
|
with self.assertRaisesRegexp(SiteProfileNotAvailable,
|
||||||
|
"app_label and model_name should be separated by a dot"):
|
||||||
|
user.get_profile()
|
||||||
|
|
||||||
# module that doesn't exist
|
# module that doesn't exist
|
||||||
settings.AUTH_PROFILE_MODULE = 'foo.bar'
|
settings.AUTH_PROFILE_MODULE = 'foo.bar'
|
||||||
self.assertRaises(SiteProfileNotAvailable, user.get_profile)
|
with self.assertRaisesRegexp(SiteProfileNotAvailable,
|
||||||
|
"Unable to load the profile model"):
|
||||||
|
user.get_profile()
|
||||||
|
|
||||||
|
|
||||||
@override_settings(USE_TZ=False)
|
@override_settings(USE_TZ=False)
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
import sys
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from django.utils import unittest
|
||||||
|
|
||||||
|
|
||||||
class TokenGeneratorTest(TestCase):
|
class TokenGeneratorTest(TestCase):
|
||||||
|
@ -51,6 +53,7 @@ class TokenGeneratorTest(TestCase):
|
||||||
p2 = Mocked(date.today() + timedelta(settings.PASSWORD_RESET_TIMEOUT_DAYS + 1))
|
p2 = Mocked(date.today() + timedelta(settings.PASSWORD_RESET_TIMEOUT_DAYS + 1))
|
||||||
self.assertFalse(p2.check_token(user, tk1))
|
self.assertFalse(p2.check_token(user, tk1))
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.version_info[:2] >= (3, 0), "Unnecessary test with Python 3")
|
||||||
def test_date_length(self):
|
def test_date_length(self):
|
||||||
"""
|
"""
|
||||||
Make sure we don't allow overly long dates, causing a potential DoS.
|
Make sure we don't allow overly long dates, causing a potential DoS.
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import urllib
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sites.models import Site, RequestSite
|
from django.contrib.sites.models import Site, RequestSite
|
||||||
|
@ -8,8 +7,9 @@ from django.contrib.auth.models import User
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.core.urlresolvers import reverse, NoReverseMatch
|
||||||
from django.http import QueryDict
|
from django.http import QueryDict
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_text
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
|
from django.utils.http import urlquote
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ class AuthViewsTestCase(TestCase):
|
||||||
self.assertTrue(SESSION_KEY in self.client.session)
|
self.assertTrue(SESSION_KEY in self.client.session)
|
||||||
|
|
||||||
def assertContainsEscaped(self, response, text, **kwargs):
|
def assertContainsEscaped(self, response, text, **kwargs):
|
||||||
return self.assertContains(response, escape(force_unicode(text)), **kwargs)
|
return self.assertContains(response, escape(force_text(text)), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AuthViewNamedURLTests(AuthViewsTestCase):
|
class AuthViewNamedURLTests(AuthViewsTestCase):
|
||||||
|
@ -256,7 +256,7 @@ class LoginTest(AuthViewsTestCase):
|
||||||
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
|
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
|
||||||
'url': login_url,
|
'url': login_url,
|
||||||
'next': REDIRECT_FIELD_NAME,
|
'next': REDIRECT_FIELD_NAME,
|
||||||
'bad_url': urllib.quote(bad_url),
|
'bad_url': urlquote(bad_url),
|
||||||
}
|
}
|
||||||
response = self.client.post(nasty_url, {
|
response = self.client.post(nasty_url, {
|
||||||
'username': 'testclient',
|
'username': 'testclient',
|
||||||
|
@ -277,7 +277,7 @@ class LoginTest(AuthViewsTestCase):
|
||||||
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
|
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
|
||||||
'url': login_url,
|
'url': login_url,
|
||||||
'next': REDIRECT_FIELD_NAME,
|
'next': REDIRECT_FIELD_NAME,
|
||||||
'good_url': urllib.quote(good_url),
|
'good_url': urlquote(good_url),
|
||||||
}
|
}
|
||||||
response = self.client.post(safe_url, {
|
response = self.client.post(safe_url, {
|
||||||
'username': 'testclient',
|
'username': 'testclient',
|
||||||
|
@ -412,7 +412,7 @@ class LogoutTest(AuthViewsTestCase):
|
||||||
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
|
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
|
||||||
'url': logout_url,
|
'url': logout_url,
|
||||||
'next': REDIRECT_FIELD_NAME,
|
'next': REDIRECT_FIELD_NAME,
|
||||||
'bad_url': urllib.quote(bad_url),
|
'bad_url': urlquote(bad_url),
|
||||||
}
|
}
|
||||||
self.login()
|
self.login()
|
||||||
response = self.client.get(nasty_url)
|
response = self.client.get(nasty_url)
|
||||||
|
@ -432,7 +432,7 @@ class LogoutTest(AuthViewsTestCase):
|
||||||
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
|
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
|
||||||
'url': logout_url,
|
'url': logout_url,
|
||||||
'next': REDIRECT_FIELD_NAME,
|
'next': REDIRECT_FIELD_NAME,
|
||||||
'good_url': urllib.quote(good_url),
|
'good_url': urlquote(good_url),
|
||||||
}
|
}
|
||||||
self.login()
|
self.login()
|
||||||
response = self.client.get(safe_url)
|
response = self.client.get(safe_url)
|
||||||
|
|
|
@ -2,6 +2,7 @@ from datetime import date
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.http import int_to_base36, base36_to_int
|
from django.utils.http import int_to_base36, base36_to_int
|
||||||
from django.utils.crypto import constant_time_compare, salted_hmac
|
from django.utils.crypto import constant_time_compare, salted_hmac
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetTokenGenerator(object):
|
class PasswordResetTokenGenerator(object):
|
||||||
|
@ -57,8 +58,8 @@ class PasswordResetTokenGenerator(object):
|
||||||
# Ensure results are consistent across DB backends
|
# Ensure results are consistent across DB backends
|
||||||
login_timestamp = user.last_login.replace(microsecond=0, tzinfo=None)
|
login_timestamp = user.last_login.replace(microsecond=0, tzinfo=None)
|
||||||
|
|
||||||
value = (unicode(user.id) + user.password +
|
value = (six.text_type(user.id) + user.password +
|
||||||
unicode(login_timestamp) + unicode(timestamp))
|
six.text_type(login_timestamp) + six.text_type(timestamp))
|
||||||
hash = salted_hmac(key_salt, value).hexdigest()[::2]
|
hash = salted_hmac(key_salt, value).hexdigest()[::2]
|
||||||
return "%s-%s" % (ts_b36, hash)
|
return "%s-%s" % (ts_b36, hash)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import urlparse
|
try:
|
||||||
|
from urllib.parse import urlparse, urlunparse
|
||||||
|
except ImportError: # Python 2
|
||||||
|
from urlparse import urlparse, urlunparse
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.http import HttpResponseRedirect, QueryDict
|
from django.http import HttpResponseRedirect, QueryDict
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
|
from django.utils.encoding import force_str
|
||||||
from django.utils.http import base36_to_int
|
from django.utils.http import base36_to_int
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.views.decorators.debug import sensitive_post_parameters
|
from django.views.decorators.debug import sensitive_post_parameters
|
||||||
|
@ -34,7 +38,7 @@ def login(request, template_name='registration/login.html',
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = authentication_form(data=request.POST)
|
form = authentication_form(data=request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
netloc = urlparse.urlparse(redirect_to)[1]
|
netloc = urlparse(redirect_to)[1]
|
||||||
|
|
||||||
# Use default setting if redirect_to is empty
|
# Use default setting if redirect_to is empty
|
||||||
if not redirect_to:
|
if not redirect_to:
|
||||||
|
@ -81,7 +85,7 @@ def logout(request, next_page=None,
|
||||||
auth_logout(request)
|
auth_logout(request)
|
||||||
redirect_to = request.REQUEST.get(redirect_field_name, '')
|
redirect_to = request.REQUEST.get(redirect_field_name, '')
|
||||||
if redirect_to:
|
if redirect_to:
|
||||||
netloc = urlparse.urlparse(redirect_to)[1]
|
netloc = urlparse(redirect_to)[1]
|
||||||
# Security check -- don't allow redirection to a different host.
|
# Security check -- don't allow redirection to a different host.
|
||||||
if not (netloc and netloc != request.get_host()):
|
if not (netloc and netloc != request.get_host()):
|
||||||
return HttpResponseRedirect(redirect_to)
|
return HttpResponseRedirect(redirect_to)
|
||||||
|
@ -116,16 +120,16 @@ def redirect_to_login(next, login_url=None,
|
||||||
"""
|
"""
|
||||||
Redirects the user to the login page, passing the given 'next' page
|
Redirects the user to the login page, passing the given 'next' page
|
||||||
"""
|
"""
|
||||||
if not login_url:
|
# urlparse chokes on lazy objects in Python 3
|
||||||
login_url = settings.LOGIN_URL
|
login_url_as_str = force_str(login_url or settings.LOGIN_URL)
|
||||||
|
|
||||||
login_url_parts = list(urlparse.urlparse(login_url))
|
login_url_parts = list(urlparse(login_url_as_str))
|
||||||
if redirect_field_name:
|
if redirect_field_name:
|
||||||
querystring = QueryDict(login_url_parts[4], mutable=True)
|
querystring = QueryDict(login_url_parts[4], mutable=True)
|
||||||
querystring[redirect_field_name] = next
|
querystring[redirect_field_name] = next
|
||||||
login_url_parts[4] = querystring.urlencode(safe='/')
|
login_url_parts[4] = querystring.urlencode(safe='/')
|
||||||
|
|
||||||
return HttpResponseRedirect(urlparse.urlunparse(login_url_parts))
|
return HttpResponseRedirect(urlunparse(login_url_parts))
|
||||||
|
|
||||||
|
|
||||||
# 4 views for password reset:
|
# 4 views for password reset:
|
||||||
|
@ -203,7 +207,7 @@ def password_reset_confirm(request, uidb36=None, token=None,
|
||||||
try:
|
try:
|
||||||
uid_int = base36_to_int(uidb36)
|
uid_int = base36_to_int(uidb36)
|
||||||
user = User.objects.get(id=uid_int)
|
user = User.objects.get(id=uid_int)
|
||||||
except (ValueError, User.DoesNotExist):
|
except (ValueError, OverflowError, User.DoesNotExist):
|
||||||
user = None
|
user = None
|
||||||
|
|
||||||
if user is not None and token_generator.check_token(user, token):
|
if user is not None and token_generator.check_token(user, token):
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.comments.models import Comment
|
from django.contrib.comments.models import Comment
|
||||||
from django.utils.translation import ugettext_lazy as _, ungettext
|
from django.utils.translation import ugettext_lazy as _, ungettext
|
||||||
|
@ -62,8 +64,8 @@ class CommentsAdmin(admin.ModelAdmin):
|
||||||
action(request, comment)
|
action(request, comment)
|
||||||
n_comments += 1
|
n_comments += 1
|
||||||
|
|
||||||
msg = ungettext(u'1 comment was successfully %(action)s.',
|
msg = ungettext('1 comment was successfully %(action)s.',
|
||||||
u'%(count)s comments were successfully %(action)s.',
|
'%(count)s comments were successfully %(action)s.',
|
||||||
n_comments)
|
n_comments)
|
||||||
self.message_user(request, msg % {'count': n_comments, 'action': done_message(n_comments)})
|
self.message_user(request, msg % {'count': n_comments, 'action': done_message(n_comments)})
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.conf import settings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.comments.models import Comment
|
from django.contrib.comments.models import Comment
|
||||||
from django.utils.crypto import salted_hmac, constant_time_compare
|
from django.utils.crypto import salted_hmac, constant_time_compare
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_text
|
||||||
from django.utils.text import get_text_list
|
from django.utils.text import get_text_list
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ungettext, ugettext, ugettext_lazy as _
|
from django.utils.translation import ungettext, ugettext, ugettext_lazy as _
|
||||||
|
@ -133,7 +133,7 @@ class CommentDetailsForm(CommentSecurityForm):
|
||||||
"""
|
"""
|
||||||
return dict(
|
return dict(
|
||||||
content_type = ContentType.objects.get_for_model(self.target_object),
|
content_type = ContentType.objects.get_for_model(self.target_object),
|
||||||
object_pk = force_unicode(self.target_object._get_pk_val()),
|
object_pk = force_text(self.target_object._get_pk_val()),
|
||||||
user_name = self.cleaned_data["name"],
|
user_name = self.cleaned_data["name"],
|
||||||
user_email = self.cleaned_data["email"],
|
user_email = self.cleaned_data["email"],
|
||||||
user_url = self.cleaned_data["url"],
|
user_url = self.cleaned_data["url"],
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
class CommentManager(models.Manager):
|
class CommentManager(models.Manager):
|
||||||
|
|
||||||
|
@ -18,5 +18,5 @@ class CommentManager(models.Manager):
|
||||||
ct = ContentType.objects.get_for_model(model)
|
ct = ContentType.objects.get_for_model(model)
|
||||||
qs = self.get_query_set().filter(content_type=ct)
|
qs = self.get_query_set().filter(content_type=ct)
|
||||||
if isinstance(model, models.Model):
|
if isinstance(model, models.Model):
|
||||||
qs = qs.filter(object_pk=force_unicode(model._get_pk_val()))
|
qs = qs.filter(object_pk=force_text(model._get_pk_val()))
|
||||||
return qs
|
return qs
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.core import urlresolvers
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
COMMENT_MAX_LENGTH = getattr(settings, 'COMMENT_MAX_LENGTH', 3000)
|
COMMENT_MAX_LENGTH = getattr(settings, 'COMMENT_MAX_LENGTH', 3000)
|
||||||
|
|
||||||
|
@ -40,6 +41,7 @@ class BaseCommentAbstractModel(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Comment(BaseCommentAbstractModel):
|
class Comment(BaseCommentAbstractModel):
|
||||||
"""
|
"""
|
||||||
A user comment about some object.
|
A user comment about some object.
|
||||||
|
@ -77,7 +79,7 @@ class Comment(BaseCommentAbstractModel):
|
||||||
verbose_name = _('comment')
|
verbose_name = _('comment')
|
||||||
verbose_name_plural = _('comments')
|
verbose_name_plural = _('comments')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "%s: %s..." % (self.name, self.comment[:50])
|
return "%s: %s..." % (self.name, self.comment[:50])
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
@ -158,6 +160,7 @@ class Comment(BaseCommentAbstractModel):
|
||||||
return _('Posted by %(user)s at %(date)s\n\n%(comment)s\n\nhttp://%(domain)s%(url)s') % d
|
return _('Posted by %(user)s at %(date)s\n\n%(comment)s\n\nhttp://%(domain)s%(url)s') % d
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class CommentFlag(models.Model):
|
class CommentFlag(models.Model):
|
||||||
"""
|
"""
|
||||||
Records a flag on a comment. This is intentionally flexible; right now, a
|
Records a flag on a comment. This is intentionally flexible; right now, a
|
||||||
|
@ -187,7 +190,7 @@ class CommentFlag(models.Model):
|
||||||
verbose_name = _('comment flag')
|
verbose_name = _('comment flag')
|
||||||
verbose_name_plural = _('comment flags')
|
verbose_name_plural = _('comment flags')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "%s flag of comment ID %s by %s" % \
|
return "%s flag of comment ID %s by %s" % \
|
||||||
(self.flag, self.comment_id, self.user.username)
|
(self.flag, self.comment_id, self.user.username)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.template.loader import render_to_string
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib import comments
|
from django.contrib import comments
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_text
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ class BaseCommentNode(template.Node):
|
||||||
|
|
||||||
qs = self.comment_model.objects.filter(
|
qs = self.comment_model.objects.filter(
|
||||||
content_type = ctype,
|
content_type = ctype,
|
||||||
object_pk = smart_unicode(object_pk),
|
object_pk = smart_text(object_pk),
|
||||||
site__pk = settings.SITE_ID,
|
site__pk = settings.SITE_ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ def flag(request, comment_id, next=None):
|
||||||
"""
|
"""
|
||||||
Flags a comment. Confirmation on GET, action on POST.
|
Flags a comment. Confirmation on GET, action on POST.
|
||||||
|
|
||||||
Templates: `comments/flag.html`,
|
Templates: :template:`comments/flag.html`,
|
||||||
Context:
|
Context:
|
||||||
comment
|
comment
|
||||||
the flagged `comments.comment` object
|
the flagged `comments.comment` object
|
||||||
|
@ -43,7 +43,7 @@ def delete(request, comment_id, next=None):
|
||||||
Deletes a comment. Confirmation on GET, action on POST. Requires the "can
|
Deletes a comment. Confirmation on GET, action on POST. Requires the "can
|
||||||
moderate comments" permission.
|
moderate comments" permission.
|
||||||
|
|
||||||
Templates: `comments/delete.html`,
|
Templates: :template:`comments/delete.html`,
|
||||||
Context:
|
Context:
|
||||||
comment
|
comment
|
||||||
the flagged `comments.comment` object
|
the flagged `comments.comment` object
|
||||||
|
@ -70,7 +70,7 @@ def approve(request, comment_id, next=None):
|
||||||
Approve a comment (that is, mark it as public and non-removed). Confirmation
|
Approve a comment (that is, mark it as public and non-removed). Confirmation
|
||||||
on GET, action on POST. Requires the "can moderate comments" permission.
|
on GET, action on POST. Requires the "can moderate comments" permission.
|
||||||
|
|
||||||
Templates: `comments/approve.html`,
|
Templates: :template:`comments/approve.html`,
|
||||||
Context:
|
Context:
|
||||||
comment
|
comment
|
||||||
the `comments.comment` object for approval
|
the `comments.comment` object for approval
|
||||||
|
|
|
@ -2,8 +2,12 @@
|
||||||
A few bits of helper functions for comment views.
|
A few bits of helper functions for comment views.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import urllib
|
|
||||||
import textwrap
|
import textwrap
|
||||||
|
try:
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
except ImportError: # Python 2
|
||||||
|
from urllib import urlencode
|
||||||
|
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.core import urlresolvers
|
from django.core import urlresolvers
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
|
@ -33,7 +37,7 @@ def next_redirect(data, default, default_view, **get_kwargs):
|
||||||
anchor = ''
|
anchor = ''
|
||||||
|
|
||||||
joiner = ('?' in next) and '&' or '?'
|
joiner = ('?' in next) and '&' or '?'
|
||||||
next += joiner + urllib.urlencode(get_kwargs) + anchor
|
next += joiner + urlencode(get_kwargs) + anchor
|
||||||
return HttpResponseRedirect(next)
|
return HttpResponseRedirect(next)
|
||||||
|
|
||||||
def confirmation_view(template, doc="Display a confirmation view."):
|
def confirmation_view(template, doc="Display a confirmation view."):
|
||||||
|
@ -56,7 +60,7 @@ def confirmation_view(template, doc="Display a confirmation view."):
|
||||||
confirmed.__doc__ = textwrap.dedent("""\
|
confirmed.__doc__ = textwrap.dedent("""\
|
||||||
%s
|
%s
|
||||||
|
|
||||||
Templates: `%s``
|
Templates: :template:`%s``
|
||||||
Context:
|
Context:
|
||||||
comment
|
comment
|
||||||
The posted comment
|
The posted comment
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""
|
"""
|
||||||
Classes allowing "generic" relations through ContentType and object-id fields.
|
Classes allowing "generic" relations through ContentType and object-id fields.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
@ -16,7 +17,7 @@ from django.forms import ModelForm
|
||||||
from django.forms.models import BaseModelFormSet, modelformset_factory, save_instance
|
from django.forms.models import BaseModelFormSet, modelformset_factory, save_instance
|
||||||
from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
|
from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_text
|
||||||
|
|
||||||
class GenericForeignKey(object):
|
class GenericForeignKey(object):
|
||||||
"""
|
"""
|
||||||
|
@ -131,7 +132,7 @@ class GenericForeignKey(object):
|
||||||
|
|
||||||
def __set__(self, instance, value):
|
def __set__(self, instance, value):
|
||||||
if instance is None:
|
if instance is None:
|
||||||
raise AttributeError(u"%s must be accessed via instance" % self.related.opts.object_name)
|
raise AttributeError("%s must be accessed via instance" % self.related.opts.object_name)
|
||||||
|
|
||||||
ct = None
|
ct = None
|
||||||
fk = None
|
fk = None
|
||||||
|
@ -168,7 +169,7 @@ class GenericRelation(RelatedField, Field):
|
||||||
|
|
||||||
def value_to_string(self, obj):
|
def value_to_string(self, obj):
|
||||||
qs = getattr(obj, self.name).all()
|
qs = getattr(obj, self.name).all()
|
||||||
return smart_unicode([instance._get_pk_val() for instance in qs])
|
return smart_text([instance._get_pk_val() for instance in qs])
|
||||||
|
|
||||||
def m2m_db_table(self):
|
def m2m_db_table(self):
|
||||||
return self.rel.to._meta.db_table
|
return self.rel.to._meta.db_table
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db.models import get_apps, get_models, signals
|
from django.db.models import get_apps, get_models, signals
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_text
|
||||||
|
from django.utils import six
|
||||||
|
from django.utils.six.moves import input
|
||||||
|
|
||||||
def update_contenttypes(app, created_models, verbosity=2, **kwargs):
|
def update_contenttypes(app, created_models, verbosity=2, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -24,17 +26,17 @@ def update_contenttypes(app, created_models, verbosity=2, **kwargs):
|
||||||
)
|
)
|
||||||
to_remove = [
|
to_remove = [
|
||||||
ct
|
ct
|
||||||
for (model_name, ct) in content_types.iteritems()
|
for (model_name, ct) in six.iteritems(content_types)
|
||||||
if model_name not in app_models
|
if model_name not in app_models
|
||||||
]
|
]
|
||||||
|
|
||||||
cts = ContentType.objects.bulk_create([
|
cts = ContentType.objects.bulk_create([
|
||||||
ContentType(
|
ContentType(
|
||||||
name=smart_unicode(model._meta.verbose_name_raw),
|
name=smart_text(model._meta.verbose_name_raw),
|
||||||
app_label=app_label,
|
app_label=app_label,
|
||||||
model=model_name,
|
model=model_name,
|
||||||
)
|
)
|
||||||
for (model_name, model) in app_models.iteritems()
|
for (model_name, model) in six.iteritems(app_models)
|
||||||
if model_name not in content_types
|
if model_name not in content_types
|
||||||
])
|
])
|
||||||
if verbosity >= 2:
|
if verbosity >= 2:
|
||||||
|
@ -48,7 +50,7 @@ def update_contenttypes(app, created_models, verbosity=2, **kwargs):
|
||||||
' %s | %s' % (ct.app_label, ct.model)
|
' %s | %s' % (ct.app_label, ct.model)
|
||||||
for ct in to_remove
|
for ct in to_remove
|
||||||
])
|
])
|
||||||
ok_to_delete = raw_input("""The following content types are stale and need to be deleted:
|
ok_to_delete = input("""The following content types are stale and need to be deleted:
|
||||||
|
|
||||||
%s
|
%s
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.encoding import smart_unicode, force_unicode
|
from django.utils.encoding import smart_text, force_text
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
class ContentTypeManager(models.Manager):
|
class ContentTypeManager(models.Manager):
|
||||||
|
|
||||||
|
@ -16,39 +17,44 @@ class ContentTypeManager(models.Manager):
|
||||||
self._add_to_cache(self.db, ct)
|
self._add_to_cache(self.db, ct)
|
||||||
return ct
|
return ct
|
||||||
|
|
||||||
def _get_opts(self, model):
|
def _get_opts(self, model, for_concrete_model):
|
||||||
return model._meta.concrete_model._meta
|
if for_concrete_model:
|
||||||
|
model = model._meta.concrete_model
|
||||||
|
elif model._deferred:
|
||||||
|
model = model._meta.proxy_for_model
|
||||||
|
return model._meta
|
||||||
|
|
||||||
def _get_from_cache(self, opts):
|
def _get_from_cache(self, opts):
|
||||||
key = (opts.app_label, opts.object_name.lower())
|
key = (opts.app_label, opts.object_name.lower())
|
||||||
return self.__class__._cache[self.db][key]
|
return self.__class__._cache[self.db][key]
|
||||||
|
|
||||||
def get_for_model(self, model):
|
def get_for_model(self, model, for_concrete_model=True):
|
||||||
"""
|
"""
|
||||||
Returns the ContentType object for a given model, creating the
|
Returns the ContentType object for a given model, creating the
|
||||||
ContentType if necessary. Lookups are cached so that subsequent lookups
|
ContentType if necessary. Lookups are cached so that subsequent lookups
|
||||||
for the same model don't hit the database.
|
for the same model don't hit the database.
|
||||||
"""
|
"""
|
||||||
opts = self._get_opts(model)
|
opts = self._get_opts(model, for_concrete_model)
|
||||||
try:
|
try:
|
||||||
ct = self._get_from_cache(opts)
|
ct = self._get_from_cache(opts)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Load or create the ContentType entry. The smart_unicode() is
|
# Load or create the ContentType entry. The smart_text() is
|
||||||
# needed around opts.verbose_name_raw because name_raw might be a
|
# needed around opts.verbose_name_raw because name_raw might be a
|
||||||
# django.utils.functional.__proxy__ object.
|
# django.utils.functional.__proxy__ object.
|
||||||
ct, created = self.get_or_create(
|
ct, created = self.get_or_create(
|
||||||
app_label = opts.app_label,
|
app_label = opts.app_label,
|
||||||
model = opts.object_name.lower(),
|
model = opts.object_name.lower(),
|
||||||
defaults = {'name': smart_unicode(opts.verbose_name_raw)},
|
defaults = {'name': smart_text(opts.verbose_name_raw)},
|
||||||
)
|
)
|
||||||
self._add_to_cache(self.db, ct)
|
self._add_to_cache(self.db, ct)
|
||||||
|
|
||||||
return ct
|
return ct
|
||||||
|
|
||||||
def get_for_models(self, *models):
|
def get_for_models(self, *models, **kwargs):
|
||||||
"""
|
"""
|
||||||
Given *models, returns a dictionary mapping {model: content_type}.
|
Given *models, returns a dictionary mapping {model: content_type}.
|
||||||
"""
|
"""
|
||||||
|
for_concrete_models = kwargs.pop('for_concrete_models', True)
|
||||||
# Final results
|
# Final results
|
||||||
results = {}
|
results = {}
|
||||||
# models that aren't already in the cache
|
# models that aren't already in the cache
|
||||||
|
@ -56,7 +62,7 @@ class ContentTypeManager(models.Manager):
|
||||||
needed_models = set()
|
needed_models = set()
|
||||||
needed_opts = set()
|
needed_opts = set()
|
||||||
for model in models:
|
for model in models:
|
||||||
opts = self._get_opts(model)
|
opts = self._get_opts(model, for_concrete_models)
|
||||||
try:
|
try:
|
||||||
ct = self._get_from_cache(opts)
|
ct = self._get_from_cache(opts)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -81,7 +87,7 @@ class ContentTypeManager(models.Manager):
|
||||||
ct = self.create(
|
ct = self.create(
|
||||||
app_label=opts.app_label,
|
app_label=opts.app_label,
|
||||||
model=opts.object_name.lower(),
|
model=opts.object_name.lower(),
|
||||||
name=smart_unicode(opts.verbose_name_raw),
|
name=smart_text(opts.verbose_name_raw),
|
||||||
)
|
)
|
||||||
self._add_to_cache(self.db, ct)
|
self._add_to_cache(self.db, ct)
|
||||||
results[ct.model_class()] = ct
|
results[ct.model_class()] = ct
|
||||||
|
@ -117,6 +123,7 @@ class ContentTypeManager(models.Manager):
|
||||||
self.__class__._cache.setdefault(using, {})[key] = ct
|
self.__class__._cache.setdefault(using, {})[key] = ct
|
||||||
self.__class__._cache.setdefault(using, {})[ct.id] = ct
|
self.__class__._cache.setdefault(using, {})[ct.id] = ct
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class ContentType(models.Model):
|
class ContentType(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
app_label = models.CharField(max_length=100)
|
app_label = models.CharField(max_length=100)
|
||||||
|
@ -130,7 +137,7 @@ class ContentType(models.Model):
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
unique_together = (('app_label', 'model'),)
|
unique_together = (('app_label', 'model'),)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
# self.name is deprecated in favor of using model's verbose_name, which
|
# self.name is deprecated in favor of using model's verbose_name, which
|
||||||
# can be translated. Formal deprecation is delayed until we have DB
|
# can be translated. Formal deprecation is delayed until we have DB
|
||||||
# migration to be able to remove the field from the database along with
|
# migration to be able to remove the field from the database along with
|
||||||
|
@ -142,7 +149,7 @@ class ContentType(models.Model):
|
||||||
if not model or self.name != model._meta.verbose_name_raw:
|
if not model or self.name != model._meta.verbose_name_raw:
|
||||||
return self.name
|
return self.name
|
||||||
else:
|
else:
|
||||||
return force_unicode(model._meta.verbose_name)
|
return force_text(model._meta.verbose_name)
|
||||||
|
|
||||||
def model_class(self):
|
def model_class(self):
|
||||||
"Returns the Python model class for this type of content."
|
"Returns the Python model class for this type of content."
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import urllib
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
@ -6,16 +6,26 @@ from django.contrib.contenttypes.views import shortcut
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.http import HttpRequest, Http404
|
from django.http import HttpRequest, Http404
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.http import urlquote
|
||||||
|
from django.utils import six
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
|
|
||||||
|
class ConcreteModel(models.Model):
|
||||||
|
name = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
class ProxyModel(ConcreteModel):
|
||||||
|
class Meta:
|
||||||
|
proxy = True
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class FooWithoutUrl(models.Model):
|
class FooWithoutUrl(models.Model):
|
||||||
"""
|
"""
|
||||||
Fake model not defining ``get_absolute_url`` for
|
Fake model not defining ``get_absolute_url`` for
|
||||||
:meth:`ContentTypesTests.test_shortcut_view_without_get_absolute_url`"""
|
:meth:`ContentTypesTests.test_shortcut_view_without_get_absolute_url`"""
|
||||||
name = models.CharField(max_length=30, unique=True)
|
name = models.CharField(max_length=30, unique=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +36,7 @@ class FooWithUrl(FooWithoutUrl):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return "/users/%s/" % urllib.quote(smart_str(self.name))
|
return "/users/%s/" % urlquote(self.name)
|
||||||
|
|
||||||
class FooWithBrokenAbsoluteUrl(FooWithoutUrl):
|
class FooWithBrokenAbsoluteUrl(FooWithoutUrl):
|
||||||
"""
|
"""
|
||||||
|
@ -112,6 +122,87 @@ class ContentTypesTests(TestCase):
|
||||||
FooWithUrl: ContentType.objects.get_for_model(FooWithUrl),
|
FooWithUrl: ContentType.objects.get_for_model(FooWithUrl),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def test_get_for_concrete_model(self):
|
||||||
|
"""
|
||||||
|
Make sure the `for_concrete_model` kwarg correctly works
|
||||||
|
with concrete, proxy and deferred models
|
||||||
|
"""
|
||||||
|
concrete_model_ct = ContentType.objects.get_for_model(ConcreteModel)
|
||||||
|
|
||||||
|
self.assertEqual(concrete_model_ct,
|
||||||
|
ContentType.objects.get_for_model(ProxyModel))
|
||||||
|
|
||||||
|
self.assertEqual(concrete_model_ct,
|
||||||
|
ContentType.objects.get_for_model(ConcreteModel,
|
||||||
|
for_concrete_model=False))
|
||||||
|
|
||||||
|
proxy_model_ct = ContentType.objects.get_for_model(ProxyModel,
|
||||||
|
for_concrete_model=False)
|
||||||
|
|
||||||
|
self.assertNotEqual(concrete_model_ct, proxy_model_ct)
|
||||||
|
|
||||||
|
# Make sure deferred model are correctly handled
|
||||||
|
ConcreteModel.objects.create(name="Concrete")
|
||||||
|
DeferredConcreteModel = ConcreteModel.objects.only('pk').get().__class__
|
||||||
|
DeferredProxyModel = ProxyModel.objects.only('pk').get().__class__
|
||||||
|
|
||||||
|
self.assertEqual(concrete_model_ct,
|
||||||
|
ContentType.objects.get_for_model(DeferredConcreteModel))
|
||||||
|
|
||||||
|
self.assertEqual(concrete_model_ct,
|
||||||
|
ContentType.objects.get_for_model(DeferredConcreteModel,
|
||||||
|
for_concrete_model=False))
|
||||||
|
|
||||||
|
self.assertEqual(concrete_model_ct,
|
||||||
|
ContentType.objects.get_for_model(DeferredProxyModel))
|
||||||
|
|
||||||
|
self.assertEqual(proxy_model_ct,
|
||||||
|
ContentType.objects.get_for_model(DeferredProxyModel,
|
||||||
|
for_concrete_model=False))
|
||||||
|
|
||||||
|
def test_get_for_concrete_models(self):
|
||||||
|
"""
|
||||||
|
Make sure the `for_concrete_models` kwarg correctly works
|
||||||
|
with concrete, proxy and deferred models.
|
||||||
|
"""
|
||||||
|
concrete_model_ct = ContentType.objects.get_for_model(ConcreteModel)
|
||||||
|
|
||||||
|
cts = ContentType.objects.get_for_models(ConcreteModel, ProxyModel)
|
||||||
|
self.assertEqual(cts, {
|
||||||
|
ConcreteModel: concrete_model_ct,
|
||||||
|
ProxyModel: concrete_model_ct,
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy_model_ct = ContentType.objects.get_for_model(ProxyModel,
|
||||||
|
for_concrete_model=False)
|
||||||
|
cts = ContentType.objects.get_for_models(ConcreteModel, ProxyModel,
|
||||||
|
for_concrete_models=False)
|
||||||
|
self.assertEqual(cts, {
|
||||||
|
ConcreteModel: concrete_model_ct,
|
||||||
|
ProxyModel: proxy_model_ct,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Make sure deferred model are correctly handled
|
||||||
|
ConcreteModel.objects.create(name="Concrete")
|
||||||
|
DeferredConcreteModel = ConcreteModel.objects.only('pk').get().__class__
|
||||||
|
DeferredProxyModel = ProxyModel.objects.only('pk').get().__class__
|
||||||
|
|
||||||
|
cts = ContentType.objects.get_for_models(DeferredConcreteModel,
|
||||||
|
DeferredProxyModel)
|
||||||
|
self.assertEqual(cts, {
|
||||||
|
DeferredConcreteModel: concrete_model_ct,
|
||||||
|
DeferredProxyModel: concrete_model_ct,
|
||||||
|
})
|
||||||
|
|
||||||
|
cts = ContentType.objects.get_for_models(DeferredConcreteModel,
|
||||||
|
DeferredProxyModel,
|
||||||
|
for_concrete_models=False)
|
||||||
|
self.assertEqual(cts, {
|
||||||
|
DeferredConcreteModel: concrete_model_ct,
|
||||||
|
DeferredProxyModel: proxy_model_ct,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def test_shortcut_view(self):
|
def test_shortcut_view(self):
|
||||||
"""
|
"""
|
||||||
Check that the shortcut view (used for the admin "view on site"
|
Check that the shortcut view (used for the admin "view on site"
|
||||||
|
@ -181,4 +272,4 @@ class ContentTypesTests(TestCase):
|
||||||
app_label = 'contenttypes',
|
app_label = 'contenttypes',
|
||||||
model = 'OldModel',
|
model = 'OldModel',
|
||||||
)
|
)
|
||||||
self.assertEqual(unicode(ct), u'Old model')
|
self.assertEqual(six.text_type(ct), 'Old model')
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.sites.models import Site, get_current_site
|
from django.contrib.sites.models import Site, get_current_site
|
||||||
|
@ -12,11 +14,11 @@ def shortcut(request, content_type_id, object_id):
|
||||||
try:
|
try:
|
||||||
content_type = ContentType.objects.get(pk=content_type_id)
|
content_type = ContentType.objects.get(pk=content_type_id)
|
||||||
if not content_type.model_class():
|
if not content_type.model_class():
|
||||||
raise http.Http404(_(u"Content type %(ct_id)s object has no associated model") %
|
raise http.Http404(_("Content type %(ct_id)s object has no associated model") %
|
||||||
{'ct_id': content_type_id})
|
{'ct_id': content_type_id})
|
||||||
obj = content_type.get_object_for_this_type(pk=object_id)
|
obj = content_type.get_object_for_this_type(pk=object_id)
|
||||||
except (ObjectDoesNotExist, ValueError):
|
except (ObjectDoesNotExist, ValueError):
|
||||||
raise http.Http404(_(u"Content type %(ct_id)s object %(obj_id)s doesn't exist") %
|
raise http.Http404(_("Content type %(ct_id)s object %(obj_id)s doesn't exist") %
|
||||||
{'ct_id': content_type_id, 'obj_id': object_id})
|
{'ct_id': content_type_id, 'obj_id': object_id})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
These classes are light wrappers around Django's database API that provide
|
These classes are light wrappers around Django's database API that provide
|
||||||
convenience functionality and permalink functions for the databrowse app.
|
convenience functionality and permalink functions for the databrowse app.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils.encoding import smart_unicode, smart_str, iri_to_uri
|
from django.utils.encoding import smart_text, smart_str, iri_to_uri
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
EMPTY_VALUE = '(None)'
|
EMPTY_VALUE = '(None)'
|
||||||
DISPLAY_SIZE = 100
|
DISPLAY_SIZE = 100
|
||||||
|
@ -17,19 +18,19 @@ class EasyModel(object):
|
||||||
def __init__(self, site, model):
|
def __init__(self, site, model):
|
||||||
self.site = site
|
self.site = site
|
||||||
self.model = model
|
self.model = model
|
||||||
self.model_list = site.registry.keys()
|
self.model_list = list(site.registry.keys())
|
||||||
self.verbose_name = model._meta.verbose_name
|
self.verbose_name = model._meta.verbose_name
|
||||||
self.verbose_name_plural = model._meta.verbose_name_plural
|
self.verbose_name_plural = model._meta.verbose_name_plural
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<EasyModel for %s>' % smart_str(self.model._meta.object_name)
|
return smart_str('<EasyModel for %s>' % self.model._meta.object_name)
|
||||||
|
|
||||||
def model_databrowse(self):
|
def model_databrowse(self):
|
||||||
"Returns the ModelDatabrowse class for this model."
|
"Returns the ModelDatabrowse class for this model."
|
||||||
return self.site.registry[self.model]
|
return self.site.registry[self.model]
|
||||||
|
|
||||||
def url(self):
|
def url(self):
|
||||||
return mark_safe('%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name))
|
return '%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name)
|
||||||
|
|
||||||
def objects(self, **kwargs):
|
def objects(self, **kwargs):
|
||||||
return self.get_query_set().filter(**kwargs)
|
return self.get_query_set().filter(**kwargs)
|
||||||
|
@ -61,7 +62,7 @@ class EasyField(object):
|
||||||
self.model, self.field = easy_model, field
|
self.model, self.field = easy_model, field
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return smart_str(u'<EasyField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
|
return smart_str('<EasyField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
|
||||||
|
|
||||||
def choices(self):
|
def choices(self):
|
||||||
for value, label in self.field.choices:
|
for value, label in self.field.choices:
|
||||||
|
@ -69,9 +70,9 @@ class EasyField(object):
|
||||||
|
|
||||||
def url(self):
|
def url(self):
|
||||||
if self.field.choices:
|
if self.field.choices:
|
||||||
return mark_safe('%s%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name))
|
return '%s%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name)
|
||||||
elif self.field.rel:
|
elif self.field.rel:
|
||||||
return mark_safe('%s%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name))
|
return '%s%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name)
|
||||||
|
|
||||||
class EasyChoice(object):
|
class EasyChoice(object):
|
||||||
def __init__(self, easy_model, field, value, label):
|
def __init__(self, easy_model, field, value, label):
|
||||||
|
@ -79,32 +80,30 @@ class EasyChoice(object):
|
||||||
self.value, self.label = value, label
|
self.value, self.label = value, label
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return smart_str(u'<EasyChoice for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
|
return smart_str('<EasyChoice for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
|
||||||
|
|
||||||
def url(self):
|
def url(self):
|
||||||
return mark_safe('%s%s/%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.field.name, iri_to_uri(self.value)))
|
return '%s%s/%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.field.name, iri_to_uri(self.value))
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class EasyInstance(object):
|
class EasyInstance(object):
|
||||||
def __init__(self, easy_model, instance):
|
def __init__(self, easy_model, instance):
|
||||||
self.model, self.instance = easy_model, instance
|
self.model, self.instance = easy_model, instance
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return smart_str(u'<EasyInstance for %s (%s)>' % (self.model.model._meta.object_name, self.instance._get_pk_val()))
|
return smart_str('<EasyInstance for %s (%s)>' % (self.model.model._meta.object_name, self.instance._get_pk_val()))
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
val = smart_unicode(self.instance)
|
|
||||||
if len(val) > DISPLAY_SIZE:
|
|
||||||
return val[:DISPLAY_SIZE] + u'...'
|
|
||||||
return val
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.__unicode__().encode('utf-8')
|
val = smart_text(self.instance)
|
||||||
|
if len(val) > DISPLAY_SIZE:
|
||||||
|
return val[:DISPLAY_SIZE] + '...'
|
||||||
|
return val
|
||||||
|
|
||||||
def pk(self):
|
def pk(self):
|
||||||
return self.instance._get_pk_val()
|
return self.instance._get_pk_val()
|
||||||
|
|
||||||
def url(self):
|
def url(self):
|
||||||
return mark_safe('%s%s/%s/objects/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, iri_to_uri(self.pk())))
|
return '%s%s/%s/objects/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, iri_to_uri(self.pk()))
|
||||||
|
|
||||||
def fields(self):
|
def fields(self):
|
||||||
"""
|
"""
|
||||||
|
@ -136,7 +135,7 @@ class EasyInstanceField(object):
|
||||||
self.raw_value = getattr(instance.instance, field.name)
|
self.raw_value = getattr(instance.instance, field.name)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return smart_str(u'<EasyInstanceField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
|
return smart_str('<EasyInstanceField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
|
||||||
|
|
||||||
def values(self):
|
def values(self):
|
||||||
"""
|
"""
|
||||||
|
@ -176,8 +175,6 @@ class EasyInstanceField(object):
|
||||||
for plugin_name, plugin in self.model.model_databrowse().plugins.items():
|
for plugin_name, plugin in self.model.model_databrowse().plugins.items():
|
||||||
urls = plugin.urls(plugin_name, self)
|
urls = plugin.urls(plugin_name, self)
|
||||||
if urls is not None:
|
if urls is not None:
|
||||||
#plugin_urls.append(urls)
|
|
||||||
values = self.values()
|
|
||||||
return zip(self.values(), urls)
|
return zip(self.values(), urls)
|
||||||
if self.field.rel:
|
if self.field.rel:
|
||||||
m = EasyModel(self.model.site, self.field.rel.to)
|
m = EasyModel(self.model.site, self.field.rel.to)
|
||||||
|
@ -186,20 +183,20 @@ class EasyInstanceField(object):
|
||||||
for value in self.values():
|
for value in self.values():
|
||||||
if value is None:
|
if value is None:
|
||||||
continue
|
continue
|
||||||
url = mark_safe('%s%s/%s/objects/%s/' % (self.model.site.root_url, m.model._meta.app_label, m.model._meta.module_name, iri_to_uri(value._get_pk_val())))
|
url = '%s%s/%s/objects/%s/' % (self.model.site.root_url, m.model._meta.app_label, m.model._meta.module_name, iri_to_uri(value._get_pk_val()))
|
||||||
lst.append((smart_unicode(value), url))
|
lst.append((smart_text(value), url))
|
||||||
else:
|
else:
|
||||||
lst = [(value, None) for value in self.values()]
|
lst = [(value, None) for value in self.values()]
|
||||||
elif self.field.choices:
|
elif self.field.choices:
|
||||||
lst = []
|
lst = []
|
||||||
for value in self.values():
|
for value in self.values():
|
||||||
url = mark_safe('%s%s/%s/fields/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name, iri_to_uri(self.raw_value)))
|
url = '%s%s/%s/fields/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name, iri_to_uri(self.raw_value))
|
||||||
lst.append((value, url))
|
lst.append((value, url))
|
||||||
elif isinstance(self.field, models.URLField):
|
elif isinstance(self.field, models.URLField):
|
||||||
val = self.values()[0]
|
val = list(self.values())[0]
|
||||||
lst = [(val, iri_to_uri(val))]
|
lst = [(val, iri_to_uri(val))]
|
||||||
else:
|
else:
|
||||||
lst = [(self.values()[0], None)]
|
lst = [(list(self.values())[0], None)]
|
||||||
return lst
|
return lst
|
||||||
|
|
||||||
class EasyQuerySet(QuerySet):
|
class EasyQuerySet(QuerySet):
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.databrowse.datastructures import EasyModel
|
from django.contrib.databrowse.datastructures import EasyModel
|
||||||
from django.contrib.databrowse.sites import DatabrowsePlugin
|
from django.contrib.databrowse.sites import DatabrowsePlugin
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
|
from django.utils.html import format_html, format_html_join
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_text
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
from django.views.generic import dates
|
from django.views.generic import dates
|
||||||
from django.utils import datetime_safe
|
from django.utils import datetime_safe
|
||||||
|
|
||||||
|
@ -61,19 +63,20 @@ class CalendarPlugin(DatabrowsePlugin):
|
||||||
def model_index_html(self, request, model, site):
|
def model_index_html(self, request, model, site):
|
||||||
fields = self.field_dict(model)
|
fields = self.field_dict(model)
|
||||||
if not fields:
|
if not fields:
|
||||||
return u''
|
return ''
|
||||||
return mark_safe(u'<p class="filter"><strong>View calendar by:</strong> %s</p>' % \
|
return format_html('<p class="filter"><strong>View calendar by:</strong> {0}</p>',
|
||||||
u', '.join(['<a href="calendars/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()]))
|
format_html_join(', ', '<a href="calendars/{0}/">{1}</a>',
|
||||||
|
((f.name, force_text(capfirst(f.verbose_name))) for f in fields.values())))
|
||||||
|
|
||||||
def urls(self, plugin_name, easy_instance_field):
|
def urls(self, plugin_name, easy_instance_field):
|
||||||
if isinstance(easy_instance_field.field, models.DateField):
|
if isinstance(easy_instance_field.field, models.DateField):
|
||||||
d = easy_instance_field.raw_value
|
d = easy_instance_field.raw_value
|
||||||
return [mark_safe(u'%s%s/%s/%s/%s/%s/' % (
|
return ['%s%s/%s/%s/%s/%s/' % (
|
||||||
easy_instance_field.model.url(),
|
easy_instance_field.model.url(),
|
||||||
plugin_name, easy_instance_field.field.name,
|
plugin_name, easy_instance_field.field.name,
|
||||||
str(d.year),
|
str(d.year),
|
||||||
datetime_safe.new_date(d).strftime('%b').lower(),
|
datetime_safe.new_date(d).strftime('%b').lower(),
|
||||||
d.day))]
|
d.day)]
|
||||||
|
|
||||||
def model_view(self, request, model_databrowse, url):
|
def model_view(self, request, model_databrowse, url):
|
||||||
self.model, self.site = model_databrowse.model, model_databrowse.site
|
self.model, self.site = model_databrowse.model, model_databrowse.site
|
||||||
|
@ -93,7 +96,7 @@ class CalendarPlugin(DatabrowsePlugin):
|
||||||
|
|
||||||
def homepage_view(self, request):
|
def homepage_view(self, request):
|
||||||
easy_model = EasyModel(self.site, self.model)
|
easy_model = EasyModel(self.site, self.model)
|
||||||
field_list = self.fields.values()
|
field_list = list(self.fields.values())
|
||||||
field_list.sort(key=lambda k:k.verbose_name)
|
field_list.sort(key=lambda k:k.verbose_name)
|
||||||
return render_to_response('databrowse/calendar_homepage.html', {
|
return render_to_response('databrowse/calendar_homepage.html', {
|
||||||
'root_url': self.site.root_url,
|
'root_url': self.site.root_url,
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.databrowse.datastructures import EasyModel
|
from django.contrib.databrowse.datastructures import EasyModel
|
||||||
from django.contrib.databrowse.sites import DatabrowsePlugin
|
from django.contrib.databrowse.sites import DatabrowsePlugin
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
|
from django.utils.html import format_html, format_html_join
|
||||||
|
from django.utils.http import urlquote
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils.encoding import smart_str, force_unicode
|
from django.utils.encoding import force_text
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
import urllib
|
|
||||||
|
|
||||||
class FieldChoicePlugin(DatabrowsePlugin):
|
class FieldChoicePlugin(DatabrowsePlugin):
|
||||||
def __init__(self, field_filter=None):
|
def __init__(self, field_filter=None):
|
||||||
|
@ -29,17 +32,17 @@ class FieldChoicePlugin(DatabrowsePlugin):
|
||||||
def model_index_html(self, request, model, site):
|
def model_index_html(self, request, model, site):
|
||||||
fields = self.field_dict(model)
|
fields = self.field_dict(model)
|
||||||
if not fields:
|
if not fields:
|
||||||
return u''
|
return ''
|
||||||
return mark_safe(u'<p class="filter"><strong>View by:</strong> %s</p>' % \
|
return format_html('<p class="filter"><strong>View by:</strong> {0}</p>',
|
||||||
u', '.join(['<a href="fields/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()]))
|
format_html_join(', ', '<a href="fields/{0}/">{1}</a>',
|
||||||
|
((f.name, force_text(capfirst(f.verbose_name))) for f in fields.values())))
|
||||||
|
|
||||||
def urls(self, plugin_name, easy_instance_field):
|
def urls(self, plugin_name, easy_instance_field):
|
||||||
if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values():
|
if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values():
|
||||||
field_value = smart_str(easy_instance_field.raw_value)
|
return ['%s%s/%s/%s/' % (
|
||||||
return [mark_safe(u'%s%s/%s/%s/' % (
|
|
||||||
easy_instance_field.model.url(),
|
easy_instance_field.model.url(),
|
||||||
plugin_name, easy_instance_field.field.name,
|
plugin_name, easy_instance_field.field.name,
|
||||||
urllib.quote(field_value, safe='')))]
|
urlquote(easy_instance_field.raw_value, safe=''))]
|
||||||
|
|
||||||
def model_view(self, request, model_databrowse, url):
|
def model_view(self, request, model_databrowse, url):
|
||||||
self.model, self.site = model_databrowse.model, model_databrowse.site
|
self.model, self.site = model_databrowse.model, model_databrowse.site
|
||||||
|
@ -60,7 +63,7 @@ class FieldChoicePlugin(DatabrowsePlugin):
|
||||||
|
|
||||||
def homepage_view(self, request):
|
def homepage_view(self, request):
|
||||||
easy_model = EasyModel(self.site, self.model)
|
easy_model = EasyModel(self.site, self.model)
|
||||||
field_list = self.fields.values()
|
field_list = list(self.fields.values())
|
||||||
field_list.sort(key=lambda k: k.verbose_name)
|
field_list.sort(key=lambda k: k.verbose_name)
|
||||||
return render_to_response('databrowse/fieldchoice_homepage.html', {'root_url': self.site.root_url, 'model': easy_model, 'field_list': field_list})
|
return render_to_response('databrowse/fieldchoice_homepage.html', {'root_url': self.site.root_url, 'model': easy_model, 'field_list': field_list})
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
|
try:
|
||||||
|
from urllib.parse import urljoin
|
||||||
|
except ImportError: # Python 2
|
||||||
|
from urlparse import urljoin
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.contrib.databrowse.datastructures import EasyModel
|
from django.contrib.databrowse.datastructures import EasyModel
|
||||||
from django.contrib.databrowse.sites import DatabrowsePlugin
|
from django.contrib.databrowse.sites import DatabrowsePlugin
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
import urlparse
|
|
||||||
|
|
||||||
class ObjectDetailPlugin(DatabrowsePlugin):
|
class ObjectDetailPlugin(DatabrowsePlugin):
|
||||||
def model_view(self, request, model_databrowse, url):
|
def model_view(self, request, model_databrowse, url):
|
||||||
# If the object ID wasn't provided, redirect to the model page, which is one level up.
|
# If the object ID wasn't provided, redirect to the model page, which is one level up.
|
||||||
if url is None:
|
if url is None:
|
||||||
return http.HttpResponseRedirect(urlparse.urljoin(request.path, '../'))
|
return http.HttpResponseRedirect(urljoin(request.path, '../'))
|
||||||
easy_model = EasyModel(model_databrowse.site, model_databrowse.model)
|
easy_model = EasyModel(model_databrowse.site, model_databrowse.model)
|
||||||
obj = easy_model.object_by_pk(url)
|
obj = easy_model.object_by_pk(url)
|
||||||
return render_to_response('databrowse/object_detail.html', {'object': obj, 'root_url': model_databrowse.site.root_url})
|
return render_to_response('databrowse/object_detail.html', {'object': obj, 'root_url': model_databrowse.site.root_url})
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue