# coding: utf-8
"""
PyLucid models
~~~~~~~~~~~~~~
Last commit info:
~~~~~~~~~~~~~~~~~
$LastChangedDate: $
$Rev: $
$Author: $
:copyleft: 2009 by the PyLucid team, see AUTHORS for more details.
:license: GNU GPL v3 or above, see LICENSE for more details.
"""
from django.db import models
from django.conf import settings
from django.contrib.auth.models import Group
from django.core.exceptions import ValidationError
from django.utils.translation.trans_real import parse_accept_lang_header
# http://code.google.com/p/django-tools/
from django_tools.middlewares import ThreadLocal
from django_tools.fields import LanguageCodeField
from pylucid_project.apps.pylucid.shortcuts import failsafe_message
from pylucid_project.apps.pylucid.models.base_models import UpdateInfoBaseModel,AutoSiteM2M
from pylucid_project.pylucid_plugins import update_journal
supported_languages = dict(settings.LANGUAGES)
TAG_INPUT_HELP_URL = \
"http://google.com/search?q=cache:django-tagging.googlecode.com/files/tagging-0.2-overview.html#tag-input"
ACCESSIBLE_LANG_CACHE = {}
class LanguageManager(models.Manager):
def filter_accessible(self, queryset, user):
""" filter all languages with can't accessible for the given user """
if user.is_anonymous():
# Anonymous user are in no user group
return queryset.filter(permitViewGroup__isnull=True)
if user.is_superuser:
# Superuser can see everything ;)
return queryset
# filter pages for not superuser and not anonymous
user_groups = user.groups.values_list('pk', flat=True)
if not user_groups:
# User is in no group
return queryset.filter(permitViewGroup__isnull=True)
# Filter out all view group
return queryset.filter(
models.Q(permitViewGroup__isnull=True) | models.Q(permitViewGroup__in=user_groups)
)
def all_accessible(self, user):
""" returns a QuerySet of all languages that the given user can access. """
queryset = self.model.on_site.all()
queryset = self.filter_accessible(queryset, user)
return queryset
def get_cached_languages(self, user):
""" returns a cached *list* of all languages that the given user can access. """
cache_key = user
if cache_key not in ACCESSIBLE_LANG_CACHE:
languages = self.all_accessible(user)
# if settings.PYLUCID.I18N_DEBUG:
# failsafe_message("all accessible languages from db: %r" % languages)
ACCESSIBLE_LANG_CACHE[cache_key] = list(languages)
# elif settings.PYLUCID.I18N_DEBUG:
# failsafe_message("all accessible languages from cache: %r" % ACCESSIBLE_LANG_CACHE[cache_key])
return ACCESSIBLE_LANG_CACHE[cache_key][:]
def get_choices(self):
""" return a tuple list for e.g. forms.ChoiceField """
return self.values_list('code', 'description')
default_lang_entry = None
def get_or_create_default(self, request):
"""
return Language instance with code from settings.LANGUAGE_CODE
Create if not exist.
"""
if self.default_lang_entry is None:
language_code = settings.LANGUAGE_CODE
self.default_lang_entry = self.get_from_code(request, language_code)
if self.default_lang_entry is None:
failsafe_message("Default language entry not in language list?")
self.default_lang_entry, created = self.get_or_create(
code=language_code, defaults={'description': language_code}
)
if created:
failsafe_message("Default language entry %r created." % self.default_lang_entry)
return self.default_lang_entry
def _get_default_language(self):
"""
return Languange instance with code from settings.LANGUAGE_CODE
Should normaly not used! Use request.PYLUCID.default_language !
e.g. needed in unittest
"""
if self.default_lang_entry is None:
language_code = settings.LANGUAGE_CODE
self.default_lang_entry = self.model.on_site.get(code=language_code)
return self.default_lang_entry
def _get_language_codes(self, request):
"""
Create a language code list.
- use all client accepted languages
- add current used language and system default language
"""
current_lang_code = getattr(request, "LANGUAGE_CODE", None)
default_lang_code = settings.LANGUAGE_CODE
accept_lang_codes = []
unsupported_lang_codes = []
fallback_lang_codes = []
accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
# if settings.PYLUCID.I18N_DEBUG:
# request.page_msg.info("HTTP_ACCEPT_LANGUAGE: %r" % accept)
if not accept:
if current_lang_code:
accept_lang_codes = [current_lang_code, default_lang_code]
else:
accept_lang_codes = [default_lang_code]
return accept_lang_codes, [], []
accept_lang_headers = parse_accept_lang_header(accept)
# print "accept_lang_headers:", accept_lang_headers
for accept_lang, unused in accept_lang_headers:
if "-" in accept_lang:
# remember the first part for later adding as a fallback language
fallback_lang = accept_lang.split("-", 1)[0]
if fallback_lang in supported_languages and fallback_lang not in fallback_lang_codes:
fallback_lang_codes.append(fallback_lang)
if accept_lang not in supported_languages:
unsupported_lang_codes.append(accept_lang)
continue
if accept_lang not in accept_lang_codes:
accept_lang_codes.append(accept_lang)
if current_lang_code:
# insert/move current language at the beginning
if not accept_lang_codes:
accept_lang_codes = [current_lang_code]
elif accept_lang_codes[0] != current_lang_code:
if current_lang_code in accept_lang_codes:
pos = accept_lang_codes.index(current_lang_code)
del(accept_lang_codes[pos])
accept_lang_codes.insert(0, current_lang_code)
# append fallback languages
for fallback_lang in fallback_lang_codes:
if fallback_lang not in accept_lang_codes:
accept_lang_codes.append(fallback_lang)
# append default language at the end
if default_lang_code not in accept_lang_codes:
accept_lang_codes.append(default_lang_code)
return accept_lang_codes, unsupported_lang_codes, fallback_lang_codes
def get_languages(self, request):
"""
Create a list of all languages sorted by client accept language priority.
added to request.PYLUCID.languages
Cache key: The language entry can have a permitViewGroup,
so we must use different caches for different users.
"""
if hasattr(request, "PYLUCID") and hasattr(request.PYLUCID, "languages"):
if settings.PYLUCID.I18N_DEBUG:
request.page_msg.info(
"return request.PYLUCID.languages: %r" % request.PYLUCID.languages
)
return request.PYLUCID.languages
user = request.user
languages = self.get_cached_languages(user)
if settings.PYLUCID.I18N_DEBUG:
failsafe_message("all accessible languages: %r" % languages)
accept_lang_codes, unsupported_lang_codes, fallback_lang_codes = self._get_language_codes(request)
if settings.PYLUCID.I18N_DEBUG:
request.page_msg.info("accept_lang_codes: %r" % accept_lang_codes)
request.page_msg.info("unsupported_lang_codes: %r" % unsupported_lang_codes)
request.page_msg.info("fallback_lang_codes: %r" % fallback_lang_codes)
# XXX: Test QuerySet order
# language_codes.sort()
# language_codes.sort(reverse=True)
# sort the language in the same order than language_codes list was.
# XXX: It there a better way to do this?
language_list = []
for language_code in accept_lang_codes:
for index, language in enumerate(languages):
if language.code.lower() == language_code.lower():
if language not in language_list:
language_list.append(language)
del(languages[index])
break
if languages:
# The Client has not all existing languages in his HTTP_ACCEPT_LANGUAGE
# Append the rest
if settings.PYLUCID.I18N_DEBUG:
request.page_msg.info(
"client not accepted languages to append: %s" % ", ".join([l.code for l in languages])
)
language_list += languages
if settings.PYLUCID.I18N_DEBUG:
request.page_msg.info("language_list: %s" % ", ".join([l.code for l in language_list]))
return language_list
def get_from_code(self, request, language_code):
language_list = self.get_languages(request)
for language in language_list:
if language.code.lower() == language_code.lower():
return language
def get_current(self, request=None):
""" return client Language instance, if not available, use get_default_lang_entry() """
if request == None:
request = ThreadLocal.get_current_request()
if request == None:
# no request available, e.g. loading fixtures
return self._get_default_language()
language_list = self.get_languages(request)
return language_list[0]
# if request:
# if hasattr(request, "PYLUCID"):
# return request.PYLUCID.current_language
#
# if hasattr(request, "LANGUAGE_CODE"):
# lang_code = request.LANGUAGE_CODE
# if "-" in lang_code:
# lang_code = lang_code.split("-", 1)[0]
# try:
# return self.get(code=lang_code)
# except Language.DoesNotExist:
# if settings.PYLUCID.I18N_DEBUG:
# msg = (
# 'Favored language "%s" does not exist -> use default lang from system preferences'
# ) % request.LANGUAGE_CODE
# failsafe_message(msg)
#
# return self.get_or_create_default(request)
class Language(AutoSiteM2M, UpdateInfoBaseModel):
"""
inherited attributes from AutoSiteM2M:
sites -> ManyToManyField to Site
on_site -> sites.managers.CurrentSiteManager instance
inherited attributes from UpdateInfoBaseModel:
createtime -> datetime of creation
lastupdatetime -> datetime of the last change
createby -> ForeignKey to user who creaded this entry
lastupdateby -> ForeignKey to user who has edited this entry
"""
objects = LanguageManager()
code = LanguageCodeField(unique=True, max_length=10)
description = models.CharField(max_length=150, blank=True,
help_text="Description of the Language (filled automaticly)"
)
permitViewGroup = models.ForeignKey(Group, related_name="%(class)s_permitViewGroup",
help_text="Limit viewable to a group for a complete language section?",
null=True, blank=True,
)
def clean_fields(self, exclude):
message_dict = {}
if "code" not in exclude and self.code not in supported_languages:
message_dict["code"] = (
"Language is not in settings.LANGUAGES!",
"Supported languages are: %s" % ",".join(sorted(supported_languages.keys()))
)
if message_dict:
raise ValidationError(message_dict)
def save(self, *args, **kwargs):
global ACCESSIBLE_LANG_CACHE
ACCESSIBLE_LANG_CACHE = {}
if not self.description:
self.description = supported_languages[self.code]
super(Language, self).save(*args, **kwargs)
def __unicode__(self):
return u"Language %s - %s" % (self.code, self.description)
class Meta:
app_label = 'pylucid'
ordering = ("code",)
|