diff --git a/AUTHORS.rst b/AUTHORS.rst index 853fbe4..95013c9 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -10,6 +10,7 @@ The application was inspired by and uses ideas from Analytical_, Joshua Krall's all-purpose analytics front-end for Rails. The work on Crazy Egg was made possible by `Bateau Knowledge`_. +The work on Intercom was made possible by `GreenKahuna`_. .. _`Joost Cassee`: mailto:joost@cassee.net .. _`Eric Davis`: https://github.com/edavis @@ -22,5 +23,6 @@ The work on Crazy Egg was made possible by `Bateau Knowledge`_. .. _`Tinnet Coronam`: https://github.com/tinnet .. _`Philippe O. Wagner`: mailto:admin@arteria.ch .. _`Max Arnold`: https://github.com/max-arnold -.. _Analytical: https://github.com/jkrall/analytical +.. _`Analytical`: https://github.com/jkrall/analytical .. _`Bateau Knowledge`: http://www.bateauknowledge.nl/ +.. _`GreenKahuna`: http://www.greenkahuna.com/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bb2344c..c04b035 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,7 @@ +Version 0.17.0 +-------------- +* Add support for Intercom.io (Steven Skoczen) + Version 0.16.0 -------------- * Add support for GA Display Advertising features (Max Arnold) diff --git a/README.rst b/README.rst index 6f7fa1f..a690b02 100644 --- a/README.rst +++ b/README.rst @@ -27,6 +27,7 @@ Currently supported services: * `Google Analytics`_ traffic analysis * `GoSquared`_ traffic monitoring * `HubSpot`_ inbound marketing +* `Intercom`_ live chat and support * `KISSinsights`_ feedback surveys * `KISSmetrics`_ funnel analysis * `Mixpanel`_ event tracking @@ -59,6 +60,7 @@ an issue to discuss your plans. .. _`Google Analytics`: http://www.google.com/analytics/ .. _`GoSquared`: http://www.gosquared.com/ .. _`HubSpot`: http://www.hubspot.com/ +.. _`Intercom`: http://www.intercom.io/ .. _`KISSinsights`: http://www.kissinsights.com/ .. _`KISSmetrics`: http://www.kissmetrics.com/ .. _`Mixpanel`: http://www.mixpanel.com/ diff --git a/analytical/__init__.py b/analytical/__init__.py index 07cae5d..3faa945 100644 --- a/analytical/__init__.py +++ b/analytical/__init__.py @@ -10,6 +10,6 @@ Django_ project. See the ``docs`` directory for more information. __author__ = "Joost Cassee" __email__ = "joost@cassee.net" -__version__ = "0.16.0" +__version__ = "0.17.0" __copyright__ = "Copyright (C) 2011-2012 Joost Cassee and others" __license__ = "MIT License" diff --git a/analytical/templatetags/analytical.py b/analytical/templatetags/analytical.py index 51ee382..d0cb1fc 100644 --- a/analytical/templatetags/analytical.py +++ b/analytical/templatetags/analytical.py @@ -23,6 +23,7 @@ TAG_MODULES = [ 'analytical.google_analytics', 'analytical.gosquared', 'analytical.hubspot', + 'analytical.intercom', 'analytical.kiss_insights', 'analytical.kiss_metrics', 'analytical.mixpanel', diff --git a/analytical/templatetags/intercom.py b/analytical/templatetags/intercom.py new file mode 100644 index 0000000..e6cc7c2 --- /dev/null +++ b/analytical/templatetags/intercom.py @@ -0,0 +1,63 @@ +""" +intercom.io template tags and filters. +""" + +from __future__ import absolute_import +import time +import re + +from django.template import Library, Node, TemplateSyntaxError + +from analytical.utils import disable_html, get_required_setting, get_user_from_context + +APP_ID_RE = re.compile(r'[\da-f]+$') +TRACKING_CODE = """ + + +""" + +register = Library() + + +@register.tag +def intercom(parser, token): + """ + Intercom.io template tag. + + Renders Javascript code to intercom.io testing. You must supply + your APP ID account number in the ``INTERCOM_APP_ID`` + setting. + """ + bits = token.split_contents() + if len(bits) > 1: + raise TemplateSyntaxError("'%s' takes no arguments" % bits[0]) + return IntercomNode() + + +class IntercomNode(Node): + def __init__(self): + self.app_id = get_required_setting( + 'INTERCOM_APP_ID', APP_ID_RE, + "must be a string looking like 'XXXXXXX'") + + def render(self, context): + html = "" + user = get_user_from_context(context) + if user is not None and user.is_authenticated(): + html = TRACKING_CODE % { + 'app_id': self.app_id, + 'full_name': "%s %s" % (user.first_name, user.last_name), + 'email': user.email, + 'created_at': int(time.mktime(user.date_joined.timetuple())), + } + else: + # Intercom is disabled for non-logged in users. + html = disable_html(html, 'Intercom') + return html + + +def contribute_to_analytical(add_node): + IntercomNode() + add_node('head_bottom', IntercomNode) diff --git a/analytical/tests/__init__.py b/analytical/tests/__init__.py index 634d29d..13f7b5f 100644 --- a/analytical/tests/__init__.py +++ b/analytical/tests/__init__.py @@ -11,6 +11,7 @@ from analytical.tests.test_tag_gauges import * from analytical.tests.test_tag_google_analytics import * from analytical.tests.test_tag_gosquared import * from analytical.tests.test_tag_hubspot import * +from analytical.tests.test_tag_intercom import * from analytical.tests.test_tag_kiss_insights import * from analytical.tests.test_tag_kiss_metrics import * from analytical.tests.test_tag_mixpanel import * diff --git a/analytical/tests/test_tag_clicky.py b/analytical/tests/test_tag_clicky.py index 1755e7e..e7d9b2d 100644 --- a/analytical/tests/test_tag_clicky.py +++ b/analytical/tests/test_tag_clicky.py @@ -54,8 +54,12 @@ class ClickyTagTestCase(TagTestCase): def test_custom(self): r = ClickyNode().render(Context({'clicky_var1': 'val1', 'clicky_var2': 'val2'})) - self.assertTrue(re.search('var clicky_custom = {.*' - '"var1": "val1", "var2": "val2".*};', r), r) + self.assertTrue( + (re.search('var clicky_custom = {.*' + '"var1": "val1", "var2": "val2".*};', r) or + re.search('var clicky_custom = {.*' + '"var2": "val2", "var1": "val1".*};', r)), r + ) @override_settings(ANALYTICAL_INTERNAL_IPS=['1.1.1.1']) def test_render_internal_ip(self): diff --git a/analytical/tests/test_tag_intercom.py b/analytical/tests/test_tag_intercom.py new file mode 100644 index 0000000..01e462b --- /dev/null +++ b/analytical/tests/test_tag_intercom.py @@ -0,0 +1,66 @@ +""" +Tests for the intercom template tags and filters. +""" + +import datetime + +from django.contrib.auth.models import User, AnonymousUser +from django.http import HttpRequest +from django.template import Context + +from analytical.templatetags.intercom import IntercomNode +from analytical.tests.utils import TagTestCase, override_settings, SETTING_DELETED +from analytical.utils import AnalyticalException + + +@override_settings(INTERCOM_APP_ID='1234567890abcdef0123456789') +class IntercomTagTestCase(TagTestCase): + """ + Tests for the ``intercom`` template tag. + """ + + def test_tag(self): + self.assertEqual("""""", + self.render_tag('intercom', 'intercom')) + + def test_node(self): + now = datetime.datetime(2014, 4, 9, 15, 15, 0) + self.assertEqual( + """ + + +""", + IntercomNode().render(Context({ + 'user': User( + username='test', + first_name='Firstname', + last_name='Lastname', + email="test@example.com", + date_joined=now) + })) + ) + + @override_settings(INTERCOM_APP_ID=SETTING_DELETED) + def test_no_account_number(self): + self.assertRaises(AnalyticalException, IntercomNode) + + @override_settings(INTERCOM_APP_ID='123abQ') + def test_wrong_account_number(self): + self.assertRaises(AnalyticalException, IntercomNode) + + def test_identify_name_email_and_created_at(self): + now = datetime.datetime(2014, 4, 9, 15, 15, 0) + r = IntercomNode().render(Context({'user': User(username='test', + first_name='Firstname', last_name='Lastname', + email="test@example.com", date_joined=now)})) + self.assertTrue("window.intercomSettings = {'app_id': '1234567890abcdef0123456789', " + "'full_name': 'Firstname Lastname', " + "'email': 'test@example.com', 'created_at': 1397074500};" in r, r) + + def test_disable_for_anonymous_users(self): + r = IntercomNode().render(Context({'user': AnonymousUser()})) + self.assertTrue(r.startswith(' + {% intercom %} + + + ... + + +.. _intercom-configuration: + +Configuration +============= + +Before you can use the Intercom.io integration, you must first set your +app id. + + +.. _intercom-site-id: + +Setting the app id +-------------------------- + +Intercom.io gives you a unique app id, and the :ttag:`intercom` +tag will include it in the rendered Javascript code. You can find your +app id by clicking the *Tracking Code* link when logged into +the on the intercom.io website. A page will display containing +HTML code looking like this:: + + + + +The code ``XXXXXXXXXXXXXXXXXXXXXXX`` is your app id. Set +:const:`INTERCOM_APP_ID` in the project :file:`settings.py` +file:: + + INTERCOM_APP_ID = 'XXXXXXXXXXXXXXXXXXXXXXX' + +If you do not set an app id, the Javascript code will not be +rendered. + + +.. _intercom-internal-ips: + +Internal IP addresses +--------------------- + +Usually you do not want to track clicks from your development or +internal IP addresses. By default, if the tags detect that the client +comes from any address in the :const:`ANALYTICAL_INTERNAL_IPS` setting +(which is :const:`INTERNAL_IPS` by default,) the tracking code is +commented out. See :ref:`identifying-visitors` for important information +about detecting the visitor IP address.