{"id":15295823,"url":"https://github.com/raratiru/django-letsagree","last_synced_at":"2026-03-02T13:12:36.210Z","repository":{"id":34596999,"uuid":"180178160","full_name":"raratiru/django-letsagree","owner":"raratiru","description":"A Django application that associates Groups with Terms requiring consent from logged in members.","archived":false,"fork":false,"pushed_at":"2024-12-24T12:52:30.000Z","size":130,"stargazers_count":14,"open_issues_count":2,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-13T18:43:42.289Z","etag":null,"topics":["authorization","django-application","gdpr-consent","middleware","terms"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/raratiru.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-04-08T15:25:04.000Z","updated_at":"2024-12-24T12:52:34.000Z","dependencies_parsed_at":"2024-05-13T20:44:42.544Z","dependency_job_id":"d4e4c2c4-871b-4dc7-90f9-dbf68f8a30c0","html_url":"https://github.com/raratiru/django-letsagree","commit_stats":{"total_commits":101,"total_committers":6,"mean_commits":"16.833333333333332","dds":"0.48514851485148514","last_synced_commit":"2b0af2157daac670ef7d517f11e15f50f8b7f15e"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/raratiru/django-letsagree","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raratiru%2Fdjango-letsagree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raratiru%2Fdjango-letsagree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raratiru%2Fdjango-letsagree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raratiru%2Fdjango-letsagree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/raratiru","download_url":"https://codeload.github.com/raratiru/django-letsagree/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raratiru%2Fdjango-letsagree/sbom","scorecard":{"id":762228,"data":{"date":"2025-08-11","repo":{"name":"github.com/raratiru/django-letsagree","commit":"cad20deede8928921aba30b0cb9544a9642fd728"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.4,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":8,"reason":"Found 22/26 approved changesets -- score normalized to 8","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:63: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:103: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:105: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:126: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:137: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:139: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:153: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/codeql.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/codeql.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/codeql.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/raratiru/django-letsagree/codeql.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:48","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:49","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:50","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:51","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:111","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:112","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:113","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:114","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:145","Info:   0 out of  10 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned","Info:   0 out of   9 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql.yml:16","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:17","Warn: no topLevel permission defined: .github/workflows/build.yml:1","Warn: no topLevel permission defined: .github/workflows/codeql.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":7,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 6 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":3,"reason":"7 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2025-47 / GHSA-7xr5-9hcq-chf9","Warn: Project is vulnerable to: PYSEC-2024-156 / GHSA-8498-2h75-472j","Warn: Project is vulnerable to: PYSEC-2025-37 / GHSA-8j24-cjrq-gr2m","Warn: Project is vulnerable to: PYSEC-2024-157 / GHSA-m9g8-fxxm-xg86","Warn: Project is vulnerable to: PYSEC-2025-13 / GHSA-p3fp-8748-vqfq","Warn: Project is vulnerable to: PYSEC-2025-1 / GHSA-qcgg-j2x8-h9g8","Warn: Project is vulnerable to: PYSEC-2025-14 / GHSA-wqfg-m96j-85vm"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T23:45:52.370Z","repository_id":34596999,"created_at":"2025-08-22T23:45:52.370Z","updated_at":"2025-08-22T23:45:52.370Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30003743,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-02T12:19:43.414Z","status":"ssl_error","status_checked_at":"2026-03-02T12:19:02.215Z","response_time":60,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authorization","django-application","gdpr-consent","middleware","terms"],"created_at":"2024-09-30T18:08:18.532Z","updated_at":"2026-03-02T13:12:36.162Z","avatar_url":"https://github.com/raratiru.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://github.com/raratiru/django-letsagree/actions/workflows/build.yml/badge.svg)](https://github.com/raratiru/django-letsagree/actions)\n[![Coverage Status](https://coveralls.io/repos/github/raratiru/django-letsagree/badge.svg?branch=travis)](https://coveralls.io/github/raratiru/django-letsagree?branch=travis)\n[![Updates](https://pyup.io/repos/github/raratiru/django-letsagree/shield.svg)](https://pyup.io/repos/github/raratiru/django-letsagree/)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)\n\n[![Python Versions](https://img.shields.io/badge/Python-3.8|%203.9|%203.10|%203.11|%20-%236600cc)](https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django)\n[![Django Versions](https://img.shields.io/badge/Django-%203.2%20|%204.1-brown.svg)](https://www.djangoproject.com/download/)\n[![Database Window Functions](https://img.shields.io/badge/Database-Window%20Functions-important.svg)](https://www.sql-workbench.eu/dbms_comparison.html)\n\nLet's Agree\n======\n\nFeatures\n--------\n\n* Terms [versioning](#version) in \"[`deque`](https://docs.python.org/3.11/library/collections.html#collections.deque)-style\" with `maxlen=1`.\n* Per-Group Term association, per-User Term acceptance for each Group a user belongs to.\n* [Max 1 query](#queries), either per request or per day for each logged-in user.\n* [Multi-language](#translation) ready.\n* [Freedom](#permissions) for each user to withdraw consent at any time.\n\n\nAbout\n---\n\n`django-letsagree`is the result of an effort to follow the spirit of [The EU General Data Protection Regulation (GDPR)](https://eugdpr.org/).\n\nA logged in user can belong to one or more Groups.\n\nIf one or more Groups are associated with `django-letsagree`, all users that login as members of those Groups will be asked to provide their consent to the Terms related with each Group. This action, will be recorded in the database.\n\nThe Terms associated with a Group, can be updated with newer versions.\n\nSuch a decision will trigger again the mechanism which asks for the consent of each user before allowing any other operation on the site.\n\nIf the user does not provide consent, the following actions are only allowed:\n\n* Logout.\n* View and delete all instances of own consent provided.\n* View all Terms\n\n\nPrerequisites\n-------\n\n* Python 3.8, 3.9, 3.10, 3.11\n* Django 3.2, 4.1\n* [Django Admin Site](https://docs.djangoproject.com/en/dev/ref/contrib/admin/) (enabled by default in Django)\n* A database with [Window Functions support](https://www.sql-workbench.eu/dbms_comparison.html)\n* [`django-translated-fields`](https://github.com/matthiask/django-translated-fields)\n\nInstallation\n-------\n1. `pip install django-letsagree`\n\n2. project/settings.py\n    ```python\n    INSTALLED_APPS = [\n        ...\n        'letsagree.apps.LetsagreeConfig',\n        ...\n    ]\n\n    MIDDLEWARE = [\n        ...\n        'letsagree.middleware.LetsAgreeMiddleware',  # Near the end of the list\n        ...\n    ]\n    ```\n\n3. `\u003cproject\u003e` is the name of the project that hosts django-letsagree\n\n    project/settings.py:\n    ```python\n    MIGRATION_MODULES = {\n        'letsagree': '\u003cproject\u003e.3p_migrations.letsagree',\n    }\n    ```\n\n4. Make sure [LANGUAGES](https://docs.djangoproject.com/en/dev/ref/settings/#languages) are properly set as explained in the [Translation](#translation) section.\n  The default implementation will create as **many fields** as the number of `LANGUAGES` Django has set by default.\n\n\n5. project/urls.py:\n\n    ```python\n    urlpatterns = [\n            ...\n            path('path/to/letsagree/', include('letsagree.urls')),\n            ...\n    ]\n    ```\n\n6. Create the migrations:\n\n    ```python\n    ./manage.py makemigrations letsagree\n    ./manage.py migrate\n    ```\n\n\n### Notes on installation\n\n* `django-letsagree` itself does not come with any migrations. It is recommended\n    that you add migrations for its models in your project and avoid using the\n    word `migrations` as the name of the folder.\n\n    The relevant Django setting is [`MIGRATION_MODULES`](https://docs.djangoproject.com/en/dev/ref/settings/#migration-modules).\n    In the above example, we store migrations inside `\u003cproject\u003e/\u003cproject\u003e/3p_migrations`.\n\n\nSettings\n--------\n\n### Default Settings\n```python\nLETSAGREE_CACHE = False\nLETSAGREE_CSS = {}\nLETSAGREE_JS = ()\nLETSAGREE_LOGOUT_APP_NAME = ''  (Deprecated -\u003e default value was 'admin')\nLETSAGREE_LOGOUT_URL = 'admin:logout'\nLETSAGREE_BROWSER_TITLE = ''\nLETSAGREE_BORDER_HEADER = ''\n```\n\u003ca name='queries'\u003e\u003c/a\u003e\n### Database queries\n\n\nThe middleware generates one database query per request in order to make sure that the user has agreed to all the terms related with the Group(s) he belongs to.\n\nIf `LETSAGREE_CACHE = True`, [Django's Cache Framework](https://docs.djangoproject.com/en/dev/topics/cache/) will be used and only one database query will be generated by the middleware, every 24 hours.\n\n`LETSAGREE_CACHE` is not enabled by default, because it exposes the unique `id` for each user by creating a cache record with key `'letsagree-\u003cuser id\u003e'`.\n\nTip: [nshafer/django-hashid-field](https://github.com/nshafer/django-hashid-field), is a library that obscures unique `id`s, without compromising their uniqueness.\n\nUpdate: [ericls/django-hashids](https://github.com/ericls/django-hashids) is another non-intrusive library that proxies the field that is applied to.\n\nBoth libraries, however, are based on [https://hashids.org/](https://hashids.org/) which is not capable of encrypting sensitive data.\n\n\u003ca name='translation'\u003e\u003c/a\u003e\n### Translation\n\n\n**Watch your `LANGUAGES`**!\n\n#### Database\n\nBy default `lestagree` installs [`django-translated-fields`](https://github.com/matthiask/django-translated-fields) to cater for translating the `title`, `summary` and `content` fields of the `Term` model. This library will create separate fields for each entry in the [`LANGUAGES`](https://docs.djangoproject.com/en/dev/ref/settings/#languages) list.\n\nThe first entry of this list is considered as the \"default language\". The relevant database field is marked as `blank=False` and it serves as a fallback value. This value is returned if an entry for the requested language does not exist.\n\nAll other fields that are related with the rest of the languages in the `LANGUAGES` list are marked as `blank=True` and can stay empty.\n\nAlthough the [`LANGUAGE_CODE`](https://docs.djangoproject.com/en/dev/ref/settings/#language-code) setting is not directly related with `letsagree` or `django-translated-fields` it is **strongly** recommended to match the first language in the `LANGUAGES` setting.\n\nExample:\n```python\nLANGUAGES = (('fr', 'French'), ('en', 'English'))\nLANGUAGE_CODE = 'fr'\n```\nThe model `Term` will include the following fields:\n```python\n{\n    'title_fr': {'blank': False},\n    'title_en': {'blank': True},\n    'summary_fr': {'blank': False},\n    'summary_en': {'blank': True},\n    'content_fr': {'blank': False},\n    'content_en': {'blank': True},\n}\n```\n\n#### Strings\n\nAll strings in `django-letsagree` are marked with one of the following ways which allow translation:\n* `django.utils.translation.gettext_lazy('\u003cstring\u003e')`\n* `{% trans \"\u003cstring\u003e\" %}`\n\n### Custom Form Assets\n\n`django-letsagree` uses`letsagree/pending.html` template which extends `admin/index.html`. Through a `FormView` this template receives a `Formset` which includes all the `Terms` that should receive consent from the user.\n\n`LETSAGREE_CSS` and `LETSAGREE_JS` when set, pass the relevant assets in the `Media` class of the `Form` that serves as the basis of the above mentioned Formset. The syntax is described in the relevant [django documentation.](https://docs.djangoproject.com/en/dev/topics/forms/media/#assets-as-a-static-definition)\n\nA good starting point could be the default css file provided by `django-letsagree`:\n\nsettings.py:\n```python\nLETSAGREE_CSS = {'all': ('letsagree/letsagree.css',)}\n```\n\nOf course, one can completely [override the templates](https://docs.djangoproject.com/en/dev/howto/overriding-templates/).\n\nIn that case, bear in mind that if `{{ empty_form }}` is False, `{{ form }}` contains a formset.\n\n\n### Other settings\n\n* `LETSAGREE_LOGOUT_URL`: String that represents a namespaced URL.\n\n    For example: `'admin:logout'` is the default, it can be any string. If the url is not found, it fails silently resulting in the disappearance of the logout option.\n\n* `LETSAGREE_BROWSER_TITLE`: A title for the default template.\n* `LETSAGREE_BORDER_HEADER`: Text that will appear on the top left corner of the default template.\n\n\u003ca name='permissions'\u003e\u003c/a\u003e\nPermissions\n-----------\n\nIt is your responsibility to assign every new user to a Group associated with `django-letsagree`. This group should at least include the `delete_notarypublic` permission in case a user whishes to revoke his consent.\n\nIf all permissions for `django-letsagree` models are delegated to a group, the below table illustrates what actions are allowed for user, with either `is_staff == True` or `is_superuser == True`:\n\n\n| Actions | superuser own entries | superuser other entries | admin-user own entries | admin-user other entries |\n| :-----| :------------------:| :--------------------: | :-------------------:  | :---------------------: |\n| view_term | **True** | **True** | **True** |**True**|\n| add_term | **True** |  | **True** |  |\n| change_term | False | False | False | False |\n| delete_term | False | False | False | False |\n| view_notarypublic | **True** | **True** |**True** | False |\n| add_notarypublic | False |  | False |  |\n| change_notarypublic | False | False | False | False |\n| delete_notarypublic | **True** | False | **True** | False |\n\n### Term changelist contents\n\nUsers who have permission to add a new term, are allowed to read all the available terms. Otherwise, each user can only read the terms related to the group that he or she belongs to.\n\n\u003ca name='version'\u003e\u003c/a\u003e\nNew Term Version\n----------------\nIf two instances of Term associate with the same Group, the instance saved-last is the latest version. All logged in users have to provide consent for this latest version, independently of any previous consent they may have or have not given for the Terms associated with this Group.\n\n`django-letsagree` takes into account if a logged-in user has provided consent only for the latest version of each Term associated with the Groups he belongs to. If not, the user can only logout or visit `django-letsagree` admin page retaining the right to delete any instances of consent he has provided.\n\nTests\n-----\n\nTo run the test suite, you need:\n\n* Virtualenv with tox installed.\n* PostgreSQL, MariaDB/MySQL databases with the same user, password, database name.\n* The following environment variables set: `TOX_DB_NAME`, `TOX_DB_USER`, `TOX_DB_PASSWD`.\n\nUnfortunatelly, the test suite is rather complicated. Sorry!\n\n### Coverage: Not tested\n\n* [`LETSAGREE_CSS`](https://github.com/raratiru/django-letsagree/blob/9436ddabb4467477ecb39d94fd09b6f574e9384f/letsagree/forms.py#L42-L44)\n* [`LETSAGREE_JS`](https://github.com/raratiru/django-letsagree/blob/9436ddabb4467477ecb39d94fd09b6f574e9384f/letsagree/forms.py#L42-L44)\n* [`letsagree.admin.term_parents`](https://github.com/raratiru/django-letsagree/blob/9436ddabb4467477ecb39d94fd09b6f574e9384f/letsagree/admin.py#L23-L27)\n\nChangelog\n---------\n1.1.9: Added support for Django-4.1 started testing for python-3.11.\n\n1.1.8: Added support for Django-4.0 started testing for python-3.10, stopped testing for python-3.6 and python-3.7 (not supported by Django-4.0).\n\n1.1.7: Added `default_auto_field` value to ` 'django.db.models.AutoField'` for `Django-3.2`.\n\n1.1.6: Fixed compatibility with Django-3.1\n\n1.1.5: Fixed bug in LETSAGREE_LOGOUT_URL setting.\n\n1.1.4: Deprecated `LETSAGREE_LOGOUT_APP_NAME` in favor of `LETSAGREE_LOGOUT_URL`\n\n1.1.3: Locked to Django-3.0 until #39 is resolved\n\n1.1.2: Added the ability to set a namespaced url in the \"logout application name\" setting.\n\n1.1.1: AnonymousUser should not access letsagree urls (receives 404)\n\n1.1.0: Refactored middleware for thread-safety\n\n1.0.4: Added support for Django-3.0, dropped support for Django-2.1\n\n1.0.3: Only users with add_perm can see all the Terms in changelist\n\n1.0.2: Addressed codacy reports, updated readme, installed pyup, snyk\n\n1.0.1: Added Travis, Coverage, LGTM, PyUp CI\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraratiru%2Fdjango-letsagree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraratiru%2Fdjango-letsagree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraratiru%2Fdjango-letsagree/lists"}