Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support custom max age + multiple links per Revision #7

Merged
merged 10 commits into from
Dec 4, 2024
26 changes: 19 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,25 +67,37 @@ draft version saved since the last time the page was published

## Settings

The following settings can be added to your Django settings file:
The following settings can be added to your Django settings file as keys in a `WAGTAILDRAFTSHARING` dictionary

### ``WAGTAILDRAFTSHARING_MAX_AGE``
```
WAGTAILDRAFTSHARING = {
...
"MAX_TTL": 123456,
...
}
```

The default expiry time for generated links, in seconds. Defaults to 1 week. Set it to a negative value to disable expiry.
### ``MAX_TTL``

### ```WAGTAILDRAFTSHARING_ADMIN_MENU_POSITION```
The default expiry time for generated links, in seconds. Defaults to 28 days. Set it to a negative value to disable expiry.

### ```ADMIN_MENU_POSITION```

Set the integer priority to control where in the admin menu
the link to the list of generated links sits. Defaults to 200.

### ```WAGTAILDRAFTSHARING_VERBOSE_NAME```
### ```VERBOSE_NAME```

Provide a different singular label for a link. Defaults to "Draftsharing Link"

### ```WAGTAILDRAFTSHARING_VERBOSE_NAME_PLURAL```
### ```VERBOSE_NAME_PLURAL```

Provide a different plural label for a link. Defaults to "Draftsharing Links"

### ```WAGTAILDRAFTSHARING_MENU_ITEM_LABEL```
### ```MENU_ITEM_LABEL```

Customise the label used in the page-level Action Menu for creating a link. Defaults to "Create draft sharing link",

## Testing

To install testing dependencies locally run `pip install -e ".[testing]"`
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "wagtaildraftsharing"
version = "0.1.3"
version = "0.2.0"
description = "Share wagtail drafts with private URLs."
readme = "README.md"
requires-python = ">=3.9"
Expand Down
13 changes: 13 additions & 0 deletions wagtaildraftsharing/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from wagtail.log_actions import LogFormatter

WAGTAILDRAFTSHARING_CREATE_SHARING_LINK = "wagtaildraftsharing.create_sharing_link"
WAGTAILDRAFTSHARING_REUSE_SHARING_LINK = "wagtaildraftsharing.reuse_sharing_link"


def register_wagtaildraftsharing_log_actions(actions):
Expand All @@ -16,3 +17,15 @@ def format_message(self, log_entry):
revision_id=log_entry.data["revision"],
username=log_entry.user,
)

@actions.register_action(WAGTAILDRAFTSHARING_REUSE_SHARING_LINK)
class ReuseSharingLink(LogFormatter):
label = _("Reuse sharing link")

def format_message(self, log_entry):
return _(
"{username} reused sharing link for revision {revision_id}"
).format(
revision_id=log_entry.data["revision"],
username=log_entry.user,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 5.1.1 on 2024-12-04 10:56

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("wagtaildraftsharing", "0002_alter_wagtaildraftsharinglink_created_by"),
]

operations = [
migrations.AlterField(
model_name="wagtaildraftsharinglink",
name="revision",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="wagtailcore.revision",
),
),
]
68 changes: 54 additions & 14 deletions wagtaildraftsharing/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,59 @@
from django.utils.timezone import timedelta
from wagtail.log_actions import log

from wagtaildraftsharing.actions import WAGTAILDRAFTSHARING_CREATE_SHARING_LINK
from wagtaildraftsharing.actions import (
WAGTAILDRAFTSHARING_CREATE_SHARING_LINK,
WAGTAILDRAFTSHARING_REUSE_SHARING_LINK,
)
from wagtaildraftsharing.utils import tz_aware_utc_now

from . import settings as draftsharing_settings

max_age = draftsharing_settings.WAGTAILDRAFTSHARING_MAX_AGE
from .settings import settings as draftsharing_settings


class WagtaildraftsharingLinkManager(models.Manager):
def get_or_create_for_revision(self, *, revision, user):
key = uuid.uuid4()
if max_age > 0:
active_until = tz_aware_utc_now() + timedelta(seconds=max_age)
def _get_active_until(self, max_ttl):
if max_ttl is None:
max_ttl = draftsharing_settings.MAX_TTL
if max_ttl > 0:
active_until = tz_aware_utc_now() + timedelta(seconds=max_ttl)
else:
active_until = None

return active_until

def create_for_revision(self, *, revision, user, max_ttl=None):
# Always creates a new sharing link
sharing_link = WagtaildraftsharingLink.objects.create(
revision=revision,
key=uuid.uuid4(),
created_by=user,
active_until=self._get_active_until(max_ttl=max_ttl),
)
log(
instance=revision.content_object,
action=WAGTAILDRAFTSHARING_CREATE_SHARING_LINK,
user=user,
revision=revision,
data={"revision": revision.id},
)

return sharing_link

def get_or_create_for_revision(self, *, revision, user, max_ttl=None):
"""
Deprecated: prefer self.create_for_revision() instead

Creates a new sharing link if it doesn't exist, else returns an
existing one. Note that this may give you a link that is soon to
expire if it was made close to max_ttl seconds ago...
"""

active_until = self._get_active_until(max_ttl=max_ttl)

