From ebd18327208389d948c139540992bee3f61f440c Mon Sep 17 00:00:00 2001 From: John Weaver Date: Wed, 4 Jul 2012 12:52:37 -0700 Subject: [PATCH 1/8] Fix Django 1.4 breakage. Passing a translation as the first positional argument causes attname to be a __proxy__ instance, which breaks when the metaclass attempts to build a docstring for the models because it can't implictly convert them to strings. --- tos/models.py | 70 +++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/tos/models.py b/tos/models.py index 6bb5f2e..d90517f 100644 --- a/tos/models.py +++ b/tos/models.py @@ -1,68 +1,72 @@ -from django.core.exceptions import ValidationError +from django.core.exceptions import ValidationError from django.db import models from django.contrib.auth.models import User -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext_lazy as _ + + +class NoActiveTermsOfService(ValidationError): + pass -class NoActiveTermsOfService(ValidationError): pass class BaseModel(models.Model): - - created = models.DateTimeField(auto_now_add=True, editable=False) - modified = models.DateTimeField(auto_now=True, editable=False) - + created = models.DateTimeField(auto_now_add=True, editable=False) + modified = models.DateTimeField(auto_now=True, editable=False) + class Meta: abstract = True - + + class TermsOfServiceManager(models.Manager): - def get_current_tos(self): try: - return self.get(active=True) + return self.get(active=True) except self.model.DoesNotExist: raise NoActiveTermsOfService('Please create an active Terms-of-Service') class TermsOfService(BaseModel): + active = models.BooleanField(verbose_name=_('active'), + _('Only one terms of service is allowed to be active')) + content = models.TextField(verbose_name=_('content'), blank=True) + objects = TermsOfServiceManager() - active = models.BooleanField(_('active'), _('Only one terms of service is allowed to be active')) - content = models.TextField(_('content'), blank=True) - objects = TermsOfServiceManager() - - class Meta: + class Meta: get_latest_by = 'created' ordering = ('-created',) - verbose_name=_('Terms of Service') - verbose_name_plural=_('Terms of Service') + verbose_name = _('Terms of Service') + verbose_name_plural = _('Terms of Service') def __unicode__(self): active = 'inactive' if self.active: - active = 'active' + active = 'active' return '%s: %s' % (self.created, active) - - def save(self, *args, **kwargs): - """ Ensure we're being saved properly """ + + def save(self, *args, **kwargs): + """ Ensure we're being saved properly """ if self.active: TermsOfService.objects.exclude(id=self.id).update(active=False) - + else: - if not TermsOfService.objects.exclude(id=self.id).filter(active=True): + if not TermsOfService.objects\ + .exclude(id=self.id)\ + .filter(active=True): raise NoActiveTermsOfService('One of the terms of service must be marked active') - super(TermsOfService,self).save(*args, **kwargs) - + super(TermsOfService, self).save(*args, **kwargs) + + class UserAgreement(BaseModel): - terms_of_service = models.ForeignKey(TermsOfService, related_name='terms') - user = models.ForeignKey(User, related_name='user_agreement') - + user = models.ForeignKey(User, related_name='user_agreement') + def __unicode__(self): - return '%s agreed to TOS: %s ' % (self.user.username, self.terms_of_service.__unicode__()) - - + return u'%s agreed to TOS: %s' % (self.user.username, + unicode(self.terms_of_service)) + + def has_user_agreed_latest_tos(user): - if UserAgreement.objects.filter(terms_of_service=TermsOfService.objects.get_current_tos(),user=user): + if UserAgreement.objects.filter(terms_of_service=TermsOfService.objects.get_current_tos(), user=user): return True return False - From 094f24f4cdbb646c70fde75b983779f78a71c0fe Mon Sep 17 00:00:00 2001 From: John Weaver Date: Wed, 4 Jul 2012 12:55:28 -0700 Subject: [PATCH 2/8] Forgot to make second argument keyword. --- tos/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tos/models.py b/tos/models.py index d90517f..d1ac4fb 100644 --- a/tos/models.py +++ b/tos/models.py @@ -21,12 +21,12 @@ class TermsOfServiceManager(models.Manager): try: return self.get(active=True) except self.model.DoesNotExist: - raise NoActiveTermsOfService('Please create an active Terms-of-Service') + raise NoActiveTermsOfService(u'Please create an active Terms-of-Service') class TermsOfService(BaseModel): active = models.BooleanField(verbose_name=_('active'), - _('Only one terms of service is allowed to be active')) + help_text=_(u'Only one terms of service is allowed to be active')) content = models.TextField(verbose_name=_('content'), blank=True) objects = TermsOfServiceManager() @@ -52,7 +52,7 @@ class TermsOfService(BaseModel): if not TermsOfService.objects\ .exclude(id=self.id)\ .filter(active=True): - raise NoActiveTermsOfService('One of the terms of service must be marked active') + raise NoActiveTermsOfService(u'One of the terms of service must be marked active') super(TermsOfService, self).save(*args, **kwargs) From 2b3efcfcb043e389c73f72054433a8c377b022c6 Mon Sep 17 00:00:00 2001 From: Krzysztof Dorosz Date: Sun, 14 Oct 2012 23:41:35 +0200 Subject: [PATCH 3/8] This commit fixes bug: 1) in urls a view "tos" has context object evaluated only in a runtime. This means that even if tos was changed, the "tos" view shows always the same old tos object. 2) check_tos view uses now messages framework instead of {{note}} 3) Support for translation in "you cannot login..." message added The following code was tested with django 1.4 and it is reported to work well. --- tos/urls.py | 24 ++++-------------------- tos/views.py | 26 +++++++++++++++++--------- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/tos/urls.py b/tos/urls.py index 73924f8..27cbbf4 100644 --- a/tos/urls.py +++ b/tos/urls.py @@ -1,26 +1,10 @@ -from django.conf.urls.defaults import * -from django.views.generic.simple import direct_to_template - -from tos.models import TermsOfService -from tos.views import check_tos +from django.conf.urls import url, patterns +from tos.views import check_tos, TosView urlpatterns = patterns('', # Terms of Service conform - url( - regex = '^confirm/$', - view = check_tos, - name = 'tos_check_tos', - ), + url(r'^confirm/$', check_tos, name='tos_check_tos'), # Terms of service simple display - url( - regex = '^$', - view = direct_to_template, - kwargs = {'template': 'tos/tos.html', - 'extra_context':{ - 'tos':TermsOfService.objects.get_current_tos() - }, - }, - name = 'tos', - ), + url(r'^$', TosView.as_view(), name='tos'), ) \ No newline at end of file diff --git a/tos/views.py b/tos/views.py index 89e847a..a85032c 100644 --- a/tos/views.py +++ b/tos/views.py @@ -1,17 +1,29 @@ +from django.views.generic import TemplateView +import re from django.conf import settings +from django.contrib import messages from django.contrib.auth import login as auth_login from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.forms import AuthenticationForm -from django.contrib.auth.models import User from django.contrib.sites.models import Site, RequestSite from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from django.template import RequestContext from django.views.decorators.cache import never_cache from django.views.decorators.csrf import csrf_protect +from django.utils.translation import ugettext_lazy as _ from tos.models import has_user_agreed_latest_tos, TermsOfService, UserAgreement +class TosView(TemplateView): + template_name = "tos/tos.html" + + def get_context_data(self, **kwargs): + context = super(TosView, self).get_context_data(**kwargs) + context['tos'] = TermsOfService.objects.get_current_tos() + return context + + def _redirect_to(redirect_to): """ Moved redirect_to logic here to avoid duplication in views""" @@ -31,13 +43,9 @@ def _redirect_to(redirect_to): @never_cache def check_tos(request, template_name='tos/tos_check.html', redirect_field_name=REDIRECT_FIELD_NAME,): - + redirect_to = _redirect_to(request.REQUEST.get(redirect_field_name, '')) - - note="" - tos = TermsOfService.objects.get_current_tos() - if request.method=="POST": if request.POST.get("accept", "") == "accept": user = request.session['tos_user'] @@ -53,12 +61,12 @@ def check_tos(request, template_name='tos/tos_check.html', return HttpResponseRedirect(redirect_to) else: - note="You cannot login without agreeing to the terms of this site." + messages.error(request, _(u"You cannot login without agreeing to the terms of this site.")) + - return render_to_response(template_name, { 'tos':tos, - 'note':note, + redirect_field_name: redirect_to, }, context_instance=RequestContext(request)) From 705356cb53eb092916f515b8d2cbd2e342e7d0ae Mon Sep 17 00:00:00 2001 From: Krzysztof Dorosz Date: Sun, 14 Oct 2012 23:45:49 +0200 Subject: [PATCH 4/8] - Bumping version to 1.1 - Adding me to AUTHORS :) - Correcting login url name to auth_login to keep up with django standards --- AUTHORS.txt | 1 + README.rst | 4 ++-- tos/__init__.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/AUTHORS.txt b/AUTHORS.txt index 0de79ad..9f2e772 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -4,3 +4,4 @@ The following is a list of much appreciated contributors: Daniel Greenfeld Frank Wiles +Krzysztof Dorosz diff --git a/README.rst b/README.rst index 263666f..1c93ea0 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,6 @@ Installation # terms of service links urlpatterns += patterns('', - (r'^login/$', 'tos.views.login', {}, 'login',), - (r'^terms-of-service/', include('tos.urls')), + url(r'^login/$', 'tos.views.login', {}, 'auth_login',), + url(r'^terms-of-service/', include('tos.urls')), ) \ No newline at end of file diff --git a/tos/__init__.py b/tos/__init__.py index 93c4c7c..e8e61c0 100644 --- a/tos/__init__.py +++ b/tos/__init__.py @@ -1 +1 @@ -VERSION = (0, 1, 0) \ No newline at end of file +VERSION = (0, 1, 1) \ No newline at end of file From b3f1dce50a6fd39f708b198096015ad67d91f0ee Mon Sep 17 00:00:00 2001 From: Krzysztof Dorosz Date: Fri, 4 Jan 2013 10:25:06 +0100 Subject: [PATCH 5/8] Fixing redirect field name bug on login form --- tos/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tos/views.py b/tos/views.py index a85032c..aef0e77 100644 --- a/tos/views.py +++ b/tos/views.py @@ -123,7 +123,7 @@ def login(request, template_name='registration/login.html', return render_to_response(template_name, { 'form': form, - 'redirect_field_name': redirect_to, + redirect_field_name: redirect_to, 'site': current_site, 'site_name': current_site.name, }, context_instance=RequestContext(request)) From 41eb35066d776b16b147aeb112b87f56311cef0b Mon Sep 17 00:00:00 2001 From: John Weaver Date: Wed, 30 Jan 2013 09:14:48 -0800 Subject: [PATCH 6/8] Load url template tag from future for support of Django versions 1.3 through 1.5. --- tos/templates/tos/tos_check.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tos/templates/tos/tos_check.html b/tos/templates/tos/tos_check.html index f21c777..9987ff2 100644 --- a/tos/templates/tos/tos_check.html +++ b/tos/templates/tos/tos_check.html @@ -1,3 +1,4 @@ +{% load url from future %} {% if note %}

