diff options
| author | Brian Rosner <brosner@gmail.com> | 2008-06-26 15:42:33 +0000 |
|---|---|---|
| committer | Brian Rosner <brosner@gmail.com> | 2008-06-26 15:42:33 +0000 |
| commit | c8da0874c78ed4c6e1ad08cc78228799a333f76c (patch) | |
| tree | bef645a8eb2c1a17f013a1031ed34494547dced0 /django | |
| parent | f15845c573f019fc7f6d7404add122f9b7c52dc4 (diff) | |
newforms-admin: Merged from trunk up to [7766].
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@7770 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
| -rw-r--r-- | django/conf/locale/pl/LC_MESSAGES/django.mo | bin | 66404 -> 66481 bytes | |||
| -rw-r--r-- | django/conf/locale/pl/LC_MESSAGES/django.po | 226 | ||||
| -rw-r--r-- | django/contrib/auth/views.py | 14 | ||||
| -rw-r--r-- | django/contrib/sites/tests.py | 4 | ||||
| -rw-r--r-- | django/core/servers/basehttp.py | 2 | ||||
| -rw-r--r-- | django/db/backends/__init__.py | 1 | ||||
| -rw-r--r-- | django/db/backends/oracle/base.py | 1 | ||||
| -rw-r--r-- | django/db/backends/oracle/creation.py | 12 | ||||
| -rw-r--r-- | django/db/models/fields/__init__.py | 4 | ||||
| -rw-r--r-- | django/db/models/fields/related.py | 9 | ||||
| -rw-r--r-- | django/db/models/options.py | 11 | ||||
| -rw-r--r-- | django/db/models/query.py | 36 | ||||
| -rw-r--r-- | django/db/models/sql/query.py | 72 | ||||
| -rw-r--r-- | django/db/models/sql/subqueries.py | 8 | ||||
| -rw-r--r-- | django/template/defaulttags.py | 24 | ||||
| -rw-r--r-- | django/templatetags/cache.py | 25 | ||||
| -rw-r--r-- | django/utils/datastructures.py | 31 | ||||
| -rw-r--r-- | django/utils/html.py | 52 |
18 files changed, 316 insertions, 216 deletions
diff --git a/django/conf/locale/pl/LC_MESSAGES/django.mo b/django/conf/locale/pl/LC_MESSAGES/django.mo Binary files differindex b82290dd04..5cdabb1d11 100644 --- a/django/conf/locale/pl/LC_MESSAGES/django.mo +++ b/django/conf/locale/pl/LC_MESSAGES/django.mo diff --git a/django/conf/locale/pl/LC_MESSAGES/django.po b/django/conf/locale/pl/LC_MESSAGES/django.po index d084d90688..6f73bb45d0 100644 --- a/django/conf/locale/pl/LC_MESSAGES/django.po +++ b/django/conf/locale/pl/LC_MESSAGES/django.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-04-14 18:02+0200\n" +"POT-Creation-Date: 2008-06-24 07:36+0200\n" "PO-Revision-Date: 2008-02-25 15:53+0100\n" -"Last-Translator: Piotr Lewandowski <django@icomputing.pl>\n" +"Last-Translator: Jarek Zgoda <jarek.zgoda@gmail.com>\n" "Language-Team: Polish <pl@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -16,191 +16,199 @@ msgstr "" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%" "100<10 || n%100>=20) ? 1 : 2);\n" -#: conf/global_settings.py:39 +#: conf/global_settings.py:44 msgid "Arabic" msgstr "Arabski" -#: conf/global_settings.py:40 +#: conf/global_settings.py:45 msgid "Bengali" msgstr "Bengalski" -#: conf/global_settings.py:41 +#: conf/global_settings.py:46 msgid "Bulgarian" msgstr "Bułgarski" -#: conf/global_settings.py:42 +#: conf/global_settings.py:47 msgid "Catalan" msgstr "Kataloński" -#: conf/global_settings.py:43 +#: conf/global_settings.py:48 msgid "Czech" msgstr "Czeski" -#: conf/global_settings.py:44 +#: conf/global_settings.py:49 msgid "Welsh" msgstr "Walijski" -#: conf/global_settings.py:45 +#: conf/global_settings.py:50 msgid "Danish" msgstr "Duński" -#: conf/global_settings.py:46 +#: conf/global_settings.py:51 msgid "German" msgstr "Niemiecki" -#: conf/global_settings.py:47 +#: conf/global_settings.py:52 msgid "Greek" msgstr "Grecki" -#: conf/global_settings.py:48 +#: conf/global_settings.py:53 msgid "English" msgstr "Angielski" -#: conf/global_settings.py:49 +#: conf/global_settings.py:54 msgid "Spanish" msgstr "Hiszpański" -#: conf/global_settings.py:50 +#: conf/global_settings.py:55 +msgid "Estonian" +msgstr "Estoński" + +#: conf/global_settings.py:56 msgid "Argentinean Spanish" msgstr "Hiszpański argentyński" -#: conf/global_settings.py:51 +#: conf/global_settings.py:57 msgid "Basque" msgstr "Baskijski" -#: conf/global_settings.py:52 +#: conf/global_settings.py:58 msgid "Persian" msgstr "Perski" -#: conf/global_settings.py:53 +#: conf/global_settings.py:59 msgid "Finnish" msgstr "Fiński" -#: conf/global_settings.py:54 +#: conf/global_settings.py:60 msgid "French" msgstr "Francuski" -#: conf/global_settings.py:55 +#: conf/global_settings.py:61 msgid "Irish" msgstr "Irlandzki" -#: conf/global_settings.py:56 +#: conf/global_settings.py:62 msgid "Galician" msgstr "Galicyjski" -#: conf/global_settings.py:57 +#: conf/global_settings.py:63 msgid "Hungarian" msgstr "Węgierski" -#: conf/global_settings.py:58 +#: conf/global_settings.py:64 msgid "Hebrew" msgstr "Hebrajski" -#: conf/global_settings.py:59 +#: conf/global_settings.py:65 msgid "Croatian" msgstr "Chorwacki" -#: conf/global_settings.py:60 +#: conf/global_settings.py:66 msgid "Icelandic" msgstr "Islandzki" -#: conf/global_settings.py:61 +#: conf/global_settings.py:67 msgid "Italian" msgstr "Włoski" -#: conf/global_settings.py:62 +#: conf/global_settings.py:68 msgid "Japanese" msgstr "Japoński" -#: conf/global_settings.py:63 +#: conf/global_settings.py:69 msgid "Georgian" msgstr "Gruziński" -#: conf/global_settings.py:64 +#: conf/global_settings.py:70 msgid "Korean" msgstr "Koreański" -#: conf/global_settings.py:65 +#: conf/global_settings.py:71 msgid "Khmer" msgstr "Khmerski" -#: conf/global_settings.py:66 +#: conf/global_settings.py:72 msgid "Kannada" msgstr "Kannada" -#: conf/global_settings.py:67 +#: conf/global_settings.py:73 msgid "Latvian" msgstr "Łotewski" -#: conf/global_settings.py:68 +#: conf/global_settings.py:74 +msgid "Lithuanian" +msgstr "Litewski" + +#: conf/global_settings.py:75 msgid "Macedonian" msgstr "Macedoński" -#: conf/global_settings.py:69 +#: conf/global_settings.py:76 msgid "Dutch" msgstr "Holenderski" -#: conf/global_settings.py:70 +#: conf/global_settings.py:77 msgid "Norwegian" msgstr "Norweski" -#: conf/global_settings.py:71 +#: conf/global_settings.py:78 msgid "Polish" msgstr "Polski" -#: conf/global_settings.py:72 +#: conf/global_settings.py:79 msgid "Portugese" msgstr "Portugalski" -#: conf/global_settings.py:73 +#: conf/global_settings.py:80 msgid "Brazilian Portuguese" msgstr "Brazylijski portugalski" -#: conf/global_settings.py:74 +#: conf/global_settings.py:81 msgid "Romanian" msgstr "Rumuński" -#: conf/global_settings.py:75 +#: conf/global_settings.py:82 msgid "Russian" msgstr "Rosyjski" -#: conf/global_settings.py:76 +#: conf/global_settings.py:83 msgid "Slovak" msgstr "Słowacki" -#: conf/global_settings.py:77 +#: conf/global_settings.py:84 msgid "Slovenian" msgstr "Słoweński" -#: conf/global_settings.py:78 +#: conf/global_settings.py:85 msgid "Serbian" msgstr "Serbski" -#: conf/global_settings.py:79 +#: conf/global_settings.py:86 msgid "Swedish" msgstr "Szwedzki" -#: conf/global_settings.py:80 +#: conf/global_settings.py:87 msgid "Tamil" msgstr "Tamilski" -#: conf/global_settings.py:81 +#: conf/global_settings.py:88 msgid "Telugu" msgstr "Telugu" -#: conf/global_settings.py:82 +#: conf/global_settings.py:89 msgid "Turkish" msgstr "Turecki" -#: conf/global_settings.py:83 +#: conf/global_settings.py:90 msgid "Ukrainian" msgstr "Ukraiński" -#: conf/global_settings.py:84 +#: conf/global_settings.py:91 msgid "Simplified Chinese" msgstr "Uproszczony chiński" -#: conf/global_settings.py:85 +#: conf/global_settings.py:92 msgid "Traditional Chinese" msgstr "Chiński tradycyjny" @@ -417,7 +425,7 @@ msgstr "" #: contrib/admin/templates/admin/delete_confirmation.html:25 msgid "Yes, I'm sure" -msgstr "Tak, usuń" +msgstr "Tak, na pewno" #: contrib/admin/templates/admin/filter.html:2 #, python-format @@ -824,15 +832,15 @@ msgstr "" "Twoja przeglądarka nie chce akceptować ciasteczek. Zmień jej ustawienia i " "spróbuj ponownie." -#: contrib/admin/views/decorators.py:90 -msgid "Usernames cannot contain the '@' character." -msgstr "Nazwy użytkowników nie mogą zawierać znaków '@'." - -#: contrib/admin/views/decorators.py:92 +#: contrib/admin/views/decorators.py:89 #, python-format msgid "Your e-mail address is not your username. Try '%s' instead." msgstr "Twój adres e-mail to nie jest twój login. Spróbuj '%s'." +#: contrib/admin/views/decorators.py:93 +msgid "Usernames cannot contain the '@' character." +msgstr "Nazwy użytkowników nie mogą zawierać znaków '@'." + #: contrib/admin/views/doc.py:48 contrib/admin/views/doc.py:50 #: contrib/admin/views/doc.py:52 msgid "tag:" @@ -1063,7 +1071,7 @@ msgstr "Zaznacz %s" msgid "Select %s to change" msgstr "Zaznacz %s aby zmienić" -#: contrib/admin/views/main.py:784 +#: contrib/admin/views/main.py:765 msgid "Database error" msgstr "Błąd bazy danych" @@ -1128,15 +1136,15 @@ msgstr "uprawnienia" msgid "group" msgstr "grupa" -#: contrib/auth/models.py:98 contrib/auth/models.py:141 +#: contrib/auth/models.py:98 contrib/auth/models.py:148 msgid "groups" msgstr "grupy" -#: contrib/auth/models.py:131 +#: contrib/auth/models.py:138 msgid "username" msgstr "użytkownik" -#: contrib/auth/models.py:131 +#: contrib/auth/models.py:138 msgid "" "Required. 30 characters or fewer. Alphanumeric characters only (letters, " "digits and underscores)." @@ -1144,23 +1152,23 @@ msgstr "" "Wymagane. 30 znaków lub mniej. Tylko znaki alfanumeryczne (litery, cyfry i " "podkreślenia)." -#: contrib/auth/models.py:132 +#: contrib/auth/models.py:139 msgid "first name" msgstr "Imię" -#: contrib/auth/models.py:133 +#: contrib/auth/models.py:140 msgid "last name" msgstr "Nazwisko" -#: contrib/auth/models.py:134 +#: contrib/auth/models.py:141 msgid "e-mail address" msgstr "adres e-mail" -#: contrib/auth/models.py:135 +#: contrib/auth/models.py:142 msgid "password" msgstr "hasło" -#: contrib/auth/models.py:135 +#: contrib/auth/models.py:142 msgid "" "Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change " "password form</a>." @@ -1168,19 +1176,19 @@ msgstr "" "Użyj '[algo]$[salt]$[hexdigest]' lub <a href=\"password/\">formularza zmiany " "hasła</a>." -#: contrib/auth/models.py:136 +#: contrib/auth/models.py:143 msgid "staff status" msgstr "w zespole" -#: contrib/auth/models.py:136 +#: contrib/auth/models.py:143 msgid "Designates whether the user can log into this admin site." msgstr "Oznacza czy użytkownik może zalogować się do panelu admina." -#: contrib/auth/models.py:137 +#: contrib/auth/models.py:144 msgid "active" msgstr "aktywny" -#: contrib/auth/models.py:137 +#: contrib/auth/models.py:144 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -1188,11 +1196,11 @@ msgstr "" "Oznacza czy użytkownika należy uważać za aktywnego. Odznacz tozamiast usuwać " "konta." -#: contrib/auth/models.py:138 +#: contrib/auth/models.py:145 msgid "superuser status" msgstr "Główny Administrator" -#: contrib/auth/models.py:138 +#: contrib/auth/models.py:145 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." @@ -1200,15 +1208,15 @@ msgstr "" "Oznacza, że ten użytkownik ma wszystkie uprawnienia bez jawnego " "przypisywania ich." -#: contrib/auth/models.py:139 +#: contrib/auth/models.py:146 msgid "last login" msgstr "ostatnio zalogowany" -#: contrib/auth/models.py:140 +#: contrib/auth/models.py:147 msgid "date joined" msgstr "data przyłączenia" -#: contrib/auth/models.py:142 +#: contrib/auth/models.py:149 msgid "" "In addition to the permissions manually assigned, this user will also get " "all permissions granted to each group he/she is in." @@ -1216,39 +1224,39 @@ msgstr "" "Oprócz uprawnień przypisanych bezpośrednio użytkownikowi otrzyma on " "uprawnienia grup, do których należy." -#: contrib/auth/models.py:143 +#: contrib/auth/models.py:150 msgid "user permissions" msgstr "uprawnienia użytkownika" -#: contrib/auth/models.py:147 +#: contrib/auth/models.py:154 msgid "user" msgstr "użytkownik" -#: contrib/auth/models.py:148 +#: contrib/auth/models.py:155 msgid "users" msgstr "użytkownicy" -#: contrib/auth/models.py:154 +#: contrib/auth/models.py:161 msgid "Personal info" msgstr "Dane osobowe" -#: contrib/auth/models.py:155 +#: contrib/auth/models.py:162 msgid "Permissions" msgstr "Uprawnienia" -#: contrib/auth/models.py:156 +#: contrib/auth/models.py:163 msgid "Important dates" msgstr "Ważne daty" -#: contrib/auth/models.py:157 +#: contrib/auth/models.py:164 msgid "Groups" msgstr "Grupy" -#: contrib/auth/models.py:316 +#: contrib/auth/models.py:323 msgid "message" msgstr "wiadomość" -#: contrib/auth/views.py:47 +#: contrib/auth/views.py:48 msgid "Logged out" msgstr "Wylogowany" @@ -3516,23 +3524,23 @@ msgstr "przekieruj" msgid "redirects" msgstr "przekierowania" -#: contrib/sessions/models.py:41 +#: contrib/sessions/models.py:45 msgid "session key" msgstr "klucz sesji" -#: contrib/sessions/models.py:42 +#: contrib/sessions/models.py:47 msgid "session data" msgstr "data sesji" -#: contrib/sessions/models.py:43 +#: contrib/sessions/models.py:48 msgid "expire date" msgstr "data wygaśnięcia sesji" -#: contrib/sessions/models.py:48 +#: contrib/sessions/models.py:53 msgid "session" msgstr "sesja" -#: contrib/sessions/models.py:49 +#: contrib/sessions/models.py:54 msgid "sessions" msgstr "sesje" @@ -3617,7 +3625,7 @@ msgstr "Rok nie może być wcześniejszy niż 1900." msgid "Invalid date: %s" msgstr "Niepoprawna data: %s" -#: core/validators.py:156 db/models/fields/__init__.py:527 +#: core/validators.py:156 db/models/fields/__init__.py:548 msgid "Enter a valid date in YYYY-MM-DD format." msgstr "Proszę wpisać poprawną datę w formacie RRRR-MM-DD." @@ -3625,7 +3633,7 @@ msgstr "Proszę wpisać poprawną datę w formacie RRRR-MM-DD." msgid "Enter a valid time in HH:MM format." msgstr "Proszę wpisać poprawną godzinę w formacie HH:MM." -#: core/validators.py:165 db/models/fields/__init__.py:604 +#: core/validators.py:165 db/models/fields/__init__.py:625 msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format." msgstr "Wprowadź poprawną datę i godzinę w formacie RRRR-MM-DD GG:MM." @@ -3888,60 +3896,60 @@ msgstr "" msgid "%(object)s with this %(type)s already exists for the given %(field)s." msgstr "%(object)s z %(type)s już istnieje dla %(field)s." -#: db/models/fields/__init__.py:52 +#: db/models/fields/__init__.py:51 #, python-format msgid "%(optname)s with this %(fieldname)s already exists." msgstr "Już istnieje %(optname)s z %(fieldname)s." -#: db/models/fields/__init__.py:161 db/models/fields/__init__.py:327 -#: db/models/fields/__init__.py:759 db/models/fields/__init__.py:770 +#: db/models/fields/__init__.py:176 db/models/fields/__init__.py:348 +#: db/models/fields/__init__.py:780 db/models/fields/__init__.py:791 #: newforms/fields.py:46 oldforms/__init__.py:374 msgid "This field is required." msgstr "To pole jest wymagane." -#: db/models/fields/__init__.py:427 +#: db/models/fields/__init__.py:448 msgid "This value must be an integer." msgstr "Ta wartość musi być liczbą całkowitą." -#: db/models/fields/__init__.py:466 +#: db/models/fields/__init__.py:487 msgid "This value must be either True or False." msgstr "" "Ta wartość musi być wartością logiczną (True, False - prawda lub fałsz)." -#: db/models/fields/__init__.py:490 +#: db/models/fields/__init__.py:511 msgid "This field cannot be null." msgstr "To pole nie może być puste." -#: db/models/fields/__init__.py:668 +#: db/models/fields/__init__.py:689 msgid "This value must be a decimal number." msgstr "Ta wartość musi być liczbą dziesiętną." -#: db/models/fields/__init__.py:779 +#: db/models/fields/__init__.py:800 msgid "Enter a valid filename." msgstr "Wpisz poprawną nazwę pliku." -#: db/models/fields/__init__.py:960 +#: db/models/fields/__init__.py:981 msgid "This value must be either None, True or False." msgstr "" "Ta wartość musi być jedną z None (nic), True (prawda) lub False (fałsz)." -#: db/models/fields/related.py:93 +#: db/models/fields/related.py:94 #, python-format msgid "Please enter a valid %s." msgstr "Proszę wpisać poprawne %s." -#: db/models/fields/related.py:701 +#: db/models/fields/related.py:746 msgid "Separate multiple IDs with commas." msgstr "Oddziel identyfikatory przecinkami." -#: db/models/fields/related.py:703 +#: db/models/fields/related.py:748 msgid "" "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" "Przytrzymaj wciśnięty klawisz \"Ctrl\" lub \"Command\" na Mac'u aby " "zaznaczyć więcej niż jeden wybór." -#: db/models/fields/related.py:750 +#: db/models/fields/related.py:795 #, python-format msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." msgid_plural "" @@ -4003,11 +4011,11 @@ msgstr "Upewnij się, że jest nie więcej niż %s miejsc po przecinku." msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Upewnij się, że jest nie więcej niż %s miejsc przed przecinkiem." -#: newforms/fields.py:263 newforms/fields.py:751 +#: newforms/fields.py:263 newforms/fields.py:750 msgid "Enter a valid date." msgstr "Wpisz poprawną datę." -#: newforms/fields.py:296 newforms/fields.py:752 +#: newforms/fields.py:296 newforms/fields.py:751 msgid "Enter a valid time." msgstr "Wpisz poprawną godzinę." @@ -4031,25 +4039,25 @@ msgstr "Wpisz poprawny URL." msgid "This URL appears to be a broken link." msgstr "Ten odnośnik jest nieprawidłowy." -#: newforms/fields.py:560 newforms/models.py:299 +#: newforms/fields.py:559 newforms/models.py:305 msgid "Select a valid choice. That choice is not one of the available choices." msgstr "Wybierz poprawną wartość. Podana nie jest jednym z dostępnych wyborów." -#: newforms/fields.py:599 +#: newforms/fields.py:598 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "" "Wybierz poprawną wartość. %(value)s nie jest jednym z dostępnych wyborów." -#: newforms/fields.py:600 newforms/fields.py:662 newforms/models.py:371 +#: newforms/fields.py:599 newforms/fields.py:661 newforms/models.py:372 msgid "Enter a list of values." msgstr "Podaj listę wartości." -#: newforms/fields.py:780 +#: newforms/fields.py:779 msgid "Enter a valid IPv4 address." msgstr "Wprowadź poprawny adres IPv4." -#: newforms/models.py:372 +#: newforms/models.py:373 #, python-format msgid "Select a valid choice. %s is not one of the available choices." msgstr "Wybierz poprawną wartość. %s nie jest jednym z dostępnych wyborów." diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index 0886d12d02..0a52240631 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -1,12 +1,13 @@ +from django.contrib.auth import REDIRECT_FIELD_NAME +from django.contrib.auth.decorators import login_required from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import PasswordResetForm, PasswordChangeForm, AdminPasswordChangeForm from django.core.exceptions import PermissionDenied from django.shortcuts import render_to_response, get_object_or_404 -from django.template import RequestContext from django.contrib.sites.models import Site, RequestSite from django.http import HttpResponseRedirect -from django.contrib.auth.decorators import login_required -from django.contrib.auth import REDIRECT_FIELD_NAME +from django.template import RequestContext +from django.utils.http import urlquote from django.utils.html import escape from django.utils.translation import ugettext as _ from django.contrib.auth.models import User @@ -62,7 +63,7 @@ def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_N if not login_url: from django.conf import settings login_url = settings.LOGIN_URL - return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, next)) + return HttpResponseRedirect('%s?%s=%s' % (login_url, urlquote(redirect_field_name), urlquote(next))) def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html', email_template_name='registration/password_reset_email.html', @@ -73,7 +74,10 @@ def password_reset(request, is_admin_site=False, template_name='registration/pas if is_admin_site: form.save(domain_override=request.META['HTTP_HOST']) else: - form.save(email_template_name=email_template_name) + if Site._meta.installed: + form.save(email_template_name=email_template_name) + else: + form.save(domain_override=RequestSite(request).domain, email_template_name=email_template_name) return HttpResponseRedirect('%sdone/' % request.path) else: form = password_reset_form() diff --git a/django/contrib/sites/tests.py b/django/contrib/sites/tests.py index d2ec331eca..5c38ee1b6d 100644 --- a/django/contrib/sites/tests.py +++ b/django/contrib/sites/tests.py @@ -2,8 +2,8 @@ >>> # Make sure that get_current() does not return a deleted Site object. >>> from django.contrib.sites.models import Site >>> s = Site.objects.get_current() ->>> s -<Site: example.com> +>>> isinstance(s, Site) +True >>> s.delete() >>> Site.objects.get_current() diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index 05f8756655..5bdf08b8ca 100644 --- a/django/core/servers/basehttp.py +++ b/django/core/servers/basehttp.py @@ -17,7 +17,7 @@ import urllib from django.utils.http import http_date __version__ = "0.1" -__all__ = ['WSGIServer','WSGIRequestHandler','demo_app'] +__all__ = ['WSGIServer','WSGIRequestHandler'] server_version = "WSGIServer/" + __version__ sys_version = "Python/" + sys.version.split()[0] diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 63a6435bdb..7a4e46a7d7 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -45,7 +45,6 @@ class BaseDatabaseFeatures(object): autoindexes_primary_keys = True inline_fk_references = True needs_datetime_string_cast = True - needs_upper_for_iops = False supports_constraints = True supports_tablespaces = False uses_case_insensitive_names = False diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index a1a9fb1c73..1df6bac069 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -27,7 +27,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259) empty_fetchmany_value = () needs_datetime_string_cast = False - needs_upper_for_iops = True supports_tablespaces = True uses_case_insensitive_names = True uses_custom_query_class = True diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py index f4ada55ac6..219686789f 100644 --- a/django/db/backends/oracle/creation.py +++ b/django/db/backends/oracle/creation.py @@ -5,9 +5,13 @@ from django.core import management # types, as strings. Column-type strings can contain format strings; they'll # be interpolated against the values of Field.__dict__ before being output. # If a column type is set to None, it won't be included in the output. +# +# Any format strings starting with "qn_" are quoted before being used in the +# output (the "qn_" prefix is stripped before the lookup is performed. + DATA_TYPES = { 'AutoField': 'NUMBER(11)', - 'BooleanField': 'NUMBER(1) CHECK (%(column)s IN (0,1))', + 'BooleanField': 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))', 'CharField': 'NVARCHAR2(%(max_length)s)', 'CommaSeparatedIntegerField': 'VARCHAR2(%(max_length)s)', 'DateField': 'DATE', @@ -19,11 +23,11 @@ DATA_TYPES = { 'ImageField': 'NVARCHAR2(%(max_length)s)', 'IntegerField': 'NUMBER(11)', 'IPAddressField': 'VARCHAR2(15)', - 'NullBooleanField': 'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))', + 'NullBooleanField': 'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(column)s IS NULL))', 'OneToOneField': 'NUMBER(11)', 'PhoneNumberField': 'VARCHAR2(20)', - 'PositiveIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)', - 'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)', + 'PositiveIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)', + 'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)', 'SlugField': 'NVARCHAR2(50)', 'SmallIntegerField': 'NUMBER(11)', 'TextField': 'NCLOB', diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 8a51193a01..d906d5bdda 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -16,6 +16,7 @@ from django.core import validators from django import oldforms from django import newforms as forms from django.core.exceptions import ObjectDoesNotExist +from django.utils.datastructures import DictWrapper from django.utils.functional import curry from django.utils.itercompat import tee from django.utils.text import capfirst @@ -153,8 +154,9 @@ class Field(object): # mapped to one of the built-in Django field types. In this case, you # can implement db_type() instead of get_internal_type() to specify # exactly which wacky database column type you want to use. + data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_") try: - return get_creation_module().DATA_TYPES[self.get_internal_type()] % self.__dict__ + return get_creation_module().DATA_TYPES[self.get_internal_type()] % data except KeyError: return None diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 1c0466f97f..5ae94e02de 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -103,13 +103,15 @@ class RelatedField(object): if hasattr(sup, 'contribute_to_class'): sup.contribute_to_class(cls, name) + + if not cls._meta.abstract and self.rel.related_name: + self.rel.related_name = self.rel.related_name % {'class': cls.__name__.lower()} + other = self.rel.to if isinstance(other, basestring): add_lazy_relation(cls, self, other) else: self.do_related_class(other, cls) - if not cls._meta.abstract and self.rel.related_name: - self.rel.related_name = self.rel.related_name % {'class': cls.__name__.lower()} def set_attributes_from_rel(self): self.name = self.name or (self.rel.to._meta.object_name.lower() + '_' + self.rel.to._meta.pk.name) @@ -119,7 +121,8 @@ class RelatedField(object): def do_related_class(self, other, cls): self.set_attributes_from_rel() related = RelatedObject(other, cls, self) - self.contribute_to_related_class(other, related) + if not cls._meta.abstract: + self.contribute_to_related_class(other, related) def get_db_prep_lookup(self, lookup_type, value): # If we are doing a lookup on a Related Field, we must be diff --git a/django/db/models/options.py b/django/db/models/options.py index c78ab1cd48..1ac24fb311 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -273,14 +273,17 @@ class Options(object): """ Initialises the field name -> field object mapping. """ - cache = dict([(f.name, (f, m, True, False)) for f, m in - self.get_fields_with_model()]) - for f, model in self.get_m2m_with_model(): - cache[f.name] = (f, model, True, True) + cache = {} + # We intentionally handle related m2m objects first so that symmetrical + # m2m accessor names can be overridden, if necessary. for f, model in self.get_all_related_m2m_objects_with_model(): cache[f.field.related_query_name()] = (f, model, False, True) for f, model in self.get_all_related_objects_with_model(): cache[f.field.related_query_name()] = (f, model, False, False) + for f, model in self.get_m2m_with_model(): + cache[f.name] = (f, model, True, True) + for f, model in self.get_fields_with_model(): + cache[f.name] = (f, model, True, False) if self.order_with_respect_to: cache['_order'] = OrderWrt(), None, True, False if app_cache_ready(): diff --git a/django/db/models/query.py b/django/db/models/query.py index 8714cffb7f..98caaf004c 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -36,7 +36,7 @@ class CollectedObjects(object): """ Adds an item. model is the class of the object being added, - pk is the primary key, obj is the object itself, + pk is the primary key, obj is the object itself, parent_model is the model of the parent object that this object was reached through, nullable should be True if this relation is nullable. @@ -77,7 +77,7 @@ class CollectedObjects(object): def ordered_keys(self): """ - Returns the models in the order that they should be + Returns the models in the order that they should be dealth with i.e. models with no dependencies first. """ dealt_with = SortedDict() @@ -92,7 +92,7 @@ class CollectedObjects(object): found = True if not found: raise CyclicDependency("There is a cyclic dependency of items to be processed.") - + return dealt_with.keys() def unordered_keys(self): @@ -218,6 +218,8 @@ class QuerySet(object): def __and__(self, other): self._merge_sanity_check(other) + if isinstance(other, EmptyQuerySet): + return other._clone() combined = self._clone() combined.query.combine(other.query, sql.AND) return combined @@ -225,6 +227,8 @@ class QuerySet(object): def __or__(self, other): self._merge_sanity_check(other) combined = self._clone() + if isinstance(other, EmptyQuerySet): + return combined combined.query.combine(other.query, sql.OR) return combined @@ -488,7 +492,9 @@ class QuerySet(object): and usually it will be more natural to use other methods. """ if isinstance(filter_obj, Q) or hasattr(filter_obj, 'add_to_query'): - return self._filter_or_exclude(None, filter_obj) + clone = self._clone() + clone.query.add_q(filter_obj) + return clone else: return self._filter_or_exclude(None, **filter_obj) @@ -583,11 +589,11 @@ class QuerySet(object): def _merge_sanity_check(self, other): """ - Checks that we are merging two comparable queryset classes. + Checks that we are merging two comparable queryset classes. By default + this does nothing, but see the ValuesQuerySet for an example of where + it's useful. """ - if self.__class__ is not other.__class__: - raise TypeError("Cannot merge querysets of different types ('%s' and '%s'." - % (self.__class__.__name__, other.__class__.__name__)) + pass class ValuesQuerySet(QuerySet): def __init__(self, *args, **kwargs): @@ -599,7 +605,7 @@ class ValuesQuerySet(QuerySet): # names of the model fields to select. def iterator(self): - if (not self.extra_names and + if (not self.extra_names and len(self.field_names) != len(self.model._meta.fields)): self.query.trim_extra_select(self.extra_names) names = self.query.extra_select.keys() + self.field_names @@ -688,9 +694,9 @@ class DateQuerySet(QuerySet): """ self.query = self.query.clone(klass=sql.DateQuery, setup=True) self.query.select = [] - self.query.add_date_select(self._field.column, self._kind, self._order) + self.query.add_date_select(self._field, self._kind, self._order) if self._field.null: - self.query.add_filter(('%s__isnull' % self._field.name, True)) + self.query.add_filter(('%s__isnull' % self._field.name, False)) def _clone(self, klass=None, setup=False, **kwargs): c = super(DateQuerySet, self)._clone(klass, False, **kwargs) @@ -705,6 +711,12 @@ class EmptyQuerySet(QuerySet): super(EmptyQuerySet, self).__init__(model, query) self._result_cache = [] + def __and__(self, other): + return self._clone() + + def __or__(self, other): + return other._clone() + def count(self): return 0 @@ -773,7 +785,7 @@ def delete_objects(seen_objs): except CyclicDependency: # if there is a cyclic dependency, we cannot in general delete # the objects. However, if an appropriate transaction is set - # up, or if the database is lax enough, it will succeed. + # up, or if the database is lax enough, it will succeed. # So for now, we go ahead and try anway. ordered_classes = seen_objs.unordered_keys() diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 3044882a86..e8d10bc55b 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -610,6 +610,10 @@ class Query(object): alias = joins[-1] col = target.column + # Must use left outer joins for nullable fields. + for join in joins: + self.promote_alias(join) + # If we get to this point and the field is a relation to another model, # append the default ordering for that model. if field.rel and len(joins) > 1 and opts.ordering: @@ -631,8 +635,10 @@ class Query(object): # We have to do the same "final join" optimisation as in # add_filter, since the final column might not otherwise be part of # the select set (so we can't order on it). - join = self.alias_map[alias] - if col == join[RHS_JOIN_COL]: + while 1: + join = self.alias_map[alias] + if col != join[RHS_JOIN_COL]: + break self.unref_alias(alias) alias = join[LHS_ALIAS] col = join[LHS_JOIN_COL] @@ -679,12 +685,16 @@ class Query(object): for the join to contain NULL values on the left. If 'unconditional' is False, the join is only promoted if it is nullable, otherwise it is always promoted. + + Returns True if the join was promoted. """ if ((unconditional or self.alias_map[alias][NULLABLE]) and self.alias_map[alias] != self.LOUTER): data = list(self.alias_map[alias]) data[JOIN_TYPE] = self.LOUTER self.alias_map[alias] = tuple(data) + return True + return False def change_aliases(self, change_map): """ @@ -826,6 +836,10 @@ class Query(object): if not always_create: for alias in self.join_map.get(t_ident, ()): if alias not in exclusions: + if lhs_table and not self.alias_refcount[self.alias_map[alias][LHS_ALIAS]]: + # The LHS of this join tuple is no longer part of the + # query, so skip this possibility. + continue self.ref_alias(alias) if promote: self.promote_alias(alias) @@ -985,20 +999,22 @@ class Query(object): col = target.column alias = join_list[-1] - if final > 1: + while final > 1: # An optimization: if the final join is against the same column as # we are comparing against, we can go back one step in the join - # chain and compare against the lhs of the join instead. The result - # (potentially) involves one less table join. + # chain and compare against the lhs of the join instead (and then + # repeat the optimization). The result, potentially, involves less + # table joins. join = self.alias_map[alias] - if col == join[RHS_JOIN_COL]: - self.unref_alias(alias) - alias = join[LHS_ALIAS] - col = join[LHS_JOIN_COL] - join_list = join_list[:-1] - final -= 1 - if final == penultimate: - penultimate = last.pop() + if col != join[RHS_JOIN_COL]: + break + self.unref_alias(alias) + alias = join[LHS_ALIAS] + col = join[LHS_JOIN_COL] + join_list = join_list[:-1] + final -= 1 + if final == penultimate: + penultimate = last.pop() if (lookup_type == 'isnull' and value is True and not negate and final > 1): @@ -1033,17 +1049,27 @@ class Query(object): self.promote_alias(table) self.where.add((alias, col, field, lookup_type, value), connector) + if negate: for alias in join_list: self.promote_alias(alias) - if final > 1 and lookup_type != 'isnull': - for alias in join_list: - if self.alias_map[alias] == self.LOUTER: - j_col = self.alias_map[alias][RHS_JOIN_COL] - entry = Node([(alias, j_col, None, 'isnull', True)]) - entry.negate() - self.where.add(entry, AND) - break + if lookup_type != 'isnull': + if final > 1: + for alias in join_list: + if self.alias_map[alias][JOIN_TYPE] == self.LOUTER: + j_col = self.alias_map[alias][RHS_JOIN_COL] + entry = Node([(alias, j_col, None, 'isnull', True)]) + entry.negate() + self.where.add(entry, AND) + break + elif not (lookup_type == 'in' and not value): + # Leaky abstraction artifact: We have to specifically + # exclude the "foo__in=[]" case from this handling, because + # it's short-circuited in the Where class. + entry = Node([(alias, col, field, 'isnull', True)]) + entry.negate() + self.where.add(entry, AND) + if can_reuse is not None: can_reuse.update(join_list) @@ -1294,10 +1320,12 @@ class Query(object): final_alias = join[LHS_ALIAS] col = join[LHS_JOIN_COL] joins = joins[:-1] + promote = False for join in joins[1:]: # Only nullable aliases are promoted, so we don't end up # doing unnecessary left outer joins here. - self.promote_alias(join) + if self.promote_alias(join, promote): + promote = True self.select.append((final_alias, col)) self.select_fields.append(field) except MultiJoin: diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py index 28436abede..0bb741d706 100644 --- a/django/db/models/sql/subqueries.py +++ b/django/db/models/sql/subqueries.py @@ -357,12 +357,14 @@ class DateQuery(Query): date = typecast_timestamp(str(date)) yield date - def add_date_select(self, column, lookup_type, order='ASC'): + def add_date_select(self, field, lookup_type, order='ASC'): """ Converts the query into a date extraction query. """ - alias = self.join((None, self.model._meta.db_table, None, None)) - select = Date((alias, column), lookup_type, + result = self.setup_joins([field.name], self.get_meta(), + self.get_initial_alias(), False) + alias = result[3][-1] + select = Date((alias, field.column), lookup_type, self.connection.ops.date_trunc_sql) self.select = [select] self.select_fields = [None] diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index cf3b35b4cc..01c43ee86f 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -39,12 +39,11 @@ class CommentNode(Node): class CycleNode(Node): def __init__(self, cyclevars, variable_name=None): - self.cycle_iter = itertools_cycle(cyclevars) + self.cycle_iter = itertools_cycle([Variable(v) for v in cyclevars]) self.variable_name = variable_name def render(self, context): - value = self.cycle_iter.next() - value = Variable(value).resolve(context) + value = self.cycle_iter.next().resolve(context) if self.variable_name: context[self.variable_name] = value return value @@ -162,10 +161,12 @@ class IfChangedNode(Node): self.nodelist = nodelist self._last_seen = None self._varlist = map(Variable, varlist) + self._id = str(id(self)) def render(self, context): - if 'forloop' in context and context['forloop']['first']: + if 'forloop' in context and self._id not in context['forloop']: self._last_seen = None + context['forloop'][self._id] = 1 try: if self._varlist: # Consider multiple parameters. This automatically behaves @@ -452,17 +453,17 @@ def cycle(parser, token): <tr class="{% cycle rowcolors %}">...</tr> <tr class="{% cycle rowcolors %}">...</tr> - You can use any number of values, seperated by spaces. Commas can also + You can use any number of values, separated by spaces. Commas can also be used to separate values; if a comma is used, the cycle values are interpreted as literal strings. """ # Note: This returns the exact same node on each {% cycle name %} call; # that is, the node object returned from {% cycle a b c as name %} and the - # one returned from {% cycle name %} are the exact same object. This + # one returned from {% cycle name %} are the exact same object. This # shouldn't cause problems (heh), but if it does, now you know. # - # Ugly hack warning: this stuffs the named template dict into parser so + # Ugly hack warning: This stuffs the named template dict into parser so # that names are only unique within each template (as opposed to using # a global variable, which would make cycle names have to be unique across # *all* templates. @@ -481,8 +482,7 @@ def cycle(parser, token): # {% cycle foo %} case. name = args[1] if not hasattr(parser, '_namedCycleNodes'): - raise TemplateSyntaxError("No named cycles in template." - " '%s' is not defined" % name) + raise TemplateSyntaxError("No named cycles in template. '%s' is not defined" % name) if not name in parser._namedCycleNodes: raise TemplateSyntaxError("Named cycle '%s' does not exist" % name) return parser._namedCycleNodes[name] @@ -682,8 +682,10 @@ ifnotequal = register.tag(ifnotequal) def do_if(parser, token): """ The ``{% if %}`` tag evaluates a variable, and if that variable is "true" - (i.e. exists, is not empty, and is not a false boolean value) the contents - of the block are output:: + (i.e., exists, is not empty, and is not a false boolean value), the + contents of the block are output: + + :: {% if athlete_list %} Number of athletes: {{ athlete_list|count }} diff --git a/django/templatetags/cache.py b/django/templatetags/cache.py index 4436b15d6e..07bd590fba 100644 --- a/django/templatetags/cache.py +++ b/django/templatetags/cache.py @@ -1,4 +1,4 @@ -from django.template import Library, Node, TemplateSyntaxError +from django.template import Library, Node, TemplateSyntaxError, Variable, VariableDoesNotExist from django.template import resolve_variable from django.core.cache import cache from django.utils.encoding import force_unicode @@ -6,20 +6,27 @@ from django.utils.encoding import force_unicode register = Library() class CacheNode(Node): - def __init__(self, nodelist, expire_time, fragment_name, vary_on): + def __init__(self, nodelist, expire_time_var, fragment_name, vary_on): self.nodelist = nodelist - self.expire_time = expire_time + self.expire_time_var = Variable(expire_time_var) self.fragment_name = fragment_name self.vary_on = vary_on def render(self, context): + try: + expire_time = self.expire_time_var.resolve(context) + except VariableDoesNotExist: + raise TemplateSyntaxError('"cache" tag got an unknkown variable: %r' % self.expire_time_var.var) + try: + expire_time = int(expire_time) + except (ValueError, TypeError): + raise TemplateSyntaxError('"cache" tag got a non-integer timeout value: %r' % expire_time) # Build a unicode key for this fragment and all vary-on's. - cache_key = u':'.join([self.fragment_name] + \ - [force_unicode(resolve_variable(var, context)) for var in self.vary_on]) + cache_key = u':'.join([self.fragment_name] + [force_unicode(resolve_variable(var, context)) for var in self.vary_on]) value = cache.get(cache_key) if value is None: value = self.nodelist.render(context) - cache.set(cache_key, value, self.expire_time) + cache.set(cache_key, value, expire_time) return value def do_cache(parser, token): @@ -48,10 +55,6 @@ def do_cache(parser, token): tokens = token.contents.split() if len(tokens) < 3: raise TemplateSyntaxError(u"'%r' tag requires at least 2 arguments." % tokens[0]) - try: - expire_time = int(tokens[1]) - except ValueError: - raise TemplateSyntaxError(u"First argument to '%r' must be an integer (got '%s')." % (tokens[0], tokens[1])) - return CacheNode(nodelist, expire_time, tokens[2], tokens[3:]) + return CacheNode(nodelist, tokens[1], tokens[2], tokens[3:]) register.tag('cache', do_cache) diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 4c278c0d8e..21a72f2d1e 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -343,3 +343,34 @@ class FileDict(dict): d = dict(self, content='<omitted>') return dict.__repr__(d) return dict.__repr__(self) + +class DictWrapper(dict): + """ + Wraps accesses to a dictionary so that certain values (those starting with + the specified prefix) are passed through a function before being returned. + The prefix is removed before looking up the real value. + + Used by the SQL construction code to ensure that values are correctly + quoted before being used. + """ + def __init__(self, data, func, prefix): + super(DictWrapper, self).__init__(data) + self.func = func + self.prefix = prefix + + def __getitem__(self, key): + """ + Retrieves the real value after stripping the prefix string (if + present). If the prefix is present, pass the value through self.func + before returning, otherwise return the raw value. + """ + if key.startswith(self.prefix): + use_func = True + key = key[len(self.prefix):] + else: + use_func = False + value = super(DictWrapper, self).__getitem__(key) + if use_func: + return self.func(value) + return value + diff --git a/django/utils/html.py b/django/utils/html.py index 07e4f0d3f4..747af52879 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -76,20 +76,20 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): """ Converts any URLs in text into clickable links. - Works on http://, https://, and www. links. Links can have trailing - punctuation (periods, commas, close-parens) and leading punctuation - (opening parens) and it'll still do the right thing. + Works on http://, https://, www. links and links ending in .org, .net or + .com. Links can have trailing punctuation (periods, commas, close-parens) + and leading punctuation (opening parens) and it'll still do the right + thing. If trim_url_limit is not None, the URLs in link text longer than this limit will truncated to trim_url_limit-3 characters and appended with an elipsis. If nofollow is True, the URLs in link text will get a rel="nofollow" attribute. + + If autoescape is True, the link text and URLs will get autoescaped. """ - if autoescape: - trim_url = lambda x, limit=trim_url_limit: conditional_escape(limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x) - else: - trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x + trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x safe_input = isinstance(text, SafeData) words = word_split_re.split(force_unicode(text)) nofollow_attr = nofollow and ' rel="nofollow"' or '' @@ -97,30 +97,30 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): match = punctuation_re.match(word) if match: lead, middle, trail = match.groups() - if safe_input: - middle = mark_safe(middle) - if middle.startswith('www.') or ('@' not in middle and not (middle.startswith('http://') or middle.startswith('https://')) and \ - len(middle) > 0 and middle[0] in string.ascii_letters + string.digits and \ - (middle.endswith('.org') or middle.endswith('.net') or middle.endswith('.com'))): - middle = 'http://%s' % middle + # Make URL we want to point to. + url = None if middle.startswith('http://') or middle.startswith('https://'): url = urlquote(middle, safe='/&=:;#?+*') - if autoescape and not safe_input: - url = escape(url) - trimmed_url = trim_url(middle) - middle = '<a href="%s"%s>%s</a>' % (url, nofollow_attr, - trimmed_url) - elif '@' in middle and not middle.startswith('www.') and \ - not ':' in middle and simple_email_re.match(middle): - if autoescape: - middle = conditional_escape(middle) - middle = '<a href="mailto:%s">%s</a>' % (middle, middle) - if lead + middle + trail != word: + elif middle.startswith('www.') or ('@' not in middle and \ + len(middle) > 0 and middle[0] in string.ascii_letters + string.digits and \ + (middle.endswith('.org') or middle.endswith('.net') or middle.endswith('.com'))): + url = urlquote('http://%s' % middle, safe='/&=:;#?+*') + elif '@' in middle and not ':' in middle and simple_email_re.match(middle): + url = 'mailto:%s' % middle + nofollow_attr = '' + # Make link. + if url: + trimmed = trim_url(middle) if autoescape and not safe_input: lead, trail = escape(lead), escape(trail) + url, trimmed = escape(url), escape(trimmed) + middle = '<a href="%s"%s>%s</a>' % (url, nofollow_attr, trimmed) words[i] = mark_safe('%s%s%s' % (lead, middle, trail)) - elif autoescape and not safe_input: - words[i] = escape(word) + else: + if safe_input: + words[i] = mark_safe(word) + elif autoescape: + words[i] = escape(word) elif safe_input: words[i] = mark_safe(word) elif autoescape: |
