Permalink
Browse files

Merge pull request #1414 from digi604/new_i18n

Removes Multilingual Middleware and updates the i18n configuration of the cms. CMS_LANGUAGES has been completely overhauled and all the old i18n settings removed.
  • Loading branch information...
2 parents 987d7de + 237609f commit 4b968ce1ff5715f5011bd6d622a84be453977f46 @digi604 digi604 committed Oct 3, 2012
Showing with 1,623 additions and 1,231 deletions.
  1. +3 −3 .travis.yml
  2. +1 −0 AUTHORS
  3. +10 −0 CHANGELOG.txt
  4. +4 −9 cms/admin/forms.py
  5. +7 −17 cms/admin/pageadmin.py
  6. +3 −2 cms/api.py
  7. +2 −1 cms/app_base.py
  8. +1 −1 cms/apphook_pool.py
  9. +69 −55 cms/appresolver.py
  10. +1 −1 cms/cms_toolbar.py
  11. +7 −6 cms/conf/__init__.py
  12. +32 −21 cms/conf/global_settings.py
  13. +130 −19 cms/conf/patch.py
  14. +18 −3 cms/exceptions.py
  15. +3 −3 cms/menu.py
  16. +0 −149 cms/middleware/multilingual.py
  17. +15 −2 cms/middleware/page.py
  18. +0 −41 cms/models/__init__.py
  19. +2 −2 cms/models/pagemodel.py
  20. +3 −2 cms/models/pluginmodel.py
  21. +1 −1 cms/models/titlemodels.py
  22. +1 −1 cms/plugin_rendering.py
  23. +1 −1 cms/plugins/inherit/cms_plugins.py
  24. +2 −2 cms/plugins/inherit/models.py
  25. +11 −2 cms/plugins/utils.py
  26. +4 −4 cms/templatetags/cms_admin.py
  27. +66 −28 cms/test_utils/cli.py
  28. +10 −1 cms/test_utils/project/nonroot_urls.py
  29. +13 −1 cms/test_utils/project/sampleapp/cms_app.py
  30. BIN cms/test_utils/project/sampleapp/media/sampleapp/img/gift.jpg
  31. +9 −0 cms/test_utils/project/sampleapp/ns_urls.py
  32. +2 −0 cms/test_utils/project/sampleapp/templates/sampleapp/app.html
  33. +1 −1 cms/test_utils/project/sampleapp/templates/sampleapp/category_view.html
  34. +8 −4 cms/test_utils/project/sampleapp/templates/sampleapp/home.html
  35. +2 −1 cms/test_utils/project/sampleapp/urls.py
  36. +5 −0 cms/test_utils/project/sampleapp/views.py
  37. +7 −0 cms/test_utils/project/second_urls_for_apphook_tests.py
  38. +18 −6 cms/test_utils/project/urls.py
  39. +8 −0 cms/test_utils/project/urls_for_apphook_tests.py
  40. +3 −1 cms/test_utils/testcases.py
  41. +0 −1 cms/tests/__init__.py
  42. +232 −69 cms/tests/apphooks.py
  43. +25 −17 cms/tests/menu.py
  44. +1 −1 cms/tests/menu_page_viewperm.py
  45. +6 −6 cms/tests/menu_page_viewperm_staff.py
  46. +17 −2 cms/tests/menu_utils.py
  47. +0 −132 cms/tests/middleware.py
  48. +115 −59 cms/tests/multilingual.py
  49. +1 −1 cms/tests/nested_plugins.py
  50. +10 −22 cms/tests/nonroot.py
  51. +19 −17 cms/tests/page.py
  52. +16 −6 cms/tests/permmod.py
  53. +0 −45 cms/tests/rendering.py
  54. +5 −3 cms/tests/reversion_tests.py
  55. +4 −4 cms/tests/site.py
  56. +14 −12 cms/tests/templatetags.py
  57. +1 −1 cms/tests/toolbar.py
  58. +13 −23 cms/tests/views.py
  59. +1 −2 cms/urls.py
  60. +4 −5 cms/utils/__init__.py
  61. +2 −7 cms/utils/admin.py
  62. +124 −24 cms/utils/i18n.py
  63. +10 −8 cms/utils/page.py
  64. +14 −5 cms/utils/page_resolver.py
  65. +63 −38 cms/views.py
  66. +33 −92 docs/advanced/i18n.rst
  67. +8 −78 docs/concepts/multiple_languages.rst
  68. +69 −25 docs/extending_cms/app_integration.rst
  69. +89 −54 docs/getting_started/configuration.rst
  70. +2 −0 docs/getting_started/installation.rst
  71. +6 −4 docs/getting_started/tutorial.rst
  72. +1 −0 docs/index.rst
  73. +152 −0 docs/upgrade/2.4.rst
  74. +1 −7 menus/base.py
  75. +5 −2 menus/templates/menu/language_chooser.html
  76. +14 −0 menus/templatetags/i18n_compat.py
  77. +16 −37 menus/templatetags/menu_tags.py
  78. +22 −9 menus/utils.py
  79. +19 −19 runtestserver.py
  80. +3 −1 test_requirements/django-1.3.txt
  81. +2 −1 test_requirements/django-1.4.txt
  82. +1 −1 test_requirements/requirements_base.txt