{{ note }} {% else %} @@ -8,7 +9,7 @@

Accept Terms of Service?

-
+ {% csrf_token %} From 24f2e4a61f19faef7dec06751d1570abebf6a20d Mon Sep 17 00:00:00 2001 From: Krzysztof Dorosz Date: Sun, 17 Feb 2013 20:56:19 +0100 Subject: [PATCH 7/8] django-tos i18n support using django-modeltranslation --- README.rst | 79 ++++++++++++++++++++++++++++++++++++++++- tos_i18n/__init__.py | 0 tos_i18n/admin.py | 13 +++++++ tos_i18n/models.py | 3 ++ tos_i18n/tests.py | 0 tos_i18n/translation.py | 9 +++++ tos_i18n/views.py | 1 + 7 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 tos_i18n/__init__.py create mode 100644 tos_i18n/admin.py create mode 100644 tos_i18n/models.py create mode 100644 tos_i18n/tests.py create mode 100644 tos_i18n/translation.py create mode 100644 tos_i18n/views.py diff --git a/README.rst b/README.rst index 1c93ea0..4724fb6 100644 --- a/README.rst +++ b/README.rst @@ -24,4 +24,81 @@ Installation urlpatterns += patterns('', url(r'^login/$', 'tos.views.login', {}, 'auth_login',), url(r'^terms-of-service/', include('tos.urls')), - ) \ No newline at end of file + ) + + +=============== +django-tos-i18n +=============== + +django-tos internationalization using django-modeltranslation. + +Installation +============ + +Assuming you have correctly installed django-tos in your app you only need to add following apps to ``INSTALLED_APPS``:: + + INSTALLED_APPS += ('modeltranslation', 'tos_i18n') + +and also you should also define your languages in django ``LANG`` variable, eg.:: + + LANGUAGES = ( + ('pl', 'Polski'), + ('en', 'English'), + ) + +Please note that adding those to ``INSTALLED_APPS`` **changes** django models. Concretely it adds for every registered ``field`` that should translated, additional fields with name ``field_``, e.g. for given model:: + + class MyModel(models.Model): + name = models.CharField(max_length=10) + +There will be generated fields: ``name`` , ``name_en``, ``name_pl``. + +You should probably migrate your database, using South is recommended. Migrations should be kept in your local project. + +How to migrate tos with South +````````````````````````````` + +Here is some step-by-step example how to turn your legacy django-tos instalation synced using syncdb into translated tos with South migrations. + + 1. Inform South that you want to store migrations in custom place:: + + # Add to INSTALLED_APS + SOUTH_MIGRATION_MODULES = { + 'tos': 'YOUR_APP.migrations.tos', + } + + 2. Add required directory (django package):: + + mkdir -p YOUR_APP/migrations/tos + touch YOUR_APP/migrations/tos/__init__.py + + 3. Create initial migration (referring to the database state as it is now):: + + python manage.py schemamigration --initial tos + + 4. Fake migration (because the changes are already in the database):: + + python manage.py migrate tos --fake + + 5. Install tos_i18n to INSTALLED_APPS:: + + INSTALLED_APS += ('tos_i18n', ) + + 6. Migrate what changed:: + + $ python manage.py schemamigration --auto tos + $ python migrate tos + + +That's it. You are now running tos in i18n mode with languages you declared in LANGUAGES setting. + +This app will also make all required adjustments in django admin. + +For more info on how translation works in details please refer to `django-modeltranslation docs`_. + + + + + + diff --git a/tos_i18n/__init__.py b/tos_i18n/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tos_i18n/admin.py b/tos_i18n/admin.py new file mode 100644 index 0000000..7b30927 --- /dev/null +++ b/tos_i18n/admin.py @@ -0,0 +1,13 @@ +from django.contrib import admin +from modeltranslation.admin import TranslationAdmin +from tos.admin import TermsOfServiceAdmin + +# Admin translation for django-plans +from tos.models import TermsOfService + + +class TranslatedTermsOfServiceAdmin(TermsOfServiceAdmin, TranslationAdmin): + pass + +admin.site.unregister(TermsOfService) +admin.site.register(TermsOfService, TranslatedTermsOfServiceAdmin) diff --git a/tos_i18n/models.py b/tos_i18n/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/tos_i18n/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/tos_i18n/tests.py b/tos_i18n/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/tos_i18n/translation.py b/tos_i18n/translation.py new file mode 100644 index 0000000..cf4807b --- /dev/null +++ b/tos_i18n/translation.py @@ -0,0 +1,9 @@ +from modeltranslation.translator import translator, TranslationOptions +from tos.models import TermsOfService + +# Translations for django-tos + +class TermsOfServiceTranslationOptions(TranslationOptions): + fields = ('content', ) + +translator.register(TermsOfService, TermsOfServiceTranslationOptions) diff --git a/tos_i18n/views.py b/tos_i18n/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/tos_i18n/views.py @@ -0,0 +1 @@ +# Create your views here. From 5ab00c5bc15a801fd40b137fad9dfb6ccd92c859 Mon Sep 17 00:00:00 2001 From: Krzysztof Dorosz Date: Sun, 17 Feb 2013 20:58:25 +0100 Subject: [PATCH 8/8] README reformatting --- README.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 4724fb6..ba37aa3 100644 --- a/README.rst +++ b/README.rst @@ -61,31 +61,31 @@ How to migrate tos with South Here is some step-by-step example how to turn your legacy django-tos instalation synced using syncdb into translated tos with South migrations. - 1. Inform South that you want to store migrations in custom place:: +1. Inform South that you want to store migrations in custom place:: # Add to INSTALLED_APS SOUTH_MIGRATION_MODULES = { 'tos': 'YOUR_APP.migrations.tos', - } + } - 2. Add required directory (django package):: +2. Add required directory (django package):: mkdir -p YOUR_APP/migrations/tos touch YOUR_APP/migrations/tos/__init__.py - 3. Create initial migration (referring to the database state as it is now):: +3. Create initial migration (referring to the database state as it is now):: python manage.py schemamigration --initial tos - 4. Fake migration (because the changes are already in the database):: +4. Fake migration (because the changes are already in the database):: python manage.py migrate tos --fake - 5. Install tos_i18n to INSTALLED_APPS:: +5. Install tos_i18n to INSTALLED_APPS:: INSTALLED_APS += ('tos_i18n', ) - 6. Migrate what changed:: +6. Migrate what changed:: $ python manage.py schemamigration --auto tos $ python migrate tos