diff --git a/analytical/context_providers/matomo.py b/analytical/context_providers/matomo.py new file mode 100644 index 0000000..9c90e7b --- /dev/null +++ b/analytical/context_providers/matomo.py @@ -0,0 +1,34 @@ +import utils +from django.conf import settings + +def matomo_consent_provider(request): + """ + Add Mamoto consent script to the requests context. + :Cases: + - If MATOMO_REQURE_CONSENT is True OR If ALWAYS_TRACK_REGISTERED True == continue on + - If ALWAYS_TRACK_REGISTERED is True AND the user is authenticated + """ + # Do we require consent? + if getattr(settings, 'MATOMO_REQUIRE_CONSENT', False): + provide_script = True + if request.user.is_authenticated and not getattr(settings, "ALWAYS_TRACK_REGISTERED", True): + provide_script = False + if provide_script: + grant_class_name = getattr(settings, 'GRANT_CONSENT_TAG_CLASSNAME') + revoke_class_name = getattr(settings, 'REVOKE_CONSENT_CLASSNAME') + return {"consent_script":""" + %s; + %s + %s + """ % ( + utils.build_paq_cmd('requireConsent'), + utils.get_event_bind_js( + class_name=grant_class_name, + matomo_event="rememberConsentGiven", + ), + utils.get_event_bind_js( + class_name=revoke_class_name, + matomo_event="forgetConsentGiven", + ) + )} + return {'consent_script': ""} \ No newline at end of file diff --git a/analytical/templatetags/matomo.py b/analytical/templatetags/matomo.py index 07739d6..c88e5bd 100644 --- a/analytical/templatetags/matomo.py +++ b/analytical/templatetags/matomo.py @@ -7,15 +7,12 @@ from collections import namedtuple from itertools import chain from django.conf import settings -from django.template import Library, Node, TemplateSyntaxError - +from django.template import Library, Node, TemplateSyntaxError, Template from analytical.utils import ( disable_html, get_identity, get_required_setting, - is_internal_ip, - build_paq_cmd, - get_event_bind_js + is_internal_ip ) # domain name (characters separated by a dot), optional port, optional URI path, no slash @@ -97,19 +94,6 @@ class MatomoNode(Node): if getattr(settings, 'MATOMO_DISABLE_COOKIES', False): commands.append(DISABLE_COOKIES_CODE) - if getattr(settings, "MATOMO_REQUIRE_CONSENT", False): - grant_class_name = settings.GRANT_CONSENT_TAG_CLASSNAME - revoke_class_name = settings.REVOKE_CONSENT_CLASSNAME - commands.append(build_paq_cmd('requireConsent')) - commands.append(get_event_bind_js( - class_name=grant_class_name, - matomo_event="rememberConsentGiven", - )) - commands.append(get_event_bind_js( - class_name=revoke_class_name, - matomo_event="forgetConsentGiven", - )) - userid = get_identity(context, 'matomo') if userid is not None: variables_code = chain(variables_code, ( @@ -122,8 +106,14 @@ class MatomoNode(Node): 'variables': '\n '.join(variables_code), 'commands': '\n '.join(commands) } + # Force the consent script to render so we can inject it into the template + consent_script = Template("{{consent_script}}").render(context) + if len(consent_script) > 1: + html += consent_script + if is_internal_ip(context, 'MATOMO'): html = disable_html(html, 'Matomo') + return html diff --git a/analytical/utils.py b/analytical/utils.py index 62679d8..f7400f5 100644 --- a/analytical/utils.py +++ b/analytical/utils.py @@ -167,8 +167,8 @@ class AnalyticalException(Exception): def build_paq_cmd(cmd, args=[]): """ :Args: - - cmd -> The command to be pushed to paq (i.e enableHeartbeatTimer or contentInteraction) - - args -> Arguments to be added to the paq command. This is mainly + - cmd: The command to be pushed to paq (i.e enableHeartbeatTimer or contentInteraction) + - args: Arguments to be added to the paq command. This is mainly used when building commands to be used on manual event trigger. :Returns: @@ -176,12 +176,12 @@ def build_paq_cmd(cmd, args=[]): """ def __to_js_arg(arg): """ + Turn 'arg' into its javascript counter-part :Args: - - arg: The variable (Matomo argument) to be converted to JS. + - arg: the argument that's to be passed to the array in _paq.push() :Return: - Javascript version of the passed arg parameter + The javascript counter-part to the argument that was passed """ - # Recursively handle dictionaries if isinstance(arg, dict): arg_cpy = deepcopy(arg) for k, v in arg_cpy.items(): @@ -196,18 +196,20 @@ def build_paq_cmd(cmd, args=[]): elif isinstance(arg, list): for elem_idx in range(len(arg)): arg[elem_idx] = __to_js_arg(arg[elem_idx]) + return arg - paq = "_paq.push(['%s', " % (cmd) + paq = "_paq.push(['%s'" % (cmd) if len(args) > 0: + paq += ", " for arg_idx in range(len(args)): current_arg = __to_js_arg(args[arg_idx]) - no_quotes = type(current_arg) in [bool, dict, list] + no_quotes = type(current_arg) in [bool, int, dict, list] if arg_idx == len(args)-1: if no_quotes: - segment = "%s])" % (current_arg) + segment = "%s]);" % (current_arg) else: - segment = "'%s'])" % (current_arg) + segment = "'%s']);" % (current_arg) else: if no_quotes: segment = "%s, "% (current_arg) @@ -215,7 +217,7 @@ def build_paq_cmd(cmd, args=[]): segment = "'%s', " % (current_arg) paq += segment else: - paq += "])" + paq += "]);" return paq def get_event_bind_js( @@ -248,4 +250,3 @@ def get_event_bind_js( }} """ % (class_name, js_event, build_paq_cmd(matomo_event, matomo_args)) return script -