diff --git a/pythonkc_site/settings.py b/pythonkc_site/settings.py
index 244225a..3c48752 100644
--- a/pythonkc_site/settings.py
+++ b/pythonkc_site/settings.py
@@ -94,10 +94,8 @@
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
+ 'pythonkc_site.ua_detection.middleware.UserAgentTypeDetectionMiddleware'
)
ROOT_URLCONF = 'pythonkc_site.urls'
@@ -107,27 +105,17 @@
)
INSTALLED_APPS = (
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.sites',
- 'django.contrib.messages',
+ #'django.contrib.contenttypes',
'django.contrib.staticfiles',
- # Uncomment the next line to enable the admin:
- #'django.contrib.admin',
- # Uncomment the next line to enable admin documentation:
- #'django.contrib.admindocs',
'south',
'pythonkc_site.contact',
)
TEMPLATE_CONTEXT_PROCESSORS = (
- "django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
- "django.contrib.messages.context_processors.messages",
"django.core.context_processors.request",
)
diff --git a/pythonkc_site/static/mobile.css b/pythonkc_site/static/mobile.css
new file mode 100644
index 0000000..896f444
--- /dev/null
+++ b/pythonkc_site/static/mobile.css
@@ -0,0 +1,57 @@
+/* ---------- RESET ---------- */
+
+html, body, div, span, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
+small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ vertical-align: baseline;
+}
+
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+
+blockquote, q { quotes: none; }
+
+blockquote:before, blockquote:after,
+q:before, q:after { content: ""; content: none; }
+
+ins { background-color: #ff9; color: #000; text-decoration: none; }
+mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
+del { text-decoration: line-through; }
+abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
+table { border-collapse: collapse; border-spacing: 0; }
+hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
+textarea, input { border: 1px solid #ddd; background: #FFFFFF; font-weight: normal; color: #333; font-size: 13px; }
+ul, li { list-style: none; }
+a { text-decoration: none; color:#646565; }
+a:hover { text-decoration: underline; }
+.clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.clearfix { display: inline-block; }
+* html .clearfix { height: 1%; }
+.clearfix { display: block; }
+
+html { height: 100%; background: #87cded; }
+body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ font: 16px/1.4 'Helvetica Neue', Arial, Helvetica, sans-serif normal;
+ color: #646565;
+ background-image: -moz-radial-gradient(50% 28%, circle cover, rgba(255,255,255, .3) 0%,#87cded 18.0%);
+ background-image: -webkit-radial-gradient(50% 28%, circle cover, rgba(255,255,255, .3) 0%,#87cded 18.0%);
+ background-image: -o-radial-gradient(50% 28%, circle cover, rgba(255,255,255, .3) 0%,#87cded 18.0%);
+ background-image: -ms-radial-gradient(50% 28%, circle cover, rgba(255,255,255, .3) 0%,#87cded 18.0%);
+ background-image: radial-gradient(50% 28%, circle cover, rgba(255,255,255, .3) 0%,#87cded 18.0%);
+}
+
diff --git a/pythonkc_site/templates/index.html b/pythonkc_site/templates/home.html
similarity index 100%
rename from pythonkc_site/templates/index.html
rename to pythonkc_site/templates/home.html
diff --git a/pythonkc_site/templates/mobile/home.html b/pythonkc_site/templates/mobile/home.html
new file mode 100644
index 0000000..2c1c5ed
--- /dev/null
+++ b/pythonkc_site/templates/mobile/home.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+ PythonKC || For all things Python in Kansas City.
+
+
+
+
+
+
+
+
+
+
+
{{ next_event.name|safe }}
+ {% if next_event.venue.id %}
{{ next_event.venue.name }}{% if next_event.venue.address_1 %} // {{ next_event.venue.address_1 }}{% if next_event.venue.address_2 %}, {{ next_event.venue.address_2 }}{% endif %}{% endif %} // {{ next_event.venue.city }}, {{ next_event.venue.state }} {{ next_event.venue.zip }}{% else %}Venue TBD{% endif %}
+ {% if next_event.description %}
{{ next_event.description|safe }}
{% endif %}
+
RSVP at 
+
+
+
+
+ Previous meetups
+
+
+
+
+
+
+
+
+
+
diff --git a/pythonkc_site/templates/mobile/past_meetups.html b/pythonkc_site/templates/mobile/past_meetups.html
new file mode 100644
index 0000000..dc9575a
--- /dev/null
+++ b/pythonkc_site/templates/mobile/past_meetups.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ PythonKC || For all things Python in Kansas City.
+
+
+
+
+
+
+
+
+
+
diff --git a/pythonkc_site/ua_detection/__init__.py b/pythonkc_site/ua_detection/__init__.py
new file mode 100644
index 0000000..0826c07
--- /dev/null
+++ b/pythonkc_site/ua_detection/__init__.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+"""
+Overview
+========
+Simple detection of user agent types, where "type" is defined as one of:
+
+* Standard
+* Mobile
+
+The idea is to generically categorize user agents with simple user agent string
+matching, instead of full determination of user agent capabilities.
+
+If we need to we could add tablet detection and that type as well.
+
+Usage
+=====
+Simply install the middleware::
+
+ pythonkc_site.ua_detection.middleware.UserAgentTypeDetectionMiddleware
+
+Then request objects will have the following additional attributes:
+
+is_mobile
+ Boolean value indicating whether or not the request was made by a mobile
+ user-agent.
+
+Derived from:
+
+* http://mobiforge.com/developing/story/build-a-mobile-and-desktop-friendly-application-django-15-minutes
+* http://www.entzeroth.com/code
+
+"""
diff --git a/pythonkc_site/ua_detection/middleware.py b/pythonkc_site/ua_detection/middleware.py
new file mode 100644
index 0000000..24d7142
--- /dev/null
+++ b/pythonkc_site/ua_detection/middleware.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+
+
+from pythonkc_site.ua_detection.types import is_mobile_browser
+
+
+class UserAgentTypeDetectionMiddleware(object):
+
+ def process_request(self, request):
+ request.is_mobile = is_mobile_browser(request)
diff --git a/pythonkc_site/ua_detection/types.py b/pythonkc_site/ua_detection/types.py
new file mode 100644
index 0000000..f0a367d
--- /dev/null
+++ b/pythonkc_site/ua_detection/types.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+"""
+Sources of prefix and hint list:
+
+* http://mobiforge.com/developing/story/build-a-mobile-and-desktop-friendly-application-django-15-minutes
+* http://www.entzeroth.com/code
+* http://www.useragentstring.com
+
+"""
+
+
+mobile_ua_prefixes = [
+ 'w3c ','acs-','alav','alca','amoi','audi','avan','benq','bird','blac',
+ 'blaz','brew','cell','cldc','cmd-','dang','doco','eric','hipt','inno',
+ 'ipaq','java','jigs','kddi','keji','leno','lg-c','lg-d','lg-g','lge-',
+ 'maui','maxo','midp','mits','mmef','mobi','mot-','moto','mwbp','nec-',
+ 'newt','noki','palm','pana','pant','phil','play','port','prox','qwap',
+ 'sage','sams','sany','sch-','sec-','send','seri','sgh-','shar','sie-',
+ 'siem','smal','smar','sony','sph-','symb','t-mo','teli','tim-','tosh',
+ 'tsm-','upg1','upsi','vk-v','voda','wap-','wapa','wapi','wapp','wapr',
+ 'webc','winw','winw','xda','xda-'
+]
+
+
+mobile_ua_hints = [
+ 'SymbianOS',
+ 'Opera Mini',
+ 'iPhone',
+ 'Android',
+ 'Opera Mobi',
+ 'webOS',
+ 'BlackBerry',
+ # Portable game consoles
+ 'Bunjalloo', # Nintendo DS
+ 'PlayStation Portable'
+]
+
+
+def is_mobile_browser(request):
+ user_agent = request.META.get('HTTP_USER_AGENT', '')
+ ua_prefix = user_agent.lower()[:4]
+
+ if ua_prefix in mobile_ua_prefixes:
+ return True
+
+ for hint in mobile_ua_hints:
+ if user_agent.find(hint) >= 0:
+ return True
+
+ return False
diff --git a/pythonkc_site/urls.py b/pythonkc_site/urls.py
index 9b998ce..3c8a827 100644
--- a/pythonkc_site/urls.py
+++ b/pythonkc_site/urls.py
@@ -4,24 +4,10 @@
from django.conf.urls.defaults import include
from django.conf.urls.defaults import patterns
from django.conf.urls.defaults import url
-from pythonkc_site.views import PythonKCHome
-# Uncomment the next two lines to enable the admin:
-# from django.contrib import admin
-# admin.autodiscover()
urlpatterns = patterns('',
- url(r'^/?$', PythonKCHome.as_view(), name='home'),
-
- # Examples:
- # url(r'^$', 'pythonkc_site.views.home', name='home'),
- # url(r'^pythonkc_site/', include('pythonkc_site.foo.urls')),
-
- # Uncomment the admin/doc line below to enable admin documentation:
- # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
-
- # Uncomment the next line to enable the admin:
- # url(r'^admin/', include(admin.site.urls)),
+ url(r'^/?$', 'pythonkc_site.views.home', name='home'),
+ url(r'^meetups/past/?$', 'pythonkc_site.views.past_meetups',
+ name='past-meetups'),
)
-
-
diff --git a/pythonkc_site/views.py b/pythonkc_site/views.py
index b92789a..fd26d16 100644
--- a/pythonkc_site/views.py
+++ b/pythonkc_site/views.py
@@ -1,15 +1,18 @@
# -*- coding: utf-8 -*-
+from django.conf import settings
from django.core.urlresolvers import reverse
+from django.shortcuts import redirect
from django.views.generic import FormView
+from django.views.generic import TemplateView
from pythonkc_site.contact.email import send_contact_form_email
from pythonkc_site.contact.forms import ContactForm
from pythonkc_site.meetups import events
-class PythonKCHome(FormView):
- template_name = 'index.html'
+class StandardHome(FormView):
+ template_name = 'home.html'
form_class = ContactForm
def get_success_url(self):
@@ -17,7 +20,7 @@ def get_success_url(self):
def form_valid(self, form):
send_contact_form_email(**form.cleaned_data)
- return super(PythonKCHome, self).form_valid(form)
+ return super(StandardHome, self).form_valid(form)
def get_context_data(self, **kwargs):
return {
@@ -25,3 +28,49 @@ def get_context_data(self, **kwargs):
'past_events': events.get_past_events(),
'form': kwargs.get('form', None) or ContactForm()
}
+
+
+class MobileHome(TemplateView):
+ template_name = 'mobile/home.html'
+
+ def get_context_data(self, **kwargs):
+ return {
+ 'next_event': events.get_next_event(),
+ 'contact_email': settings.CONTACT_EMAIL_TO[0]
+ }
+
+
+class MobilePastMeetups(TemplateView):
+ template_name = 'mobile/past_meetups.html'
+
+ def get_context_data(self, **kwargs):
+ return {
+ 'past_events': events.get_past_events()
+ }
+
+
+standard_home_view = StandardHome.as_view()
+mobile_home_view = MobileHome.as_view()
+mobile_past_view = MobilePastMeetups.as_view()
+
+
+def home(request, *args, **kwargs):
+ """
+ Standard browsers: show home page with next and past meetups
+ Mobile browsers: show home page with next meetup
+
+ """
+ if request.is_mobile:
+ return mobile_home_view(request, *args, **kwargs)
+ return standard_home_view(request, *args, **kwargs)
+
+
+def past_meetups(request, *args, **kwargs):
+ """
+ Standard browsers: redirect to home page
+ Mobile browsers: show list of past meetups with links to meetup.com
+
+ """
+ if request.is_mobile:
+ return mobile_past_view(request, *args, **kwargs)
+ return redirect(reverse('home'))