View
@@ -4,10 +4,10 @@ python:
- "2.6"
- "2.7"
env:
- - DJANGO="django>=1.3,<1.4"
- - DJANGO="django>=1.4,<1.5"
+ - DJANGO=test_requirements/django-1.3.txt
+ - DJANGO=test_requirements/django-1.4.txt
install:
- - pip install -q $DJANGO django-mptt==0.5.1 django-reversion==1.6 django-classy-tags==0.3.4.1 django-sekizai==0.6.1 html5lib==0.95 jinja2==2.6 PIL==1.1.7 pygments==1.5 south==0.7.5 sphinx==1.1.3 argparse
+ - pip install -q -r $DJANGO --use-mirrors
script:
python runtests.py
notifications:
View
@@ -92,6 +92,7 @@ Contributors (in alphabetical order):
* Ian Lewis
* indexofire
* Ionel Cristian Maries
+* Iván Raskovsky
* Ivan Vershigora
* izi
* Jameel Al-Aziz
View
@@ -110,6 +110,16 @@
- fixed an incompatibility with Python 2.5
+==== 2.4.0 ===-
+
+- CMS_LANGUAGE setting has changed
+- CMS_HIDE_UNTRANSLATED setting removed
+- CMS_LANGUAGE_FALLBACK setting removed
+- CMS_LANGUAGE_CONF setting removed
+- CMS_SITE_LANGUAGES setting removed
+- CMS_FRONTEND_LANGUAGES setting removed
+- MultilingualMiddleware has been removed
+
View
@@ -3,6 +3,7 @@
from cms.forms.widgets import UserSelectAdminWidget
from cms.models import (Page, PagePermission, PageUser, ACCESS_PAGE,
PageUserGroup)
+from cms.utils.i18n import get_language_tuple, get_language_list
from cms.utils.mail import mail_page_user_change
from cms.utils.page import is_valid_page_slug
from cms.utils.page_resolver import get_page_from_path, is_valid_url
@@ -60,7 +61,7 @@ class PageAddForm(forms.ModelForm):
help_text=_('The default title'))
slug = forms.CharField(label=_("Slug"), widget=forms.TextInput(),
help_text=_('The part of the title that is used in the URL'))
- language = forms.ChoiceField(label=_("Language"), choices=settings.CMS_LANGUAGES,
+ language = forms.ChoiceField(label=_("Language"), choices=get_language_tuple(),
help_text=_('The current language of the content fields.'))
class Meta:
@@ -74,13 +75,7 @@ def __init__(self, *args, **kwargs):
if not self.fields['site'].initial:
self.fields['site'].initial = Site.objects.get_current().pk
site_id = self.fields['site'].initial
- languages = []
- language_mappings = dict(settings.LANGUAGES)
- if site_id in settings.CMS_SITE_LANGUAGES:
- for lang in settings.CMS_SITE_LANGUAGES[site_id]:
- languages.append((lang, language_mappings.get(lang, lang)))
- else:
- languages = settings.CMS_LANGUAGES
+ languages = get_language_tuple(site_id)
self.fields['language'].choices = languages
if not self.fields['language'].initial:
self.fields['language'].initial = get_language()
@@ -139,7 +134,7 @@ def clean_slug(self):
def clean_language(self):
language = self.cleaned_data['language']
- if not language in dict(settings.CMS_LANGUAGES).keys():
+ if not language in get_language_list():
raise ValidationError("Given language does not match language settings.")
return language
@@ -17,6 +17,7 @@
from cms.utils import (copy_plugins, helpers, moderator, permissions, plugins,
get_template_from_request, get_language_from_request,
placeholder as placeholder_utils, admin as admin_utils, cms_static_url)
+from cms.utils.i18n import get_language_dict, get_language_list, get_language_tuple, get_language_object
from cms.utils.page_resolver import is_valid_url
from cms.utils.admin import jsonify_request
from cms.utils.permissions import has_plugin_permission
@@ -441,13 +442,13 @@ def get_form(self, request, obj=None, **kwargs):
installed_plugins = plugin_pool.get_all_plugins(placeholder_name, obj)
plugin_list = CMSPlugin.objects.filter(language=language, placeholder=placeholder, parent=None).order_by('position')
other_plugins = CMSPlugin.objects.filter(placeholder=placeholder, parent=None).exclude(language=language)
- dict_cms_languages = dict(settings.CMS_LANGUAGES)
+ dict_cms_languages = get_language_dict()
for plugin in other_plugins:
if (not plugin.language in copy_languages) and (plugin.language in dict_cms_languages):
copy_languages[plugin.language] = dict_cms_languages[plugin.language]
language = get_language_from_request(request, obj)
- if copy_languages and len(settings.CMS_LANGUAGES) > 1:
+ if copy_languages and len(get_language_list()) > 1:
show_copy = True
widget = PluginEditor(attrs={
'installed': installed_plugins,
@@ -566,14 +567,7 @@ def _get_site_languages(self, obj):
site_id = None
if obj:
site_id = obj.site_id
- languages = []
- if site_id and site_id in settings.CMS_SITE_LANGUAGES:
- for lang in settings.CMS_SITE_LANGUAGES[site_id]:
- lang_label = dict(settings.CMS_LANGUAGES).get(lang, dict(settings.LANGUAGES).get(lang, lang))
- languages.append((lang, lang_label))
- else:
- languages = settings.CMS_LANGUAGES
- return languages
+ return get_language_tuple(site_id)
def update_language_tab_context(self, request, obj, context=None):
if not context:
@@ -676,11 +670,7 @@ def changelist_view(self, request, extra_context=None):
site_id = int(site_id)
# languages
- languages = []
- if site_id and site_id in settings.CMS_SITE_LANGUAGES:
- languages = settings.CMS_SITE_LANGUAGES[site_id]
- else:
- languages = [lang[0] for lang in settings.CMS_LANGUAGES]
+ languages = get_language_list(site_id)
# parse the cookie that saves which page trees have
# been opened already and extracts the page ID
@@ -986,7 +976,7 @@ def delete_translation(self, request, object_id, extra_context=None):
raise PermissionDenied
message = _('Title and plugins with language %(language)s was deleted') % {
- 'language': [name for code, name in settings.CMS_LANGUAGES if code == language][0]
+ 'language': get_language_object(language)['name']
}
self.log_change(request, titleobj, message)
self.message_user(request, message)
@@ -1189,7 +1179,7 @@ def copy_plugins(self, request):
if not page.has_change_permission(request):
return HttpResponseForbidden(ugettext("You do not have permission to change this page"))
- if not language or not language in [ lang[0] for lang in settings.CMS_LANGUAGES ]:
+ if not language or not language in get_language_list():
return HttpResponseBadRequest(ugettext("Language must be set to a supported language!"))
if language == copy_from:
return HttpResponseBadRequest(ugettext("Language must be different than the copied language!"))
View
@@ -7,6 +7,7 @@
calling these methods!
"""
import datetime
+from cms.utils.i18n import get_language_list
from django.conf import settings
from django.contrib.auth.models import User
@@ -125,7 +126,7 @@ def create_page(title, template, language, menu_title=None, slug=None,
assert template in [tpl[0] for tpl in settings.CMS_TEMPLATES]
# validate language:
- assert language in [lang[0] for lang in settings.CMS_LANGUAGES]
+ assert language in get_language_list()
# set default slug:
if not slug:
@@ -220,7 +221,7 @@ def create_title(language, title, page, menu_title=None, slug=None,
See docs/extending_cms/api_reference.rst for more info
"""
# validate language:
- assert language in [lang[0] for lang in settings.CMS_LANGUAGES]
+ assert language in get_language_list()
# validate page
assert isinstance(page, Page)
View
@@ -2,4 +2,5 @@
class CMSApp(object):
name = None
urls = None
- menus = []
+ menus = []
+ app_name = None
View
@@ -43,7 +43,7 @@ def register(self, app):
"but the 'menus' attribute is empty, did you make a typo?")
name = app.__name__
if name in self.apps.keys():
- raise AppAlreadyRegistered, "[%s] an cms app with this name is already registered" % name
+ raise AppAlreadyRegistered, "[%s] a cms app with this name is already registered" % name
self.apps[name] = app
def get_apphooks(self):
View
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
+from __future__ import with_statement
from cms.apphook_pool import apphook_pool
+from cms.utils.i18n import force_language, get_language_list
from cms.utils.moderator import get_page_queryset
from django.conf import settings
@@ -10,6 +12,7 @@
RegexURLPattern
from django.db.models import Q
from django.utils.importlib import import_module
+from django.utils.translation import get_language
APP_RESOLVERS = []
@@ -18,8 +21,8 @@ def clear_app_resolvers():
APP_RESOLVERS = []
def applications_page_check(request, current_page=None, path=None):
- """Tries to find if given path was resolved over application.
- Applications have higher priority than other cms pages.
+ """Tries to find if given path was resolved over application.
+ Applications have higher priority than other cms pages.
"""
if current_page:
return current_page
@@ -28,6 +31,9 @@ def applications_page_check(request, current_page=None, path=None):
# This removes the non-CMS part of the URL.
path = request.path.replace(reverse('pages-root'), '', 1)
# check if application resolver can resolve this
+ for lang in get_language_list():
+ if path.startswith(lang + "/"):
+ path = path[len(lang + "/"):]
for resolver in APP_RESOLVERS:
try:
page_id = resolver.resolve_page_id(path)
@@ -46,30 +52,44 @@ def applications_page_check(request, current_page=None, path=None):
class AppRegexURLResolver(RegexURLResolver):
page_id = None
- url_patterns = None
-
+ url_patterns_dict = {}
+
+ @property
+ def url_patterns(self):
+ language = get_language()
+ if language in self.url_patterns_dict:
+ return self.url_patterns_dict[language]
+ else:
+ return []
+
def resolve_page_id(self, path):
"""Resolves requested path similar way how resolve does, but instead
- of return callback,.. returns page_id to which was application
+ of return callback,.. returns page_id to which was application
assigned.
"""
tried = []
match = self.regex.search(path)
if match:
new_path = path[match.end():]
for pattern in self.url_patterns:
- try:
- sub_match = pattern.resolve(new_path)
- except Resolver404, e:
- if 'tried' in e.args[0]:
- tried.extend([(pattern.regex.pattern + ' ' + t) for t in e.args[0]['tried']])
- elif 'path' in e.args[0]:
- tried.extend([(pattern.regex.pattern + ' ' + t) for t in e.args[0]['path']])
+ if isinstance(pattern, AppRegexURLResolver):
+ try:
+ return pattern.resolve_page_id(new_path)
+ except Resolver404:
+ pass
else:
- if sub_match:
- return pattern.page_id
- tried.append(pattern.regex.pattern)
- raise Resolver404, {'tried': tried, 'path': new_path}
+ try:
+ sub_match = pattern.resolve(new_path)
+ except Resolver404, e:
+ if 'tried' in e.args[0]:
+ tried.extend([[pattern] + t for t in e.args[0]['tried']])
+ elif 'path' in e.args[0]:
+ tried.extend([[pattern] + t for t in e.args[0]['path']])
+ else:
+ if sub_match:
+ return pattern.page_id
+ tried.append(pattern.regex.pattern)
+ raise Resolver404, {'tried': tried, 'path': new_path}
def recurse_patterns(path, pattern_list, page_id):
@@ -117,7 +137,7 @@ def get_app_urls(urls):
yield getattr(mod, 'urlpatterns')
else:
yield urlconf
-
+
def get_patterns_for_title(path, title):
"""
@@ -138,12 +158,12 @@ def get_patterns_for_title(path, title):
def get_app_patterns():
"""
Get a list of patterns for all hooked apps.
-
+
How this works:
-
+
By looking through all titles with an app hook (application_urls) we find all
urlconf modules we have to hook into titles.
-
+
If we use the ML URL Middleware, we namespace those patterns with the title
language.
@@ -156,54 +176,48 @@ def get_app_patterns():
except Site.DoesNotExist:
current_site = None
included = []
-
+
# we don't have a request here so get_page_queryset() can't be used,
- # so, if CMS_MODERATOR, use, public() queryset, otherwise
- # use draft(). This can be done, because url patterns are used just
+ # so, if CMS_MODERATOR, use, public() queryset, otherwise
+ # use draft(). This can be done, because url patterns are used just
# in frontend
is_draft = not settings.CMS_MODERATOR
title_qs = Title.objects.filter(page__publisher_is_draft=is_draft, page__site=current_site)
-
- if 'cms.middleware.multilingual.MultilingualURLMiddleware' in settings.MIDDLEWARE_CLASSES:
- use_namespaces = True
- hooked_applications = {}
- else:
- use_namespaces = False
- hooked_applications = []
-
+
+ hooked_applications = {}
+
# Loop over all titles with an application hooked to them
for title in title_qs.exclude(application_urls=None).exclude(application_urls='').select_related():
path = title.path
- if use_namespaces:
- mixid = "%s:%s:%s" % (path + "/", title.application_urls, title.language)
- else:
- mixid = "%s:%s" % (path + "/", title.application_urls)
- if mixid in included:
+ mix_id = "%s:%s:%s" % (path + "/", title.application_urls, title.language)
+ if mix_id in included:
# don't add the same thing twice
- continue
+ continue
if not settings.APPEND_SLASH:
path += '/'
- if use_namespaces:
- if title.language not in hooked_applications:
- hooked_applications[title.language] = []
- hooked_applications[title.language] += get_patterns_for_title(path, title)
+ if title.page_id not in hooked_applications:
+ hooked_applications[title.page_id] = {}
+ app = apphook_pool.get_apphook(title.application_urls)
+ if app.app_name:
+ inst_ns = title.page.reverse_id if title.page.reverse_id else app.app_name
+ app_ns = app.app_name, inst_ns
else:
- hooked_applications += get_patterns_for_title(path, title)
- included.append(mixid)
+ app_ns = None, None
+ with force_language(title.language):
+ hooked_applications[title.page_id][title.language] = (app_ns, get_patterns_for_title(path, title))
+ included.append(mix_id)
# Build the app patterns to be included in the cms urlconfs
app_patterns = []
- if use_namespaces:
- for ns, currentpatterns in hooked_applications.items():
- extra_patterns = patterns('', *currentpatterns)
- resolver = AppRegexURLResolver(r'', 'app_resolver', namespace=ns)
- resolver.url_patterns = extra_patterns
- app_patterns.append(resolver)
- APP_RESOLVERS.append(resolver)
- else:
- extra_patterns = patterns('', *hooked_applications)
- resolver = AppRegexURLResolver(r'', 'app_resolver')
- resolver.url_patterns = extra_patterns
+ for page_id in hooked_applications.keys():
+ resolver = None
+ for lang in hooked_applications[page_id].keys():
+ (app_ns, inst_ns), current_patterns = hooked_applications[page_id][lang]
+ if not resolver:
+ resolver = AppRegexURLResolver(r'', 'app_resolver', app_name=app_ns, namespace=inst_ns)
+ resolver.page_id = page_id
+ extra_patterns = patterns('', *current_patterns)
+ resolver.url_patterns_dict[lang] = extra_patterns
app_patterns.append(resolver)
APP_RESOLVERS.append(resolver)
- return app_patterns
+ return app_patterns
Oops, something went wrong. Retry.

1 comment on commit 4b968ce

@ojii
Collaborator
ojii commented on 4b968ce Oct 3, 2012

It would be really cool if you didn't just merge your own pull requests, especially big ones like this...

Please sign in to comment.