sharing_link, created = WagtaildraftsharingLink.objects.get_or_create(
revision=revision,
defaults={
"key": key,
"key": uuid.uuid4(),
"created_by": user,
"active_until": active_until,
},
Expand All @@ -39,6 +73,14 @@ def get_or_create_for_revision(self, *, revision, user):
revision=revision,
data={"revision": revision.id},
)
else:
log(
instance=revision.content_object,
action=WAGTAILDRAFTSHARING_REUSE_SHARING_LINK,
user=user,
revision=revision,
data={"revision": revision.id},
)

return sharing_link

Expand All @@ -50,7 +92,7 @@ class WagtaildraftsharingLink(models.Model):
editable=False,
primary_key=False,
)
revision = models.OneToOneField(
revision = models.ForeignKey(
"wagtailcore.Revision",
on_delete=models.CASCADE,
related_name="+",
Expand All @@ -77,10 +119,8 @@ def __init__(self, *args, **kwargs):

# Set the verbose names from settings,
# in a way that doesn't trigger migrations
self._meta.verbose_name = draftsharing_settings.WAGTAILDRAFTSHARING_VERBOSE_NAME
self._meta.verbose_name_plural = (
draftsharing_settings.WAGTAILDRAFTSHARING_VERBOSE_NAME_PLURAL
)
self._meta.verbose_name = draftsharing_settings.VERBOSE_NAME
self._meta.verbose_name_plural = draftsharing_settings.VERBOSE_NAME_PLURAL

def __str__(self):
return f"Revision {self.revision_id} of {self.revision.content_object}"
Expand Down
76 changes: 44 additions & 32 deletions wagtaildraftsharing/settings.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,44 @@
from django.conf import settings

WAGTAILDRAFTSHARING_ADMIN_MENU_POSITION = getattr(
settings,
"WAGTAILDRAFTSHARING_ADMIN_MENU_POSITION",
200,
)

WAGTAILDRAFTSHARING_VERBOSE_NAME = getattr(
settings,
"WAGTAILDRAFTSHARING_VERBOSE_NAME",
"Draftsharing Link",
)

WAGTAILDRAFTSHARING_VERBOSE_NAME_PLURAL = getattr(
settings,
"WAGTAILDRAFTSHARING_VERBOSE_NAME_PLURAL",
"Draftsharing Links",
)

WAGTAILDRAFTSHARING_MENU_ITEM_LABEL = getattr(
settings,
"WAGTAILDRAFTSHARING_MENU_ITEM_LABEL",
"Create draft sharing link",
)

DEFAULT_MAX_AGE = 7 * 24 * 60 * 60 # 7 days
WAGTAILDRAFTSHARING_MAX_AGE = getattr(
settings,
"WAGTAILDRAFTSHARING_MAX_AGE",
DEFAULT_MAX_AGE,
)
import dataclasses
from typing import cast

from django.conf import settings as django_settings
from django.utils.functional import SimpleLazyObject

_DEFAULT_MAX_TTL = 28 * 24 * 60 * 60 # 28 days


@dataclasses.dataclass
class WagtaildraftsharingSettings:
ADMIN_MENU_POSITION: int = 200
VERBOSE_NAME: str = "Draftsharing Link"
VERBOSE_NAME_PLURAL: str = "Draftsharing Links"
MENU_ITEM_LABEL: str = "Create draft sharing link"
MAX_TTL: int = _DEFAULT_MAX_TTL


def _init_settings():
"""
Get and validate Wagtaildraftsharing settings from the Django settings.
"""

setting_name = "WAGTAILDRAFTSHARING"
django_settings_dict = getattr(django_settings, setting_name, {})

overriden_defaults = {}

for optional_setting_name in [
"ADMIN_MENU_POSITION",
"VERBOSE_NAME",
"VERBOSE_NAME_PLURAL",
"MENU_ITEM_LABEL",
"MAX_TTL",
]:
if optional_setting_name in django_settings_dict:
overriden_defaults[optional_setting_name] = django_settings_dict[
optional_setting_name
]

return WagtaildraftsharingSettings(**overriden_defaults)


settings = cast(WagtaildraftsharingSettings, SimpleLazyObject(_init_settings))
4 changes: 2 additions & 2 deletions wagtaildraftsharing/snippets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

from wagtaildraftsharing.models import WagtaildraftsharingLink

from . import settings as draftsharing_settings
from .settings import settings as draftsharing_settings


class WagtaildraftsharingLinkSnippetViewSet(SnippetViewSet):
model = WagtaildraftsharingLink
base_url_path = "wagtaildraftsharing"
menu_icon = "view"
menu_order = draftsharing_settings.WAGTAILDRAFTSHARING_ADMIN_MENU_POSITION
menu_order = draftsharing_settings.ADMIN_MENU_POSITION
add_to_settings_menu = False
add_to_admin_menu = True
list_display = ("__str__", "is_active", "created_by", "share_url")
Expand Down
7 changes: 7 additions & 0 deletions wagtaildraftsharing/tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,10 @@
WAGTAILADMIN_BASE_URL = "http://localhost:8000"

USE_TZ = True

# Wagtaildraftsharing settings

WAGTAILDRAFTSHARING = {
# Here is where settings would be overridden in a real project
# "MAX_TTL": 12312321,
}
Loading
Loading