django-plausible-iplweb 0.6.0__tar.gz

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.
Files changed (39) hide show
  1. django_plausible_iplweb-0.6.0/.github/workflows/ci.yml +84 -0
  2. django_plausible_iplweb-0.6.0/.github/workflows/deploy.yml +30 -0
  3. django_plausible_iplweb-0.6.0/.gitignore +135 -0
  4. django_plausible_iplweb-0.6.0/.pre-commit-config.yaml +28 -0
  5. django_plausible_iplweb-0.6.0/LICENSE +29 -0
  6. django_plausible_iplweb-0.6.0/PKG-INFO +193 -0
  7. django_plausible_iplweb-0.6.0/README.md +147 -0
  8. django_plausible_iplweb-0.6.0/django_plausible_iplweb.egg-info/PKG-INFO +193 -0
  9. django_plausible_iplweb-0.6.0/django_plausible_iplweb.egg-info/SOURCES.txt +37 -0
  10. django_plausible_iplweb-0.6.0/django_plausible_iplweb.egg-info/dependency_links.txt +1 -0
  11. django_plausible_iplweb-0.6.0/django_plausible_iplweb.egg-info/requires.txt +13 -0
  12. django_plausible_iplweb-0.6.0/django_plausible_iplweb.egg-info/top_level.txt +1 -0
  13. django_plausible_iplweb-0.6.0/docs/superpowers/specs/2026-06-13-plausible-new-tracker-url-masking-design.md +93 -0
  14. django_plausible_iplweb-0.6.0/manage.py +10 -0
  15. django_plausible_iplweb-0.6.0/plausible/__init__.py +0 -0
  16. django_plausible_iplweb-0.6.0/plausible/contrib/__init__.py +0 -0
  17. django_plausible_iplweb-0.6.0/plausible/contrib/wagtail/__init__.py +0 -0
  18. django_plausible_iplweb-0.6.0/plausible/contrib/wagtail/apps.py +9 -0
  19. django_plausible_iplweb-0.6.0/plausible/contrib/wagtail/migrations/0001_initial.py +63 -0
  20. django_plausible_iplweb-0.6.0/plausible/contrib/wagtail/migrations/0002_remove_plausiblesettings_plausible_domain_and_more.py +30 -0
  21. django_plausible_iplweb-0.6.0/plausible/contrib/wagtail/migrations/__init__.py +0 -0
  22. django_plausible_iplweb-0.6.0/plausible/contrib/wagtail/models.py +14 -0
  23. django_plausible_iplweb-0.6.0/plausible/contrib/wagtail/templatetags/__init__.py +0 -0
  24. django_plausible_iplweb-0.6.0/plausible/contrib/wagtail/templatetags/plausible_wagtail.py +12 -0
  25. django_plausible_iplweb-0.6.0/plausible/py.typed +0 -0
  26. django_plausible_iplweb-0.6.0/plausible/templates/plausible/plausible.html +14 -0
  27. django_plausible_iplweb-0.6.0/plausible/templatetags/__init__.py +0 -0
  28. django_plausible_iplweb-0.6.0/plausible/templatetags/plausible.py +65 -0
  29. django_plausible_iplweb-0.6.0/pyproject.toml +86 -0
  30. django_plausible_iplweb-0.6.0/renovate.json +7 -0
  31. django_plausible_iplweb-0.6.0/scripts/test.sh +11 -0
  32. django_plausible_iplweb-0.6.0/setup.cfg +4 -0
  33. django_plausible_iplweb-0.6.0/tests/__init__.py +0 -0
  34. django_plausible_iplweb-0.6.0/tests/settings.py +60 -0
  35. django_plausible_iplweb-0.6.0/tests/templates/simple.html +3 -0
  36. django_plausible_iplweb-0.6.0/tests/templates/simple_wagtail.html +3 -0
  37. django_plausible_iplweb-0.6.0/tests/test_django.py +113 -0
  38. django_plausible_iplweb-0.6.0/tests/test_wagtail.py +55 -0
  39. django_plausible_iplweb-0.6.0/tests/urls.py +7 -0
