Browse Source

Ability to use different templates based on user client #42

Ralfp 12 years ago
parent
commit
9318d85022

+ 30 - 0
misago/admin/layout/system.py

@@ -1,6 +1,7 @@
 from django.conf.urls import patterns, include, url
 from django.conf.urls import patterns, include, url
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
 from misago.admin import AdminAction
 from misago.admin import AdminAction
+from misago.themes.models import ThemeAdjustment
 
 
 ADMIN_ACTIONS = (
 ADMIN_ACTIONS = (
    AdminAction(
    AdminAction(
@@ -16,4 +17,33 @@ ADMIN_ACTIONS = (
                         url(r'^(?P<group_slug>([a-z0-9]|-)+)-(?P<group_id>\d+)/$', 'settings', name='admin_settings')
                         url(r'^(?P<group_slug>([a-z0-9]|-)+)-(?P<group_id>\d+)/$', 'settings', name='admin_settings')
                     ),
                     ),
                ),
                ),
+   AdminAction(
+               section='system',
+               id='clients',
+               name=_("Clients"),
+               help=_("Adjust presentation layer to clients"),
+               icon='tint',
+               model=ThemeAdjustment,
+               actions=[
+                        {
+                         'id': 'list',
+                         'name': _("Browse Clients"),
+                         'help': _("Browse all existing clients"),
+                         'route': 'admin_clients'
+                         },
+                        {
+                         'id': 'new',
+                         'name': _("Add New Adjustment"),
+                         'help': _("Create new client adjustment"),
+                         'route': 'admin_clients_new'
+                         },
+                        ],
+               route='admin_clients',
+               urlpatterns=patterns('misago.themes.views',
+                        url(r'^$', 'List', name='admin_clients'),
+                        url(r'^new/$', 'New', name='admin_clients_new'),
+                        url(r'^edit/(?P<slug>([a-z0-9]|-)+)-(?P<target>\d+)/$', 'Edit', name='admin_clients_edit'),
+                        url(r'^delete/(?P<slug>([a-z0-9]|-)+)-(?P<target>\d+)/$', 'Delete', name='admin_clients_delete'),
+                    ),
+               ),
 )
 )

+ 3 - 3
misago/monitor/monitor.py

@@ -9,12 +9,12 @@ class Monitor(object):
         self.refresh()
         self.refresh()
 
 
     def refresh(self):
     def refresh(self):
-        self._items = cache.get('misago.monitor')
+        self._items = cache.get('monitor')
         if not self._items:
         if not self._items:
             self._items = {}
             self._items = {}
             for i in Item.objects.all():
             for i in Item.objects.all():
                 self._items[i.id] = [i.value, i.updated]
                 self._items[i.id] = [i.value, i.updated]
-            cache.set('misago.monitor', self._items)
+            cache.set('monitor', self._items)
 
 
     def __contains__(self, key):
     def __contains__(self, key):
         return key in self._items
         return key in self._items
@@ -24,7 +24,7 @@ class Monitor(object):
 
 
     def __setitem__(self, key, value):
     def __setitem__(self, key, value):
         self._items[key][0] = value
         self._items[key][0] = value
-        cache.set('misago.monitor', self._items)
+        cache.set('monitor', self._items)
         sync_item = Item(id=key, value=value, updated=timezone.now())
         sync_item = Item(id=key, value=value, updated=timezone.now())
         sync_item.save(force_update=True)
         sync_item.save(force_update=True)
         return value
         return value

+ 3 - 3
misago/settings/settings.py

@@ -9,14 +9,14 @@ class Settings(object):
         self.refresh()
         self.refresh()
 
 
     def refresh(self):
     def refresh(self):
-        self._models = cache.get('misago.settings')
+        self._models = cache.get('settings')
         if not self._models:
         if not self._models:
             self._models = {}
             self._models = {}
             try:
             try:
                 for i in Setting.objects.all():
                 for i in Setting.objects.all():
                     self._models[i.pk] = i
                     self._models[i.pk] = i
                     self._settings[i.pk] = i.get_value()
                     self._settings[i.pk] = i.get_value()
-                cache.set('misago.settings', self._models)
+                cache.set('settings', self._models)
             except DatabaseError:
             except DatabaseError:
                 pass
                 pass
         else:
         else:
@@ -37,7 +37,7 @@ class Settings(object):
             self._models[key].set_value(value)
             self._models[key].set_value(value)
             self._models[key].save(force_update=True)
             self._models[key].save(force_update=True)
             self._settings[key] = value
             self._settings[key] = value
