Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
.tox/
/.coverage
/.idea
db.sqlite
__pycache__
/django_mobile.egg-info
5 changes: 1 addition & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ is enabled and working.
6. Add ``django_mobile.context_processors.flavour`` to your
``context_processors`` list for ``TEMPLATES`` setting. You can read more about ``loaders`` and ``context_processors`` in `Django docs`_.

*Note:* If you are using Django 1.7 or older, you need to change step 5 and 6 slightly. Use the ``TEMPLATE_LOADERS`` and ``TEMPLATE_CONTEXT_PROCESSORS`` settings instead of ``TEMPLATES``.

Now you should be able to use **django-mobile** in its glory. Read below of how
things work and which settings can be tweaked to modify **django-mobile**'s
behaviour.
Expand Down Expand Up @@ -99,8 +97,7 @@ This will add ``(mobile version)`` to the title of your site if viewed with
the mobile flavour enabled.

*Note:* The ``flavour`` template variable is only available if you have set up the
``django_mobile.context_processors.flavour`` context processor and used
django's ``RequestContext`` as context instance to render the template.
``django_mobile.context_processors.flavour`` context processor.

Changing the current flavour
----------------------------
Expand Down
2 changes: 1 addition & 1 deletion django_mobile/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-

__author__ = u'Gregor Müllegger'
__version__ = '0.7.0.dev1'
__version__ = '1.0.0.RC1'


import threading
Expand Down
10 changes: 6 additions & 4 deletions django_mobile/cache/middleware.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import warnings

from django.utils.deprecation import MiddlewareMixin
from django.utils.cache import patch_vary_headers
from django_mobile import get_flavour, _set_request_header


class CacheFlavourMiddleware(object):
def __init__(self):
class CacheFlavourMiddleware(MiddlewareMixin):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
warnings.warn('CacheFlavourMiddleware does nothing and should be abandoned.'
'The intended behavior cannot be implemented using one middleware.'
'Use separate FetchFromCacheFlavourMiddleware and UpdateCacheFlavourMiddleware instead.'
'Refer to https://github.com/gregmuellegger/django-mobile/pull/64 for details',
category=DeprecationWarning)


class FetchFromCacheFlavourMiddleware(object):
class FetchFromCacheFlavourMiddleware(MiddlewareMixin):
def process_request(self, request):
_set_request_header(request, get_flavour(request))


class UpdateCacheFlavourMiddleware(object):
class UpdateCacheFlavourMiddleware(MiddlewareMixin):
def process_response(self, request, response):
patch_vary_headers(response, ['X-Flavour'])
return response
27 changes: 0 additions & 27 deletions django_mobile/compat.py

This file was deleted.

43 changes: 24 additions & 19 deletions django_mobile/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@


class SettingsProxy(object):
def __init__(self, settings, defaults):
self.settings = settings
def __init__(self, proxied, defaults):
self.settings = proxied
self.defaults = defaults

def __getattr__(self, attr):
Expand All @@ -17,26 +17,31 @@ def __getattr__(self, attr):
try:
return getattr(self.defaults, attr)
except AttributeError:
raise AttributeError(u'settings object has no attribute "%s"' % attr)
raise AttributeError('settings object has no attribute "%s"' % attr)