@@ -0,0 +1,84 @@
1
+ name: CI
2
+
3
+ on: [pull_request, push]
4
+
5
+ permissions:
6
+ contents: read
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ # Derived from requires-python (>=3.10) + Django floor (>=5.2) +
15
+ # canonical Django x Python compatibility. Django 6.0 needs Python >=3.12.
16
+ include:
17
+ # Django 5.2 LTS
18
+ - { python-version: "3.10", django-version: "5.2" }
19
+ - { python-version: "3.11", django-version: "5.2" }
20
+ - { python-version: "3.12", django-version: "5.2" }
21
+ - { python-version: "3.13", django-version: "5.2" }
22
+ # Django 6.0
23
+ - { python-version: "3.12", django-version: "6.0" }
24
+ - { python-version: "3.13", django-version: "6.0" }
25
+ - { python-version: "3.14", django-version: "6.0" }
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ with:
29
+ fetch-depth: 0 # full history so setuptools-scm can derive the version
30
+
31
+ - name: Install uv
32
+ uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
33
+
34
+ - name: Set up Python ${{ matrix.python-version }}
35
+ run: uv python install ${{ matrix.python-version }}
36
+
37
+ - name: Install dependencies (project + Django ${{ matrix.django-version }})
38
+ run: |
39
+ uv sync --all-extras
40
+ uv pip install "django~=${{ matrix.django-version }}.0"
41
+
42
+ - name: Run tests
43
+ run: uv run pytest --verbose --cov plausible/ --cov-report term tests/
44
+
45
+ lint:
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+ with:
50
+ fetch-depth: 0
51
+
52
+ - name: Install uv
53
+ uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
54
+
55
+ - name: Set up Python
56
+ run: uv python install 3.14
57
+
58
+ - name: Install dependencies
59
+ run: uv sync --all-extras
60
+
61
+ - name: Lint with ruff
62
+ run: uv run ruff check . || true
63
+
64
+ - name: Check formatting with ruff
65
+ run: uv run ruff format --check . || true
66
+
67
+ - name: Type-check with mypy
68
+ run: uv run mypy plausible tests
69
+
70
+ build:
71
+ runs-on: ubuntu-latest
72
+ steps:
73
+ - uses: actions/checkout@v4
74
+ with:
75
+ fetch-depth: 0 # setuptools-scm needs tags/history
76
+
77
+ - name: Install uv
78
+ uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
79
+
80
+ - name: Build package
81
+ run: uv build
82
+
83
+ - name: Check package metadata
84
+ run: uv run --with twine twine check dist/*
@@ -0,0 +1,30 @@
1
+ name: Upload Python Package
2
+
3
+ on:
4
+ release:
5
+ types: [created]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ deploy:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0 # full history + tags so setuptools-scm derives the version
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
20
+
21
+ - name: Build package
22
+ run: uv build
23
+
24
+ # NOTE: still using a PyPI token (secrets.PYPI_PASSWORD). Consider migrating
25
+ # to PyPI Trusted Publishing (OIDC) so no token needs to live in repo secrets.
26
+ - name: Publish to PyPI
27
+ env:
28
+ TWINE_USERNAME: __token__
29
+ TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
30
+ run: uv run --with twine twine upload dist/*
@@ -0,0 +1,135 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ target/
76
+
77
+ # Jupyter Notebook
78
+ .ipynb_checkpoints
79
+
80
+ # IPython
81
+ profile_default/
82
+ ipython_config.py
83
+
84
+ # pyenv
85
+ .python-version
86
+
87
+ # pipenv
88
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
90
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
91
+ # install all needed dependencies.
92
+ #Pipfile.lock
93
+
94
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95
+ __pypackages__/
96
+
97
+ # Celery stuff
98
+ celerybeat-schedule
99
+ celerybeat.pid
100
+
101
+ # SageMath parsed files
102
+ *.sage.py
103
+
104
+ # uv (library: lockfile not committed)
105
+ uv.lock
106
+
107
+ # Ruff
108
+ .ruff_cache/
109
+
110
+ # Environments
111
+ .env
112
+ .venv
113
+ env/
114
+ venv/
115
+ ENV/
116
+ env.bak/
117
+ venv.bak/
118
+
119
+ # Spyder project settings
120
+ .spyderproject
121
+ .spyproject
122
+
123
+ # Rope project settings
124
+ .ropeproject
125
+
126
+ # mkdocs documentation
127
+ /site
128
+
129
+ # mypy
130
+ .mypy_cache/
131
+ .dmypy.json
132
+ dmypy.json
133
+
134
+ # Pyre type checker
135
+ .pyre/
@@ -0,0 +1,28 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-added-large-files
9
+ - id: detect-private-key
10
+
11
+ - repo: https://github.com/asottile/pyupgrade
12
+ rev: v3.21.2
13
+ hooks:
14
+ - id: pyupgrade
15
+ args: [--py310-plus]
16
+
17
+ - repo: https://github.com/adamchainz/django-upgrade
18
+ rev: 1.30.0
19
+ hooks:
20
+ - id: django-upgrade
21
+ args: [--target-version, "5.2"]
22
+
23
+ - repo: https://github.com/astral-sh/ruff-pre-commit
24
+ rev: v0.15.17
25
+ hooks:
26
+ - id: ruff
27
+ args: [--fix]
28
+ - id: ruff-format
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2021, Jake Howard
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-plausible-iplweb
3
+ Version: 0.6.0
4
+ Summary: Django module to provide easy Plausible integration, with Wagtail support
5
+ Author: Jake Howard
6
+ Maintainer-email: Michał Pasternak <michal.dtz@gmail.com>
7
+ License-Expression: BSD-3-Clause
8
+ Project-URL: Homepage, https://github.com/mpasternak/django-plausible-iplweb
9
+ Project-URL: Repository, https://github.com/mpasternak/django-plausible-iplweb
10
+ Project-URL: Issues, https://github.com/mpasternak/django-plausible-iplweb/issues
11
+ Keywords: django,plausible,wagtail,analytics
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Environment :: Web Environment
14
+ Classifier: Framework :: Django
15
+ Classifier: Framework :: Django :: 5.2
16
+ Classifier: Framework :: Django :: 6.0
17
+ Classifier: Framework :: Wagtail
18
+ Classifier: Framework :: Wagtail :: 6
19
+ Classifier: Framework :: Wagtail :: 7
20
+ Classifier: Intended Audience :: Developers
21
+ Classifier: Operating System :: OS Independent
22
+ Classifier: Programming Language :: Python :: 3
23
+ Classifier: Programming Language :: Python :: 3.10
24
+ Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
26
+ Classifier: Programming Language :: Python :: 3.13
27
+ Classifier: Programming Language :: Python :: 3.14
28
+ Classifier: Topic :: Internet :: WWW/HTTP
29
+ Classifier: Topic :: Software Development
30
+ Classifier: Typing :: Typed
31
+ Requires-Python: >=3.10
32
+ Description-Content-Type: text/markdown
33
+ License-File: LICENSE
34
+ Requires-Dist: Django>=5.2
35
+ Provides-Extra: wagtail
36
+ Requires-Dist: wagtail>=7.0; extra == "wagtail"
37
+ Provides-Extra: dev
38
+ Requires-Dist: django-plausible-iplweb[wagtail]; extra == "dev"
39
+ Requires-Dist: ruff; extra == "dev"
40
+ Requires-Dist: mypy; extra == "dev"
41
+ Requires-Dist: pytest; extra == "dev"
42
+ Requires-Dist: pytest-django; extra == "dev"
43
+ Requires-Dist: pytest-cov; extra == "dev"
44
+ Requires-Dist: pre-commit; extra == "dev"
45
+ Dynamic: license-file
46
+
47
+ # django-plausible-iplweb
48
+
49
+ [![CI](https://github.com/mpasternak/django-plausible-iplweb/actions/workflows/ci.yml/badge.svg)](https://github.com/mpasternak/django-plausible-iplweb/actions/workflows/ci.yml)
50
+ ![PyPI](https://img.shields.io/pypi/v/django-plausible-iplweb.svg)
51
+ ![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)
52
+ ![Django](https://img.shields.io/badge/django-5.2%20%7C%206.0-blue)
53
+ ![PyPI - Status](https://img.shields.io/pypi/status/django-plausible-iplweb.svg)
54
+ ![PyPI - License](https://img.shields.io/pypi/l/django-plausible-iplweb.svg)
55
+
56
+
57
+ Django module to provide easy [Plausible](https://plausible.io/) integration, with [Wagtail](https://wagtail.io/) support.
58
+
59
+ ## Installation
60
+
61
+ ```
62
+ pip install django-plausible-iplweb
63
+ ```
64
+
65
+ Then simply add `plausible` to `INSTALLED_APPS`.
66
+
67
+ ## Usage
68
+
69
+ `django-plausible-iplweb` provides a `plausible` template tag that emits Plausible's
70
+ current per-site tracking script. Each Plausible site now has its **own script URL**
71
+ (e.g. `https://plausible.io/js/pa-XXXXXXXX.js`) — there is no `data-domain` attribute
72
+ any more. Copy the script URL from your Plausible dashboard (**Site Settings → General →
73
+ Site Installation**) into `PLAUSIBLE_SCRIPT_URL`, then place the tag in your `<head>`:
74
+
75
+ ```html
76
+ {% load plausible %}
77
+
78
+ {% plausible %}
79
+ ```
80
+
81
+ With `PLAUSIBLE_SCRIPT_URL = "https://plausible.io/js/pa-XXXXXXXX.js"`, this renders:
82
+
83
+ ```html
84
+ <script defer src="https://plausible.io/js/pa-XXXXXXXX.js"></script>
85
+ <script>
86
+ window.plausible = window.plausible || function () { (window.plausible.q = window.plausible.q || []).push(arguments) };
87
+ plausible.init({"autoCapturePageviews": false});
88
+ (function () {
89
+ var path = window.location.pathname;
90
+ var masks = [];
91
+ for (var i = 0; i < masks.length; i++) {
92
+ path = path.replace(new RegExp(masks[i].pattern, masks[i].flags || "g"), masks[i].replacement);
93
+ }
94
+ var url = window.location.origin + path + window.location.search;
95
+ plausible("pageview", { url: url });
96
+ })();
97
+ </script>
98
+ ```
99
+
100
+ Automatic pageview capture is **disabled** (`autoCapturePageviews: false`) so the package
101
+ can send a single, optionally-masked pageview itself (see [URL masking](#url-masking)).
102
+
103
+ If `PLAUSIBLE_SCRIPT_URL` is empty (the default), the tag renders nothing — a convenient
104
+ way to switch analytics off in development.
105
+
106
+ ### Configuration
107
+
108
+ All configuration lives in `settings.py`:
109
+
110
+ | Setting | Default | Purpose |
111
+ |---|---|---|
112
+ | `PLAUSIBLE_SCRIPT_URL` | `""` | Full per-site script URL from your Plausible dashboard. Empty → tag renders nothing. |
113
+ | `PLAUSIBLE_INIT_OPTIONS` | `{"autoCapturePageviews": False}` | Dict passed (as JSON) to [`plausible.init(...)`](https://plausible.io/docs/script-extensions). Add any init option here, e.g. `{"autoCapturePageviews": False, "hashBasedRouting": True}`. |
114
+ | `PLAUSIBLE_URL_MASKS` | `[]` | Ordered list of client-side path-masking rules (see below). |
115
+ | `PLAUSIBLE_KEEP_QUERY_STRING` | `True` | Append `location.search` to the reported URL (keeps `utm_*`/`ref` for acquisition reports). Set `False` to drop the query string entirely. |
116
+
117
+ You can also pass the script URL at call time, e.g. to use a different site on one page:
118
+
119
+ ```html
120
+ {% plausible script_url="https://plausible.io/js/pa-OTHER.js" %}
121
+ ```
122
+
123
+ ### URL masking
124
+
125
+ Plausible records the page URL. If your paths contain identifiers (invitation codes,
126
+ user IDs, UUIDs, …) those would leak into your analytics. `PLAUSIBLE_URL_MASKS` lets you
127
+ redact them **client-side** before the pageview is sent. Each rule is applied to
128
+ `location.pathname` in order via JavaScript's
129
+ [`String.replace(new RegExp(pattern, flags), replacement)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace):
130
+
131
+ ```python
132
+ PLAUSIBLE_URL_MASKS = [
133
+ # Mask invitation/passphrase codes: /i/<passphrase> -> /i/__code__
134
+ {"pattern": r"^/i/[^/]+", "replacement": "/i/__code__"},
135
+ # Mask UUIDs anywhere in the path: /<uuid> -> /__id__
136
+ {"pattern": r"/[0-9a-f-]{36}", "replacement": "/__id__", "flags": "gi"},
137
+ # Mask numeric ids: /123 -> /__id__
138
+ {"pattern": r"/\d+", "replacement": "/__id__"},
139
+ ]
140
+ ```
141
+
142
+ `pattern` and `flags` are **JavaScript** regular-expression syntax (the rules run in the
143
+ browser). `flags` defaults to `"g"`. The patterns above are examples — adapt them to your
144
+ own routes; the default (`[]`) applies no masking.
145
+
146
+ > **Caveat:** like Plausible's own [redaction guidance](https://plausible.io/docs/custom-locations),
147
+ > this masking runs once when the page loads. It may not behave as expected in
148
+ > single-page applications or with hash-based routing, where `location` changes
149
+ > without a full page load.
150
+
151
+ ### Overriding the rendered markup
152
+
153
+ The snippet is rendered from the `plausible/plausible.html` template. To fully customise
154
+ it, shadow that path in your own project's templates directory.
155
+
156
+ ## Usage with Wagtail
157
+
158
+ `django-plausible-iplweb` also provides an optional [Wagtail](https://wagtail.org)
159
+ integration that lets editors set the script URL **per site** through the Wagtail admin.
160
+ Add `plausible.contrib.wagtail` to `INSTALLED_APPS` and run `migrate`.
161
+
162
+ Configuration is done through the "Plausible Analytics"
163
+ [setting](https://docs.wagtail.org/en/stable/reference/contrib/settings.html):
164
+
165
+ - `script_url`: the full per-site Plausible script URL. Blank (the default) → nothing is rendered for that site.
166
+
167
+ Masking (`PLAUSIBLE_URL_MASKS`), init options, and query-string handling are still
168
+ configured globally via the Django settings above.
169
+
170
+ Load `plausible_wagtail` rather than `plausible`; the tag itself is still `plausible`:
171
+
172
+ ```html
173
+ {% load plausible_wagtail %}
174
+
175
+ {% plausible %}
176
+ ```
177
+
178
+ ## Migrating from 0.5.x
179
+
180
+ Version 0.6.0 is a clean break to match Plausible's new tracker. Update your settings:
181
+
182
+ | Old (0.5.x) | New (0.6.0) |
183
+ |---|---|
184
+ | `PLAUSIBLE_DOMAIN` + `PLAUSIBLE_SCRIPT_NAME` | `PLAUSIBLE_SCRIPT_URL` (the full script URL from your dashboard) |
185
+ | `data-domain` (request host / `site_domain`) | *(gone — the site is identified by the script URL)* |
186
+ | `{% plausible site_domain=… plausible_domain=… script_name=… %}` | `{% plausible %}` (optionally `script_url=…`) |
187
+ | Wagtail `site_domain` / `plausible_domain` / `script_name` fields | Wagtail `script_url` field (migration `0002` applies the change) |
188
+
189
+ ## License
190
+
191
+ This project is licensed under the **BSD 3-Clause License**. See the [LICENSE](LICENSE) file for details.
192
+
193
+ This is a fork of [`django-plausible`](https://github.com/RealOrangeOne/django-plausible) by Jake Howard.
@@ -0,0 +1,147 @@
1
+ # django-plausible-iplweb
2
+
3
+ [![CI](https://github.com/mpasternak/django-plausible-iplweb/actions/workflows/ci.yml/badge.svg)](https://github.com/mpasternak/django-plausible-iplweb/actions/workflows/ci.yml)
4
+ ![PyPI](https://img.shields.io/pypi/v/django-plausible-iplweb.svg)
5
+ ![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)
6
+ ![Django](https://img.shields.io/badge/django-5.2%20%7C%206.0-blue)
7
+ ![PyPI - Status](https://img.shields.io/pypi/status/django-plausible-iplweb.svg)
8
+ ![PyPI - License](https://img.shields.io/pypi/l/django-plausible-iplweb.svg)
9
+
10
+
11
+ Django module to provide easy [Plausible](https://plausible.io/) integration, with [Wagtail](https://wagtail.io/) support.
12
+
13
+ ## Installation
14
+
15
+ ```
16
+ pip install django-plausible-iplweb
17
+ ```
18
+
19
+ Then simply add `plausible` to `INSTALLED_APPS`.
20
+
21
+ ## Usage
22
+
23
+ `django-plausible-iplweb` provides a `plausible` template tag that emits Plausible's
24
+ current per-site tracking script. Each Plausible site now has its **own script URL**
25
+ (e.g. `https://plausible.io/js/pa-XXXXXXXX.js`) — there is no `data-domain` attribute
26
+ any more. Copy the script URL from your Plausible dashboard (**Site Settings → General →
27
+ Site Installation**) into `PLAUSIBLE_SCRIPT_URL`, then place the tag in your `<head>`:
28
+
29
+ ```html
30
+ {% load plausible %}
31
+
32
+ {% plausible %}
33
+ ```
34
+
35
+ With `PLAUSIBLE_SCRIPT_URL = "https://plausible.io/js/pa-XXXXXXXX.js"`, this renders:
36
+
37
+ ```html
38
+ <script defer src="https://plausible.io/js/pa-XXXXXXXX.js"></script>
39
+ <script>
40
+ window.plausible = window.plausible || function () { (window.plausible.q = window.plausible.q || []).push(arguments) };
41
+ plausible.init({"autoCapturePageviews": false});
42
+ (function () {
43
+ var path = window.location.pathname;
44
+ var masks = [];
45
+ for (var i = 0; i < masks.length; i++) {
46
+ path = path.replace(new RegExp(masks[i].pattern, masks[i].flags || "g"), masks[i].replacement);
47
+ }
48
+ var url = window.location.origin + path + window.location.search;
49
+ plausible("pageview", { url: url });
50
+ })();
51
+ </script>
52
+ ```
53
+
54
+ Automatic pageview capture is **disabled** (`autoCapturePageviews: false`) so the package
55
+ can send a single, optionally-masked pageview itself (see [URL masking](#url-masking)).
56
+
57
+ If `PLAUSIBLE_SCRIPT_URL` is empty (the default), the tag renders nothing — a convenient
58
+ way to switch analytics off in development.
59
+
60
+ ### Configuration
61
+
62
+ All configuration lives in `settings.py`:
63
+
64
+ | Setting | Default | Purpose |
65
+ |---|---|---|
66
+ | `PLAUSIBLE_SCRIPT_URL` | `""` | Full per-site script URL from your Plausible dashboard. Empty → tag renders nothing. |
67
+ | `PLAUSIBLE_INIT_OPTIONS` | `{"autoCapturePageviews": False}` | Dict passed (as JSON) to [`plausible.init(...)`](https://plausible.io/docs/script-extensions). Add any init option here, e.g. `{"autoCapturePageviews": False, "hashBasedRouting": True}`. |
68
+ | `PLAUSIBLE_URL_MASKS` | `[]` | Ordered list of client-side path-masking rules (see below). |
69
+ | `PLAUSIBLE_KEEP_QUERY_STRING` | `True` | Append `location.search` to the reported URL (keeps `utm_*`/`ref` for acquisition reports). Set `False` to drop the query string entirely. |
70
+
71
+ You can also pass the script URL at call time, e.g. to use a different site on one page:
72
+
73
+ ```html
74
+ {% plausible script_url="https://plausible.io/js/pa-OTHER.js" %}
75
+ ```
76
+
77
+ ### URL masking
78
+
79
+ Plausible records the page URL. If your paths contain identifiers (invitation codes,
80
+ user IDs, UUIDs, …) those would leak into your analytics. `PLAUSIBLE_URL_MASKS` lets you
81
+ redact them **client-side** before the pageview is sent. Each rule is applied to
82
+ `location.pathname` in order via JavaScript's
83
+ [`String.replace(new RegExp(pattern, flags), replacement)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace):
84
+
85
+ ```python
86
+ PLAUSIBLE_URL_MASKS = [
87
+ # Mask invitation/passphrase codes: /i/<passphrase> -> /i/__code__
88
+ {"pattern": r"^/i/[^/]+", "replacement": "/i/__code__"},
89
+ # Mask UUIDs anywhere in the path: /<uuid> -> /__id__
90
+ {"pattern": r"/[0-9a-f-]{36}", "replacement": "/__id__", "flags": "gi"},
91
+ # Mask numeric ids: /123 -> /__id__
92
+ {"pattern": r"/\d+", "replacement": "/__id__"},
93
+ ]
94
+ ```
95
+
96
+ `pattern` and `flags` are **JavaScript** regular-expression syntax (the rules run in the
97
+ browser). `flags` defaults to `"g"`. The patterns above are examples — adapt them to your
98
+ own routes; the default (`[]`) applies no masking.
99
+
100
+ > **Caveat:** like Plausible's own [redaction guidance](https://plausible.io/docs/custom-locations),
101
+ > this masking runs once when the page loads. It may not behave as expected in
102
+ > single-page applications or with hash-based routing, where `location` changes
103
+ > without a full page load.
104
+
105
+ ### Overriding the rendered markup
106
+
107
+ The snippet is rendered from the `plausible/plausible.html` template. To fully customise
108
+ it, shadow that path in your own project's templates directory.
109
+
110
+ ## Usage with Wagtail
111
+
112
+ `django-plausible-iplweb` also provides an optional [Wagtail](https://wagtail.org)
113
+ integration that lets editors set the script URL **per site** through the Wagtail admin.
114
+ Add `plausible.contrib.wagtail` to `INSTALLED_APPS` and run `migrate`.
115
+
116
+ Configuration is done through the "Plausible Analytics"
117
+ [setting](https://docs.wagtail.org/en/stable/reference/contrib/settings.html):
118
+
119
+ - `script_url`: the full per-site Plausible script URL. Blank (the default) → nothing is rendered for that site.
120
+
121
+ Masking (`PLAUSIBLE_URL_MASKS`), init options, and query-string handling are still
122
+ configured globally via the Django settings above.
123
+
124
+ Load `plausible_wagtail` rather than `plausible`; the tag itself is still `plausible`:
125
+
126
+ ```html
127
+ {% load plausible_wagtail %}
128
+
129
+ {% plausible %}
130
+ ```
131
+
132
+ ## Migrating from 0.5.x
133
+
134
+ Version 0.6.0 is a clean break to match Plausible's new tracker. Update your settings:
135
+
136
+ | Old (0.5.x) | New (0.6.0) |
137
+ |---|---|
138
+ | `PLAUSIBLE_DOMAIN` + `PLAUSIBLE_SCRIPT_NAME` | `PLAUSIBLE_SCRIPT_URL` (the full script URL from your dashboard) |
139
+ | `data-domain` (request host / `site_domain`) | *(gone — the site is identified by the script URL)* |
140
+ | `{% plausible site_domain=… plausible_domain=… script_name=… %}` | `{% plausible %}` (optionally `script_url=…`) |
141
+ | Wagtail `site_domain` / `plausible_domain` / `script_name` fields | Wagtail `script_url` field (migration `0002` applies the change) |
142
+
143
+ ## License
144
+
145
+ This project is licensed under the **BSD 3-Clause License**. See the [LICENSE](LICENSE) file for details.
146
+
147
+ This is a fork of [`django-plausible`](https://github.com/RealOrangeOne/django-plausible) by Jake Howard.