Commits

ken cochrane committed 921bd41

initial checkin of code, version 0.1

  • Participants

Comments (0)

Files changed (10)

+syntax: glob
+*.pyc
+dist
+MANIFEST
+_build
+_static
+.DS_Store
+Copyright (c) 2011, Ken Cochrane
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of the author nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Author: Ken Cochrane
+Fork of: http://minidetector.googlecode.com
+Fork Description: I reorganized the code, added caching, and made a few tweaks here and there.
+Description: Django middleware and view decorator to detect phones and small-screen devices
+Version: 0.1
+Last Update: 7/4/2011
+
+Requirements:
+-------------
+Django 1.1 or newer
+Django caching to be enabled if you want to cache the objects
+
+How to use:
+-----------
+Using django-mobi is very simple. Simply place the mobi package into your project's path, and then do one of the following:
+
+
+Using the mobi.MobileDetectionMiddleware Middleware
+---------------------------------------------------
+This middleware will scan all incoming requests to see if it is a mobile device. If it is it will set the request.mobile property to True.
+
+To use all you have to do is add mobi.MobileDetectionMiddleware to your MIDDLEWARE_CLASSES tuple in your settings.py
+
+Then in your view you can check request.mobile - if it's True then treat it like a small screen device. If it's False then it's probably a desktop browser, or a spider or something else.
+
+
+Using the mobi.MobileRedirectMiddleware Middleware
+---------------------------------------------------
+This middleware will scan all incoming requests to see if it is a mobile device, if so it will redirect the request to a different URL. This is good if you want to force all mobile traffic to a mobile only version of your site.
+
+To use all you have to do is add mobi.MobileRedirectMiddleware to your MIDDLEWARE_CLASSES tuple in your settings.py, and also add MOBI_REDIRECT_URL = "http://example.mobi" where http://example.mobi is the website you want to redirect all mobile traffic.
+
+
+Not using the Middleware
+------------------------
+If you only have certain views that need the distinction, you can choose not to search every request you receive. All you need to do is wrap the relevant views like this:
+
+from mobi.decorators import detect_mobile
+
+@detect_mobile
+def my_mobile_view(request):
+    if request.mobile:
+        #do something with mobile
+

File mobi/__init__.py

Empty file added.

File mobi/decorators.py

+from mobi.useragents import search_strings
+from mobi.middleware import MobileDetectionMiddleware
+
+def detect_mobile(view):
+    """View Decorator that adds a "mobile" attribute to the request which is
+       True or False depending on whether the request should be considered
+       to come from a small-screen device such as a phone or a PDA"""
+
+    def detected(request, *args, **kwargs):
+        MobileDetectionMiddleware.process_request(request)
+        return view(request, *args, **kwargs)
+    detected.__doc__ = "%s\n[Wrapped by detect_mobile which detects if the request is from a phone]" % view.__doc__
+    return detected
+

File mobi/middleware.py