class defaults(object):
FLAVOURS = (u'full', u'mobile',)
DEFAULT_MOBILE_FLAVOUR = u'mobile'
FLAVOURS_TEMPLATE_PREFIX = u''
FLAVOURS_GET_PARAMETER = u'flavour'
FLAVOURS_STORAGE_BACKEND = u'cookie'
FLAVOURS_COOKIE_KEY = u'flavour'
class Defaults:
FLAVOURS = ('full', 'mobile',)
DEFAULT_MOBILE_FLAVOUR = 'mobile'
FLAVOURS_TEMPLATE_PREFIX = ''
FLAVOURS_GET_PARAMETER = 'flavour'
FLAVOURS_STORAGE_BACKEND = 'cookie'
FLAVOURS_COOKIE_KEY = 'flavour'
FLAVOURS_COOKIE_HTTPONLY = False
FLAVOURS_SESSION_KEY = u'flavour'
FLAVOURS_SESSION_KEY = 'flavour'
FLAVOURS_TEMPLATE_LOADERS = []
for loader in django_settings.TEMPLATE_LOADERS:
if isinstance(loader, (tuple, list)) and loader[0] == CACHE_LOADER_NAME:
for cached_loader in loader[1]:
if cached_loader != DJANGO_MOBILE_LOADER:
FLAVOURS_TEMPLATE_LOADERS.append(cached_loader)
elif loader != DJANGO_MOBILE_LOADER:
FLAVOURS_TEMPLATE_LOADERS.append(loader)
for template in django_settings.TEMPLATES:
try:
for loader in template['OPTIONS'].get('loaders', []):
if isinstance(loader, (tuple, list)) and loader[0] == CACHE_LOADER_NAME:
for cached_loader in loader[1]:
if cached_loader != DJANGO_MOBILE_LOADER:
FLAVOURS_TEMPLATE_LOADERS.append(cached_loader)
elif loader != DJANGO_MOBILE_LOADER:
FLAVOURS_TEMPLATE_LOADERS.append(loader)
except KeyError:
pass
FLAVOURS_TEMPLATE_LOADERS = tuple(FLAVOURS_TEMPLATE_LOADERS)

settings = SettingsProxy(django_settings, defaults)

settings = SettingsProxy(django_settings, Defaults)
76 changes: 27 additions & 49 deletions django_mobile/loader.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import hashlib
from django.template import TemplateDoesNotExist
from django.template.exceptions import TemplateDoesNotExist
from django.template.loaders.cached import Loader as DjangoCachedLoader
from django_mobile import get_flavour
from django_mobile.conf import settings
from django_mobile.compat import BaseLoader, template_loader, template_from_string
from django.utils.encoding import force_bytes

from django.template.engine import Engine
from django.template.loaders.base import Loader as BaseLoader


class Loader(BaseLoader):
Expand All @@ -14,55 +14,39 @@ class Loader(BaseLoader):
def get_contents(self, origin):
return origin.loader.get_contents(origin)

def get_template_sources(self, template_name, template_dirs=None):
def get_template_sources(self, template_name):
template_name = self.prepare_template_name(template_name)
for loader in self.template_source_loaders:
if hasattr(loader, 'get_template_sources'):
try:
for result in loader.get_template_sources(template_name, template_dirs):
yield result
except UnicodeDecodeError:
# The template dir name was a bytestring that wasn't valid UTF-8.
raise
except ValueError:
# The joined path was located outside of this particular
# template_dir (it might be inside another one, so this isn't
# fatal).
pass
try:
for result in loader.get_template_sources(template_name):
yield result
except ValueError:
# The joined path was located outside this particular
# template_dir (it might be inside another one, so this isn't
# fatal).
pass

def prepare_template_name(self, template_name):
template_name = u'%s/%s' % (get_flavour(), template_name)
if settings.FLAVOURS_TEMPLATE_PREFIX:
template_name = settings.FLAVOURS_TEMPLATE_PREFIX + template_name
return template_name

def load_template(self, template_name, template_dirs=None):
def get_template(self, template_name, skip=None):
template_name = self.prepare_template_name(template_name)
for loader in self.template_source_loaders:
try:
return loader(template_name, template_dirs)
return loader.get_template(template_name, skip=skip)
except TemplateDoesNotExist:
pass
raise TemplateDoesNotExist("Tried %s" % template_name)

def load_template_source(self, template_name, template_dirs=None):
template_name = self.prepare_template_name(template_name)
for loader in self.template_source_loaders:
if hasattr(loader, 'load_template_source'):
try:
return loader.load_template_source(
template_name,
template_dirs)
except TemplateDoesNotExist:
pass
raise TemplateDoesNotExist("Tried %s" % template_name)

@property
def template_source_loaders(self):
if not self._template_source_loaders:
loaders = []
for loader_name in settings.FLAVOURS_TEMPLATE_LOADERS:
loader = template_loader(loader_name)
loader = Engine.get_default().find_template_loader(loader_name)
if loader is not None:
loaders.append(loader)
self._template_source_loaders = tuple(loaders)
Expand All @@ -72,38 +56,32 @@ def template_source_loaders(self):
class CachedLoader(DjangoCachedLoader):
is_usable = True

