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..ba37aa3 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,83 @@ Installation # terms of service links urlpatterns += patterns('', - (r'^login/$', 'tos.views.login', {}, 'login',), - (r'^terms-of-service/', include('tos.urls')), - ) \ No newline at end of file + url(r'^login/$', 'tos.views.login', {}, 'auth_login',), + url(r'^terms-of-service/', include('tos.urls')), + ) + + +=============== +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/__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 diff --git a/tos/models.py b/tos/models.py index 6bb5f2e..d1ac4fb 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') + raise NoActiveTermsOfService(u'Please create an active Terms-of-Service') class TermsOfService(BaseModel): + active = models.BooleanField(verbose_name=_('active'), + help_text=_(u'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): - raise NoActiveTermsOfService('One of the terms of service must be marked active') - super(TermsOfService,self).save(*args, **kwargs) - + else: + if not TermsOfService.objects\ + .exclude(id=self.id)\ + .filter(active=True): + raise NoActiveTermsOfService(u'One of the terms of service must be marked active') + + 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 - 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 %} 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..aef0e77 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)) @@ -115,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)) 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.