*.pyc
-build
-*.swp
-*~
-*.egg-info
+.*
+dist
+MANIFEST
+
+++ /dev/null
-The primary author of thie app is:
-
- * Clint Ecker <me@clintecker.com>
-
-There are a number of other, awesome contributors:
-
- * Kevin Fricovsky <http://github.com/howiworkdaily>
- * J. Cliff Dyer <http://github.com/jcdyer>
- * Chris Adams <http://github.com/acdha>
+++ /dev/null
-The MIT License
-
-Copyright (c) 2009 Clint Ecker
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
\ No newline at end of file
--- /dev/null
+include README.mdown
+
+recursive-include googletools/templates *
Google Analytics for Django Projects
====================================
-I manage a lot of Django projects that present slightly-different forms to
-users depending on the site/domain they're visiting. There's also a bunch of
-custom submission code that differs from form to form, but that's neither here
-nor there.
+## Installation
-I need different Google Analytics codes depending on the sites and after
-sticking these tags into every single template, I thought it would be cool to
-be able to manage these Google analytics accounts from the Django admin page.
-I also added a mode of operation that excludes the admin interface altogether
-(you can just use the template tag)
+1. Add `googletools` to your `INSTALLED_APPS`
+2. Run `./manage.py migrate` (or `./manage.py syncdb` if you do not make use of
+ South)
-## Two modes of operation ##
+## Management
-### Administering and associating codes with Django `Sites` framework ###
+Go to the admin interface. When correctly installed, you will find the
+*Googletools* app. Here you can manage your Google Analytics and Site Verification
+codes.
-1. Add the `google_analytics` application to your `INSTALLED_APPS` section of your `settings.py`. This mode requires that you be using the Django sites framework too, so make sure you have that set up as well.
-2. Add `GOOGLE_ANALYTICS_MODEL = True` to your `settings.py`
-3. Run a `./manage.py syncdb` to add the database tables
-4. Go to your project's admin page (usually `/admin/`) and click into a site objects
-5. You'll now see a new field under the normal site information called "Analytics Code". In this box you put your unique analytics code for your project's domain. It looks like `UA-xxxxxx-x` and save the site.
-6. In your base template (usually a `base.html`) insert this tag at the very top: `{% load analytics %}`
-7. In the same template, insert the following code right before the closing body tag: `{% analytics %}`
-### Just using the template tag ###
+## Templatetags
+### Google Analytics
-1. Add the `google_analytics` application to your `INSTALLED_APPS` section of your `settings.py`.
-2. In your base template, usually a `base.html`, insert this tag at the very top: `{% load analytics %}`
-3. In the same template, insert the following code right before the closing body tag: `{% analytics "UA-xxxxxx-x" %}` the `UA-xxxxxx-x` is a unique Google Analytics code for you domain when you sign up for a new account.
+Use `{% analytics_code %}` for inserting your Analytics code. This will return
+an empty string when no code is setup for the current site.
+### Google Site Verification
-## Asynchronous Tracking ##
-
-Google's recent asynchronous tracking API is also supported, as of v0.2. To use it,
-simply call the templatetag `{% analytics_async %}` in the document head instead
-of calling `{% analytics %}` just before the end of the body. You can also use
-`{% analytics_async "UA-xxxxxx-x" %}` as with the old templatetags.
-
-This is being added as an option rather than a replacement for two reasons:
-
-1. Google recommends the Asynchronous Tracking snippet goes in the <head> tag, while
- the old snippet goes at the end of the <body> tag, so as not to disrupt page loading.
- Therefore it is not a drop in replacement in your template
-2. The new snippet is reported to [break sites that have a comment before the head tag](http://www.stevesouders.com/blog/2009/12/01/google-analytics-goes-async/#comment-1171).
- Adding the asynchronous tracking to existing code would cause backwards
- incompatiblity.
-
-## Supporting other tracking methods ##
-
-Sometimes, the built-in code snippets are not sufficient for what you want to
-do with Google Analytics. You might need to use different access methods,
-or to support more complex Google Analytics functionality. Fortunately, using
-different code snippets is dead easy, and there are two ways to do it.
-
-
-### Overriding the analytics template ###
-
-The easiest way is to override the `'google_analytics/analytics_template.html'`
-template in a template directory that gets loaded before the one in the
-`google_analytics` app.
-
-
-### Registering a new analytics tag ###
-
-You may want to keep the existing snippets around, while adding a new method.
-Perhaps some of your pages need one snippet, but other pages need a different
-one. In this case all you have to do is register a new tag in your tag
-library using `do_get_analytics` like so:
-
- from django import template
- from google_analytics.templatetags import analytics
-
- register = template.Library()
- register.tag('my_analytics', analytics.do_get_analytics)
-
-Then create a template at `'google_analytics/%(tag_name)s_template.html'`.
-In this case the template name would be
-`'google_analytics/my_analytics_template.html'`. Pass the variable
-`{{ analytics_code }}` to the template wherever you need it.
-
-The new tag will have all the same properties as the default tag, supporting
-site-based analytics codes, as well as explicitly defined codes.
-
-The best way to do this is to create a tiny app just for this purpose, so
-you don't have to modify the code in `google_analytics`. Just put the above
-code in `[app_name]/templatetags/[tag_library_name].py`. Then put your
-template in `[app_name]/templates/google_analytics/[template_name]`. If your
-app is named `my_analytics_app`, your tag library is named `more_analytics`,
-and your tag is registered as `my_analytics`, the resulting app will have a
-directory structure like this:
-
- my_analytics_app/
- +-- templatetags/
- | +-- __init__.py
- | \-- more_analytics.py
- \-- templates/
- \-- google_analytics/
- \-- my_analytics_template.html
-
-Finally, add `'my_analytics_app'` to `INSTALLED_APPS` in your `settings.py` file. Your new tag is
-ready to go. To use the tag, put `{% load more_analytics %}` at the head of
-your template. You can now access the `{% my_analytics %}` tag the same way
-you would use `{% analytics %}`.
-
-
-## License ##
-
-The MIT License
-
-Copyright (c) 2009 Clint Ecker
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+Use `{% site_verification_code %}` for inserting your site verification code.
+This will return an empty string when no code is setup for the current site.
+++ /dev/null
-from google_analytics.models import Analytics
-from django.contrib import admin
-
-admin.site.register(Analytics)
+++ /dev/null
-[
- {
- "pk": 1,
- "model": "sites.site",
- "fields": {
- "domain": "example.com",
- "name": "example.com"
- }
- },
- {
- "pk": 1,
- "model": "google_analytics.analytics",
- "fields": {
- "analytics_code": "UA-777777-3",
- "site": 1
- }
- }
-]
+++ /dev/null
-from django.db import models
-from django.conf import settings
-from django.contrib.sites.models import Site
-
-class Analytics(models.Model):
- site = models.ForeignKey(Site)
- analytics_code = models.CharField(blank=True, max_length=100)
-
- def __unicode__(self):
- return u"%s" % (self.analytics_code)
-
- class Meta:
- verbose_name_plural = "Analytics"
+++ /dev/null
-<script type="text/javascript">
-(function() {
- var _gaq = _gaq || [];
- _gaq.push(['_setAccount', '{{ analytics_code }}']);
- _gaq.push(['_trackPageview']);
-
- var ga=document.createElement('script');
- ga.src=('https:'==document.location.protocol ?
- 'https://ssl' : 'http://www')+'.google-analytics.com/ga.js';
- ga.setAttribute('async','true');
- // Avoid problems if the document doesn't actually contain a HEAD element:
- var container = document.getElementsByTagName("head")[0] || document.body;
- container.appendChild(ga);
-})();
-</script>
+++ /dev/null
-<script type="text/javascript">
-var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
-document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
-</script>
-<script type="text/javascript">
-var pageTracker = _gat._getTracker("{{ analytics_code }}");
-pageTracker._initData();
-pageTracker._trackPageview();
-</script>
+++ /dev/null
-Tracking code: {{ analytics_code }}
+++ /dev/null
-from django import template
-from django.db import models
-from django.contrib.sites.models import Site
-
-from django.template import Context, loader
-
-
-register = template.Library()
-Analytics = models.get_model('googleanalytics', 'analytics')
-
-def do_get_analytics(parser, token):
- contents = token.split_contents()
- tag_name = contents[0]
- template_name = 'google_analytics/%s_template.html' % tag_name
- if len(contents) == 2:
- # split_contents() knows not to split quoted strings.
- code = contents[1]
- elif len(contents) == 1:
- code = None
- else:
- raise template.TemplateSyntaxError, "%r cannot take more than one argument" % tag_name
-
- if not code:
- current_site = Site.objects.get_current()
- else:
- if not (code[0] == code[-1] and code[0] in ('"', "'")):
- raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name
- code = code[1:-1]
- current_site = None
-
- return AnalyticsNode(current_site, code, template_name)
-
-class AnalyticsNode(template.Node):
- def __init__(self, site=None, code=None, template_name='google_analytics/analytics_template.html'):
- self.site = site
- self.code = code
- self.template_name = template_name
-
- def render(self, context):
- content = ''
- if self.site:
- code_set = self.site.analytics_set.all()
- if code_set:
- code = code_set[0].analytics_code
- else:
- return ''
- elif self.code:
- code = self.code
- else:
- return ''
-
- if code.strip() != '':
- t = loader.get_template(self.template_name)
- c = Context({
- 'analytics_code': code,
- })
- return t.render(c)
- else:
- return ''
-
-register.tag('analytics', do_get_analytics)
-register.tag('analytics_async', do_get_analytics)
+++ /dev/null
-from test_templatetags import *
+++ /dev/null
-from django.test import TestCase
-
-from django import template
-from django.contrib.sites.models import Site
-from google_analytics.models import Analytics
-from google_analytics.templatetags import analytics
-
-code7 = 'UA-777777-3' # for fixture-based codes
-code9 = 'UA-999999-1' # for explicit codes
-
-class ParserTest(TestCase):
- """Test parsing of template tokens"""
-
- fixtures = ['analytics_test']
-
- def setUp(self):
- self.parser = "unused"
- #################################
- # Pathological case - Do not test
- #self.token_null = template.Token(template.TOKEN_BLOCK, "")
- #################################
-
- self.token_noarg = template.Token(template.TOKEN_BLOCK, "test")
- self.token_onearg = template.Token(template.TOKEN_BLOCK, "test '%s'" % code9)
- self.token_twoarg = template.Token(template.TOKEN_BLOCK, "test '%s' '%s'" % (code9, code7))
- self.site = Site.objects.get_current()
-
- def test_basic_return(self):
- node = analytics.do_get_analytics(self.parser, self.token_noarg)
- self.assertTrue(isinstance(node, template.Node))
-
- def _test_null_node_template(self):
- node = analytics.do_get_analytics(self.parser, self.token_null)
- self.assertEqual(node.template_name, 'google_analytics/_template.html')
-
- def test_noarg_node_template(self):
- node = analytics.do_get_analytics(self.parser, self.token_noarg)
- self.assertEqual(node.template_name, 'google_analytics/test_template.html')
-
- def test_onearg_node_template(self):
- node = analytics.do_get_analytics(self.parser, self.token_onearg)
- self.assertEqual(node.template_name, 'google_analytics/test_template.html')
-
- def test_twoarg_node_exception(self):
- self.assertRaises(template.TemplateSyntaxError, analytics.do_get_analytics, self.parser, self.token_twoarg)
-
- def test_noarg_site(self):
- """If no access code is provided, the site will be set to the currently active site"""
- node = analytics.do_get_analytics(self.parser, self.token_noarg)
- self.assertEqual(node.site, self.site)
-
- def test_onearg_site(self):
- """If an access code is provided, the site will not be set"""
- node = analytics.do_get_analytics(self.parser, self.token_onearg)
- self.assertEqual(node.site, None)
-
-
-class NodeTest(TestCase):
- """Test set-up and rendering of AnalyticsNodes"""
-
- fixtures = ['analytics_test']
-
- def setUp(self):
- self.site = Site.objects.get_current()
- self.node_noarg = analytics.AnalyticsNode()
- self.node_code = analytics.AnalyticsNode(code=code9)
- self.node_explicit_template = analytics.AnalyticsNode(code=code9, template_name='google_analytics/test_template.html')
- self.node_site = analytics.AnalyticsNode(site=self.site, template_name='google_analytics/test_template.html')
- self.node_code_and_site = analytics.AnalyticsNode(site=self.site, code=code9, template_name='google_analytics/test_template.html')
-
- def test_fixture(self):
- """Fixtures have been loaded"""
- self.assertNotEqual(Analytics.objects.count(), 0)
-
- def test_default_template_name(self):
- self.assertEqual(
- self.node_code.template_name,
- 'google_analytics/analytics_template.html'
- )
-
- def test_explicit_code_name(self):
- self.assertEqual(self.node_code.code, code9)
- self.assertTrue(code9 in self.node_code.render(template.Context()))
-
- def test_noarg_code_name(self):
- """If the node is constructed with no code and no site, it will return
- an empty string"""
- self.assertEqual(self.node_noarg.code, None)
- self.assertEqual(self.node_noarg.render(template.Context()), "")
-
- def test_explicit_template_name(self):
- self.assertEqual(
- self.node_explicit_template.template_name,
- 'google_analytics/test_template.html'
- )
- self.assertEqual(
- self.node_explicit_template.render(template.Context()).strip(),
- 'Tracking code: %s' % code9
- )
-
- def test_defined_site(self):
- self.assertEqual(self.node_site.site, self.site)
- self.assertEqual(self.node_site.code, None)
- self.assertEqual(
- self.node_site.render(template.Context()).strip(),
- 'Tracking code: %s' % code7
- )
-
- def test_site_overrides_explicit_code(self):
- """If both code and site are set, the site code will override the
- explicitly set code. This is contrary to how the tag works, but
- the parser never passes this combination of arguments."""
-
- self.assertEqual(self.node_code_and_site.code, code9)
- self.assertEqual(self.node_code_and_site.site, self.site)
- self.assertEqual(
- self.node_code_and_site.render(template.Context()).strip(),
- 'Tracking code: %s' % code7
- )
-
-
--- /dev/null
+from django.contrib import admin
+from django.utils.translation import ugettext_lazy as _
+
+from googletools.models import AnalyticsCode, SiteVerificationCode
+
+
+class AnalyticsCodeAdmin(admin.ModelAdmin):
+ list_display = ('code', 'site')
+
+
+class SiteVerificationCodeAdmin(admin.ModelAdmin):
+ list_display = ('code', 'site')
+
+
+admin.site.register(AnalyticsCode, AnalyticsCodeAdmin)
+admin.site.register(SiteVerificationCode, SiteVerificationCodeAdmin)
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'AnalyticsCode'
+ db.create_table('googletools_analyticscode', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('site', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sites.Site'], unique=True)),
+ ('code', self.gf('django.db.models.fields.CharField')(max_length=100)),
+ ))
+ db.send_create_signal('googletools', ['AnalyticsCode'])
+
+ # Adding model 'SiteVerificationCode'
+ db.create_table('googletools_siteverificationcode', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('site', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sites.Site'], unique=True)),
+ ('code', self.gf('django.db.models.fields.CharField')(max_length=100)),
+ ))
+ db.send_create_signal('googletools', ['SiteVerificationCode'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'AnalyticsCode'
+ db.delete_table('googletools_analyticscode')
+
+ # Deleting model 'SiteVerificationCode'
+ db.delete_table('googletools_siteverificationcode')
+
+
+ models = {
+ 'googletools.analyticscode': {
+ 'Meta': {'object_name': 'AnalyticsCode'},
+ 'code': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']", 'unique': 'True'})
+ },
+ 'googletools.siteverificationcode': {
+ 'Meta': {'object_name': 'SiteVerificationCode'},
+ 'code': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']", 'unique': 'True'})
+ },
+ 'sites.site': {
+ 'Meta': {'object_name': 'Site', 'db_table': "'django_site'"},
+ 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ }
+ }
+
+ complete_apps = ['googletools']
--- /dev/null
+from django.contrib.sites.models import Site
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+
+class AnalyticsCode(models.Model):
+ site = models.ForeignKey(Site, verbose_name=_('site'), unique=True)
+ code = models.CharField(_('code'), max_length=100)
+
+ def __unicode__(self):
+ return self.code
+
+ class Meta:
+ ordering = ('site', 'code')
+ verbose_name = _('analytics code')
+ verbose_name_plural = _('analytics codes')
+
+
+class SiteVerificationCode(models.Model):
+ site = models.ForeignKey(Site, verbose_name=_('site'), unique=True)
+ code = models.CharField(_('code'), max_length=100)
+
+ def __unicode__(self):
+ return self.code
+
+ class Meta:
+ ordering = ('site', 'code')
+ verbose_name = _('site verification code')
+ verbose_name_plural = _('site verification codes')
--- /dev/null
+<script type="text/javascript">
+var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+</script>
+<script type="text/javascript">
+var pageTracker = _gat._getTracker("{{ analytics_code }}");
+pageTracker._initData();
+pageTracker._trackPageview();
+</script>
\ No newline at end of file
--- /dev/null
+<meta name="google-site-verification" content="{{ site_verification_code }}" />
\ No newline at end of file
--- /dev/null
+from django import template
+from django.contrib.sites.models import Site
+from django.db import models
+from django.template import loader, Context
+
+AnalyticsCode = models.get_model('googletools', 'analyticscode')
+SiteVerificationCode = models.get_model('googletools', 'siteverificationcode')
+
+
+register = template.Library()
+
+class AnalyticsCodeNode(template.Node):
+
+ def __init__(self):
+ self.site = Site.objects.get_current()
+ self.template = 'googletools/analytics_code.html'
+ try:
+ self.code = AnalyticsCode.objects.get(site=self.site)
+ except AnalyticsCode.DoesNotExist:
+ self.code = None
+
+ def render(self, context):
+ if not self.code:
+ return ''
+
+ t = loader.get_template(self.template)
+ return t.render(Context({
+ 'analytics_code': self.code,
+ }))
+
+
+class SiteVerificationCodeNode(template.Node):
+
+ def __init__(self):
+ self.site = Site.objects.get_current()
+ self.template = 'googletools/site_verification_code.html'
+ try:
+ self.code = SiteVerificationCode.objects.get(site=self.site)
+ except SiteVerificationCode.DoesNotExist:
+ self.code = None
+
+ def render(self, context):
+ if not self.code:
+ return ''
+
+ t = loader.get_template(self.template)
+ return t.render(Context({
+ 'site_verification_code': self.code,
+ }))
+
+
+@register.tag
+def analytics_code(parser, token):
+ """
+ Get the analytics code for the current site::
+
+ {% analytics_code %}
+
+ Returns an empty string when no code could be found.
+ """
+ try:
+ tag_name = token.split_contents()
+ except ValueError:
+ raise template.TemplateSyntaxError, '%r does not take any arguments' % token.contents.split()[0]
+ return AnalyticsCodeNode()
+
+
+@register.tag
+def site_verification_code(parser, token):
+ """
+ Get the site verification code for the current site::
+
+ {% site_verification_code %}
+
+ Returns an empty string when no code could be found.
+ """
+ try:
+ tag_name = token.split_contents()
+ except ValueError:
+ raise template.TemplateSyntaxError, '%r does not take any arguments' % token.contents.split()[0]
+ return SiteVerificationCodeNode()
from distutils.core import setup
-setup(name='google_analytics',
- version='0.2',
- description='A simple Django application to integrate Google Analytics into your projects',
- author='Clint Ecker',
- author_email='me@clintecker.com',
- url='http://github.com/clintecker/django-google-analytics/tree/master',
- packages=['google_analytics','google_analytics.templatetags',],
- package_data={'google_analytics': ['templates/google_analytics/*.html']},
- classifiers=['Development Status :: 4 - Beta',
- 'Environment :: Web Environment',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: BSD License',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Topic :: Utilities'],
- )
+
+setup(
+ name='Django Google Tools',
+ version='0.1',
+ description='A simple Django app for managing Google Analytics and Site Verification codes.',
+ author='Orne Brocaar',
+ author_email='info@brocaar.com',
+ url='http://github.com/LUKKIEN/django-google-tools',
+ packages=[
+ 'googletools',
+ 'googletools.migrations',
+ 'googletools.templatetags',
+ ]
+)