Skip to content

Commit

Permalink
Update approval process
Browse files Browse the repository at this point in the history
Changed how some features work (not breaking previous behaviour).
Removed the use of `AutoOneToOneField` from `django-annoying` in favor
of a standard `OneToOneField`.

The change allows removing approval sandboxes upon approval.
Fixed an issue where an object with changes denied will still fetch
denied content in the admin or forms.

Remove the message in admin telling that an object has changes
waiting for approval, when the changes have already been denied.
that allows automatically deleting approval sandboxes when accepted.
Warning: you won't know who niether when changes were approved.
  • Loading branch information
artscoop committed Feb 4, 2024
1 parent 0f07932 commit 06966a2
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 62 deletions.
16 changes: 9 additions & 7 deletions src/approval/admin/monitored.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,23 @@ def get_object(self, request, object_id, from_field: str = None) -> MonitoredMod
obj: MonitoredModel = super().get_object(request, object_id)
if isinstance(obj, MonitoredModel):
# Only display approval warning if the object has not been approved.
if hasattr(obj, "approval") and obj.approval:
if getattr(obj, "approval", None) and obj.approval.approved is None:
obj.approval._update_source(default=False, save=False)
obj.request = request
if obj.approval.approved != True:
if obj.approval.approved is None:
self.message_user(
request,
pgettext_lazy("approval", "This form is showing changes currently pending."),
pgettext_lazy(
"approval", "This form is showing changes currently pending."
),
level=messages.WARNING,
)
else:
raise ImproperlyConfigured(f"No approval model was declared for this model.")
return obj
return obj
else:
raise ImproperlyConfigured(f"No approval model was declared for this model.")

@display(description=pgettext_lazy("approval", "status"), ordering="approval__approved")
def get_approval_status(self, obj):
if isinstance(obj, MonitoredModel) and hasattr(obj, "approval") and obj.approval:
return obj.approval.get_approved_display()
return "N/A"
return pgettext_lazy("approval", "Unavailable")
2 changes: 2 additions & 0 deletions src/approval/admin/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ def do_approve(self, request, queryset):
"""Accept selected approval requests."""
for approval in queryset:
approval.approve(user=request.user, save=True)
if approval.delete_on_approval:
self.message_user(request, _("Upon approval, deletion has been performed."))
self.message_user(request, _("Selected edits have been accepted."))

