django-cookie-consent 0.4.0.dev0__py3-none-any.whl → 0.5.0b0__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.
- cookie_consent/__init__.py +1 -1
- cookie_consent/cache.py +4 -1
- cookie_consent/compat.py +2 -0
- cookie_consent/models.py +21 -1
- cookie_consent/static/cookie_consent/cookiebar.js +2 -1
- cookie_consent/static/cookie_consent/cookiebar.module.js +203 -0
- cookie_consent/templatetags/cookie_consent_tags.py +39 -9
- cookie_consent/urls.py +15 -9
- cookie_consent/util.py +28 -11
- cookie_consent/views.py +53 -4
- {django_cookie_consent-0.4.0.dev0.dist-info → django_cookie_consent-0.5.0b0.dist-info}/METADATA +4 -3
- django_cookie_consent-0.5.0b0.dist-info/RECORD +28 -0
- {django_cookie_consent-0.4.0.dev0.dist-info → django_cookie_consent-0.5.0b0.dist-info}/WHEEL +1 -1
- django_cookie_consent-0.4.0.dev0.dist-info/RECORD +0 -27
- {django_cookie_consent-0.4.0.dev0.dist-info → django_cookie_consent-0.5.0b0.dist-info}/AUTHORS +0 -0
- {django_cookie_consent-0.4.0.dev0.dist-info → django_cookie_consent-0.5.0b0.dist-info}/LICENSE +0 -0
- {django_cookie_consent-0.4.0.dev0.dist-info → django_cookie_consent-0.5.0b0.dist-info}/top_level.txt +0 -0
cookie_consent/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.5.0b0"
|
cookie_consent/cache.py
CHANGED
|
@@ -31,7 +31,10 @@ def all_cookie_groups():
|
|
|
31
31
|
|
|
32
32
|
qs = CookieGroup.objects.filter(is_required=False)
|
|
33
33
|
qs = qs.prefetch_related("cookie_set")
|
|
34
|
-
items =
|
|
34
|
+
# items = qs.in_bulk(field_name="varname")
|
|
35
|
+
# FIXME -> doesn't work because varname is not a unique fieldl, we need to
|
|
36
|
+
# make this unique
|
|
37
|
+
items = {group.varname: group for group in qs}
|
|
35
38
|
cache.set(CACHE_KEY, items, CACHE_TIMEOUT)
|
|
36
39
|
return items
|
|
37
40
|
|
cookie_consent/compat.py
CHANGED
cookie_consent/models.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
import re
|
|
3
|
+
from typing import TypedDict
|
|
3
4
|
|
|
4
5
|
from django.core.validators import RegexValidator
|
|
5
6
|
from django.db import models
|
|
@@ -18,6 +19,16 @@ validate_cookie_name = RegexValidator(
|
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
class CookieGroupDict(TypedDict):
|
|
23
|
+
varname: str
|
|
24
|
+
name: str
|
|
25
|
+
description: str
|
|
26
|
+
is_required: bool
|
|
27
|
+
# TODO: should we output this? page cache busting would be
|
|
28
|
+
# required if we do this. Alternatively, set up a JSONView to output these?
|
|
29
|
+
# version: str
|
|
30
|
+
|
|
31
|
+
|
|
21
32
|
class CookieGroup(models.Model):
|
|
22
33
|
varname = models.CharField(
|
|
23
34
|
_("Variable name"), max_length=32, validators=[validate_cookie_name]
|
|
@@ -45,7 +56,7 @@ class CookieGroup(models.Model):
|
|
|
45
56
|
def __str__(self):
|
|
46
57
|
return self.name
|
|
47
58
|
|
|
48
|
-
def get_version(self):
|
|
59
|
+
def get_version(self) -> str:
|
|
49
60
|
try:
|
|
50
61
|
return str(self.cookie_set.all()[0].get_version())
|
|
51
62
|
except IndexError:
|
|
@@ -59,6 +70,15 @@ class CookieGroup(models.Model):
|
|
|
59
70
|
super(CookieGroup, self).save(*args, **kwargs)
|
|
60
71
|
delete_cache()
|
|
61
72
|
|
|
73
|
+
def for_json(self) -> CookieGroupDict:
|
|
74
|
+
return {
|
|
75
|
+
"varname": self.varname,
|
|
76
|
+
"name": self.name,
|
|
77
|
+
"description": self.description,
|
|
78
|
+
"is_required": self.is_required,
|
|
79
|
+
# "version": self.get_version(),
|
|
80
|
+
}
|
|
81
|
+
|
|
62
82
|
|
|
63
83
|
class Cookie(models.Model):
|
|
64
84
|
cookiegroup = models.ForeignKey(
|
|
@@ -10,7 +10,7 @@ function evalXCookieConsent(script) {
|
|
|
10
10
|
script.remove();
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
function
|
|
13
|
+
function lecacyShowCookieBar (options) {
|
|
14
14
|
const defaults = {
|
|
15
15
|
content: '',
|
|
16
16
|
cookie_groups: [],
|
|
@@ -64,3 +64,4 @@ function showCookieBar (options) {
|
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
window.legacyShowCookieBar = window.showCookieBar = lecacyShowCookieBar;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookiebar functionality, as a Javascript module.
|
|
3
|
+
*
|
|
4
|
+
* About modules: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
|
|
5
|
+
*
|
|
6
|
+
* The code is organized here in a way to make the templates work with Django's page
|
|
7
|
+
* cache. This means that anything user-specific (so different django session and even
|
|
8
|
+
* cookie consent cookies) cannot be baked into the templates, as that breaks caches.
|
|
9
|
+
*
|
|
10
|
+
* The cookie bar operates on the following principles:
|
|
11
|
+
*
|
|
12
|
+
* - The developer using the library includes the desired template in their django
|
|
13
|
+
* templates, using the HTML <template> element. This contains the content for the
|
|
14
|
+
* cookie bar.
|
|
15
|
+
* - The developer is responsible for loading some Javascript that loads this script.
|
|
16
|
+
* - The main export of this script needs to be called (showCookieBar), with the
|
|
17
|
+
* appropriate options.
|
|
18
|
+
* - The options include the backend URLs where the retrieve data, which selectors/DOM
|
|
19
|
+
* nodes to use for various functionality and the hooks to tap into the accept/decline
|
|
20
|
+
* life-cycle.
|
|
21
|
+
* - When a user accepts or declines (all) cookies, the call to the backend is made via
|
|
22
|
+
* a fetch request, bypassing any page caches and preventing full-page reloads.
|
|
23
|
+
*/
|
|
24
|
+
const DEFAULTS = {
|
|
25
|
+
statusUrl: undefined,
|
|
26
|
+
// TODO: also accept element rather than selector?
|
|
27
|
+
templateSelector: '#cookie-consent__cookie-bar',
|
|
28
|
+
cookieGroupsSelector: '#cookie-consent__cookie-groups',
|
|
29
|
+
acceptSelector: '.cookie-consent__accept',
|
|
30
|
+
declineSelector: '.cookie-consent__decline',
|
|
31
|
+
/**
|
|
32
|
+
* Either a string (selector), DOMNode or null.
|
|
33
|
+
*
|
|
34
|
+
* If null, the bar is appended to the body. If provided, the node is used or looked
|
|
35
|
+
* up.
|
|
36
|
+
*/
|
|
37
|
+
insertBefore: null,
|
|
38
|
+
onShow: null, // callback when the cookie bar is being shown -> add class to body...
|
|
39
|
+
onAccept: null, // callback when cookies are accepted
|
|
40
|
+
onDecline: null, // callback when cookies are declined
|
|
41
|
+
csrfHeaderName: 'X-CSRFToken', // Django's default, can be overridden with settings.CSRF_HEADER_NAME
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const DEFAULT_HEADERS = {'X-Cookie-Consent-Fetch': '1'};
|
|
45
|
+
|
|
46
|
+
let CONFIGURATION = DEFAULTS;
|
|
47
|
+
/**
|
|
48
|
+
* Cookie accept status, including the accept/decline URLs, csrftoken... See
|
|
49
|
+
* backend view CookieStatusView.
|
|
50
|
+
*/
|
|
51
|
+
let COOKIE_STATUS = null;
|
|
52
|
+
|
|
53
|
+
export const loadCookieGroups = (selector) => {
|
|
54
|
+
const node = document.querySelector(selector);
|
|
55
|
+
if (!node) {
|
|
56
|
+
throw new Error(`No cookie groups (script) tag found, using selector: '${selector}'`);
|
|
57
|
+
}
|
|
58
|
+
return JSON.parse(node.innerText);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const doInsertBefore = (beforeNode, newNode) => {
|
|
62
|
+
const parent = beforeNode.parentNode;
|
|
63
|
+
parent.insertBefore(newNode, beforeNode);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Register the accept/decline event handlers.
|
|
68
|
+
*
|
|
69
|
+
* Note that we can't just set the decline or accept cookie purely client-side, as the
|
|
70
|
+
* cookie possibly has the httpOnly flag set.
|
|
71
|
+
*
|
|
72
|
+
* @param {HTMLEelement} cookieBarNode The DOM node containing the cookiebar markup.
|
|
73
|
+
* @param {Array} cookieGroups The array of all configured cookie groups.
|
|
74
|
+
* @return {Void}
|
|
75
|
+
*/
|
|
76
|
+
const registerEvents = (cookieBarNode, cookieGroups) => {
|
|
77
|
+
const {acceptSelector, onAccept, declineSelector, onDecline} = CONFIGURATION;
|
|
78
|
+
const {
|
|
79
|
+
acceptedCookieGroups: accepted,
|
|
80
|
+
declinedCookieGroups: declined,
|
|
81
|
+
notAcceptedOrDeclinedCookieGroups: undecided,
|
|
82
|
+
} = COOKIE_STATUS;
|
|
83
|
+
|
|
84
|
+
cookieBarNode
|
|
85
|
+
.querySelector(acceptSelector)
|
|
86
|
+
.addEventListener('click', event => {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
const acceptedGroups = filterCookieGroups(cookieGroups, accepted.concat(undecided));
|
|
89
|
+
onAccept?.(acceptedGroups, event);
|
|
90
|
+
acceptCookiesBackend();
|
|
91
|
+
cookieBarNode.parentNode.removeChild(cookieBarNode);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
cookieBarNode
|
|
95
|
+
.querySelector(declineSelector)
|
|
96
|
+
.addEventListener('click', event => {
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
const declinedGroups = filterCookieGroups(cookieGroups, declined.concat(undecided));
|
|
99
|
+
onDecline?.(declinedGroups, event);
|
|
100
|
+
declineCookiesBackend();
|
|
101
|
+
cookieBarNode.parentNode.removeChild(cookieBarNode);
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const loadCookieStatus = async () => {
|
|
106
|
+
const {statusUrl} = CONFIGURATION;
|
|
107
|
+
if (!statusUrl) console.error('Missing status URL option, did you forget to pass the statusUrl option?');
|
|
108
|
+
const response = await window.fetch(
|
|
109
|
+
CONFIGURATION.statusUrl,
|
|
110
|
+
{
|
|
111
|
+
method: 'GET',
|
|
112
|
+
credentials: 'same-origin',
|
|
113
|
+
headers: DEFAULT_HEADERS
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
// assign to module level variable, once the page is loaded these details should
|
|
117
|
+
// not change.
|
|
118
|
+
COOKIE_STATUS = await response.json();
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const saveCookiesStatusBackend = async (urlProperty) => {
|
|
122
|
+
const status = COOKIE_STATUS || {};
|
|
123
|
+
const url = status[urlProperty];
|
|
124
|
+
if (!url) {
|
|
125
|
+
console.error(`Missing url for ${urlProperty} - was the cookie status not loaded properly?`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
await window.fetch(url, {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
credentials: 'same-origin',
|
|
131
|
+
headers: {
|
|
132
|
+
...DEFAULT_HEADERS,
|
|
133
|
+
[CONFIGURATION.csrfHeaderName]: status.csrftoken
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Make the call to the backend to accept the cookies.
|
|
140
|
+
*/
|
|
141
|
+
const acceptCookiesBackend = async () => await saveCookiesStatusBackend('acceptUrl');
|
|
142
|
+
/**
|
|
143
|
+
* Make the call to the backend to decline the cookies.
|
|
144
|
+
*/
|
|
145
|
+
const declineCookiesBackend = async () => await saveCookiesStatusBackend('declineUrl');
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Filter the cookie groups down to a subset of specified varnames.
|
|
149
|
+
*/
|
|
150
|
+
const filterCookieGroups = (cookieGroups, varNames) => {
|
|
151
|
+
return cookieGroups.filter(group => varNames.includes(group.varname));
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const showCookieBar = async (options={}) => {
|
|
155
|
+
// merge defaults and provided options
|
|
156
|
+
CONFIGURATION = {...DEFAULTS, ...options};
|
|
157
|
+
const {
|
|
158
|
+
cookieGroupsSelector,
|
|
159
|
+
templateSelector,
|
|
160
|
+
insertBefore,
|
|
161
|
+
onShow,
|
|
162
|
+
onAccept,
|
|
163
|
+
onDecline,
|
|
164
|
+
} = CONFIGURATION;
|
|
165
|
+
const cookieGroups = loadCookieGroups(cookieGroupsSelector);
|
|
166
|
+
|
|
167
|
+
// no cookie groups -> abort
|
|
168
|
+
if (!cookieGroups.length) return;
|
|
169
|
+
|
|
170
|
+
const templateNode = document.querySelector(templateSelector);
|
|
171
|
+
|
|
172
|
+
// insert before a given node, if specified, or append to the body as default behaviour
|
|
173
|
+
const doInsert = insertBefore === null
|
|
174
|
+
? (cookieBarNode) => document.querySelector('body').appendChild(cookieBarNode)
|
|
175
|
+
: typeof insertBefore === 'string'
|
|
176
|
+
? (cookieBarNode) => doInsertBefore(document.querySelector(insertBefore), cookieBarNode)
|
|
177
|
+
: (cookieBarNode) => doInsertBefore(insertBefore, cookieBarNode)
|
|
178
|
+
;
|
|
179
|
+
await loadCookieStatus();
|
|
180
|
+
|
|
181
|
+
// calculate the cookie groups to invoke the callbacks. We deliberately fire those
|
|
182
|
+
// without awaiting so that our cookie bar is shown/hidden as soon as possible.
|
|
183
|
+
const {
|
|
184
|
+
acceptedCookieGroups: accepted,
|
|
185
|
+
declinedCookieGroups: declined,
|
|
186
|
+
notAcceptedOrDeclinedCookieGroups
|
|
187
|
+
} = COOKIE_STATUS;
|
|
188
|
+
|
|
189
|
+
const acceptedGroups = filterCookieGroups(cookieGroups, accepted);
|
|
190
|
+
if (acceptedGroups.length) onAccept?.(acceptedGroups);
|
|
191
|
+
const declinedGroups = filterCookieGroups(cookieGroups, declined);
|
|
192
|
+
if (declinedGroups.length) onDecline?.(declinedGroups);
|
|
193
|
+
|
|
194
|
+
// there are no (more) cookie groups to accept, don't show the bar
|
|
195
|
+
if (!notAcceptedOrDeclinedCookieGroups.length) return;
|
|
196
|
+
|
|
197
|
+
// grab the contents from the template node and add them to the DOM, optionally
|
|
198
|
+
// calling the onShow callback
|
|
199
|
+
const cookieBarNode = templateNode.content.firstElementChild.cloneNode(true);
|
|
200
|
+
registerEvents(cookieBarNode, cookieGroups);
|
|
201
|
+
onShow?.();
|
|
202
|
+
doInsert(cookieBarNode);
|
|
203
|
+
};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
from django import template
|
|
1
|
+
import warnings
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
from django.core.urlresolvers import reverse
|
|
3
|
+
from django import template
|
|
4
|
+
from django.urls import reverse
|
|
5
|
+
from django.utils.html import json_script
|
|
8
6
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
7
|
+
from ..cache import all_cookie_groups as get_all_cookie_groups
|
|
8
|
+
from ..conf import settings
|
|
9
|
+
from ..util import (
|
|
11
10
|
are_all_cookies_accepted,
|
|
12
11
|
get_accepted_cookies,
|
|
13
12
|
get_cookie_dict_from_request,
|
|
@@ -90,10 +89,15 @@ def cookie_consent_decline_url(cookie_groups):
|
|
|
90
89
|
|
|
91
90
|
|
|
92
91
|
@register.simple_tag
|
|
93
|
-
def get_accept_cookie_groups_cookie_string(request, cookie_groups):
|
|
92
|
+
def get_accept_cookie_groups_cookie_string(request, cookie_groups): # pragma: no cover
|
|
94
93
|
"""
|
|
95
94
|
Tag returns accept cookie string suitable to use in javascript.
|
|
96
95
|
"""
|
|
96
|
+
warnings.warn(
|
|
97
|
+
"Cookie string template tags for JS are deprecated and will be removed "
|
|
98
|
+
"in django-cookie-consent 1.0",
|
|
99
|
+
DeprecationWarning,
|
|
100
|
+
)
|
|
97
101
|
cookie_dic = get_cookie_dict_from_request(request)
|
|
98
102
|
for cookie_group in cookie_groups:
|
|
99
103
|
cookie_dic[cookie_group.varname] = cookie_group.get_version()
|
|
@@ -105,6 +109,11 @@ def get_decline_cookie_groups_cookie_string(request, cookie_groups):
|
|
|
105
109
|
"""
|
|
106
110
|
Tag returns decline cookie string suitable to use in javascript.
|
|
107
111
|
"""
|
|
112
|
+
warnings.warn(
|
|
113
|
+
"Cookie string template tags for JS are deprecated and will be removed "
|
|
114
|
+
"in django-cookie-consent 1.0",
|
|
115
|
+
DeprecationWarning,
|
|
116
|
+
)
|
|
108
117
|
cookie_dic = get_cookie_dict_from_request(request)
|
|
109
118
|
for cookie_group in cookie_groups:
|
|
110
119
|
cookie_dic[cookie_group.varname] = settings.COOKIE_CONSENT_DECLINE
|
|
@@ -124,6 +133,13 @@ def js_type_for_cookie_consent(request, varname, cookie=None):
|
|
|
124
133
|
alert("Social cookie accepted");
|
|
125
134
|
</script>
|
|
126
135
|
"""
|
|
136
|
+
# This approach doesn't work with page caches and/or strict Content-Security-Policies
|
|
137
|
+
# (unless you use nonces, which again doesn't work with aggressive page caching).
|
|
138
|
+
warnings.warn(
|
|
139
|
+
"Template tags for use in/with JS are deprecated and will be removed "
|
|
140
|
+
"in django-cookie-consent 1.0",
|
|
141
|
+
DeprecationWarning,
|
|
142
|
+
)
|
|
127
143
|
enabled = is_cookie_consent_enabled(request)
|
|
128
144
|
if not enabled:
|
|
129
145
|
res = True
|
|
@@ -147,3 +163,17 @@ def accepted_cookies(request):
|
|
|
147
163
|
|
|
148
164
|
"""
|
|
149
165
|
return [c.varname for c in get_accepted_cookies(request)]
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@register.simple_tag
|
|
169
|
+
def all_cookie_groups(element_id: str):
|
|
170
|
+
"""
|
|
171
|
+
Serialize all cookie groups to JSON and output them in a script tag.
|
|
172
|
+
|
|
173
|
+
:param element_id: The ID for the script tag so you can look it up in JS later.
|
|
174
|
+
|
|
175
|
+
This uses Django's core json_script filter under the hood.
|
|
176
|
+
"""
|
|
177
|
+
groups = get_all_cookie_groups()
|
|
178
|
+
value = [group.for_json() for group in groups.values()]
|
|
179
|
+
return json_script(value, element_id)
|
cookie_consent/urls.py
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from django.urls import re_path
|
|
2
|
+
from django.urls import path, re_path
|
|
3
3
|
from django.views.decorators.csrf import csrf_exempt
|
|
4
4
|
|
|
5
|
-
from .views import
|
|
5
|
+
from .views import (
|
|
6
|
+
CookieGroupAcceptView,
|
|
7
|
+
CookieGroupDeclineView,
|
|
8
|
+
CookieGroupListView,
|
|
9
|
+
CookieStatusView,
|
|
10
|
+
)
|
|
6
11
|
|
|
7
12
|
urlpatterns = [
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
path(
|
|
14
|
+
"accept/",
|
|
10
15
|
csrf_exempt(CookieGroupAcceptView.as_view()),
|
|
11
16
|
name="cookie_consent_accept_all",
|
|
12
17
|
),
|
|
18
|
+
# TODO: use form or query string params for this instead?
|
|
13
19
|
re_path(
|
|
14
20
|
r"^accept/(?P<varname>.*)/$",
|
|
15
21
|
csrf_exempt(CookieGroupAcceptView.as_view()),
|
|
16
22
|
name="cookie_consent_accept",
|
|
17
23
|
),
|
|
24
|
+
# TODO: use form or query string params for this instead?
|
|
18
25
|
re_path(
|
|
19
26
|
r"^decline/(?P<varname>.*)/$",
|
|
20
27
|
csrf_exempt(CookieGroupDeclineView.as_view()),
|
|
21
28
|
name="cookie_consent_decline",
|
|
22
29
|
),
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
path(
|
|
31
|
+
"decline/",
|
|
25
32
|
csrf_exempt(CookieGroupDeclineView.as_view()),
|
|
26
33
|
name="cookie_consent_decline_all",
|
|
27
34
|
),
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
),
|
|
35
|
+
path("status/", CookieStatusView.as_view(), name="cookie_consent_status"),
|
|
36
|
+
path("", CookieGroupListView.as_view(), name="cookie_consent_cookie_group_list"),
|
|
31
37
|
]
|
cookie_consent/util.py
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
import datetime
|
|
3
|
+
from typing import Union
|
|
3
4
|
|
|
4
|
-
from
|
|
5
|
-
|
|
6
|
-
from
|
|
7
|
-
from cookie_consent.conf import settings
|
|
8
|
-
from cookie_consent.models import ACTION_ACCEPTED, ACTION_DECLINED, LogItem
|
|
5
|
+
from .cache import all_cookie_groups, get_cookie, get_cookie_group
|
|
6
|
+
from .conf import settings
|
|
7
|
+
from .models import ACTION_ACCEPTED, ACTION_DECLINED, LogItem
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
def parse_cookie_str(cookie):
|
|
@@ -100,7 +99,7 @@ def accept_cookies(request, response, varname=None):
|
|
|
100
99
|
def delete_cookies(response, cookie_group):
|
|
101
100
|
if cookie_group.is_deletable:
|
|
102
101
|
for cookie in cookie_group.cookie_set.all():
|
|
103
|
-
response.delete_cookie(
|
|
102
|
+
response.delete_cookie(cookie.name, cookie.path, cookie.domain)
|
|
104
103
|
|
|
105
104
|
|
|
106
105
|
def decline_cookies(request, response, varname=None):
|
|
@@ -132,17 +131,35 @@ def are_all_cookies_accepted(request):
|
|
|
132
131
|
)
|
|
133
132
|
|
|
134
133
|
|
|
135
|
-
def
|
|
136
|
-
"""
|
|
137
|
-
Returns all cookie groups that are neither accepted or declined.
|
|
138
|
-
"""
|
|
134
|
+
def _get_cookie_groups_by_state(request, state: Union[bool, None]):
|
|
139
135
|
return [
|
|
140
136
|
cookie_group
|
|
141
137
|
for cookie_group in get_cookie_groups()
|
|
142
|
-
if get_cookie_value_from_request(request, cookie_group.varname) is
|
|
138
|
+
if get_cookie_value_from_request(request, cookie_group.varname) is state
|
|
143
139
|
]
|
|
144
140
|
|
|
145
141
|
|
|
142
|
+
def get_not_accepted_or_declined_cookie_groups(request):
|
|
143
|
+
"""
|
|
144
|
+
Returns all cookie groups that are neither accepted or declined.
|
|
145
|
+
"""
|
|
146
|
+
return _get_cookie_groups_by_state(request, state=None)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def get_accepted_cookie_groups(request):
|
|
150
|
+
"""
|
|
151
|
+
Returns all cookie groups that are accepted.
|
|
152
|
+
"""
|
|
153
|
+
return _get_cookie_groups_by_state(request, state=True)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def get_declined_cookie_groups(request):
|
|
157
|
+
"""
|
|
158
|
+
Returns all cookie groups that are declined.
|
|
159
|
+
"""
|
|
160
|
+
return _get_cookie_groups_by_state(request, state=False)
|
|
161
|
+
|
|
162
|
+
|
|
146
163
|
def is_cookie_consent_enabled(request):
|
|
147
164
|
"""
|
|
148
165
|
Returns if django-cookie-consent is enabled for given request.
|
cookie_consent/views.py
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
from django.core.exceptions import SuspiciousOperation
|
|
3
|
-
from django.http import HttpResponse, HttpResponseRedirect
|
|
3
|
+
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect, JsonResponse
|
|
4
|
+
from django.middleware.csrf import get_token as get_csrf_token
|
|
4
5
|
from django.urls import reverse
|
|
5
6
|
from django.views.generic import ListView, View
|
|
6
7
|
|
|
7
8
|
from .compat import RedirectURLMixin, url_has_allowed_host_and_scheme
|
|
8
9
|
from .models import CookieGroup
|
|
9
|
-
from .util import
|
|
10
|
+
from .util import (
|
|
11
|
+
accept_cookies,
|
|
12
|
+
decline_cookies,
|
|
13
|
+
get_accepted_cookie_groups,
|
|
14
|
+
get_declined_cookie_groups,
|
|
15
|
+
get_not_accepted_or_declined_cookie_groups,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def is_ajax_like(request: HttpRequest) -> bool:
|
|
20
|
+
# legacy ajax, removed in Django 4.0 (used to be request.is_ajax())
|
|
21
|
+
ajax_header = request.headers.get("X-Requested-With")
|
|
22
|
+
if ajax_header == "XMLHttpRequest":
|
|
23
|
+
return True
|
|
24
|
+
|
|
25
|
+
# module-js uses fetch and a custom header
|
|
26
|
+
return bool(request.headers.get("X-Cookie-Consent-Fetch"))
|
|
10
27
|
|
|
11
28
|
|
|
12
29
|
class CookieGroupListView(ListView):
|
|
@@ -33,12 +50,12 @@ class CookieGroupBaseProcessView(RedirectURLMixin, View):
|
|
|
33
50
|
raise SuspiciousOperation("Unsafe open redirect suspected.")
|
|
34
51
|
return redirect_to or reverse("cookie_consent_cookie_group_list")
|
|
35
52
|
|
|
36
|
-
def process(self, request, response, varname):
|
|
53
|
+
def process(self, request, response, varname): # pragma: no cover
|
|
37
54
|
raise NotImplementedError()
|
|
38
55
|
|
|
39
56
|
def post(self, request, *args, **kwargs):
|
|
40
57
|
varname = kwargs.get("varname", None)
|
|
41
|
-
if request
|
|
58
|
+
if is_ajax_like(request):
|
|
42
59
|
response = HttpResponse()
|
|
43
60
|
else:
|
|
44
61
|
response = HttpResponseRedirect(self.get_success_url())
|
|
@@ -65,3 +82,35 @@ class CookieGroupDeclineView(CookieGroupBaseProcessView):
|
|
|
65
82
|
|
|
66
83
|
def delete(self, request, *args, **kwargs):
|
|
67
84
|
return self.post(request, *args, **kwargs)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class CookieStatusView(View):
|
|
88
|
+
"""
|
|
89
|
+
Check the current accept/decline status for cookies.
|
|
90
|
+
|
|
91
|
+
The returned accept and decline URLs are specific to this user and include the
|
|
92
|
+
cookie groups that weren't accepted or declined yet.
|
|
93
|
+
|
|
94
|
+
Note that this endpoint also returns a CSRF Token to be used by the frontend,
|
|
95
|
+
as baking a CSRFToken into a cached page will not reliably work.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
def get(self, request: HttpRequest) -> JsonResponse:
|
|
99
|
+
accepted = get_accepted_cookie_groups(request)
|
|
100
|
+
declined = get_declined_cookie_groups(request)
|
|
101
|
+
not_accepted_or_declined = get_not_accepted_or_declined_cookie_groups(request)
|
|
102
|
+
# TODO: change this csv URL param into proper POST params
|
|
103
|
+
varnames = ",".join([group.varname for group in not_accepted_or_declined])
|
|
104
|
+
data = {
|
|
105
|
+
"csrftoken": get_csrf_token(request),
|
|
106
|
+
"acceptUrl": reverse("cookie_consent_accept", kwargs={"varname": varnames}),
|
|
107
|
+
"declineUrl": reverse(
|
|
108
|
+
"cookie_consent_decline", kwargs={"varname": varnames}
|
|
109
|
+
),
|
|
110
|
+
"acceptedCookieGroups": [group.varname for group in accepted],
|
|
111
|
+
"declinedCookieGroups": [group.varname for group in declined],
|
|
112
|
+
"notAcceptedOrDeclinedCookieGroups": [
|
|
113
|
+
group.varname for group in not_accepted_or_declined
|
|
114
|
+
],
|
|
115
|
+
}
|
|
116
|
+
return JsonResponse(data)
|
{django_cookie_consent-0.4.0.dev0.dist-info → django_cookie_consent-0.5.0b0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: django-cookie-consent
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0b0
|
|
4
4
|
Summary: Django cookie consent application
|
|
5
5
|
Home-page: https://github.com/jazzband/django-cookie-consent
|
|
6
6
|
Author: Informatika Mihelac
|
|
@@ -11,7 +11,7 @@ Project-URL: Changelog, https://github.com/jazzband/django-cookie-consent/blob/m
|
|
|
11
11
|
Project-URL: Bug Tracker, https://github.com/jazzband/django-cookie-consent/issues
|
|
12
12
|
Project-URL: Source Code, https://github.com/jazzband/django-cookie-consent
|
|
13
13
|
Keywords: cookies,cookie-consent,cookie bar
|
|
14
|
-
Classifier: Development Status ::
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
15
|
Classifier: Framework :: Django
|
|
16
16
|
Classifier: Framework :: Django :: 3.2
|
|
17
17
|
Classifier: Framework :: Django :: 4.1
|
|
@@ -30,7 +30,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
30
30
|
Description-Content-Type: text/markdown
|
|
31
31
|
License-File: LICENSE
|
|
32
32
|
License-File: AUTHORS
|
|
33
|
-
Requires-Dist: django
|
|
33
|
+
Requires-Dist: django >=3.2
|
|
34
34
|
Requires-Dist: django-appconf
|
|
35
35
|
Provides-Extra: coverage
|
|
36
36
|
Requires-Dist: pytest-cov ; extra == 'coverage'
|
|
@@ -45,6 +45,7 @@ Requires-Dist: twine ; extra == 'release'
|
|
|
45
45
|
Provides-Extra: tests
|
|
46
46
|
Requires-Dist: pytest ; extra == 'tests'
|
|
47
47
|
Requires-Dist: pytest-django ; extra == 'tests'
|
|
48
|
+
Requires-Dist: pytest-playwright ; extra == 'tests'
|
|
48
49
|
Requires-Dist: tox ; extra == 'tests'
|
|
49
50
|
Requires-Dist: isort ; extra == 'tests'
|
|
50
51
|
Requires-Dist: black ; extra == 'tests'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
cookie_consent/__init__.py,sha256=dOConuWcry21fudkiQhlg2ykFSpmcOjEK2ytveI7SmA,24
|
|
2
|
+
cookie_consent/admin.py,sha256=9IoTczdhLqbn4QMuJSbIWNe6dahj6niiOz3IDKw4sk4,1112
|
|
3
|
+
cookie_consent/apps.py,sha256=nldd3hZnNChnrajmCD3crvxFfieWgLi625grNTEVYis,248
|
|
4
|
+
cookie_consent/cache.py,sha256=B-65iwYvyR6kbT5LIiGb2PDLUwnW2xr3xHWTbDf9qsc,1446
|
|
5
|
+
cookie_consent/compat.py,sha256=6zIzPpufUOlSr6J1bvJdacYhenIVrKMfOOS995mflUQ,464
|
|
6
|
+
cookie_consent/conf.py,sha256=Q-xpL-HgxMf1PTrZL7TtGOdOUgaUYs8_d1ZwBipK7Nk,555
|
|
7
|
+
cookie_consent/middleware.py,sha256=DvpJC-YabhAt-8V2u6mTdpzGN1q833nnuAeO4E9cBaA,1981
|
|
8
|
+
cookie_consent/models.py,sha256=hPDyyerJeVExLVB_yLJl4jKiqRdghDSO_QYwtZ_lbBs,4242
|
|
9
|
+
cookie_consent/urls.py,sha256=U5ssRnjWoJM3GynERe0CUVsDdZ0-98BKX6vyimg312A,1119
|
|
10
|
+
cookie_consent/util.py,sha256=vS0bd9WJDnEpS8lBlBTJCbApJw8Flr98Rh-D0edjIQs,5948
|
|
11
|
+
cookie_consent/views.py,sha256=OPeWNXCp3zEZAR7EE4UnCtaHqQAWYORHI3RMrEi54x4,4126
|
|
12
|
+
cookie_consent/fixtures/common_cookies.json,sha256=iOEeMtZhERoeoxBz5Kj-Mo_XOUk5F1ljNon4w2odBJo,2449
|
|
13
|
+
cookie_consent/migrations/0001_initial.py,sha256=jIGX_k1nIm7GGpqgE-zvI1NIBizD8wkxYEb-BL6FR4c,4180
|
|
14
|
+
cookie_consent/migrations/0002_auto__add_logitem.py,sha256=rZ_kkXI3eij3k6PiGP5CsA41psENNZBbMD0qwUxBBTM,1633
|
|
15
|
+
cookie_consent/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
cookie_consent/static/cookie_consent/cookiebar.js,sha256=vdouyXSBRkzKDiLdMAdOTWKzNO3vgXZNehUTIVUW5Gk,1919
|
|
17
|
+
cookie_consent/static/cookie_consent/cookiebar.module.js,sha256=9h1i6AhvNhxw6kl9vC4T72uXTHmWpgy5Le0ezyChDh4,7371
|
|
18
|
+
cookie_consent/templates/cookie_consent/_cookie_group.html,sha256=wozvbdRjuKu3gnegN8vex_lDkku22pne830NWuzHXVw,1496
|
|
19
|
+
cookie_consent/templates/cookie_consent/base.html,sha256=ghOQ9vqSzKG32LfSQmj5xTF1PBs-SDtQEcOC1wjAemQ,85
|
|
20
|
+
cookie_consent/templates/cookie_consent/cookiegroup_list.html,sha256=PMO9iLxQrgRpQGLC0stlDamMonc3tLS3o5PWPF378Zg,308
|
|
21
|
+
cookie_consent/templatetags/__init__.py,sha256=FZk9hm7vBsK8G16pNugputbrrEciYqJc_XRAhTCcojI,46
|
|
22
|
+
cookie_consent/templatetags/cookie_consent_tags.py,sha256=gBn1Z61KeFcCRd368zxYCN9FJFQ1x5RX2E12ricdRgU,5175
|
|
23
|
+
django_cookie_consent-0.5.0b0.dist-info/AUTHORS,sha256=1IpYKADNdC03tx3VzMmPLRJo8MHHC_JlWcPEcdcTDA8,323
|
|
24
|
+
django_cookie_consent-0.5.0b0.dist-info/LICENSE,sha256=ZZi-io1uazO5jpDoNJScUfjeVI2dEzvDPOcyFhU0KYw,1349
|
|
25
|
+
django_cookie_consent-0.5.0b0.dist-info/METADATA,sha256=xxKagY9VvX-6lpC3rAasVdWAWqUnarscSoelQ1cX8aQ,4660
|
|
26
|
+
django_cookie_consent-0.5.0b0.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
|
27
|
+
django_cookie_consent-0.5.0b0.dist-info/top_level.txt,sha256=wD8Ix58rJguYeYfils_Z40QfVybiRYb1vwRNm09HN4M,15
|
|
28
|
+
django_cookie_consent-0.5.0b0.dist-info/RECORD,,
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
cookie_consent/__init__.py,sha256=5B0Zd_u0SQA_ltxO2HzzO8rTslrZ8C_2tUU0wv4lhYg,28
|
|
2
|
-
cookie_consent/admin.py,sha256=9IoTczdhLqbn4QMuJSbIWNe6dahj6niiOz3IDKw4sk4,1112
|
|
3
|
-
cookie_consent/apps.py,sha256=nldd3hZnNChnrajmCD3crvxFfieWgLi625grNTEVYis,248
|
|
4
|
-
cookie_consent/cache.py,sha256=nxSba7Xr9Xn53jxUoMObRLpb9w3owlMxCDP2jpM9PuQ,1281
|
|
5
|
-
cookie_consent/compat.py,sha256=vMaI1CIAgcS2BODgnUg1lZwq5kNiHM6B4U2qtXASoaI,397
|
|
6
|
-
cookie_consent/conf.py,sha256=Q-xpL-HgxMf1PTrZL7TtGOdOUgaUYs8_d1ZwBipK7Nk,555
|
|
7
|
-
cookie_consent/middleware.py,sha256=DvpJC-YabhAt-8V2u6mTdpzGN1q833nnuAeO4E9cBaA,1981
|
|
8
|
-
cookie_consent/models.py,sha256=cmxDFAXLfbdmwyyuBn2NLbAW8AhQUWGxHbciV3FqVow,3660
|
|
9
|
-
cookie_consent/urls.py,sha256=VlUdWUyJ7G1-w5uVtxSsigvvHslOQXIbNcVNGs_8OLE,903
|
|
10
|
-
cookie_consent/util.py,sha256=792FMk8a7SAa67m6chhMvZLe-Iic2ETkJCAJXm4zA5w,5553
|
|
11
|
-
cookie_consent/views.py,sha256=RrM6z6i5n5RfC6uNv9ToaqZOy9ToHOrdFV5jUvupICk,2173
|
|
12
|
-
cookie_consent/fixtures/common_cookies.json,sha256=iOEeMtZhERoeoxBz5Kj-Mo_XOUk5F1ljNon4w2odBJo,2449
|
|
13
|
-
cookie_consent/migrations/0001_initial.py,sha256=jIGX_k1nIm7GGpqgE-zvI1NIBizD8wkxYEb-BL6FR4c,4180
|
|
14
|
-
cookie_consent/migrations/0002_auto__add_logitem.py,sha256=rZ_kkXI3eij3k6PiGP5CsA41psENNZBbMD0qwUxBBTM,1633
|
|
15
|
-
cookie_consent/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
cookie_consent/static/cookie_consent/cookiebar.js,sha256=H0AzuvEMcZNzb53VYcw-U5UE6aDgt9MChxOmCZJBMMA,1840
|
|
17
|
-
cookie_consent/templates/cookie_consent/_cookie_group.html,sha256=wozvbdRjuKu3gnegN8vex_lDkku22pne830NWuzHXVw,1496
|
|
18
|
-
cookie_consent/templates/cookie_consent/base.html,sha256=ghOQ9vqSzKG32LfSQmj5xTF1PBs-SDtQEcOC1wjAemQ,85
|
|
19
|
-
cookie_consent/templates/cookie_consent/cookiegroup_list.html,sha256=PMO9iLxQrgRpQGLC0stlDamMonc3tLS3o5PWPF378Zg,308
|
|
20
|
-
cookie_consent/templatetags/__init__.py,sha256=FZk9hm7vBsK8G16pNugputbrrEciYqJc_XRAhTCcojI,46
|
|
21
|
-
cookie_consent/templatetags/cookie_consent_tags.py,sha256=j6_8e-mu3dy2syXwAxXjOlx6C90CGSdEqM391y2jqx0,4026
|
|
22
|
-
django_cookie_consent-0.4.0.dev0.dist-info/AUTHORS,sha256=1IpYKADNdC03tx3VzMmPLRJo8MHHC_JlWcPEcdcTDA8,323
|
|
23
|
-
django_cookie_consent-0.4.0.dev0.dist-info/LICENSE,sha256=ZZi-io1uazO5jpDoNJScUfjeVI2dEzvDPOcyFhU0KYw,1349
|
|
24
|
-
django_cookie_consent-0.4.0.dev0.dist-info/METADATA,sha256=CsHdJQ4fKqM5a9yov4i82Iz2O997JhPcxW1gWefNqBQ,4614
|
|
25
|
-
django_cookie_consent-0.4.0.dev0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
26
|
-
django_cookie_consent-0.4.0.dev0.dist-info/top_level.txt,sha256=wD8Ix58rJguYeYfils_Z40QfVybiRYb1vwRNm09HN4M,15
|
|
27
|
-
django_cookie_consent-0.4.0.dev0.dist-info/RECORD,,
|
{django_cookie_consent-0.4.0.dev0.dist-info → django_cookie_consent-0.5.0b0.dist-info}/AUTHORS
RENAMED
|
File without changes
|
{django_cookie_consent-0.4.0.dev0.dist-info → django_cookie_consent-0.5.0b0.dist-info}/LICENSE
RENAMED
|
File without changes
|
{django_cookie_consent-0.4.0.dev0.dist-info → django_cookie_consent-0.5.0b0.dist-info}/top_level.txt
RENAMED
|
File without changes
|