django-cooco 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_cooco/__init__.py +0 -0
- django_cooco/admin.py +14 -0
- django_cooco/apps.py +7 -0
- django_cooco/migrations/0001_initial.py +34 -0
- django_cooco/migrations/__init__.py +0 -0
- django_cooco/models.py +26 -0
- django_cooco/py.typed +0 -0
- django_cooco/templatetags/__init__.py +0 -0
- django_cooco/templatetags/cooco.py +39 -0
- django_cooco/urls.py +8 -0
- django_cooco/utils.py +132 -0
- django_cooco/views.py +25 -0
- django_cooco-0.0.1.dist-info/LICENSE.md +21 -0
- django_cooco-0.0.1.dist-info/METADATA +329 -0
- django_cooco-0.0.1.dist-info/RECORD +16 -0
- django_cooco-0.0.1.dist-info/WHEEL +4 -0
django_cooco/__init__.py
ADDED
|
File without changes
|
django_cooco/admin.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from django.contrib.admin import ModelAdmin, register
|
|
2
|
+
from solo.admin import SingletonModelAdmin
|
|
3
|
+
|
|
4
|
+
from django_cooco.models import BannerConfig, CookieGroup
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@register(BannerConfig)
|
|
8
|
+
class BannerConfigAdmin(SingletonModelAdmin):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@register(CookieGroup)
|
|
13
|
+
class CookieGroupAdmin(ModelAdmin):
|
|
14
|
+
readonly_fields = ("version",)
|
django_cooco/apps.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Generated by Django 5.1.5 on 2025-01-20 19:44
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
initial = True
|
|
8
|
+
|
|
9
|
+
dependencies = ()
|
|
10
|
+
|
|
11
|
+
operations = (
|
|
12
|
+
migrations.CreateModel(
|
|
13
|
+
name="BannerConfig",
|
|
14
|
+
fields=[
|
|
15
|
+
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
|
16
|
+
("title", models.CharField(max_length=100, unique=True)),
|
|
17
|
+
("text", models.CharField(max_length=200)),
|
|
18
|
+
("show_banner", models.BooleanField(default=False)),
|
|
19
|
+
],
|
|
20
|
+
options={
|
|
21
|
+
"abstract": False,
|
|
22
|
+
},
|
|
23
|
+
),
|
|
24
|
+
migrations.CreateModel(
|
|
25
|
+
name="CookieGroup",
|
|
26
|
+
fields=[
|
|
27
|
+
("cookie_id", models.CharField(max_length=50, primary_key=True, serialize=False)),
|
|
28
|
+
("name", models.CharField(max_length=50)),
|
|
29
|
+
("description", models.TextField()),
|
|
30
|
+
("is_required", models.BooleanField()),
|
|
31
|
+
("version", models.PositiveBigIntegerField(default=0, editable=False)),
|
|
32
|
+
],
|
|
33
|
+
),
|
|
34
|
+
)
|
|
File without changes
|
django_cooco/models.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from solo.models import SingletonModel
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BannerConfig(SingletonModel): # type: ignore[django-manager-missing] # https://github.com/typeddjango/django-stubs/issues/1023
|
|
6
|
+
title = models.CharField(max_length=100, unique=True)
|
|
7
|
+
text = models.CharField(max_length=200)
|
|
8
|
+
show_banner = models.BooleanField(default=False)
|
|
9
|
+
|
|
10
|
+
def __str__(self) -> str:
|
|
11
|
+
return "Cookie consent banner"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CookieGroup(models.Model): # type: ignore[django-manager-missing] # https://github.com/typeddjango/django-stubs/issues/1023
|
|
15
|
+
cookie_id = models.CharField(max_length=50, primary_key=True)
|
|
16
|
+
name = models.CharField(max_length=50)
|
|
17
|
+
description = models.TextField()
|
|
18
|
+
is_required = models.BooleanField()
|
|
19
|
+
version = models.PositiveBigIntegerField(default=0, editable=False)
|
|
20
|
+
|
|
21
|
+
def __str__(self) -> str:
|
|
22
|
+
return self.name
|
|
23
|
+
|
|
24
|
+
def save(self, *args, **kwargs):
|
|
25
|
+
self.version += 1
|
|
26
|
+
super().save(*args, **kwargs)
|
django_cooco/py.typed
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
|
|
3
|
+
from django import template
|
|
4
|
+
from django.http import HttpRequest
|
|
5
|
+
|
|
6
|
+
from django_cooco.models import BannerConfig, CookieGroup
|
|
7
|
+
from django_cooco.utils import CooCoManager
|
|
8
|
+
|
|
9
|
+
register = template.Library()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@register.simple_tag
|
|
13
|
+
def get_cooco_banner_config() -> BannerConfig:
|
|
14
|
+
return BannerConfig.get_solo()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@register.simple_tag
|
|
18
|
+
def get_cookie_groups() -> Iterable[CookieGroup]:
|
|
19
|
+
return CookieGroup.objects.all() # type: ignore[attr-defined]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@register.simple_tag
|
|
23
|
+
def get_cooco_manager(request: HttpRequest) -> CooCoManager:
|
|
24
|
+
return CooCoManager.from_request(request)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@register.filter
|
|
28
|
+
def ask_for_cooco(cooco_manager: CooCoManager) -> bool:
|
|
29
|
+
return cooco_manager.is_cooco_outdated()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@register.filter
|
|
33
|
+
def is_cookie_group_accepted(cooco_manager: CooCoManager, cookie_group: CookieGroup) -> bool:
|
|
34
|
+
return cookie_group.is_required or cooco_manager.is_cookie_group_accepted(cookie_group.cookie_id)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@register.filter
|
|
38
|
+
def any_optional_cookie_group(cookie_groups: Iterable[CookieGroup]) -> bool:
|
|
39
|
+
return any(cookie_group for cookie_group in cookie_groups if not cookie_group.is_required)
|
django_cooco/urls.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from django.urls import path
|
|
2
|
+
|
|
3
|
+
from django_cooco.views import AcceptAllCookies, SetCookiePreferences
|
|
4
|
+
|
|
5
|
+
urlpatterns = (
|
|
6
|
+
path("accept-all", AcceptAllCookies.as_view(), name="accept_all_cookies"),
|
|
7
|
+
path("set-preferences", SetCookiePreferences.as_view(), name="set_cookie_preferences"),
|
|
8
|
+
)
|
django_cooco/utils.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from collections.abc import Iterable, Sequence
|
|
3
|
+
from typing import Any, NamedTuple, TypeVar
|
|
4
|
+
|
|
5
|
+
from django.conf import settings
|
|
6
|
+
from django.db.models.query import QuerySet
|
|
7
|
+
from django.http import HttpRequest, HttpResponse
|
|
8
|
+
from typing_extensions import Self
|
|
9
|
+
|
|
10
|
+
from django_cooco.models import CookieGroup
|
|
11
|
+
|
|
12
|
+
THttpResponse = TypeVar("THttpResponse", bound=HttpResponse)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CooCoStatus(NamedTuple):
|
|
16
|
+
version: int
|
|
17
|
+
is_accepted: bool
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CooCo(NamedTuple):
|
|
21
|
+
cookie_id: str
|
|
22
|
+
current_version: int
|
|
23
|
+
cooco_status: CooCoStatus
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def is_version_outdated(self) -> bool:
|
|
27
|
+
return self.current_version != self.cooco_status.version
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def is_accepted(self) -> bool:
|
|
31
|
+
return not self.is_version_outdated and self.cooco_status.is_accepted
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class CooCoManager:
|
|
35
|
+
COOCO_COOKIE_NAME = getattr(settings, "COOCO_COOKIE_NAME", "cooco")
|
|
36
|
+
COOCO_COOKIE_MAX_AGE = getattr(settings, "COOCO_COOKIE_MAX_AGE", 60 * 60 * 24 * 365 * 1)
|
|
37
|
+
DEFAULT_COOCO_STATUS = CooCoStatus(0, False)
|
|
38
|
+
|
|
39
|
+
def __init__(self, cooco: Iterable[CooCo] | None) -> None:
|
|
40
|
+
if cooco is not None:
|
|
41
|
+
self.__coocos = tuple(cooco)
|
|
42
|
+
self.__is_cooco_set = True
|
|
43
|
+
else:
|
|
44
|
+
self.__coocos = ()
|
|
45
|
+
self.__is_cooco_set = False
|
|
46
|
+
|
|
47
|
+
def __getitem__(self, cookie_id: str) -> CooCo:
|
|
48
|
+
for cooco in self.__coocos:
|
|
49
|
+
if cooco.cookie_id == cookie_id:
|
|
50
|
+
return cooco
|
|
51
|
+
|
|
52
|
+
message = f"Cookie ID '{cookie_id}' not found"
|
|
53
|
+
raise KeyError(message)
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def __get_optional_cookie_groups() -> QuerySet[CookieGroup]:
|
|
57
|
+
return CookieGroup.objects.exclude(is_required=True) # type: ignore[attr-defined]
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def from_request(cls, request: HttpRequest) -> Self:
|
|
61
|
+
try:
|
|
62
|
+
cooco_dict = json.loads(request.COOKIES[cls.COOCO_COOKIE_NAME])
|
|
63
|
+
except (KeyError, json.decoder.JSONDecodeError):
|
|
64
|
+
return cls(None)
|
|
65
|
+
|
|
66
|
+
_coocos = []
|
|
67
|
+
|
|
68
|
+
for cookie_group in cls.__get_optional_cookie_groups():
|
|
69
|
+
cooco_status = cooco_dict.get(cookie_group.cookie_id)
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
not isinstance(cooco_status, Sequence)
|
|
73
|
+
or len(cooco_status) != len(cls.DEFAULT_COOCO_STATUS)
|
|
74
|
+
or not isinstance(cooco_status[0], int)
|
|
75
|
+
or not isinstance(cooco_status[1], bool)
|
|
76
|
+
):
|
|
77
|
+
cooco_status = cls.DEFAULT_COOCO_STATUS
|
|
78
|
+
|
|
79
|
+
_coocos.append(
|
|
80
|
+
CooCo(
|
|
81
|
+
cookie_id=cookie_group.cookie_id,
|
|
82
|
+
current_version=cookie_group.version,
|
|
83
|
+
cooco_status=CooCoStatus(*cooco_status),
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return cls(_coocos)
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def parse_cooco_form(cls, cooco_form: dict[str, Any]) -> Self:
|
|
91
|
+
return cls(
|
|
92
|
+
(
|
|
93
|
+
CooCo(
|
|
94
|
+
cookie_id=cookie_group.cookie_id,
|
|
95
|
+
current_version=cookie_group.version,
|
|
96
|
+
cooco_status=CooCoStatus(
|
|
97
|
+
cookie_group.version,
|
|
98
|
+
cooco_form.get(cookie_group.cookie_id) == "on",
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
for cookie_group in cls.__get_optional_cookie_groups()
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def all_cookies_accepted(cls) -> Self:
|
|
107
|
+
return cls(
|
|
108
|
+
(
|
|
109
|
+
CooCo(
|
|
110
|
+
cookie_id=cookie_group.cookie_id,
|
|
111
|
+
current_version=cookie_group.version,
|
|
112
|
+
cooco_status=CooCoStatus(cookie_group.version, True),
|
|
113
|
+
)
|
|
114
|
+
for cookie_group in cls.__get_optional_cookie_groups()
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def set_cooco_cookie(self, response: THttpResponse) -> THttpResponse:
|
|
119
|
+
response.set_cookie(
|
|
120
|
+
self.COOCO_COOKIE_NAME,
|
|
121
|
+
json.dumps({cooco.cookie_id: cooco.cooco_status for cooco in self.__coocos}),
|
|
122
|
+
max_age=self.COOCO_COOKIE_MAX_AGE,
|
|
123
|
+
samesite="Lax",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return response
|
|
127
|
+
|
|
128
|
+
def is_cooco_outdated(self) -> bool:
|
|
129
|
+
return not self.__is_cooco_set or any(cooco.is_version_outdated for cooco in self.__coocos)
|
|
130
|
+
|
|
131
|
+
def is_cookie_group_accepted(self, cookie_id: str) -> bool:
|
|
132
|
+
return self.__is_cooco_set and self[cookie_id].is_accepted
|
django_cooco/views.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from django.http import HttpRequest, HttpResponseRedirect
|
|
4
|
+
from django.views.generic.base import View
|
|
5
|
+
|
|
6
|
+
from django_cooco.utils import CooCoManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SetCookiesBaseView(View, ABC):
|
|
10
|
+
@staticmethod
|
|
11
|
+
def _set_cookie_and_redirect(request: HttpRequest, cookie_group_statuses: CooCoManager) -> HttpResponseRedirect:
|
|
12
|
+
return cookie_group_statuses.set_cooco_cookie(HttpResponseRedirect(request.POST.get("next", "/")))
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def post(self, request: HttpRequest) -> HttpResponseRedirect: ...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AcceptAllCookies(SetCookiesBaseView):
|
|
19
|
+
def post(self, request: HttpRequest) -> HttpResponseRedirect:
|
|
20
|
+
return self._set_cookie_and_redirect(request, CooCoManager.all_cookies_accepted())
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SetCookiePreferences(SetCookiesBaseView):
|
|
24
|
+
def post(self, request: HttpRequest) -> HttpResponseRedirect:
|
|
25
|
+
return self._set_cookie_and_redirect(request, CooCoManager.parse_cooco_form(request.POST))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Roberto Fernández Iglesias
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: django-cooco
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Django app to manage cookie consents
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: roberfi
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Framework :: Django
|
|
15
|
+
Classifier: Framework :: Django :: 5.0
|
|
16
|
+
Classifier: Framework :: Django :: 5.1
|
|
17
|
+
Classifier: Framework :: Django :: 5.2
|
|
18
|
+
Classifier: Operating System :: Unix
|
|
19
|
+
Classifier: Operating System :: MacOS
|
|
20
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Classifier: Intended Audience :: Developers
|
|
23
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Requires-Dist: django (>=5.0,<6.0)
|
|
26
|
+
Requires-Dist: django-solo (>=2.4,<2.5)
|
|
27
|
+
Project-URL: Repository, https://github.com/roberfi/django-cooco
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# django-cooco
|
|
31
|
+
|
|
32
|
+
django-cooco is a Django app to manage cookie consent in Django projects. This library was inspired by [django-cookie-consent](https://github.com/jazzband/django-cookie-consent), which I have taken as referece to create a simplier and more customizable solution.
|
|
33
|
+
|
|
34
|
+
## Quick start
|
|
35
|
+
|
|
36
|
+
1. Add "django_cooco" to your `INSTALLED_APPS` setting like this:
|
|
37
|
+
```python
|
|
38
|
+
INSTALLED_APPS = [
|
|
39
|
+
...,
|
|
40
|
+
"django_cooco",
|
|
41
|
+
]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
2. Include the django_cooco URLconf in your project `urls.py` like this:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
urlpatterns = (
|
|
48
|
+
...,
|
|
49
|
+
path("cooco/", include("django_cooco.urls")),
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
3. Run `python manage.py migrate` to create the models.
|
|
54
|
+
|
|
55
|
+
## Settings
|
|
56
|
+
You can configure the following settings in your project `settings.py` file:
|
|
57
|
+
|
|
58
|
+
| Setting | Type | Definition | Mandatory | Default value |
|
|
59
|
+
| ---------------------- | ----- | --------------------------------------------------------------- | --------- | ------------------- |
|
|
60
|
+
| `COOCO_COOKIE_NAME` | `str` | Name of the cookie to store the cookie consent status | False | `"cooco"` |
|
|
61
|
+
| `COOCO_COOKIE_MAX_AGE` | `int` | Max age of the cookie where the cookie consent status is stored | False | `31536000` (1 year) |
|
|
62
|
+
|
|
63
|
+
## Database
|
|
64
|
+
### Models
|
|
65
|
+
|
|
66
|
+
#### `BannerConfig`
|
|
67
|
+
A singleton model to configure the cookie consent banner title and text:
|
|
68
|
+
|
|
69
|
+
| Field | Type | Definition |
|
|
70
|
+
| ------------- | -------------- | ---------------------------------------------------- |
|
|
71
|
+
| `title` | `CharField` | Title of the cookie consent banner |
|
|
72
|
+
| `text` | `CharField` | Text to show in the cookie consent banner |
|
|
73
|
+
| `show_banner` | `BooleanField` | Whether cookie consent banner should be shown or not |
|
|
74
|
+
|
|
75
|
+
#### `CookieGroup`
|
|
76
|
+
The model to set the groups of cookies that will be used by the web:
|
|
77
|
+
|
|
78
|
+
| Field | Type | Definition |
|
|
79
|
+
| ------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
80
|
+
| `cookie_id` | `CharField` | Unique id of the cookie group |
|
|
81
|
+
| `name` | `CharField` | Human readable name of the cookie group |
|
|
82
|
+
| `description` | `TextField` | Description of the cookies of that cookie group |
|
|
83
|
+
| `is_required` | `BooleanField` | Whether the cookie group is required or optional |
|
|
84
|
+
| `version` | `PositiveBigIntegerField` | Non-editable version of the cookie group object. It will be automatically increased after every saving of the model. |
|
|
85
|
+
|
|
86
|
+
### Database Relationships
|
|
87
|
+
To use optional cookies, you could add a `ForeignKey` field to your cookie consent dependent model. For example:
|
|
88
|
+
```python
|
|
89
|
+
from django.db import models
|
|
90
|
+
from django_cooco.models import CookieGroup
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class GoogleAnalytics(models.Model):
|
|
94
|
+
gtag = models.CharField(max_length=20)
|
|
95
|
+
cookie_consent = models.ForeignKey(CookieGroup, on_delete=models.RESTRICT)
|
|
96
|
+
|
|
97
|
+
def __str__(self):
|
|
98
|
+
return "Google Analytics"
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Tags and filters
|
|
103
|
+
To use them you need to load `cooco` tags:
|
|
104
|
+
```
|
|
105
|
+
{% load cooco %}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Tags
|
|
109
|
+
#### `get_cooco_banner_config`
|
|
110
|
+
|
|
111
|
+
Returns the needed data to display the cookie consent banner.
|
|
112
|
+
|
|
113
|
+
**Returns:**
|
|
114
|
+
- (`BannerConfig`): the data stored in the BannerConfig database model.
|
|
115
|
+
|
|
116
|
+
**Usage example:**
|
|
117
|
+
```html
|
|
118
|
+
{% get_cooco_banner_config as banner %}
|
|
119
|
+
{% if banner.show_banner %}
|
|
120
|
+
<div id="cooco_banner">
|
|
121
|
+
<div>
|
|
122
|
+
<h2>
|
|
123
|
+
{{ banner.title }}
|
|
124
|
+
</h2>
|
|
125
|
+
<p>
|
|
126
|
+
{{ banner.text }}
|
|
127
|
+
</p>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
{% endif %}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### `get_cookie_groups`
|
|
134
|
+
|
|
135
|
+
Returns the data of the configured cookie groups.
|
|
136
|
+
|
|
137
|
+
**Returns:**
|
|
138
|
+
- (`Iterable[CookieGroup]`): the data stored in the CookieGroup database model.
|
|
139
|
+
|
|
140
|
+
**Usage example:**
|
|
141
|
+
```
|
|
142
|
+
{% get_cookie_groups as cookie_groups %}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### `get_cooco_manager`
|
|
146
|
+
|
|
147
|
+
Returns the status of the cookie consent in the request to the server.
|
|
148
|
+
|
|
149
|
+
**Arguments:**
|
|
150
|
+
- `request` (`HttpRequest`): the request to the server.
|
|
151
|
+
|
|
152
|
+
**Returns:**
|
|
153
|
+
- (`CooCoManager`): object containing the status of the cookie consent built from the request that can be used in some filters (see below).
|
|
154
|
+
|
|
155
|
+
**Usage example:**
|
|
156
|
+
```
|
|
157
|
+
{% get_cooco_manager request as cooco_manager %}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Filters
|
|
161
|
+
#### `ask_for_cooco`
|
|
162
|
+
|
|
163
|
+
Checks whether the cookie consent banner should be shown or not giving the object returned from `get_cooco_manager` tag.
|
|
164
|
+
|
|
165
|
+
**Arguments:**
|
|
166
|
+
- `cooco_manager` (`CooCoManager`): object containing the status of the cookie consent.
|
|
167
|
+
|
|
168
|
+
**Returns:**
|
|
169
|
+
- (`bool`): whether the cookie consent banner should be shown or not.
|
|
170
|
+
|
|
171
|
+
**Usage example:**
|
|
172
|
+
```
|
|
173
|
+
{% if cooco_manager|ask_for_cooco %}
|
|
174
|
+
...
|
|
175
|
+
{% endif %}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### `is_cookie_group_accepted`
|
|
179
|
+
|
|
180
|
+
Checks whether the given cookie group is accepted by the user or not giving the object returned from `get_cooco_manager` tag.
|
|
181
|
+
|
|
182
|
+
**Arguments:**
|
|
183
|
+
- `cooco_manager` (`CooCoManager`): object containing the status of the cookie consent.
|
|
184
|
+
- `cookie_group` (`CookieGroup`): cookie group to check.
|
|
185
|
+
|
|
186
|
+
**Returns:**
|
|
187
|
+
- (`bool`): whether the given cookie group is accepted by the user or not.
|
|
188
|
+
|
|
189
|
+
**Usage example:**
|
|
190
|
+
```
|
|
191
|
+
{% if cooco_manager|is_cookie_group_accepted:cookie_consent %}
|
|
192
|
+
...
|
|
193
|
+
{% endif %}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### `any_optional_cookie_group`
|
|
197
|
+
|
|
198
|
+
Checks if any of the given cookies is optional. This could be useful if you have to display a settings button or just "accept" option.
|
|
199
|
+
|
|
200
|
+
**Arguments:**
|
|
201
|
+
- `cookie_groups` (`Iterable[CookieGroup]`): iterable of cookie groups to check.
|
|
202
|
+
|
|
203
|
+
**Returns:**
|
|
204
|
+
- (`bool`): where there is any optional cookie group or not.
|
|
205
|
+
|
|
206
|
+
**Usage example:**
|
|
207
|
+
```
|
|
208
|
+
{% if cookie_groups|any_optional_cookie_group %}
|
|
209
|
+
...
|
|
210
|
+
{% endif %}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## URLs
|
|
214
|
+
#### `POST 'set_cookie_preferences'`
|
|
215
|
+
Set cookie preferences.
|
|
216
|
+
|
|
217
|
+
**Request fields:**
|
|
218
|
+
| Field | Type | Definition |
|
|
219
|
+
| ---------------------------------- | ----- | ------------------------------------- |
|
|
220
|
+
| `next` | `str` | Path to redirect when request is sent |
|
|
221
|
+
| cookie_id* (additional properties) | `str` | "on" if accepted, otherwise rejected |
|
|
222
|
+
|
|
223
|
+
**Example:**
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"next": "/home",
|
|
227
|
+
"analytics": "on",
|
|
228
|
+
"ads": "off",
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### `POST 'accept_all_cookies'`
|
|
233
|
+
Accept all cookies.
|
|
234
|
+
|
|
235
|
+
**Request fields:**
|
|
236
|
+
| Field | Type | Definition |
|
|
237
|
+
| ------ | ----- | ------------------------------------- |
|
|
238
|
+
| `next` | `str` | Path to redirect when request is sent |
|
|
239
|
+
|
|
240
|
+
**Example:**
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"next": "/home",
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Full example
|
|
248
|
+
Here you can see an example of how the full functionality implementation could look like. Note that `"analytics"` entry is present in the template context, which contains a database entry with a `ForeignKey` field to `CookieGroup` table called `"cookie_consent"` (see "Database Relationships" section of this document).
|
|
249
|
+
|
|
250
|
+
```html
|
|
251
|
+
{% load cooco %}
|
|
252
|
+
|
|
253
|
+
{% get_cooco_manager request as cooco_manager %}
|
|
254
|
+
|
|
255
|
+
<!DOCTYPE html>
|
|
256
|
+
<html>
|
|
257
|
+
<head>
|
|
258
|
+
<title>Cooco Example</title>
|
|
259
|
+
|
|
260
|
+
{% if cooco_manager|is_cookie_group_accepted:analytics.cookie_consent %}
|
|
261
|
+
<script>
|
|
262
|
+
// Script to use Analytics
|
|
263
|
+
</script>
|
|
264
|
+
{% endif %}
|
|
265
|
+
</head>
|
|
266
|
+
|
|
267
|
+
<body>
|
|
268
|
+
<p>This is an example of Cooco usage</p>
|
|
269
|
+
|
|
270
|
+
{% if cooco_manager|ask_for_cooco %}
|
|
271
|
+
{% get_cooco_banner_config as banner %}
|
|
272
|
+
|
|
273
|
+
{% if banner.show_banner %}
|
|
274
|
+
<div id="cooco_banner">
|
|
275
|
+
<div>
|
|
276
|
+
<h2>
|
|
277
|
+
{{ banner.title }}
|
|
278
|
+
</h2>
|
|
279
|
+
<p>
|
|
280
|
+
{{ banner.text }}
|
|
281
|
+
</p>
|
|
282
|
+
|
|
283
|
+
{% get_cookie_groups as cookie_groups %}
|
|
284
|
+
|
|
285
|
+
{% if cookie_groups|any_optional_cookie_group %}
|
|
286
|
+
<button onclick="showCookiesModal()">Settings</button>
|
|
287
|
+
<dialog>
|
|
288
|
+
<form class="inline-block"
|
|
289
|
+
action="{% url 'set_cookie_preferences' %}"
|
|
290
|
+
method="post">
|
|
291
|
+
{% csrf_token %}
|
|
292
|
+
<input name="next" type="hidden" value="{{ request.path }}" />
|
|
293
|
+
{% for cookie_group in cookie_groups %}
|
|
294
|
+
<div>
|
|
295
|
+
<h1>
|
|
296
|
+
{{ cookie_group.name }}
|
|
297
|
+
</h1>
|
|
298
|
+
<p>
|
|
299
|
+
{{ cookie_group.description }}
|
|
300
|
+
</p>
|
|
301
|
+
<input name="{{ cookie_group.cookie_id }}"
|
|
302
|
+
type="checkbox"
|
|
303
|
+
checked="checked"
|
|
304
|
+
{% if cookie_group.is_required %}disabled{% endif %} />
|
|
305
|
+
</div>
|
|
306
|
+
{% endfor %}
|
|
307
|
+
<button type="submit">Save</button>
|
|
308
|
+
</form>
|
|
309
|
+
</dialog>
|
|
310
|
+
{% endif %}
|
|
311
|
+
|
|
312
|
+
<form class="inline-block"
|
|
313
|
+
action="{% url 'accept_all_cookies' %}"
|
|
314
|
+
method="post">
|
|
315
|
+
{% csrf_token %}
|
|
316
|
+
<input name="next" type="hidden" value="{{ request.path }}">
|
|
317
|
+
<button type="submit">Accept</button>
|
|
318
|
+
</form>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
{% endif %}
|
|
322
|
+
|
|
323
|
+
{% endif %}
|
|
324
|
+
|
|
325
|
+
</body>
|
|
326
|
+
|
|
327
|
+
</html>
|
|
328
|
+
```
|
|
329
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
django_cooco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
django_cooco/admin.py,sha256=DfkualLHgCpy6PeOo-LqCQtn-5ILyJfX-sTYycKjh90,333
|
|
3
|
+
django_cooco/apps.py,sha256=rnwVVKl7XMIFi6-0zSBy9Xs2n56VL_92aRd2lrS5DdU,169
|
|
4
|
+
django_cooco/migrations/0001_initial.py,sha256=ZEegJyMtcrxTlehcViMn-e7n_P9iW43BmmVdefPVRCU,1170
|
|
5
|
+
django_cooco/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
django_cooco/models.py,sha256=KnLDecv4OiFwdIWrPPKX7Y5vxqnfcS8D4imqenlR4hg,978
|
|
7
|
+
django_cooco/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
django_cooco/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
django_cooco/templatetags/cooco.py,sha256=d07Tji5JFTIhAlCAp-5obJ4xczzQNoTq__545ubbhyc,1137
|
|
10
|
+
django_cooco/urls.py,sha256=pAXW1Q3Qd5S6EaQAHSKCTJoCmmMH-nyQurm__wwcos0,290
|
|
11
|
+
django_cooco/utils.py,sha256=HPApab38G8w6OXV8Pwdzsz82XDQ_nzu5wc7yG3aTqFA,4319
|
|
12
|
+
django_cooco/views.py,sha256=8b25_KErgiL5T-rv0ovkPChA0p_P7cMHDuUrTpzEzXA,973
|
|
13
|
+
django_cooco-0.0.1.dist-info/LICENSE.md,sha256=vmr_jObZmNHVZH-gySvJQv6Kz0HrEfy-Sq1QcSzPiTA,1084
|
|
14
|
+
django_cooco-0.0.1.dist-info/METADATA,sha256=oaWNrcYzzsYad-Svb3j_jOUDheq1PQN3snIjWufmclA,11617
|
|
15
|
+
django_cooco-0.0.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
16
|
+
django_cooco-0.0.1.dist-info/RECORD,,
|