def cache_key(self, template_name, template_dirs, *args):
def cache_key(self, template_name, *args, **kwargs):
if len(args) > 0: # Django >= 1.9
key = super(CachedLoader, self).cache_key(template_name, template_dirs, *args)
key = super(CachedLoader, self).cache_key(template_name, *args, **kwargs)
else:
if template_dirs:
key = '-'.join([
template_name,
hashlib.sha1(force_bytes('|'.join(template_dirs))).hexdigest()
])
else:
key = template_name
key = template_name

return '{0}:{1}'.format(get_flavour(), key)

def load_template(self, template_name, template_dirs=None):
key = self.cache_key(template_name, template_dirs)
template_tuple = self.template_cache.get(key)
def get_template(self, template_name, skip=None):
key = self.cache_key(template_name)
template_tuple = self.get_template_cache.get(key)

if template_tuple is TemplateDoesNotExist:
raise TemplateDoesNotExist('Template not found: %s' % template_name)
elif template_tuple is None:
template, origin = self.find_template(template_name, template_dirs)
template, origin = self.get_template(template_name, skip=skip)
if not hasattr(template, 'render'):
try:
template = template_from_string(template)
template = Engine.get_default().from_string(template)
except TemplateDoesNotExist:
# If compiling the template we found raises TemplateDoesNotExist,
# back off to returning the source and display name for the template
# we were asked to load. This allows for correct identification (later)
# of the actual template that does not exist.
self.template_cache[key] = (template, origin)
self.get_template_cache[key] = (template, origin)

self.template_cache[key] = (template, None)
self.get_template_cache[key] = (template, None)

return self.template_cache[key]
return self.get_template_cache[key]
12 changes: 7 additions & 5 deletions django_mobile/middleware.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import re
from django.utils.deprecation import MiddlewareMixin
from django_mobile import flavour_storage
from django_mobile import set_flavour, _init_flavour
from django_mobile.conf import settings


class SetFlavourMiddleware(object):
class SetFlavourMiddleware(MiddlewareMixin):
def process_request(self, request):
_init_flavour(request)

Expand All @@ -18,7 +19,7 @@ def process_response(self, request, response):
return response


class MobileDetectionMiddleware(object):
class MobileDetectionMiddleware(MiddlewareMixin):
user_agents_test_match = (
"w3c ", "acs-", "alav", "alca", "amoi", "audi",
"avan", "benq", "bird", "blac", "blaz", "brew",
Expand All @@ -42,9 +43,10 @@ class MobileDetectionMiddleware(object):
user_agents_exception_search = u"(?:%s)" % u'|'.join((
'ipad',
))
http_accept_regex = re.compile("application/vnd\.wap\.xhtml\+xml", re.IGNORECASE)
http_accept_regex = re.compile(r'application/vnd\.wap\.xhtml\+xml', re.IGNORECASE)

def __init__(self):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
user_agents_test_match = r'^(?:%s)' % '|'.join(self.user_agents_test_match)
self.user_agents_test_match_regex = re.compile(user_agents_test_match, re.IGNORECASE)
self.user_agents_test_search_regex = re.compile(self.user_agents_test_search, re.IGNORECASE)
Expand All @@ -64,7 +66,7 @@ def process_request(self, request):
# Nokia like test for WAP browsers.
# http://www.developershome.com/wap/xhtmlmp/xhtml_mp_tutorial.asp?page=mimeTypesFileExtension

if 'HTTP_ACCEPT' in request.META :
if 'HTTP_ACCEPT' in request.META:
http_accept = request.META['HTTP_ACCEPT']
if self.http_accept_regex.search(http_accept):
is_mobile = True
Expand Down
3 changes: 2 additions & 1 deletion django_mobile_tests/manage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python
import os, sys
import sys
import os

os.environ['DJANGO_SETTINGS_MODULE'] = 'django_mobile_tests.settings'
parent = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Expand Down
Loading