From 3ce530367e37d215513c2b20852b7c680a66443c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Fri, 1 Apr 2022 15:57:21 +0200 Subject: [PATCH] Added docs extracted from the README.md --- .gitignore | 1 + README.md | 182 +---------------------------------------- docs/Makefile | 20 +++++ docs/changelog.rst | 23 ++++++ docs/conf.py | 85 +++++++++++++++++++ docs/configuration.rst | 182 +++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 23 ++++++ docs/installation.rst | 58 +++++++++++++ docs/requirements.txt | 3 + docs/usage.rst | 78 ++++++++++++++++++ setup.cfg | 40 ++++++++- setup.py | 32 +------- 12 files changed, 517 insertions(+), 210 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/changelog.rst create mode 100644 docs/conf.py create mode 100644 docs/configuration.rst create mode 100644 docs/index.rst create mode 100644 docs/installation.rst create mode 100644 docs/requirements.txt create mode 100644 docs/usage.rst diff --git a/.gitignore b/.gitignore index 83f560f..cab8b8e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ git-push.bat .python-version .coverage /.idea/ +docs/_build/ diff --git a/README.md b/README.md index 87c1e4b..dbb1e33 100644 --- a/README.md +++ b/README.md @@ -5,190 +5,16 @@ [![PyPI Python Versions](https://img.shields.io/pypi/pyversions/django-invitations.svg)](https://pypi.python.org/pypi/django-invitations) [![Build status](https://github.com/jazzband/django-invitations/actions/workflows/test.yml/badge.svg)](https://github.com/jazzband/django-invitations/actions/workflows/test.yml) [![Coverage Status](https://coveralls.io/repos/bee-keeper/django-invitations/badge.svg?branch=master&service=github)](https://coveralls.io/github/bee-keeper/django-invitations?branch=master) +[![Documentation](https://readthedocs.org/projects/django-invitations/badge/?version=latest)](https://django-invitations.readthedocs.io/en/latest/?badge=latest) ## About -Generic invitations solution with adaptable backend and support for django-allauth. All emails and messages are fully customisable. - -Originally written as an invitations solution for the excellent [django-allauth](https://github.com/pennersr/django-allauth), this app has been refactored to remove the allauth dependency whilst retaining 100% backwards compatibility. +Generic invitations solution with adaptable backend and support for django-allauth. ## Contributing As we are members of a [JazzBand project](https://jazzband.co/projects), `django-invitations` contributors should adhere to the [Contributor Code of Conduct](https://jazzband.co/about/conduct). -## Installation - -1. Install with pip -``` -pip install django-invitations -``` - -2. Add django.contrib.sites" and 'invitations to INSTALLED_APPS - -``` -INSTALLED_APPS = [ - ... - "django.contrib.sites", - "invitations", - ... -] -``` - -2. Make sure you have SITE_ID defined in settings -``` -SITE_ID = 1 -``` - -3. Append to urls.py - -``` -urlpatterns = [ - ... - path("invitations/", include('invitations.urls', namespace='invitations')), - ... -] -``` - -4. Run migrations - -``` -python manage.py migrate -``` - -## Usage - -There are two primary ways to use `django-invitations` described below. - -Generic Invitation flow: - -* Priviledged user invites prospective user by email (via either Django admin, form post, JSON post or programmatically) -* User receives invitation email with confirmation link -* User clicks link and is redirected to a preconfigured url (default is accounts/signup) - -Allauth Invitation flow: - -* As above but.. -* User clicks link, their email is confirmed and they are redirected to signup -* The signup URL has the email prefilled and upon signing up the user is logged into the site - -Further details can be found in the following sections. - -### Allauth Integration - -As above but note that invitations must come after allauth in the INSTALLED_APPS - -``` -# Add to settings.py -ACCOUNT_ADAPTER = 'invitations.models.InvitationsAdapter' -``` - -### Sending Invites - -First import the model: - -``` -from invitations.utils import get_invitation_model -``` - -Make an instance of the model: - -``` -Invitation = get_invitation_model() -``` - -Then finally pass the recipient to the model and send. - -``` -# inviter argument is optional -invite = Invitation.create('email@example.com', inviter=request.user) -invite.send_invitation(request) -``` - -To send invites via django admin, just add an invite and save. - - -### Bulk Invites - -Bulk invites are supported via JSON. Post a list of comma separated emails to the dedicated URL and Invitations will return a data object containing a list of valid and invalid invitations. - - -### Testing - -`python manage.py test` or `tox` - -### Additional Configuration - -* `INVITATIONS_INVITATION_EXPIRY` (default=`3`) - - Integer. How many days before the invitation expires. - -* `INVITATIONS_INVITATION_ONLY` (default=`False`) - - Boolean. If the site is invite only, or open to all (only relevant when using allauth). - -* `INVITATIONS_CONFIRM_INVITE_ON_GET` (default=`True`) - - Boolean. If confirmations can be accepted via a `GET` request. - -* `INVITATIONS_ACCEPT_INVITE_AFTER_SIGNUP` (default=`False`) - - Boolean. If `True`, invitations will be accepted after users finish signup. - If `False`, invitations will be accepted right after the invitation link is clicked. - Note that this only works with Allauth for now, which means `ACCOUNT_ADAPTER` has to be - `'invitations.models.InvitationsAdapter'`. - -* `INVITATIONS_GONE_ON_ACCEPT_ERROR` (default=`True`) - - Boolean. If `True`, return an HTTP 410 GONE response if the invitation key - is invalid, or the invitation is expired or previously accepted when - accepting an invite. If `False`, display an error message and redirect on - errors: - - * Redirects to `INVITATIONS_SIGNUP_REDIRECT` on an expired key - * Otherwise, redirects to `INVITATIONS_LOGIN_REDIRECT` on other errors. - -* `INVITATIONS_ALLOW_JSON_INVITES` (default=`False`) - - Expose a URL for authenticated posting of invitees - -* `INVITATIONS_SIGNUP_REDIRECT` (default=`'account_signup'`) - - URL name of your signup URL. - -* `INVITATIONS_LOGIN_REDIRECT` (default=`LOGIN_URL` from Django settings) - - URL name of your login URL. - -* `INVITATIONS_ADAPTER` (default=`'invitations.adapters.BaseInvitationsAdapter'`) - - Used for custom integrations. Set this to `ACCOUNT_ADAPTER` if using django-allauth. - -* `INVITATIONS_EMAIL_MAX_LENGTH` (default=`254`) - - If set to `None` (the default), invitation email max length will be set up to 254. Set this to an integer value to set up a custome email max length value. - -* `INVITATIONS_EMAIL_SUBJECT_PREFIX` (default=`None`) - - If set to `None` (the default), invitation email subjects will be prefixed with the name of the current Site in brackets (such as `[example.com]`). Set this to a string to for a custom email subject prefix, or an empty string for no prefix. - -* `INVITATIONS_INVITATION_MODEL` (default=`invitations.Invitation`) - - App registry path of the invitation model used in the current project, for customization purposes. - -* `CONFIRMATION_URL_NAME` (default=`invitations:accept-invite`) - - String. Invitation confirmation URL - -### Signals - -The following signals are emitted: - -* `invite_url_sent` -* `invite_accepted` - - -### Management Commands - -Expired and accepted invites can be cleared as so: +### Documentation -`python manage.py clear_expired_invitations` +Documentation can be found at https://django-invitations.readthedocs.io/ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d5ca222 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= "-W" +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 0000000..e24dd74 --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,23 @@ +Changelog +========= + +1.9 (2017-02-11) +---------------- + +- Added get_signup_redirect to allow custom implementations by subclasses + +- Fixed invitation form displaying "None" when first displayed + +- Fixed deprecation warnings + +- Added get_signup_redirect to allow custom implementations by subclasses + +- Fixed flake8 errors + +- Import reverse from django.urls if available, otherwise fall back to old import + +- Set ForeignKey field to explicitly cascade on deletion + +- flake8 styling formatting + +- Add email max length setting diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..3b6773f --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,85 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html +from __future__ import annotations + +import sys +from pathlib import Path + +# -- Path setup -------------------------------------------------------------- + +here = Path(__file__).parent.resolve() +sys.path.insert(0, str(here / ".." / "src")) + +# -- Project information ----------------------------------------------------- + +project = "django-invitations" +copyright = "-" +author = "-" + +# The version info for the project you're documenting, acts as replacement +# for |version| and |release|, also used in various other places throughout +# the built documents. + + +def _get_version() -> str: + lines = (here / ".." / "setup.cfg").read_text().splitlines() + version_lines = [line.strip() for line in lines if line.startswith("version = ")] + + assert len(version_lines) == 1 + return version_lines[0].split(" = ")[1] + + +version = _get_version() +release = version + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", +] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [ + "_build", + "venv", +] + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "furo" + +# -- Options for LaTeX output ------------------------------------------ + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass +# [howto/manual]). +latex_documents = [ + ( + "index", + "django-invitations.tex", + "django-invitations Documentation", + "Jazzband", + "manual", + ), +] + +# -- Options for Intersphinx ------------------------------------------- + +intersphinx_mapping = { + "django": ( + "https://docs.djangoproject.com/en/stable/", + "https://docs.djangoproject.com/en/stable/_objects/", + ), +} diff --git a/docs/configuration.rst b/docs/configuration.rst new file mode 100644 index 0000000..221c639 --- /dev/null +++ b/docs/configuration.rst @@ -0,0 +1,182 @@ +Configuration +============= + + +General settings +---------------- + +INVITATION_EXPIRY +***************** + +Setting name: ``INVITATIONS_INVITATION_EXPIRY`` + +Type: Integer + +Default: 3 + +How many days before the invitation expires. + +---- + +CONFIRM_INVITE_ON_GET +********************* + +Setting name: ``INVITATIONS_CONFIRM_INVITE_ON_GET`` + +Type: Boolean + +Default: True + +If confirmations can be accepted via a `GET` request. + +---- + +ACCEPT_INVITE_AFTER_SIGNUP +************************** + +Setting name: ``INVITATIONS_ACCEPT_INVITE_AFTER_SIGNUP`` + +Type: Boolean + +Default: False + +If ``True``, invitations will be accepted after users finish signup. +If ``False``, invitations will be accepted right after the invitation link is clicked. +Note that this only works with Allauth for now, which means `ACCOUNT_ADAPTER` has to be +``invitations.models.InvitationsAdapter``. + +---- + +GONE_ON_ACCEPT_ERROR +******************** + +Setting name: ``INVITATIONS_GONE_ON_ACCEPT_ERROR`` + +Type: Boolean + +Default: True + +If `True`, return an HTTP 410 GONE response if the invitation key +is invalid, or the invitation is expired or previously accepted when +accepting an invite. If `False`, display an error message and redirect on +errors: + +* Redirects to `INVITATIONS_SIGNUP_REDIRECT` on an expired key +* Otherwise, redirects to `INVITATIONS_LOGIN_REDIRECT` on other errors. + +---- + +ALLOW_JSON_INVITES +****************** + +Setting name: ``INVITATIONS_ALLOW_JSON_INVITES`` + +Type: Boolean + +Default: False + +Expose a URL for authenticated posting of invitees + +---- + +SIGNUP_REDIRECT +*************** + +Setting name: ``INVITATIONS_SIGNUP_REDIRECT`` + +Type: String + +Default: "account_signup" + +URL name of your signup URL. + +---- + +LOGIN_REDIRECT +************** + +Setting name: ``INVITATIONS_LOGIN_REDIRECT`` + +Type: String + +Default: ``LOGIN_URL`` from Django settings + +URL name of your login URL. + +---- + +ADAPTER +******* + +Setting name: ``INVITATIONS_ADAPTER`` + +Type: String + +Default: "invitations.adapters.BaseInvitationsAdapter" + +Used for custom integrations. Set this to `ACCOUNT_ADAPTER` if using django-allauth. + +---- + +EMAIL_MAX_LENGTH +**************** + +Setting name: ``INVITATIONS_EMAIL_MAX_LENGTH`` + +Type: Integer + +Default: 254 + +If set to `None` (the default), invitation email max length will be set up to 254. Set this to an integer value to set up a custome email max length value. + +---- + +EMAIL_SUBJECT_PREFIX +******************** + +Setting name: ``INVITATIONS_EMAIL_SUBJECT_PREFIX`` + +Type: String or None + +Default: None + +If set to `None` (the default), invitation email subjects will be prefixed with the name of the current Site in brackets (such as `[example.com]`). Set this to a string to for a custom email subject prefix, or an empty string for no prefix. + +---- + +INVITATION_MODEL +**************** + +Setting name: ``INVITATIONS_INVITATION_MODEL`` + +Type: String + +Default: ``invitations.Invitation`` + +App registry path of the invitation model used in the current project, for customization purposes. + +---- + +CONFIRMATION_URL_NAME +********************* +Setting name: ``INVITATIONS_CONFIRMATION_URL_NAME`` + +Type: String + +Default: "invitations:accept-invite" + +Invitation confirmation URL + +Allauth related settings +------------------------ + +INVITATION_ONLY +*************** + +Setting name: ``INVITATIONS_INVITATION_ONLY`` + +Type: Boolean + +Default: False + +If the site is invite only, or open to all (only relevant when using allauth). diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..ac49e9b --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,23 @@ +django-invitations documentation +========================= + +Generic invitations solution with adaptable backend and support for django-allauth. All emails and messages are fully customisable. + +Originally written as an invitations solution for the excellent `django-allauth `_, this app has been refactored to remove the allauth dependency whilst retaining 100% backwards compatibility. + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + installation + usage + configuration + changelog + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000..665d7a3 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,58 @@ +Installation +============ + +Requirements +------------ + +Python 3.7 to 3.10 supported. + +Django 3.2 to 4.0 supported. + +Installation +------------ + +1. Install with **pip**: + + .. code-block:: sh + + python -m pip install django-invitations + +2. Add "django.contrib.sites" and "invitations" to INSTALLED_APPS + + .. code-block:: python + + INSTALLED_APPS = [ + ... + "django.contrib.sites", + "invitations", + ... + ] + +.. note:: **Allauth support** + + For allauth support ``invitations`` must come after ``allauth`` in the INSTALLED_APPS + + +3. Make sure you have SITE_ID defined in settings: + + .. code-block:: python + + ... + SITE_ID = 1 + ... + +3. Add invitations urls to your urlpatterns: + + .. code-block:: python + + urlpatterns = [ + ... + path("invitations/", include('invitations.urls', namespace='invitations')), + ... + ] + +4. Run migrations + + .. code-block:: sh + + python manage.py migrate diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..4c929e6 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +django +furo +sphinx diff --git a/docs/usage.rst b/docs/usage.rst new file mode 100644 index 0000000..9d2e3a8 --- /dev/null +++ b/docs/usage.rst @@ -0,0 +1,78 @@ +Usage +===== + +There are two primary ways to use `django-invitations` described below. + +Generic Invitation flow: + +* Priviledged user invites prospective user by email (via either Django admin, form post, JSON post or programmatically) +* User receives invitation email with confirmation link +* User clicks link and is redirected to a preconfigured url (default is accounts/signup) + +Allauth Invitation flow: + +* As above but.. +* User clicks link, their email is confirmed and they are redirected to signup +* The signup URL has the email prefilled and upon signing up the user is logged into the site + +Further details can be found in the following sections. + +Allauth Integration +------------------- + +As above but note that invitations must come after allauth in the INSTALLED_APPS + +Set the allauth ``ACCOUNT_ADAPTER`` setting + +.. code-block:: python + + ACCOUNT_ADAPTER = 'invitations.models.InvitationsAdapter' + +Sending Invites +--------------- + +First import the model: + +.. code-block:: python + + from invitations.utils import get_invitation_model + +Make an instance of the model: + +.. code-block:: python + + Invitation = get_invitation_model() + +Then finally pass the recipient to the model and send. + +.. code-block:: python + + # inviter argument is optional + invite = Invitation.create('email@example.com', inviter=request.user) + invite.send_invitation(request) + +To send invites via django admin, just add an invite and save. + + +Bulk Invites +------------ + +Bulk invites are supported via JSON. Post a list of comma separated emails to the dedicated URL and Invitations will return a data object containing a list of valid and invalid invitations. + +Signals +------- + +The following signals are emitted: + +* ``invite_url_sent`` +* ``invite_accepted`` + + +Management Commands +------------------- + +Expired and accepted invites can be cleared with the ``clear_expired_invitations`` management command: + +.. code-block:: sh + + python manage.py clear_expired_invitations diff --git a/setup.cfg b/setup.cfg index b88034e..b4aafb4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,40 @@ [metadata] -description-file = README.md +name = django-invitations +version = 1.9.3 +description = Generic invitations app with support for django-allauth +long_description = file: README.md +author = https://github.com/bee-keeper +author_email = none@none.com +url = https://github.com/jazzband/django-invitations.git +keywords = django invitation django-allauth invite +license = GPL-3.0-only +classifiers = + Development Status :: 4 - Beta + Intended Audience :: Developers + Topic :: Software Development :: Libraries :: Python Modules + Environment :: Web Environment + Topic :: Internet + Framework :: Django + Framework :: Django:: 3.2 + Framework :: Django:: 4.0 + Programming Language :: Python + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + License :: OSI Approved :: GPL-3.0-only +license_file = LICENSE + +[options] +package_dir= + =invitations +packages = find: +include_package_data = True +python_requires = >=3.7 +zip_safe = False + +[options.package_data] +invitations = "templates/*.*" + +[options.packages.find] +where = invitations diff --git a/setup.py b/setup.py index b9cff5e..6068493 100644 --- a/setup.py +++ b/setup.py @@ -1,33 +1,3 @@ -from setuptools import find_packages from setuptools import setup -setup( - name="django-invitations", - packages=find_packages(), - package_data={"invitations": ["templates/*.*"]}, - include_package_data=True, - zip_safe=False, - version="1.9.3", - description="Generic invitations app with support for django-allauth", - author="https://github.com/bee-keeper", - author_email="none@none.com", - url="https://github.com/jazzband/django-invitations.git", - keywords=["django", "invitation", "django-allauth", "invite"], - license="GPL-3.0-only", - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "Topic :: Software Development :: Libraries :: Python Modules", - "Environment :: Web Environment", - "Topic :: Internet", - "Framework :: Django", - "Framework :: Django:: 3.2", - "Framework :: Django:: 4.0", - "Programming Language :: Python", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "License :: OSI Approved :: GPL-3.0-only", - ], -) +setup()