# Getter
Expand Down
5 changes: 3 additions & 2 deletions src/approval/forms/monitored.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def __init__(self, *args, **kwargs):
"""
instance = kwargs.get("instance", None)
if instance and isinstance(instance, MonitoredModel):
instance.approval._update_source()
logger.debug(pgettext_lazy("approval", f"{self.__class__.__name__} fetched approval data for form."))
if getattr(instance, "approval", None) and instance.approval.approved is None:
instance.approval._update_source()
logger.debug(pgettext_lazy("approval", f"{self.__class__.__name__} fetched approval data for form."))
super().__init__(*args, **kwargs)
11 changes: 9 additions & 2 deletions src/approval/listeners/monitored.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ def before_save(sender: Type[models.Model], instance: MonitoredModel, **kwargs):
instance: Instance to process.
"""
if issubclass(sender, MonitoredModel) and not getattr(instance, "_ignore_approval", False):
if instance.pk is not None: # Sandbox updated instances only
# Create approval instance if not present
if not getattr(instance, "approval", None):
for obj in instance._meta.related_objects:
if getattr(obj.related_model, "_is_sandbox", False):
approval: models.Model = obj.related_model(source=instance)
approval.save()
break
if instance.pk is not None and getattr(instance, "approval", None): # Sandbox updated instances only
logger.debug(pgettext_lazy("approval", "Pre-save signal handled on updated {cls}").format(cls=sender))
users = instance._get_authors()
instance.approval._update_sandbox()
Expand All @@ -48,7 +55,7 @@ def after_save(sender: Type[models.Model], instance: models.Model, **kwargs):
:param created: Is the instance new in the database ?
"""
if issubclass(sender, MonitoredModel) and not getattr(instance, "_ignore_approval", False):
if kwargs.get("created", False):
if kwargs.get("created", False) and getattr(instance, "approval", None):
logger.debug(pgettext_lazy("approval", "Post-save signal handled on new {cls}").format(cls=sender))
users = instance._get_authors()
instance.approval._update_sandbox()
Expand Down
Binary file modified src/approval/locale/fr/LC_MESSAGES/django.mo
Binary file not shown.
104 changes: 62 additions & 42 deletions src/approval/locale/fr/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.36\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-08-19 22:54+0200\n"
"POT-Creation-Date: 2024-02-03 21:38+0100\n"
"PO-Revision-Date: 2023-08-18 05:11+0200\n"
"Last-Translator: Steve Kossouho <[email protected]>\n"
"Language-Team: French - France <[email protected]>\n"
Expand All @@ -16,204 +16,224 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.3.2\n"

#: admin/monitored.py:40
#: src/approval/admin/monitored.py:31
msgctxt "approval"
msgid "This form is showing changes currently pending."
msgstr ""
"Ce formulaire affiche des modifications toujours en attente de validation."

#: admin/monitored.py:47
#: src/approval/admin/monitored.py:39
msgctxt "approval"
msgid "status"
msgstr "statut"

#: admin/sandbox.py:27
#: src/approval/admin/monitored.py:43
msgctxt "approval"
msgid "Unavailable"
msgstr "Indisponible"

#: src/approval/admin/sandbox.py:28
msgid "Deny selected approval requests"
msgstr "Refuser les demandes de modération sélectionnées"

#: admin/sandbox.py:32
#: src/approval/admin/sandbox.py:33
msgid "Selected edits have been denied."
msgstr "Les modification sélectionnées ont été refusées."

#: admin/sandbox.py:34
#: src/approval/admin/sandbox.py:35
msgid "Approve selected approval requests"
msgstr "Accepter les demandes de modération sélectionnées"

#: admin/sandbox.py:39
#: src/approval/admin/sandbox.py:41
msgid "Upon approval, deletion has been performed."
msgstr "Suite à la validation, une suppression a été effectuée."

#: src/approval/admin/sandbox.py:42
msgid "Selected edits have been accepted."
msgstr "Les modification sélectionnées ont été acceptées."

#: admin/sandbox.py:42
#: src/approval/admin/sandbox.py:45
msgid "Content"
msgstr "Contenu"

#: apps.py:9
#: src/approval/apps.py:9
msgctxt "approval"
msgid "approval"
msgstr "modération"

#: forms/monitored.py:23
#, python-brace-format
#: src/approval/forms/monitored.py:23
#, fuzzy, python-brace-format
#| msgctxt "approval"
#| msgid "{self.__class__.__name__} fetched approval data."
msgctxt "approval"
msgid "{self.__class__.__name__} fetched approval data."
msgid "{self.__class__.__name__} fetched approval data for form."
msgstr "{self.__class__.__name__} a récupéré des données de modération."

#: listeners/monitored.py:29
#: src/approval/listeners/monitored.py:35
#, python-brace-format
msgctxt "approval"
msgid "Pre-save signal handled on updated {cls}"
msgstr "Signal pre-save traité sur un objet {cls} mis à jour"

#: listeners/monitored.py:52
#: src/approval/listeners/monitored.py:58
#, python-brace-format
msgctxt "approval"
msgid "Post-save signal handled on new {cls}"
msgstr "Signal post-save traité sur un nouvel objet {cls}"

#: models/monitored.py:24
#: src/approval/models/monitored.py:47
#, python-brace-format
msgctxt "approval"
msgid "Using request user as author of {self}."
msgstr "Utilisation de l'utilisateur connecté comme auteur de {self}."

#: models/monitored.py:41
#: src/approval/models/monitored.py:64
#, fuzzy, python-brace-format
#| msgctxt "approval"
#| msgid "Monitored {self} was reverted to last DB state."
msgctxt "approval"
msgid "Monitored {obj} was reverted to last DB state."
msgstr "L'état de l'objet à modérer {obj} a été restauré depuis la base de données."
msgstr ""
"L'état de l'objet à modérer {obj} a été restauré depuis la base de données."

#: models/sandbox.py:22
#: src/approval/models/monitoring.py:49
msgctxt "approval.moderation"
msgid "Pending"
msgstr "En attente"

#: models/sandbox.py:23
#: src/approval/models/monitoring.py:50
msgctxt "approval.moderation"
msgid "Rejected"
msgstr "Rejeté"

#: models/sandbox.py:24
#: src/approval/models/monitoring.py:51
msgctxt "approval.moderation"
msgid "Approved"
msgstr "Approuvé"

#: models/sandbox.py:29
#: src/approval/models/monitoring.py:54
msgctxt "approval.draft"
msgid "Draft"
msgstr "Brouillon"

#: models/sandbox.py:30
#: src/approval/models/monitoring.py:55
msgctxt "approval.draft"
msgid "Waiting for moderation"
msgstr "En attente de modération"

#: models/sandbox.py:90
#: src/approval/models/monitoring.py:67
msgid "UUID"
msgstr "UUID"

#: models/sandbox.py:96
#: src/approval/models/monitoring.py:75
msgctxt "approval_entry"
msgid "Data"
msgstr "Données"

#: models/sandbox.py:102
#: src/approval/models/monitoring.py:81
msgctxt "approval_entry"
msgid "Moderated"
msgstr "Modéré"

#: models/sandbox.py:110
#: src/approval/models/monitoring.py:89
msgctxt "approval_entry"
msgid "Moderated by"
msgstr "Modéré par"

#: models/sandbox.py:116
#: src/approval/models/monitoring.py:95
msgctxt "approval_entry"
msgid "Draft"
msgstr "Brouillon"

#: models/sandbox.py:118
#: src/approval/models/monitoring.py:98
msgctxt "approval_entry"
msgid "Moderated at"
msgstr "Modéré le"

#: models/sandbox.py:119
#: src/approval/models/monitoring.py:100
msgctxt "approval"
msgid "Reason"
msgstr "Raison"

#: models/sandbox.py:120
#: src/approval/models/monitoring.py:102
msgctxt "approval_entry"
msgid "Updated"
msgstr "Mis à jour"

#: models/sandbox.py:126
#: src/approval/models/monitoring.py:109
#, python-brace-format
msgctxt "approval"
msgid "{name} approval"
msgstr "modération de {name}"

#: models/sandbox.py:127
#: src/approval/models/monitoring.py:112
#, python-brace-format
msgctxt "approval"
msgid "{name} approvals"
msgstr "modérations de {name}"

#: models/sandbox.py:156
#: src/approval/models/monitoring.py:153
msgctxt "approval"
msgid "Updated monitored object fields using sandbox."
msgstr ""
"Les champs de l'objet à modérer ont été mis à jour depuis le bac à sable."

#: models/sandbox.py:160
#: src/approval/models/monitoring.py:160
msgctxt "approval"
msgid "Updated monitored object fields using default values."
msgstr ""
"Les champs de l'objet à modérer ont été mis à jour avec des valeurs par "
"défaut."

#: models/sandbox.py:167
#: src/approval/models/monitoring.py:170
msgctxt "approval"
msgid "Updated monitored object was persisted."
msgstr "Les modifications sur l'objet à modérer ont été persistées."

#: models/sandbox.py:171
#: src/approval/models/monitoring.py:177
#, python-brace-format
msgctxt "approval"
msgid "Field {name} could not be persisted to model."
msgstr "Le champ {name} n'a pas pu être persisté vers le modèle."

#: models/sandbox.py:202
#: src/approval/models/monitoring.py:218
#, python-brace-format
msgctxt "approval"
msgid "Sandbox for {source} was updated."
msgstr "Le bac à sable pour {source} a été mis à jour."

#: models/sandbox.py:327
#: src/approval/models/monitoring.py:348
#, fuzzy
#| msgctxt "approval.draft"
#| msgid "Waiting for moderation"
msgctxt "approval"
msgid "Set sandbox as waiting for moderation."
msgstr "Définir le bac à sable comme en attente de modération."

#: models/sandbox.py:351
#: src/approval/models/monitoring.py:374
msgctxt "approval_entry"
msgid "Congratulations, your edits have been approved."
msgstr "Félicitations, vos modifications ont été acceptées."

#: models/sandbox.py:356
#: src/approval/models/monitoring.py:382
#, fuzzy
#| msgctxt "approval"
#| msgid "Changes in sandbox were approved."
msgctxt "approval"
msgid "Changes in sandbox were deleted upon approval."
msgstr "Les modifications du bac à sable ont été acceptées."

#: src/approval/models/monitoring.py:384
msgctxt "approval"
msgid "Changes in sandbox were approved."
msgstr "Les modifications du bac à sable ont été acceptées."

#: models/sandbox.py:375
#: src/approval/models/monitoring.py:404
msgctxt "approval_entry"
msgid "Your edits have been refused."
msgstr "Vos modifications ont été refusées."

#: models/sandbox.py:379
#: src/approval/models/monitoring.py:409
msgctxt "approval"
msgid "Changes in sandbox were rejected."
msgstr "Les modifications du bac à sable ont été rejetées."
Expand Down
12 changes: 7 additions & 5 deletions src/approval/models/monitored.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@ class Meta:
def _get_authors(self) -> List[models.Model]:
"""Get the authors of the current instance."""
# Use user from request as author only if allowed
if self.approval.auto_approve_by_request:
if getattr(self, "request") and getattr(self.request, "user", None):
logger.debug(pgettext_lazy("approval", f"Using request user as author of {self}."))
return [self.request.user]
return self.approval._get_authors()
if getattr(self, "approval", None):
if self.approval.auto_approve_by_request:
if getattr(self, "request") and getattr(self.request, "user", None):
logger.debug(pgettext_lazy("approval", f"Using request user as author of {self}."))
return [self.request.user]
return self.approval._get_authors()
return []

def _revert(self) -> bool:
"""
Expand Down
Loading

0 comments on commit 06966a2

Please sign in to comment.