Add guarded content draft and publication services
This commit is contained in:
@@ -70,6 +70,12 @@ class SiteIdentity(models.Model):
|
||||
|
||||
class Meta:
|
||||
constraints = [models.CheckConstraint(condition=Q(id=1), name="content_single_site")]
|
||||
permissions = [
|
||||
("change_content_draft", "Can change content drafts"),
|
||||
("preview_content_draft", "Can preview content drafts"),
|
||||
("publish_content", "Can publish content"),
|
||||
("restore_content", "Can restore published content"),
|
||||
]
|
||||
|
||||
def clean(self):
|
||||
if self.published_revision_id and self.published_revision.site_id != self.pk:
|
||||
@@ -114,6 +120,14 @@ class SiteRevision(models.Model):
|
||||
if not self.logo_id and self.logo_alt:
|
||||
raise ValidationError({"logo_alt": "Alt text requires a logo."})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.pk:
|
||||
previous = type(self).objects.get(pk=self.pk)
|
||||
protected = ("site_id", "number", "official_name", "short_name", "faculty_subline", "description", "logo_id", "logo_alt", "navigation", "contacts", "footer_copy", "seo_title", "seo_description", "seo_image_id", "created_by_id", "source_seed_key", "source_seed_version", "content_checksum")
|
||||
if any(getattr(self, field) != getattr(previous, field) for field in protected):
|
||||
raise ValidationError("Site revision content is immutable.")
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class EditorialPage(models.Model):
|
||||
slug = models.CharField(max_length=20, unique=True)
|
||||
@@ -155,6 +169,16 @@ class PageRevision(models.Model):
|
||||
for field in ("title", "intro", "seo_title", "seo_description"):
|
||||
validators.text(getattr(self, field), self._meta.get_field(field).max_length, required=field != "intro")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.pk:
|
||||
previous = type(self).objects.get(pk=self.pk)
|
||||
protected = ("page_id", "number", "title", "intro", "seo_title", "seo_description", "seo_image_id", "created_by_id", "source_seed_key", "source_seed_version")
|
||||
if any(getattr(self, field) != getattr(previous, field) for field in protected):
|
||||
raise ValidationError("Page revision content is immutable.")
|
||||
if self.content_checksum != previous.content_checksum and previous.content_checksum:
|
||||
raise ValidationError("Page revision checksum is immutable.")
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class PageSection(models.Model):
|
||||
revision = models.ForeignKey(PageRevision, on_delete=models.CASCADE, related_name="sections")
|
||||
@@ -226,6 +250,16 @@ class PageSection(models.Model):
|
||||
if self.body and self.entries:
|
||||
raise ValidationError("Use body or entries, not both.")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.pk:
|
||||
raise ValidationError("Page sections are immutable; create a new revision.")
|
||||
if self.revision.status != "draft":
|
||||
raise ValidationError("Sections can only be added to a draft revision.")
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
raise ValidationError("Page sections are immutable; create a new revision.")
|
||||
|
||||
|
||||
class PublicationAudit(models.Model):
|
||||
actor = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
|
||||
@@ -238,3 +272,11 @@ class PublicationAudit(models.Model):
|
||||
|
||||
class Meta:
|
||||
constraints = [models.CheckConstraint(condition=(Q(site__isnull=False, page__isnull=True) | Q(site__isnull=True, page__isnull=False)), name="content_audit_one_target")]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.pk:
|
||||
raise ValidationError("Publication audit records are append only.")
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
raise ValidationError("Publication audit records are append only.")
|
||||
|
||||
Reference in New Issue
Block a user