Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions changelog/2026-02-27/CDD-1379.page-previews.md

Large diffs are not rendered by default.

5 changes: 0 additions & 5 deletions cms/common/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ class CommonPage(UKHSAPage):

objects = CommonPageManager()

@classmethod
def is_previewable(cls) -> bool:
"""Returns False. Since this is a headless CMS the preview panel is not supported"""
return False


class CommonPageRelatedLink(UKHSAPageRelatedLink):
page = ParentalKey(
Expand Down
5 changes: 0 additions & 5 deletions cms/composite/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,6 @@ class CompositePage(UKHSAPage):

objects = CompositePageManager()

@classmethod
def is_previewable(cls) -> bool:
"""Returns False. Since this is a headless CMS the preview panel is not supported"""
return False

@property
def last_updated_at(self) -> datetime.datetime:
"""Takes the most recent update of this page and any of its children
Expand Down
13 changes: 13 additions & 0 deletions cms/dashboard/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
from decimal import Decimal
from typing import override

from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
Expand Down Expand Up @@ -52,6 +53,13 @@ class UKHSAPage(Page):

"""

# Overridable setting for disabling previews
# on derived UKHSAPage objects.
# When disabled, the CMS will not allow page previews.
# Page previews allow the user to view draft content
# and pre-release, "embargoed" data.
custom_preview_enabled: bool = True

body = RichTextField(features=AVAILABLE_RICH_TEXT_FEATURES)
seo_change_frequency = models.IntegerField(
verbose_name="SEO change frequency",
Expand Down Expand Up @@ -107,6 +115,11 @@ class UKHSAPage(Page):
class Meta:
abstract = True

@override
def is_previewable(self) -> bool:
"""Disable built-in Wagtail preview for all headless dashboard pages."""
return False

def _raise_error_if_slug_not_unique(self) -> None:
"""Compares the provided slug against all pages to confirm the slug's `uniqueness`
this is against all pages and not just siblings, which is the default behavior of wagtail.
Expand Down
34 changes: 0 additions & 34 deletions cms/dashboard/serializers.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,7 @@
from collections import OrderedDict

from rest_framework import serializers
from wagtail.api.v2.views import PageSerializer
from wagtail.models import Page

from cms.dashboard.fields import ListablePageParentField

PAGE_HAS_NO_DRAFTS = (
"Page has no unpublished changes. Use the `api/pages/` for live pages instead."
)


class CMSDraftPagesSerializer(PageSerializer):
class Meta:
model = Page
fields = "__all__"

def to_representation(self, instance: Page) -> OrderedDict:
"""Provides a representation of the serialized instance

Notes:
This provides some additional logic to ensure
the `instance` being serialized has unpublished changes.
If not, then the serializer is invalidated

Args:
instance: The `Page` model being serialized

Returns:
A dict representation of the serialized instance

"""
if not instance.has_unpublished_changes:
raise serializers.ValidationError({"error_message": PAGE_HAS_NO_DRAFTS})

return super().to_representation(instance=instance)


class ListablePageSerializer(PageSerializer):
parent = ListablePageParentField(read_only=True)
220 changes: 220 additions & 0 deletions cms/dashboard/static/css/embargo_time_picker.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
#embargo-time-modal {
padding: 0;
border: 0;
background: transparent;
z-index: 9999;
max-width: none;
max-height: none;
width: 100vw;
height: 100vh;
}

#embargo-time-modal[open] {
display: flex;
align-items: center;
justify-content: center;
}

#embargo-time-modal::backdrop {
background: rgba(0, 0, 0, 0.3);
}

.embargo-modal-content {
background: #fff;
padding: 2em;
border-radius: 8px;
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.2);
min-width: 300px;
}

.embargo-modal-title {
margin: 0 0 1em 0;
font-size: 1.19em;
font-weight: 700;
color: #111;
}

.embargo-modal-buttons {
display: flex;
gap: 0.5em;
margin-top: 1.5em;
}

.embargo-modal-content .embargo-modal-btn {
margin: 0.5em 0.5em 0 0;
font-size: 0.85em;
padding: 0.3em 1.1em;
height: 2.1em;
line-height: 1.2;
min-width: 0;
}

.embargo-modal-content .embargo-modal-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}

/* Fix flatpickr header clipping and reduce calendar size */
.flatpickr-calendar .flatpickr-months {
font-size: 0.95em;
min-height: 38px;
overflow: visible !important;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
gap: 2px;
}

.flatpickr-calendar .flatpickr-month {
padding: 0 0 8px;
overflow: visible;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}

.flatpickr-calendar .flatpickr-current-month {
font-size: 0.95em;
padding: 0 2px 2px;
overflow: visible;
position: static;
left: auto;
width: auto;
display: flex !important;
flex-direction: row !important;
align-items: center !important;
justify-content: center !important;
gap: 10px;
flex-wrap: nowrap !important;
white-space: nowrap;
}

.flatpickr-calendar .embargo-nav-row {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
white-space: nowrap;
}

.flatpickr-calendar .embargo-custom-nav {
display: grid;
align-items: center;
justify-content: stretch;
gap: 6px;
width: 100%;
margin-bottom: 8px;
white-space: nowrap;
}

.flatpickr-calendar .embargo-custom-nav-group {
display: inline-flex;
align-items: center;
gap: 4px;
flex: 0 0 auto;
}

.flatpickr-calendar .embargo-nav-btn {
border: 0;
background: transparent;
color: #111;
cursor: pointer;
font-size: 1em;
line-height: 1;
padding: 0 4px;
height: 1.8em;
display: inline-flex;
align-items: center;
justify-content: center;
}

.flatpickr-calendar .embargo-nav-btn:hover {
color: #0b57d0;
}

.flatpickr-calendar .embargo-nav-label {
min-width: 8em;
text-align: center;
line-height: 1.8em;
color: #111;
font-weight: 600;
}

.flatpickr-calendar .flatpickr-monthDropdown-months {
font-size: 0.95em;
padding: 0 2px;
margin: 0;
height: 1.8em;
}

.flatpickr-calendar .embargo-month-display {
min-width: 6.4em;
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
height: 1.8em;
text-align: center;
line-height: 1.8em;
}

.flatpickr-calendar .numInputWrapper {
overflow: visible;
padding-bottom: 8px;
}

.flatpickr-calendar .numInput.cur-year {
font-size: 0.95em;
width: 3.5em;
height: auto;
line-height: 1.2;
padding-bottom: 0;
}

.flatpickr-calendar .embargo-year-controls {
display: inline-flex !important;
align-items: center !important;
flex-wrap: nowrap !important;
flex: 0 0 auto;
gap: 3px;
margin: 0;
position: static;
}

.flatpickr-calendar .embargo-year-btn {
border: 1px solid #d9d9d9;
background: #fff;
border-radius: 4px;
width: 1.8em;
height: 1.8em;
line-height: 1;
padding: 0;
cursor: pointer;
}

.flatpickr-calendar .embargo-year-btn:hover {
background: #f5f5f5;
}

.flatpickr-calendar .embargo-year-display {
min-width: 2.6em;
display: flex;
align-items: center;
justify-content: center;
height: 1.8em;
text-align: center;
line-height: 1.8em;
}

.flatpickr-calendar .arrowUp,
.flatpickr-calendar .arrowDown {
height: 0.7em;
}

.flatpickr-calendar .flatpickr-prev-month,
.flatpickr-calendar .flatpickr-next-month {
display: none !important;
}

14 changes: 14 additions & 0 deletions cms/dashboard/static/css/vendor/flatpickr.min.css

Large diffs are not rendered by default.

Loading
Loading