-            cache.set('misago.settings', self._models)
+            cache.set('settings', self._models)
         return value
         return value
 
 
     def __delitem__(self, key):
     def __delitem__(self, key):

+ 51 - 0
misago/themes/forms.py

@@ -0,0 +1,51 @@
+from django.conf import settings
+from django.core.exceptions import ValidationError
+from django.utils.translation import ugettext_lazy as _
+from django import forms
+from misago.forms import Form
+from misago.themes.models import ThemeAdjustment
+from misago.utils.validators import validate_sluggable
+
+available_themes = []
+for theme in settings.INSTALLED_THEMES[0:-1]:
+    available_themes.append((theme, theme))
+
+
+class ThemeAdjustmentForm(Form):
+    theme = forms.ChoiceField(choices=available_themes, required=False)
+    useragents = forms.CharField(widget=forms.Textarea, required=False)
+    
+    layout = (
+              (
+               _("Theme Adjustment"),
+               (
+                ('theme', {'label': _("Theme"), 'help_text': _("Select theme that is to replace default one.")}),
+                ('useragents', {'label': _("UserAgent Strings"), 'help_text': _("Enter UserAgent strings for which selected theme has to replace default one. Each string has to be entered in new line. This is case insensitive")}),
+                ),
+               ),
+              )
+
+    def __init__(self, adjustment=None, *args, **kwargs):
+        self.request = kwargs['request']
+        if adjustment:
+            self.adjustment = adjustment
+        else:
+            self.adjustment = ThemeAdjustment()
+        super(ThemeAdjustmentForm, self).__init__(*args, **kwargs)
+        
+    def clean_theme(self):
+        self.adjustment.theme = self.cleaned_data['theme']
+        self.adjustment.full_clean()
+        return self.cleaned_data['theme']
+
+    def clean_useragents(self):
+        agents_raw = self.cleaned_data['useragents'].strip().lower().splitlines()
+        agents = []
+        for line in agents_raw:
+            line = line.strip()
+            if line and not line in agents:
+                agents.append(line)
+        self.cleaned_data['useragents'] = agents
+        if not agents:
+            raise ValidationError(_("You have to enter at least one UserAgent."))
+        return self.cleaned_data['useragents']

+ 17 - 1
misago/themes/middleware.py

@@ -1,8 +1,24 @@
 from django.conf import settings
 from django.conf import settings
-from theme import Theme
+from django.core.cache import cache
+from misago.themes.theme import Theme
+from misago.themes.models import ThemeAdjustment
 
 
 class ThemeMiddleware(object):
 class ThemeMiddleware(object):
     def process_request(self, request):
     def process_request(self, request):
         if not settings.INSTALLED_THEMES:
         if not settings.INSTALLED_THEMES:
             raise ValueError('There are no themes installed!')
             raise ValueError('There are no themes installed!')
         request.theme = Theme(settings.INSTALLED_THEMES[0])
         request.theme = Theme(settings.INSTALLED_THEMES[0])
+        
+        # Adjust theme for specific client?
+        if request.META.get('HTTP_USER_AGENT'):
+            adjustments = cache.get('client_adjustments', 'nada')
+            if adjustments == 'nada':
+                adjustments = ThemeAdjustment.objects.all()
+                cache.set('client_adjustments', adjustments)
+            if adjustments:
+                user_agent = request.META.get('HTTP_USER_AGENT').lower()
+                for item in adjustments:
+                    if item.adjust_theme(user_agent):
+                        request.theme = Theme(item.theme)
+                        break
+            

+ 25 - 0
misago/themes/models.py

@@ -0,0 +1,25 @@
+from django.core.cache import cache
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+class ThemeAdjustment(models.Model):
+    """
+    ThemeAdjustment - theme that is set for specified user agents
+    """
+    theme = models.CharField(max_length=255, unique=True,
+                             error_messages={'unique': _("User agents for this theme are already defined.")})
+    useragents = models.TextField(null=True, blank=True)
+    
+    def adjust_theme(self, useragent):
+        for string in self.useragents.splitlines():
+            if string in useragent:
+                return True
+        return False
+    
+    def save(self, *args, **kwargs):
+        cache.delete('client_adjustments')
+        super(ThemeAdjustment, self).save(*args, **kwargs)
+
+    def delete(self, *args, **kwargs):
+        cache.delete('client_adjustments')
+        super(ThemeAdjustment, self).delete(*args, **kwargs)

+ 109 - 0
misago/themes/views.py

