Compare commits

...

7 commits

Author SHA1 Message Date
Alex
3f92be7a18
Merge 4592af9569 into e6f12719cc 2026-03-13 05:02:51 +00:00
Ronard
e6f12719cc
Ask for consent - Matomo (Continuation Jayhaluska's work) (#245)
Some checks failed
Check / build (audit) (push) Has been cancelled
Check / build (docs) (push) Has been cancelled
Check / build (format) (push) Has been cancelled
Check / build (lint) (push) Has been cancelled
Check / build (package) (push) Has been cancelled
Test / python-django (4.2, 3.10) (push) Has been cancelled
Test / python-django (4.2, 3.11) (push) Has been cancelled
Test / python-django (4.2, 3.12) (push) Has been cancelled
Test / python-django (4.2, 3.9) (push) Has been cancelled
Test / python-django (5.1, 3.10) (push) Has been cancelled
Test / python-django (5.1, 3.11) (push) Has been cancelled
Test / python-django (5.1, 3.12) (push) Has been cancelled
Test / python-django (5.1, 3.13) (push) Has been cancelled
Test / python-django (5.2, 3.10) (push) Has been cancelled
Test / python-django (5.2, 3.11) (push) Has been cancelled
Test / python-django (5.2, 3.12) (push) Has been cancelled
Test / python-django (5.2, 3.13) (push) Has been cancelled
Co-authored-by: Julian Haluska <j.haluska@gmx.de>
2026-03-08 17:10:32 +01:00
odi1n
4592af9569 Text correction 2022-09-19 17:47:02 +03:00
odi1n
20a14a8c82 Add tests 2022-09-19 17:41:43 +03:00
odi1n
acede461a9 Change text 2022-09-19 16:44:16 +03:00
Alex
dd880885b8
Update docs/services/liveinternet.rst
Co-authored-by: Peter Bittner <django@bittner.it>
2022-09-19 16:42:35 +03:00
odi1n
3729eaa960 Add Liveinternet 2022-09-14 21:19:30 +03:00
8 changed files with 253 additions and 0 deletions

View file

@ -3,6 +3,7 @@
* Fix GA gtag user_id setup and add support for custom dimensions (Erick Massip)
* Change spelling of "JavaScript" across all files in docstrings and docs
(Peter Bittner)
* Ask site visitors for consent when using Matomo (Julian Haluska & Ronard Luna)
Version 3.2.0
-------------

View file

@ -0,0 +1,77 @@
from django.template import Library, Node
from analytical.utils import is_internal_ip, disable_html
LIVEINTERNET_WITH_IMAGE = """
<a href="https://www.liveinternet.ru/click"
target="_blank"><img id="licnt515E" width="31" height="31" style="border:0"
title="LiveInternet"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7"
alt=""/></a><script>(function(d,s){d.getElementById("licnt515E").src=
"https://counter.yadro.ru/hit?t50.6;r"+escape(d.referrer)+
((typeof(s)=="undefined")?"":";s"+s.width+"*"+s.height+"*"+
(s.colorDepth?s.colorDepth:s.pixelDepth))+";u"+escape(d.URL)+
";h"+escape(d.title.substring(0,150))+";"+Math.random()})
(document,screen)</script>
"""
LIVEINTERNET_CODE = """
<script>
new Image().src = "https://counter.yadro.ru/hit?r"+
escape(document.referrer)+((typeof(screen)=="undefined")?"":
";s"+screen.width+"*"+screen.height+"*"+(screen.colorDepth?
screen.colorDepth:screen.pixelDepth))+";u"+escape(document.URL)+
";h"+escape(document.title.substring(0,150))+
";"+Math.random();
</script>
"""
LIVEINTERNET_IMAGE = """
<a href="https://www.liveinternet.ru/click"
target="_blank"><img src="https://counter.yadro.ru/logo?50.6"
title="LiveInternet"
alt="" style="border:0" width="31" height="31"/>
</a>
"""
register = Library()
@register.tag
def liveinternet(parser, token):
"""
Body Liveinternet, full image and code template tag.
Render the body Javascript code and image for Liveinternet.
"""
return LiveInternetNode(LIVEINTERNET_WITH_IMAGE, 'liveinternet_with_image')
@register.tag
def liveinternet_code(parser, token):
"""
Top Liveinternet,code template tag.
Render the top Javascript code for Liveinternet.
"""
return LiveInternetNode(LIVEINTERNET_CODE, 'liveinternet_code')
@register.tag
def liveinternet_img(parser, token):
"""
Body Liveinternet image template tag.
Render the body Javascript code for Liveinternet.
"""
return LiveInternetNode(LIVEINTERNET_IMAGE, 'liveinternet_image')
class LiveInternetNode(Node):
def __init__(self, key, name):
self.key = key
self.name = name
def render(self, context):
if is_internal_ip(context):
return disable_html(self.key, self.name)
return LIVEINTERNET_CODE

View file

@ -46,6 +46,30 @@ VARIABLE_CODE = (
IDENTITY_CODE = '_paq.push(["setUserId", "%(userid)s"]);'
DISABLE_COOKIES_CODE = "_paq.push(['disableCookies']);"
GIVE_CONSENT_CLASS = 'matomo_give_consent'
REMOVE_CONSENT_CLASS = 'matomo_remove_consent'
ASK_FOR_CONSENT_CODE = """
_paq.push(['requireConsent']);
var elements = document.getElementsByClassName("{}");
for (var i = 0; i < elements.length; i++) {{
elements[i].addEventListener("click",
function () {{
_paq.push(["forgetConsentGiven"]);
}}
);
}}
var elements = document.getElementsByClassName("{}");
for (var i = 0; i < elements.length; i++) {{
elements[i].addEventListener("click",
function () {{
_paq.push(["rememberConsentGiven"]);
}}
);
}}
""".format(REMOVE_CONSENT_CLASS, GIVE_CONSENT_CLASS)
DEFAULT_SCOPE = 'page'
MatomoVar = namedtuple('MatomoVar', ('index', 'name', 'value', 'scope'))
@ -103,6 +127,9 @@ class MatomoNode(Node):
if getattr(settings, 'MATOMO_DISABLE_COOKIES', False):
commands.append(DISABLE_COOKIES_CODE)
if getattr(settings, 'MATOMO_ASK_FOR_CONSENT', False):
commands.append(ASK_FOR_CONSENT_CODE)
userid = get_identity(context, 'matomo')
if userid is not None:
variables_code = chain(

View file

@ -0,0 +1,61 @@
==================================
Liveinternet -- traffic analysis
==================================
`Liveinternet`_ is an analytics tool like as google analytics.
.. _`Liveinternet`: https://www.liveinternet.ru/code/
.. yandex-metrica-installation:
Installation
============
To start using the Liveinternet integration, you must have installed the
django-analytical package and have added the ``analytical`` application
to :const:`INSTALLED_APPS` in your project :file:`settings.py` file.
See :doc:`../install` for details.
Next you need to add the Liveinternet template tag to your templates.
The Liveinternet counter code is inserted into templates using a template
tag. Load the :mod:`liveinternet` template tag library and insert the
:ttag:`liveinternet` tag. To display as a single image combining a counter
and the LiveInternet logo::
{% load liveinternet %}
<html>
<head>
...
{% liveinternet %}
</head>
...
In the form of two images, one of which is a counter (transparent GIF size 1x1),
and the other is the LiveInternet logo. This placement method will allow you to
insert the code of the invisible counter at the beginning of the page, and the
logo - where the design and content of the page allows. ::
{% load liveinternet %}
<html>
<head>
...
{% liveinternet_code %}
</head>
<body>
...
{% liveinternet_img %}
...
</body>
Internal IP addresses
---------------------
Usually you do not want to track clicks from your development or
internal IP addresses. It takes the value of
:const:`ANALYTICAL_INTERNAL_IPS` by default (which in turn is
:const:`INTERNAL_IPS` by default). See :ref:`identifying-visitors` for
important information about detecting the visitor IP address.

View file

@ -149,6 +149,28 @@ If you want to `disable cookies`_, set :data:`MATOMO_DISABLE_COOKIES` to
.. _`disable cookies`: https://matomo.org/faq/general/faq_157/
Ask for consent
---------------
If you do not want to track visitors without permission, you can `ask for consent`_ first.
To enable this, set :data:`MATOMO_ASK_FOR_CONSENT` to :const:`True`.
By default, no consent for tracking is needed (i.e. :const:`False`).
To give and remove consent in your page, create DOM elements with the following classes:
`matomo_give_consent` - class name for element to click when visitors want to **give** consent
`matomo_remove_consent` - class name for element to click when visitors want to **remove** consent
Examples::
# button to allow tracking
<button class="matomo_give_consent">Track me!</button>
# button to remove tracking consent
<button class="matomo_remove_consent">Don't track me anymore!</button>
.. _`asking for consent`: https://developer.matomo.org/guides/tracking-javascript-guide#asking-for-consent
Internal IP addresses
---------------------

View file

@ -59,6 +59,8 @@ authors = [
{name = "Tinnet Coronam", email = "tinnet@coronam.net"},
{name = "Uros Trebec", email = "uros@trebec.org"},
{name = "Walter Renner", email = "walter.renner@me.com"},
{name = "Julian Haluska", email = "mail@julianhaluska.de"},
{name = "Ronard Luna", email = "rlunag@proton.me"},
]
maintainers = [
{name = "Jazzband community", email = "jazzband-bot@users.noreply.github.com"},

View file

@ -0,0 +1,58 @@
"""
Tests for the LiveInternet template tags and filters.
"""
from django.http import HttpRequest
from django.template import Context
from django.test.utils import override_settings
from analytical.templatetags.liveinternet import (LiveInternetNode,
LIVEINTERNET_WITH_IMAGE,
LIVEINTERNET_CODE,
LIVEINTERNET_IMAGE)
from utils import TagTestCase
class LiveInternetTagTestCase(TagTestCase):
"""
Tests for the ``liveinternet`` template tag.
"""
def test_render_liveinternet(self):
response = self.render_tag('liveinternet', 'liveinternet')
assert '<a href="https://www.liveinternet.ru/click"' in response
def test_render_liveinternet_code(self):
response = self.render_tag('liveinternet', 'liveinternet_code')
assert 'new Image().src = "https://counter.yadro.ru/hit?r"' in response
def test_render_liveinternet_img(self):
response = self.render_tag('liveinternet', 'liveinternet_img')
assert '<a href="https://www.liveinternet.ru/click"' in response
@override_settings(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
def test_render_liveinternet_render_ip(self):
req = HttpRequest()
req.META['REMOTE_ADDR'] = '1.1.1.1'
context = Context({'request': req})
r = LiveInternetNode(LIVEINTERNET_WITH_IMAGE, 'liveinternet_with_image').render(context)
assert r.startswith('<!-- liveinternet disabled on internal IP address')
assert r.endswith('-->')
@override_settings(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
def test_render_liveinternet_code_render_ip(self):
req = HttpRequest()
req.META['REMOTE_ADDR'] = '1.1.1.1'
context = Context({'request': req})
r = LiveInternetNode(LIVEINTERNET_CODE, 'liveinternet_code').render(context)
assert r.startswith('<!-- liveinternet_code disabled on internal IP address')
assert r.endswith('-->')
@override_settings(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
def test_render_liveinternet_img_render_ip(self):
req = HttpRequest()
req.META['REMOTE_ADDR'] = '1.1.1.1'
context = Context({'request': req})
r = LiveInternetNode(LIVEINTERNET_IMAGE, 'liveinternet_image').render(context)
assert r.startswith('<!-- liveinternet_img disabled on internal IP address')
assert r.endswith('-->')

View file

@ -159,3 +159,8 @@ class MatomoTagTestCase(TagTestCase):
def test_disable_cookies(self):
r = MatomoNode().render(Context({}))
assert "_paq.push(['disableCookies']);" in r
@override_settings(MATOMO_ASK_FOR_CONSENT=True)
def test_ask_for_consent(self):
r = MatomoNode().render(Context({}))
self.assertTrue("_paq.push(['requireConsent']);" in r, r)