+from django.conf import settings
+from django.http import HttpResponseRedirect
+from mobi.useragents import search_strings
+
+class MobileDetectionMiddleware(object):
+    @staticmethod
+    def process_request(request):
+        """Adds a "mobile" attribute to the request which is True or False
+           depending on whether the request should be considered to come from a
+           small-screen device such as a phone or a PDA"""
+
+        if request.META.has_key("HTTP_X_OPERAMINI_FEATURES"):
+            #Then it's running opera mini. 'Nuff said.
+            #Reference from:
+            # http://dev.opera.com/articles/view/opera-mini-request-headers/
+            request.mobile = True
+            return None
+
+        if request.META.has_key("HTTP_ACCEPT"):
+            s = request.META["HTTP_ACCEPT"].lower()
+            if 'application/vnd.wap.xhtml+xml' in s:
+                # Then it's a wap browser
+                request.mobile = True
+                return None
+
+        if request.META.has_key("HTTP_USER_AGENT"):
+            # This takes the most processing. Surprisingly enough, when I
+            # Experimented on my own machine, this was the most efficient
+            # algorithm. Certainly more so than regexes.
+            # Also, Caching didn't help much, with real-world caches.
+            s = request.META["HTTP_USER_AGENT"].lower()
+            for ua in search_strings:
+                if ua in s:
+                    request.mobile = True
+                    return None
+
+        #Otherwise it's not a mobile
+        request.mobile = False
+        return None
+
+#===============================================================================
+class MobileRedirectMiddleware(object):
+    
+    # Add MOBI_REDIRECT_URL to your settings.py file with a fully qualified 
+    # url that you want to redirect mobile clients too.
+    # i.e. http://example.mobi
+    MOBI_REDIRECT_URL = getattr(settings, 'MOBI_REDIRECT_URL', None)
+
+    #---------------------------------------------------------------------------
+    def process_request(self, request):
+        do_redirect = False
+
+        user_agent = request.META.get('HTTP_USER_AGENT',None)
+
+        # mobile browsers are the only people who send this.
+        x_wap = request.META.get('HTTP_X_WAP_PROFILE',None)
+        http_profile = request.META.get('HTTP_PROFILE',None)
+
+        if x_wap or http_profile:
+            do_redirect = True
+
+        #look at the user agent if they don't have x_wap and http_profile
+        if user_agent and not do_redirect:
+            user_agent = user_agent.lower()
+            is_mobile = [w for w in search_strings if w in user_agent]
+            if is_mobile:
+                do_redirect = True
+
+        if do_redirect and MOBI_REDIRECT_URL:
+             # tell adaptation services (transcoders and proxies) to not alter the content based on user agent as it's already being managed by this script
+             # http://mobiforge.com/developing/story/setting-http-headers-advise-transcoding-proxies
+             response = HttpResponseRedirect(MOBI_REDIRECT_URL)
+             response['Cache-Control'] = 'no-transform'
+             response['Vary'] = 'User-Agent, Accept'
+             return response
+        else:
+            return None
+

File mobi/models.py

Empty file added.

File mobi/search_strings.txt

+# Adapted from http://pub.mowser.com/wiki/Main/CodeExamples
+# With a few additions by Moof
+# The latest version of this file is always available from:
+# http://minidetector.googlecode.com/svn/trunk/minidetector/search_strings.txt
+#
+# This list is public domain, please feel free to use it for your own projects
+# If HTTP_USER_AGENT.lower() contains any of these strings, it's a mobile
+# Also include some games consoles, see below
+sony
+symbian
+nokia
+samsung
+mobile
+windows ce
+epoc
+opera mini
+nitro
+j2me
+midp-
+cldc-
+netfront
+mot
+up.browser
+up.link
+audiovox
+blackberry
+ericsson,
+panasonic
+philips
+sanyo
+sharp
+sie-
+portalmmm
+blazer
+avantgo
+danger
+palm
+series60
+palmsource
+pocketpc
+smartphone
+rover
+ipaq
+au-mic,
+alcatel
+ericy
+up.link
+docomo
+vodafone/
+wap1.
+wap2.
+plucker
+480x640
+sec
+fennec
+android
+# The Google transcoder
+google wireless transcoder
+# These are games consoles that either have a small screen or display on a
+# TV. Best to treat them as mobiles for web display
+nintendo
+webtv
+playstation

File mobi/useragents.py

+import os.path
+
+from django.core.cache import cache
+
+def load_from_search_strings_file():
+    CACHE_KEY = 'MOBI_USER_AGENT'
+    CACHE_TIMEOUT = 86400
+    agents = cache.get(CACHE_KEY)
+    
+    if agents:
+        # we got something, we are done, send it back.
+        return agents
+    
+    # it wasn't in the cache, get it from the file, then store in the cache
+    f = None
+    try:
+        f = open(os.path.join(os.path.dirname(__file__), 'search_strings.txt'))
+        ss = f.readlines()
+    finally:
+        if f:
+            f.close()
+    agents = [s.strip() for s in ss if not s.startswith('#')]
+    # store to the cache
+    cache.set(CACHE_KEY, agents, CACHE_TIMEOUT)
+    return agents
+
+search_strings = load_from_search_strings_file()
+from distutils.core import setup
+
+setup(
+    name='django-mobi',
+    version='0.1',
+    description='Django middleware and view decorator to detect phones and small-screen devices',
+    maintainer='Ken Cochrane',
+    maintainer_email='KenCochrane@gmail.com',
+    url='https://bitbucket.org/kencochrane/django-mobi/',
+    classifiers=[
+        'Programming Language :: Python',
+        'Framework :: Django',
+    ],
+    packages=['mobi',],
+    long_description=open('README').read(),
+)
+