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 %}
+