@@ -0,0 +1,109 @@
+from django.core.urlresolvers import reverse as django_reverse
+from django import forms
+from django.utils.translation import ugettext as _
+from misago.admin import site
+from misago.admin.widgets import *
+from misago.forms import Form
+from misago.utils import slugify
+from misago.themes.forms import ThemeAdjustmentForm
+from misago.themes.models import ThemeAdjustment
+
+def reverse(route, target=None):
+    if target:
+        return django_reverse(route, kwargs={'target': target.pk, 'slug': slugify(target.theme)})
+    return django_reverse(route)
+
+"""
+Views
+"""
+class List(ListWidget):
+    admin = site.get_action('clients')
+    id = 'list'
+    columns = (
+               ('theme', _("Theme")),
+               )
+    nothing_checked_message = _('You have to check at least one adjustment.')
+    actions = (
+               ('delete', _("Delete selected adjustments"), _("Are you sure you want to delete selected theme adjustments?")),
+               )
+
+    def get_item_actions(self, item):
+        return (
+                self.action('pencil', _("Edit Adjustment"), reverse('admin_clients_edit', item)),
+                self.action('remove', _("Delete Adjustment"), reverse('admin_clients_delete', item), post=True, prompt=_("Are you sure you want to delete this adjustment?")),
+                )
+
+    def action_delete(self, items, checked):
+        ThemeAdjustment.objects.filter(id__in=checked).delete()
+        return Message(_('Selected adjustment have been deleted successfully.'), 'success'), reverse('admin_clients')
+
+
+class New(FormWidget):
+    admin = site.get_action('clients')
+    id = 'new'
+    fallback = 'admin_clients'
+    form = ThemeAdjustmentForm
+    submit_button = _("Save Rank")
+
+    def get_form_instance(self, form, model, initial, post=False):
+        if post:
+            return form(model, self.request.POST, request=self.request, initial=self.get_initial_data(model))
+        return form(model, request=self.request, initial=self.get_initial_data(model))
+    
+    def get_new_url(self, model):
+        return reverse('admin_clients_new')
+
+    def get_edit_url(self, model):
+        return reverse('admin_clients_edit', model)
+
+    def submit_form(self, form, target):
+        new_rank = ThemeAdjustment.objects.create(
+                                                  theme=form.cleaned_data['theme'],
+                                                  useragents='\r\n'.join(form.cleaned_data['useragents']),
+                                                  )
+        return new_rank, Message(_('New adjustment has been created.'), 'success')
+
+
+class Edit(FormWidget):
+    admin = site.get_action('clients')
+    id = 'edit'
+    name = _("Edit Rank")
+    fallback = 'admin_clients'
+    form = ThemeAdjustmentForm
+    target_name = 'theme'
+    notfound_message = _('Requested adjustment could not be found.')
+    submit_fallback = True
+
+    def get_url(self, model):
+        return reverse('admin_clients_edit', model)
+
+    def get_edit_url(self, model):
+        return self.get_url(model)
+
+    def get_form_instance(self, form, model, initial, post=False):
+        if post:
+            return form(model, self.request.POST, request=self.request, initial=self.get_initial_data(model))
+        return form(model, request=self.request, initial=self.get_initial_data(model))
+    
+    def get_initial_data(self, model):
+        return {
+                'theme': model.theme,
+                'useragents': model.useragents,
+                }
+
+    def submit_form(self, form, target):
+        target.theme = form.cleaned_data['theme']
+        target.useragents = '\r\n'.join(form.cleaned_data['useragents'])
+        target.save(force_update=True)
+        return target, Message(_('Adjustment using theme "%(name)s" has been saved.') % {'name': target.theme}, 'success')
+
+
+class Delete(ButtonWidget):
+    admin = site.get_action('clients')
+    id = 'delete'
+    fallback = 'admin_clients'
+    notfound_message = _('Requested adjustment could not be found.')
+
+    def action(self, target):
+        target.delete()
+        return Message(_('Adjustment using theme "%(name)s" has been deleted.') % {'name': target.theme}, 'success'), False

+ 10 - 0
templates/admin/clients/list.html

@@ -0,0 +1,10 @@
+{% extends "admin/admin/list.html" %}
+{% load i18n %}
+{% load l10n %}
+{% load url from future %}
+
+{% block table_row scoped %}
+  <td class="lead-cell">
+  	<strong>{{ _(item.theme) }}</strong> <span class="muted">{{ ', '.join(item.useragents.splitlines()) }}</span>
+  </td>
+{% endblock%}